diff --git a/plugins/org.jkiss.dbeaver.core/META-INF/MANIFEST.MF b/plugins/org.jkiss.dbeaver.core/META-INF/MANIFEST.MF index f43ddd6091320f0a77e2361e9c2540016b4d6cda..ac308f0fb9427da4bdb531e3cc6ab1e9aecea0f2 100644 --- a/plugins/org.jkiss.dbeaver.core/META-INF/MANIFEST.MF +++ b/plugins/org.jkiss.dbeaver.core/META-INF/MANIFEST.MF @@ -122,6 +122,7 @@ Require-Bundle: org.eclipse.core.runtime, org.jkiss.dbeaver.model;visibility:=reexport, org.jkiss.dbeaver.ui;visibility:=reexport, org.jkiss.dbeaver.ui.editors.base;visibility:=reexport, + org.jkiss.dbeaver.ui.editors.data;visibility:=reexport, org.jkiss.utils;visibility:=reexport, org.jkiss.dbeaver.data.transfer, org.jkiss.bundle.bouncycastle;resolution:=optional diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreMessages.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreMessages.java index c18d88764e301f5a446abccc37135318b4226942..71eb94e33d7787ad32d2319b51ebcb14fb88f550 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreMessages.java +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreMessages.java @@ -1375,9 +1375,6 @@ public class CoreMessages extends NLS { public static String toolbar_cmd_redo_name; //toolbar editor menu item - public static String grid_tooltip_sort_by_column; - public static String grid_tooltip_filter_by_column; - public static String CreateLinkHandler_e_create_link_message; public static String CreateLinkHandler_e_create_link_title; public static String CreateLinkHandler_e_create_link_validation; diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources.properties b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources.properties index 4b8d8d0f336048dc620c47ad3fddf065be7f17f6..534c8758d430dfcfd515e8ff924a0f49d8762e32 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources.properties +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources.properties @@ -1329,9 +1329,6 @@ toolbar_cmd_undo_name = Undo toolbar_cmd_redo_name = Redo ##toolbar editor menu item## -grid_tooltip_sort_by_column = Sort by column -grid_tooltip_filter_by_column = Filter by column value - CreateLinkHandler_e_create_link_message=Unable to create link CreateLinkHandler_e_create_link_title=Create link CreateLinkHandler_e_create_link_validation=Unable to create link inside {0} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_de.properties b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_de.properties index f20be3b681fcb1a07bf7a443e8591bccc03fde52..726635f964a8d54debd8a4a7eff865d81784e52c 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_de.properties +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_de.properties @@ -1475,10 +1475,6 @@ editors_sql_status_statement_executed_no_rows_updated = Anweisung ausgef\u00FChr editors_sql_staus_connected_to = Verbunden mit '' -grid_tooltip_filter_by_column = Filtern mit Spaltenwert - -grid_tooltip_sort_by_column = Sortieren mit Spalte - model_html_read_database_meta_data = Datenbank Metadaten lesen model_jdbc_bad_content_value_ = Schlechter Inhaltswert: diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_fr.properties b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_fr.properties index b1182c9aa4925b46c9fbd3c615876833f51c9eb2..cb6a9cc0bc2cb2b909d56478e1f7a9ddbaafed9c 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_fr.properties +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_fr.properties @@ -1825,6 +1825,3 @@ pref_page_error_handle_recover_retry_count_tip = Nombre de tentatives de reconne toolbar_cmd_undo_name = Annuler toolbar_cmd_redo_name = Refaire ##toolbar editor menu item## - -grid_tooltip_sort_by_column = Trier par colonne -grid_tooltip_filter_by_column = Filtrer par la valeur de colonne diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_zh.properties b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_zh.properties index 4f7dbc6d6125e5704dee24ae271037c30e2739ed..896a4dc5ea21c174f3bf820e63cd3efe42b13fd3 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_zh.properties +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources_zh.properties @@ -1478,10 +1478,6 @@ editors_sql_status_statement_executed_no_rows_updated = \u8BED\u53E5\u5DF2\u6267 editors_sql_staus_connected_to = \u8FDE\u63A5\u5230 '' -grid_tooltip_filter_by_column = \u6309\u5217\u503C\u8FC7\u6EE4 - -grid_tooltip_sort_by_column = \u6309\u5217\u8FC7\u6EE4 - model_html_read_database_meta_data = \u8BFB\u53D6\u6570\u636E\u5E93\u5143\u6570\u636E model_jdbc_bad_content_value_ = \u65E0\u6548\u5185\u5BB9: diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/resultset/panel/grouping/GroupingResultsDecorator.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/resultset/panel/grouping/GroupingResultsDecorator.java index f10d87d03dd0e1461c7b207f5f2127856895861d..6a264a04361ebaf72054a5986b14664b8e213458 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/resultset/panel/grouping/GroupingResultsDecorator.java +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/resultset/panel/grouping/GroupingResultsDecorator.java @@ -29,8 +29,6 @@ import org.jkiss.dbeaver.model.DBPEvaluationContext; import org.jkiss.dbeaver.model.data.DBDAttributeBinding; import org.jkiss.dbeaver.model.sql.SQLDialect; import org.jkiss.dbeaver.model.sql.SQLUtils; -import org.jkiss.dbeaver.model.struct.DBSDataContainer; -import org.jkiss.dbeaver.model.struct.DBSObject; import org.jkiss.dbeaver.runtime.ui.DBUserInterface; import org.jkiss.dbeaver.ui.controls.lightgrid.LightGrid; import org.jkiss.dbeaver.ui.controls.resultset.IResultSetDecorator; diff --git a/plugins/org.jkiss.dbeaver.ui.editors.data/META-INF/MANIFEST.MF b/plugins/org.jkiss.dbeaver.ui.editors.data/META-INF/MANIFEST.MF index 169f87d28ef2e9df25a23eeb4bcb74e32aede39c..b5811ff1dd16fbbbadcf60a0d478351bcbdacce4 100644 --- a/plugins/org.jkiss.dbeaver.ui.editors.data/META-INF/MANIFEST.MF +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/META-INF/MANIFEST.MF @@ -7,6 +7,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Release-Date: 20181224 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy +Bundle-Activator: org.jkiss.dbeaver.ui.editors.data.internal.DataEditorsActivator Require-Bundle: org.eclipse.core.runtime, org.eclipse.core.resources, org.eclipse.core.expressions, @@ -15,4 +16,6 @@ Require-Bundle: org.eclipse.core.runtime, org.jkiss.dbeaver.ui, org.jkiss.dbeaver.ui.editors.base Bundle-ClassPath: . -Export-Package: org.jkiss.dbeaver.ui.editors.data +Export-Package: org.jkiss.dbeaver.ui.controls.lightgrid, + org.jkiss.dbeaver.ui.controls.resultset, + org.jkiss.dbeaver.ui.editors.data diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/AbstractRenderer.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/AbstractRenderer.java similarity index 96% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/AbstractRenderer.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/AbstractRenderer.java index b4b4bbe17e43bf7929807c5e410154b06ab68d64..b5ba53275fad3afee1d1e795ce88dffdf694ae9d 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/AbstractRenderer.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/AbstractRenderer.java @@ -1,42 +1,42 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.swt.widgets.Display; - -/** - * Abstract grid renderer - */ -abstract class AbstractRenderer -{ - protected final LightGrid grid; - - protected AbstractRenderer(LightGrid grid) { - this.grid = grid; - } - - /** - * Sets the display for the renderer. - * - * @return Returns the display. - */ - public Display getDisplay() - { - return grid.getDisplay(); - } - -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.swt.widgets.Display; + +/** + * Abstract grid renderer + */ +abstract class AbstractRenderer +{ + protected final LightGrid grid; + + protected AbstractRenderer(LightGrid grid) { + this.grid = grid; + } + + /** + * Sets the display for the renderer. + * + * @return Returns the display. + */ + public Display getDisplay() + { + return grid.getDisplay(); + } + +} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCell.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCell.java similarity index 96% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCell.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCell.java index 122694712a5343237c065eb8ff967fc7cc37b64b..510486e4a6f64f064f9cbad5458e8cdef8db4fb6 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCell.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCell.java @@ -1,62 +1,62 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.jkiss.utils.CommonUtils; - -/** - * GridCell - */ -public class GridCell -{ - public Object col; - public Object row; - - public GridCell(Object col, Object row) - { - this.col = col; - this.row = row; - } - - public GridCell(GridCell copy) - { - this.col = copy.col; - this.row = copy.row; - } - - public boolean equals(Object object) - { - return object instanceof GridCell && equalsTo((GridCell) object); - } - - public boolean equalsTo(GridCell pos) - { - return CommonUtils.equalObjects(this.col, pos.col) && - CommonUtils.equalObjects(this.row, pos.row); - } - - public String toString() - { - return col + ":" + row; - } - - public int hashCode() - { - return col.hashCode() ^ row.hashCode(); - } -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.jkiss.utils.CommonUtils; + +/** + * GridCell + */ +public class GridCell +{ + public Object col; + public Object row; + + public GridCell(Object col, Object row) + { + this.col = col; + this.row = row; + } + + public GridCell(GridCell copy) + { + this.col = copy.col; + this.row = copy.row; + } + + public boolean equals(Object object) + { + return object instanceof GridCell && equalsTo((GridCell) object); + } + + public boolean equalsTo(GridCell pos) + { + return CommonUtils.equalObjects(this.col, pos.col) && + CommonUtils.equalObjects(this.row, pos.row); + } + + public String toString() + { + return col + ":" + row; + } + + public int hashCode() + { + return col.hashCode() ^ row.hashCode(); + } +} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCellRenderer.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCellRenderer.java similarity index 96% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCellRenderer.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCellRenderer.java index 0b1474458f317854f946822bd5f637ed2eb35f46..0374ea6a28578bd63507577d8d1ea89392c4a6e5 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCellRenderer.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridCellRenderer.java @@ -1,210 +1,208 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.*; -import org.jkiss.dbeaver.core.DBeaverUI; -import org.jkiss.dbeaver.model.DBPImage; -import org.jkiss.dbeaver.ui.DBeaverIcons; -import org.jkiss.dbeaver.ui.TextUtils; -import org.jkiss.dbeaver.ui.UIIcon; -import org.jkiss.dbeaver.ui.UIUtils; - -/** - * Grid cell renderer - */ -class GridCellRenderer extends AbstractRenderer -{ - private static final int LEFT_MARGIN = 6; - private static final int RIGHT_MARGIN = 6; - private static final int TOP_MARGIN = 0; - - private static final int TEXT_TOP_MARGIN = 1; - private static final int INSIDE_MARGIN = 3; - - static final Image LINK_IMAGE = DBeaverIcons.getImage(UIIcon.LINK); - static final Image LINK2_IMAGE = DBeaverIcons.getImage(UIIcon.LINK2); - static final Rectangle LINK_IMAGE_BOUNDS = new Rectangle(0, 0, 13, 13); - - protected Color colorLineFocused; - - public GridCellRenderer(LightGrid grid) - { - super(grid); - colorLineFocused = grid.getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND); - } - - public void paint(GC gc, Rectangle bounds, boolean selected, boolean focus, Object col, Object row) - { - boolean drawBackground = true; - - //if (grid.isEnabled()) { - Color back = grid.getCellBackground(col, row, selected); - - if (back != null) { - gc.setBackground(back); - } else { - drawBackground = false; - } - /*} else { - grid.setDefaultBackground(gc); - }*/ - gc.setForeground(grid.getCellForeground(col, row, selected)); - - if (drawBackground) { - gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height); - } - - String text = grid.getCellText(col, row); - final int state = grid.getContentProvider().getCellState(col, row, text); - int x = LEFT_MARGIN; - - Image image; - Rectangle imageBounds = null; - - if (isLinkState(state)) { - image = ((state & IGridContentProvider.STATE_LINK) != 0) ? LINK_IMAGE : LINK2_IMAGE; - imageBounds = LINK_IMAGE_BOUNDS; - } else { - DBPImage cellImage = grid.getCellImage(col, row); - if (cellImage != null) { - image = DBeaverIcons.getImage(cellImage); - imageBounds = image.getBounds(); - } else { - image = null; - } - } - - int columnAlign = grid.getContentProvider().getColumnAlign(col); - - if (image != null && columnAlign != IGridContentProvider.ALIGN_RIGHT) { - int y = bounds.y + (bounds.height - imageBounds.height) / 2; - gc.drawImage(image, bounds.x + x, y); - - x += imageBounds.width + INSIDE_MARGIN; - } - - int width = bounds.width - x - RIGHT_MARGIN; - - // Get cell text - if (text != null && !text.isEmpty()) { - // Get shortern version of string - text = TextUtils.getShortString(grid.fontMetrics, text, width); - // Replace linefeeds with space - text = TextUtils.getSingleLineString(text); - - gc.setFont(grid.normalFont); - - switch (columnAlign) { - // Center - case IGridContentProvider.ALIGN_CENTER: - break; - case IGridContentProvider.ALIGN_RIGHT: - // Right (numbers, datetimes) - int imageMargin = 0; - if (image != null) { - // Reduce bounds by link image size - imageMargin = imageBounds.width + INSIDE_MARGIN; - gc.setClipping(bounds.x, bounds.y, bounds.width - imageMargin, bounds.height); - } else { - gc.setClipping(bounds); - } - Point textSize = gc.textExtent(text); - gc.drawString( - text, - bounds.x + bounds.width - (textSize.x + RIGHT_MARGIN + imageMargin), - bounds.y + TEXT_TOP_MARGIN + TOP_MARGIN, - true); - gc.setClipping((Rectangle) null); - break; - default: - gc.drawString( - text, - bounds.x + x, - bounds.y + TEXT_TOP_MARGIN + TOP_MARGIN, - true); - break; - } - } - - if (image != null && columnAlign == IGridContentProvider.ALIGN_RIGHT) { - int y = bounds.y + (bounds.height - imageBounds.height) / 2; - gc.drawImage(image, bounds.x + bounds.width - imageBounds.width - RIGHT_MARGIN, y); - } - - if (grid.isLinesVisible()) { - if (selected) { - gc.setForeground(grid.getLineSelectedColor()); - } else { - gc.setForeground(grid.getLineColor()); - } - gc.drawLine( - bounds.x, - bounds.y + bounds.height, - bounds.x + bounds.width, - bounds.y + bounds.height); - gc.drawLine( - bounds.x + bounds.width - 1, - bounds.y, - bounds.x + bounds.width - 1, - bounds.y + bounds.height); - } - - if (focus) { - - gc.setForeground(colorLineFocused); - gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height); - - if (grid.isFocusControl()) { - gc.drawRectangle(bounds.x + 1, bounds.y + 1, bounds.width - 3, bounds.height - 2); - } - } - } - - public boolean isOverLink(GridColumn column, int row, int x, int y) { - int state = grid.getContentProvider().getCellState(column.getElement(), grid.getRowElement(row), null); - - if (isLinkState(state)) { - Point origin = grid.getOrigin(column, row); - int verMargin = (grid.getItemHeight() - LINK_IMAGE_BOUNDS.height) / 2; - int columnAlign = grid.getContentProvider().getColumnAlign(column.getElement()); - - if (columnAlign != IGridContentProvider.ALIGN_RIGHT) { - if (x >= origin.x + LEFT_MARGIN && x <= origin.x + LEFT_MARGIN + LINK_IMAGE_BOUNDS.width && - y >= origin.y + verMargin && y <= origin.y + verMargin + LINK_IMAGE_BOUNDS.height) { - return true; - } - } else { - int width = column.getWidth(); - if (x >= origin.x + width - (LEFT_MARGIN + LINK_IMAGE_BOUNDS.width) && x <= origin.x + width - RIGHT_MARGIN && - y >= origin.y + verMargin && y <= origin.y + verMargin + LINK_IMAGE_BOUNDS.height) { - return true; - } - } - - } - return false; - } - - public static boolean isLinkState(int state) { - return - (state & IGridContentProvider.STATE_LINK) != 0 || - (state & IGridContentProvider.STATE_HYPER_LINK) != 0; - } -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.*; +import org.jkiss.dbeaver.model.DBPImage; +import org.jkiss.dbeaver.ui.DBeaverIcons; +import org.jkiss.dbeaver.ui.TextUtils; +import org.jkiss.dbeaver.ui.UIIcon; + +/** + * Grid cell renderer + */ +class GridCellRenderer extends AbstractRenderer +{ + private static final int LEFT_MARGIN = 6; + private static final int RIGHT_MARGIN = 6; + private static final int TOP_MARGIN = 0; + + private static final int TEXT_TOP_MARGIN = 1; + private static final int INSIDE_MARGIN = 3; + + static final Image LINK_IMAGE = DBeaverIcons.getImage(UIIcon.LINK); + static final Image LINK2_IMAGE = DBeaverIcons.getImage(UIIcon.LINK2); + static final Rectangle LINK_IMAGE_BOUNDS = new Rectangle(0, 0, 13, 13); + + protected Color colorLineFocused; + + public GridCellRenderer(LightGrid grid) + { + super(grid); + colorLineFocused = grid.getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND); + } + + public void paint(GC gc, Rectangle bounds, boolean selected, boolean focus, Object col, Object row) + { + boolean drawBackground = true; + + //if (grid.isEnabled()) { + Color back = grid.getCellBackground(col, row, selected); + + if (back != null) { + gc.setBackground(back); + } else { + drawBackground = false; + } + /*} else { + grid.setDefaultBackground(gc); + }*/ + gc.setForeground(grid.getCellForeground(col, row, selected)); + + if (drawBackground) { + gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height); + } + + String text = grid.getCellText(col, row); + final int state = grid.getContentProvider().getCellState(col, row, text); + int x = LEFT_MARGIN; + + Image image; + Rectangle imageBounds = null; + + if (isLinkState(state)) { + image = ((state & IGridContentProvider.STATE_LINK) != 0) ? LINK_IMAGE : LINK2_IMAGE; + imageBounds = LINK_IMAGE_BOUNDS; + } else { + DBPImage cellImage = grid.getCellImage(col, row); + if (cellImage != null) { + image = DBeaverIcons.getImage(cellImage); + imageBounds = image.getBounds(); + } else { + image = null; + } + } + + int columnAlign = grid.getContentProvider().getColumnAlign(col); + + if (image != null && columnAlign != IGridContentProvider.ALIGN_RIGHT) { + int y = bounds.y + (bounds.height - imageBounds.height) / 2; + gc.drawImage(image, bounds.x + x, y); + + x += imageBounds.width + INSIDE_MARGIN; + } + + int width = bounds.width - x - RIGHT_MARGIN; + + // Get cell text + if (text != null && !text.isEmpty()) { + // Get shortern version of string + text = TextUtils.getShortString(grid.fontMetrics, text, width); + // Replace linefeeds with space + text = TextUtils.getSingleLineString(text); + + gc.setFont(grid.normalFont); + + switch (columnAlign) { + // Center + case IGridContentProvider.ALIGN_CENTER: + break; + case IGridContentProvider.ALIGN_RIGHT: + // Right (numbers, datetimes) + int imageMargin = 0; + if (image != null) { + // Reduce bounds by link image size + imageMargin = imageBounds.width + INSIDE_MARGIN; + gc.setClipping(bounds.x, bounds.y, bounds.width - imageMargin, bounds.height); + } else { + gc.setClipping(bounds); + } + Point textSize = gc.textExtent(text); + gc.drawString( + text, + bounds.x + bounds.width - (textSize.x + RIGHT_MARGIN + imageMargin), + bounds.y + TEXT_TOP_MARGIN + TOP_MARGIN, + true); + gc.setClipping((Rectangle) null); + break; + default: + gc.drawString( + text, + bounds.x + x, + bounds.y + TEXT_TOP_MARGIN + TOP_MARGIN, + true); + break; + } + } + + if (image != null && columnAlign == IGridContentProvider.ALIGN_RIGHT) { + int y = bounds.y + (bounds.height - imageBounds.height) / 2; + gc.drawImage(image, bounds.x + bounds.width - imageBounds.width - RIGHT_MARGIN, y); + } + + if (grid.isLinesVisible()) { + if (selected) { + gc.setForeground(grid.getLineSelectedColor()); + } else { + gc.setForeground(grid.getLineColor()); + } + gc.drawLine( + bounds.x, + bounds.y + bounds.height, + bounds.x + bounds.width, + bounds.y + bounds.height); + gc.drawLine( + bounds.x + bounds.width - 1, + bounds.y, + bounds.x + bounds.width - 1, + bounds.y + bounds.height); + } + + if (focus) { + + gc.setForeground(colorLineFocused); + gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height); + + if (grid.isFocusControl()) { + gc.drawRectangle(bounds.x + 1, bounds.y + 1, bounds.width - 3, bounds.height - 2); + } + } + } + + public boolean isOverLink(GridColumn column, int row, int x, int y) { + int state = grid.getContentProvider().getCellState(column.getElement(), grid.getRowElement(row), null); + + if (isLinkState(state)) { + Point origin = grid.getOrigin(column, row); + int verMargin = (grid.getItemHeight() - LINK_IMAGE_BOUNDS.height) / 2; + int columnAlign = grid.getContentProvider().getColumnAlign(column.getElement()); + + if (columnAlign != IGridContentProvider.ALIGN_RIGHT) { + if (x >= origin.x + LEFT_MARGIN && x <= origin.x + LEFT_MARGIN + LINK_IMAGE_BOUNDS.width && + y >= origin.y + verMargin && y <= origin.y + verMargin + LINK_IMAGE_BOUNDS.height) { + return true; + } + } else { + int width = column.getWidth(); + if (x >= origin.x + width - (LEFT_MARGIN + LINK_IMAGE_BOUNDS.width) && x <= origin.x + width - RIGHT_MARGIN && + y >= origin.y + verMargin && y <= origin.y + verMargin + LINK_IMAGE_BOUNDS.height) { + return true; + } + } + + } + return false; + } + + public static boolean isLinkState(int state) { + return + (state & IGridContentProvider.STATE_LINK) != 0 || + (state & IGridContentProvider.STATE_HYPER_LINK) != 0; + } +} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumn.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumn.java similarity index 96% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumn.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumn.java index a02a85188bed9a144925fe0c97edfc8098e27053..3cbd15f3d97f8030515ef88672e2deb70b7d04eb 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumn.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumn.java @@ -1,389 +1,389 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.jkiss.code.Nullable; -import org.jkiss.dbeaver.model.DBPImage; -import org.jkiss.dbeaver.ui.DBeaverIcons; -import org.jkiss.utils.CommonUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * Grid column info. - * Holds information about column width and other UI properties - * - * @author serge@jkiss.org - */ -class GridColumn { - - /** - * Default width of the column. - */ - private static final int DEFAULT_WIDTH = 10; - - private static final int topMargin = 6; - private static final int bottomMargin = 6; - private static final int leftMargin = 6; - private static final int rightMargin = 6; - private static final int imageSpacing = 3; - private static final int insideMargin = 3; - - private final LightGrid grid; - private final Object element; - private final GridColumn parent; - private List children; - - private int level; - private int width = DEFAULT_WIDTH; - private int height = -1; - - public GridColumn(LightGrid grid, Object element) { - this.grid = grid; - this.element = element; - this.parent = null; - this.level = 0; - grid.newColumn(this, -1); - } - - public GridColumn(GridColumn parent, Object element) { - this.grid = parent.grid; - this.element = element; - this.parent = parent; - this.level = parent.level + 1; - parent.addChild(this); - grid.newColumn(this, -1); - } - - public Object getElement() { - return element; - } - - public int getIndex() - { - return grid.indexOf(this); - } - - /** - * Returns the width of the column. - * - * @return width of column - */ - public int getWidth() { - return width; - } - - /** - * Sets the width of the column. - * - * @param width - * new width - */ - public void setWidth(int width) { - setWidth(width, true); - } - - void setWidth(int width, boolean redraw) { - int delta = width - this.width; - this.width = width; - if (parent != null) { - parent.width += delta; - } - if (redraw) { - grid.setScrollValuesObsolete(); - grid.redraw(); - } - } - - public boolean isOverFilterButton(int x, int y) { - if (!isFilterable()) { - return false; - } - Rectangle bounds = getBounds(); - if (y < bounds.y || y > bounds.y + bounds.height) { - return false; - } - Rectangle sortBounds = isSortable() ? GridColumnRenderer.getSortControlBounds() : null; - Rectangle filterBounds = GridColumnRenderer.getFilterControlBounds(); - - int filterEnd = bounds.width - (sortBounds == null ? GridColumnRenderer.ARROW_MARGIN : sortBounds.width + GridColumnRenderer.IMAGE_SPACING); - int filterBegin = filterEnd - filterBounds.width; - - return - x >= filterBegin && x <= filterEnd && - y < bounds.y + (filterBounds == null ? 0 : filterBounds.height) + GridColumnRenderer.TOP_MARGIN; - } - - public boolean isOverSortArrow(int x, int y) - { - if (!isSortable()) { - return false; - } - Rectangle bounds = getBounds(); - if (y < bounds.y || y > bounds.y + bounds.height) { - return false; - } - int arrowEnd = bounds.width - rightMargin + GridColumnRenderer.IMAGE_SPACING; - Rectangle sortBounds = GridColumnRenderer.getSortControlBounds(); - int arrowBegin = arrowEnd - sortBounds.width; - return - x >= arrowBegin && x <= arrowEnd && - y <= bounds.y + sortBounds.height + GridColumnRenderer.TOP_MARGIN; - } - - public boolean isOverIcon(int x, int y) { - Rectangle bounds = getBounds(); - if (y < bounds.y || y > bounds.y + bounds.height) { - return false; - } - Image image = grid.getLabelProvider().getImage(element); - if (image == null) { - return false; - } - Rectangle imgBounds = image.getBounds(); - if (x >= bounds.x + GridColumnRenderer.LEFT_MARGIN && - x <= bounds.x + GridColumnRenderer.LEFT_MARGIN + imgBounds.width + GridColumnRenderer.IMAGE_SPACING && - y > bounds.y + GridColumnRenderer.TOP_MARGIN && - y <= bounds.y + GridColumnRenderer.TOP_MARGIN + imgBounds.height) - { - return true; - } - return false; - } - - int getHeaderHeight(boolean includeChildren, boolean forceRefresh) - { - if (forceRefresh) { - height = -1; - } - if (height < 0) { - height = topMargin + grid.fontMetrics.getHeight() + bottomMargin; - Image image = grid.getLabelProvider().getImage(element); - if (image != null) { - height = Math.max(height, topMargin + image.getBounds().height + bottomMargin); - } - final String description = grid.getLabelProvider().getDescription(element); - if (!CommonUtils.isEmpty(description)) { - height += topMargin + grid.fontMetrics.getHeight(); - } - } - int childHeight = 0; - if (includeChildren && !CommonUtils.isEmpty(children)) { - for (GridColumn child : children) { - childHeight = Math.max(childHeight, child.getHeaderHeight(true, false)); - } - } - return height + childHeight; - } - - int computeHeaderWidth() - { - int x = leftMargin; - final IGridLabelProvider labelProvider = grid.getLabelProvider(); - Image image = labelProvider.getImage(element); - if (image != null) { - x += image.getBounds().width + imageSpacing; - } - { - int textWidth; - if (Boolean.TRUE.equals(labelProvider.getGridOption(IGridLabelProvider.OPTION_EXCLUDE_COLUMN_NAME_FOR_WIDTH_CALC))) { - textWidth = grid.sizingGC.stringExtent("X").x; - } else { - String text = labelProvider.getText(element); - String description = labelProvider.getDescription(element); - textWidth = grid.sizingGC.stringExtent(text).x; - if (!CommonUtils.isEmpty(description)) { - int descWidth = grid.sizingGC.stringExtent(description).x; - if (descWidth > textWidth) { - textWidth = descWidth; - } - } - } - x += textWidth + rightMargin; - } - if (isSortable()) { - x += rightMargin + GridColumnRenderer.getSortControlBounds().width + GridColumnRenderer.IMAGE_SPACING; - } - - x+= GridColumnRenderer.getFilterControlBounds().width; - - if (!CommonUtils.isEmpty(children)) { - int childWidth = 0; - for (GridColumn child : children) { - childWidth += child.computeHeaderWidth(); - } - return Math.max(x, childWidth); - } - - return x; - } - - public boolean isSortable() - { - return grid.getContentProvider().getSortOrder(element) != SWT.NONE; - } - - public boolean isFilterable() - { - return grid.getContentProvider().isElementSupportsFilter(element); - } - - /** - * Causes the receiver to be resized to its preferred size. - * - */ - void pack(boolean reflect) { - int newWidth = computeHeaderWidth(); - if (CommonUtils.isEmpty(children)) { - // Calculate width of visible cells - int topIndex = grid.getTopIndex(); - int bottomIndex = grid.getBottomIndex(); - if (topIndex >= 0 && bottomIndex >= topIndex) { - int itemCount = grid.getItemCount(); - for (int i = topIndex; i <= bottomIndex && i < itemCount; i++) { - newWidth = Math.max(newWidth, computeCellWidth(element, grid.getRowElement(i))); - } - } - } else { - int childrenWidth = 0; - for (GridColumn child : children) { - child.pack(reflect); - childrenWidth += child.getWidth(); - } - if (newWidth > childrenWidth) { - // Header width bigger than children width - GridColumn lastChild = children.get(children.size() - 1); - lastChild.setWidth(lastChild.getWidth() + newWidth - childrenWidth); - } else { - newWidth = childrenWidth; - } - } - if (reflect) { - setWidth(newWidth, false); - } else { - this.width = newWidth; - } - } - - private int computeCellWidth(Object col, Object row) { - int x = 0; - - x += leftMargin; - - String cellText = grid.getCellText(col, row); - int state = grid.getContentProvider().getCellState(col, row, cellText); - Rectangle imageBounds; - if (GridCellRenderer.isLinkState(state)) { - imageBounds = GridCellRenderer.LINK_IMAGE_BOUNDS; - } else { - DBPImage image = grid.getContentProvider().getCellImage(col, row); - imageBounds = image == null ? null : DBeaverIcons.getImage(image).getBounds(); - } - if (imageBounds != null) { - x += imageBounds.width + insideMargin; - } - - x += grid.sizingGC.textExtent(cellText).x + rightMargin; - return x; - } - - /** - * Returns the bounds of this column's header. - * - * @return bounds of the column header - */ - Rectangle getBounds() { - Rectangle bounds = new Rectangle(0, 0, 0, 0); - - Point loc = grid.getOrigin(this, -1); - bounds.x = loc.x; - bounds.y = loc.y; - bounds.width = getWidth(); - bounds.height = grid.getHeaderHeight(); - - return bounds; - } - - /** - * Returns the parent grid. - * - * @return the parent grid. - */ - public LightGrid getGrid() { - return grid; - } - - /** - * Returns the tooltip of the column header. - * - * @return the tooltip text - */ - @Nullable - public String getHeaderTooltip() { - String tip = grid.getLabelProvider().getToolTipText(element); - if (tip == null) { - tip = grid.getLabelProvider().getText(element); - } - return tip; - } - - public GridColumn getParent() { - return parent; - } - - public List getChildren() { - return children; - } - - private void addChild(GridColumn gridColumn) { - if (children == null) { - children = new ArrayList<>(); - } - children.add(gridColumn); - } - - private void removeChild(GridColumn column) { - children.remove(column); - } - - public int getLevel() { - return level; - } - - public boolean isParent(GridColumn col) { - for (GridColumn p = parent; p != null; p = p.parent) { - if (p == col) { - return true; - } - } - return false; - } - - public GridColumn getFirstLeaf() { - if (children == null) { - return this; - } else { - return children.get(0).getFirstLeaf(); - } - } - +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.model.DBPImage; +import org.jkiss.dbeaver.ui.DBeaverIcons; +import org.jkiss.utils.CommonUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Grid column info. + * Holds information about column width and other UI properties + * + * @author serge@jkiss.org + */ +class GridColumn { + + /** + * Default width of the column. + */ + private static final int DEFAULT_WIDTH = 10; + + private static final int topMargin = 6; + private static final int bottomMargin = 6; + private static final int leftMargin = 6; + private static final int rightMargin = 6; + private static final int imageSpacing = 3; + private static final int insideMargin = 3; + + private final LightGrid grid; + private final Object element; + private final GridColumn parent; + private List children; + + private int level; + private int width = DEFAULT_WIDTH; + private int height = -1; + + public GridColumn(LightGrid grid, Object element) { + this.grid = grid; + this.element = element; + this.parent = null; + this.level = 0; + grid.newColumn(this, -1); + } + + public GridColumn(GridColumn parent, Object element) { + this.grid = parent.grid; + this.element = element; + this.parent = parent; + this.level = parent.level + 1; + parent.addChild(this); + grid.newColumn(this, -1); + } + + public Object getElement() { + return element; + } + + public int getIndex() + { + return grid.indexOf(this); + } + + /** + * Returns the width of the column. + * + * @return width of column + */ + public int getWidth() { + return width; + } + + /** + * Sets the width of the column. + * + * @param width + * new width + */ + public void setWidth(int width) { + setWidth(width, true); + } + + void setWidth(int width, boolean redraw) { + int delta = width - this.width; + this.width = width; + if (parent != null) { + parent.width += delta; + } + if (redraw) { + grid.setScrollValuesObsolete(); + grid.redraw(); + } + } + + public boolean isOverFilterButton(int x, int y) { + if (!isFilterable()) { + return false; + } + Rectangle bounds = getBounds(); + if (y < bounds.y || y > bounds.y + bounds.height) { + return false; + } + Rectangle sortBounds = isSortable() ? GridColumnRenderer.getSortControlBounds() : null; + Rectangle filterBounds = GridColumnRenderer.getFilterControlBounds(); + + int filterEnd = bounds.width - (sortBounds == null ? GridColumnRenderer.ARROW_MARGIN : sortBounds.width + GridColumnRenderer.IMAGE_SPACING); + int filterBegin = filterEnd - filterBounds.width; + + return + x >= filterBegin && x <= filterEnd && + y < bounds.y + (filterBounds == null ? 0 : filterBounds.height) + GridColumnRenderer.TOP_MARGIN; + } + + public boolean isOverSortArrow(int x, int y) + { + if (!isSortable()) { + return false; + } + Rectangle bounds = getBounds(); + if (y < bounds.y || y > bounds.y + bounds.height) { + return false; + } + int arrowEnd = bounds.width - rightMargin + GridColumnRenderer.IMAGE_SPACING; + Rectangle sortBounds = GridColumnRenderer.getSortControlBounds(); + int arrowBegin = arrowEnd - sortBounds.width; + return + x >= arrowBegin && x <= arrowEnd && + y <= bounds.y + sortBounds.height + GridColumnRenderer.TOP_MARGIN; + } + + public boolean isOverIcon(int x, int y) { + Rectangle bounds = getBounds(); + if (y < bounds.y || y > bounds.y + bounds.height) { + return false; + } + Image image = grid.getLabelProvider().getImage(element); + if (image == null) { + return false; + } + Rectangle imgBounds = image.getBounds(); + if (x >= bounds.x + GridColumnRenderer.LEFT_MARGIN && + x <= bounds.x + GridColumnRenderer.LEFT_MARGIN + imgBounds.width + GridColumnRenderer.IMAGE_SPACING && + y > bounds.y + GridColumnRenderer.TOP_MARGIN && + y <= bounds.y + GridColumnRenderer.TOP_MARGIN + imgBounds.height) + { + return true; + } + return false; + } + + int getHeaderHeight(boolean includeChildren, boolean forceRefresh) + { + if (forceRefresh) { + height = -1; + } + if (height < 0) { + height = topMargin + grid.fontMetrics.getHeight() + bottomMargin; + Image image = grid.getLabelProvider().getImage(element); + if (image != null) { + height = Math.max(height, topMargin + image.getBounds().height + bottomMargin); + } + final String description = grid.getLabelProvider().getDescription(element); + if (!CommonUtils.isEmpty(description)) { + height += topMargin + grid.fontMetrics.getHeight(); + } + } + int childHeight = 0; + if (includeChildren && !CommonUtils.isEmpty(children)) { + for (GridColumn child : children) { + childHeight = Math.max(childHeight, child.getHeaderHeight(true, false)); + } + } + return height + childHeight; + } + + int computeHeaderWidth() + { + int x = leftMargin; + final IGridLabelProvider labelProvider = grid.getLabelProvider(); + Image image = labelProvider.getImage(element); + if (image != null) { + x += image.getBounds().width + imageSpacing; + } + { + int textWidth; + if (Boolean.TRUE.equals(labelProvider.getGridOption(IGridLabelProvider.OPTION_EXCLUDE_COLUMN_NAME_FOR_WIDTH_CALC))) { + textWidth = grid.sizingGC.stringExtent("X").x; + } else { + String text = labelProvider.getText(element); + String description = labelProvider.getDescription(element); + textWidth = grid.sizingGC.stringExtent(text).x; + if (!CommonUtils.isEmpty(description)) { + int descWidth = grid.sizingGC.stringExtent(description).x; + if (descWidth > textWidth) { + textWidth = descWidth; + } + } + } + x += textWidth + rightMargin; + } + if (isSortable()) { + x += rightMargin + GridColumnRenderer.getSortControlBounds().width + GridColumnRenderer.IMAGE_SPACING; + } + + x+= GridColumnRenderer.getFilterControlBounds().width; + + if (!CommonUtils.isEmpty(children)) { + int childWidth = 0; + for (GridColumn child : children) { + childWidth += child.computeHeaderWidth(); + } + return Math.max(x, childWidth); + } + + return x; + } + + public boolean isSortable() + { + return grid.getContentProvider().getSortOrder(element) != SWT.NONE; + } + + public boolean isFilterable() + { + return grid.getContentProvider().isElementSupportsFilter(element); + } + + /** + * Causes the receiver to be resized to its preferred size. + * + */ + void pack(boolean reflect) { + int newWidth = computeHeaderWidth(); + if (CommonUtils.isEmpty(children)) { + // Calculate width of visible cells + int topIndex = grid.getTopIndex(); + int bottomIndex = grid.getBottomIndex(); + if (topIndex >= 0 && bottomIndex >= topIndex) { + int itemCount = grid.getItemCount(); + for (int i = topIndex; i <= bottomIndex && i < itemCount; i++) { + newWidth = Math.max(newWidth, computeCellWidth(element, grid.getRowElement(i))); + } + } + } else { + int childrenWidth = 0; + for (GridColumn child : children) { + child.pack(reflect); + childrenWidth += child.getWidth(); + } + if (newWidth > childrenWidth) { + // Header width bigger than children width + GridColumn lastChild = children.get(children.size() - 1); + lastChild.setWidth(lastChild.getWidth() + newWidth - childrenWidth); + } else { + newWidth = childrenWidth; + } + } + if (reflect) { + setWidth(newWidth, false); + } else { + this.width = newWidth; + } + } + + private int computeCellWidth(Object col, Object row) { + int x = 0; + + x += leftMargin; + + String cellText = grid.getCellText(col, row); + int state = grid.getContentProvider().getCellState(col, row, cellText); + Rectangle imageBounds; + if (GridCellRenderer.isLinkState(state)) { + imageBounds = GridCellRenderer.LINK_IMAGE_BOUNDS; + } else { + DBPImage image = grid.getContentProvider().getCellImage(col, row); + imageBounds = image == null ? null : DBeaverIcons.getImage(image).getBounds(); + } + if (imageBounds != null) { + x += imageBounds.width + insideMargin; + } + + x += grid.sizingGC.textExtent(cellText).x + rightMargin; + return x; + } + + /** + * Returns the bounds of this column's header. + * + * @return bounds of the column header + */ + Rectangle getBounds() { + Rectangle bounds = new Rectangle(0, 0, 0, 0); + + Point loc = grid.getOrigin(this, -1); + bounds.x = loc.x; + bounds.y = loc.y; + bounds.width = getWidth(); + bounds.height = grid.getHeaderHeight(); + + return bounds; + } + + /** + * Returns the parent grid. + * + * @return the parent grid. + */ + public LightGrid getGrid() { + return grid; + } + + /** + * Returns the tooltip of the column header. + * + * @return the tooltip text + */ + @Nullable + public String getHeaderTooltip() { + String tip = grid.getLabelProvider().getToolTipText(element); + if (tip == null) { + tip = grid.getLabelProvider().getText(element); + } + return tip; + } + + public GridColumn getParent() { + return parent; + } + + public List getChildren() { + return children; + } + + private void addChild(GridColumn gridColumn) { + if (children == null) { + children = new ArrayList<>(); + } + children.add(gridColumn); + } + + private void removeChild(GridColumn column) { + children.remove(column); + } + + public int getLevel() { + return level; + } + + public boolean isParent(GridColumn col) { + for (GridColumn p = parent; p != null; p = p.parent) { + if (p == col) { + return true; + } + } + return false; + } + + public GridColumn getFirstLeaf() { + if (children == null) { + return this; + } else { + return children.get(0).getFirstLeaf(); + } + } + } \ No newline at end of file diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumnRenderer.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumnRenderer.java similarity index 97% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumnRenderer.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumnRenderer.java index 97d45f7bc28fbd96c60fb62ebfe57ff07783c566..78dec9ca9c3aa6b7c0fb8b736b841c6611b105c6 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumnRenderer.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridColumnRenderer.java @@ -1,263 +1,263 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Rectangle; -import org.jkiss.code.Nullable; -import org.jkiss.dbeaver.ui.DBeaverIcons; -import org.jkiss.dbeaver.ui.TextUtils; -import org.jkiss.dbeaver.ui.UIIcon; -import org.jkiss.utils.CommonUtils; - -/** - * Grid column renderer - */ -class GridColumnRenderer extends AbstractRenderer -{ - public static final int LEFT_MARGIN = 6; - public static final int RIGHT_MARGIN = 6; - public static final int BOTTOM_MARGIN = 6; - public static final int TOP_MARGIN = 6; - public static final int ARROW_MARGIN = 6; - public static final int IMAGE_SPACING = 3; - - public static final Image IMAGE_ASTERISK = DBeaverIcons.getImage(UIIcon.SORT_UNKNOWN); - public static final Image IMAGE_DESC = DBeaverIcons.getImage(UIIcon.SORT_DECREASE); - public static final Image IMAGE_ASC = DBeaverIcons.getImage(UIIcon.SORT_INCREASE); - public static final Image IMAGE_FILTER = DBeaverIcons.getImage(UIIcon.FILTER_SMALL); - - public static final int SORT_WIDTH = IMAGE_DESC.getBounds().width; - public static final int FILTER_WIDTH = IMAGE_FILTER.getBounds().width; - - - - public GridColumnRenderer(LightGrid grid) { - super(grid); - } - - public static Rectangle getSortControlBounds() { - return IMAGE_DESC.getBounds(); - } - - public static Rectangle getFilterControlBounds() { - return IMAGE_FILTER.getBounds(); - } - - @Nullable - protected Image getColumnImage(Object element) { - return grid.getLabelProvider().getImage(element); - } - - protected String getColumnText(Object element) - { - return grid.getLabelProvider().getText(element); - } - - protected String getColumnDescription(Object element) - { - return grid.getLabelProvider().getDescription(element); - } - - protected Font getColumnFont(Object element) { - Font font = grid.getLabelProvider().getFont(element); - return font != null ? font : grid.normalFont; - } - - public void paint(GC gc, Rectangle bounds, boolean selected, boolean hovering, Object element) { - - boolean hasFilters = grid.getContentProvider().isElementSupportsFilter(element); - - //GridColumn col = grid.getColumnByElement(cell.col); - //AbstractRenderer arrowRenderer = col.getSortRenderer(); - int sortOrder = grid.getContentProvider().getSortOrder(element); - final Rectangle sortBounds = getSortControlBounds(); - final Rectangle filterBounds = getFilterControlBounds(); - - // set the font to be used to display the text. - gc.setFont(getColumnFont(element)); - - boolean flat = true; - - boolean drawSelected = false; - - if (flat && (selected || hovering)) { - gc.setBackground(grid.getContentProvider().getCellHeaderSelectionBackground(element)); - } else { - gc.setBackground(grid.getContentProvider().getCellHeaderBackground(element)); - } - gc.setForeground(grid.getContentProvider().getCellHeaderForeground(element)); - - gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height); - - int pushedDrawingOffset = 0; - if (drawSelected) { - pushedDrawingOffset = 1; - } - - int x = LEFT_MARGIN; - - Image columnImage = getColumnImage(element); - if (columnImage != null) { - int y = bounds.y + pushedDrawingOffset + TOP_MARGIN; - - gc.drawImage(columnImage, bounds.x + x + pushedDrawingOffset, y); - x += columnImage.getBounds().width + IMAGE_SPACING; - } - - int width = bounds.width - x; - - if (sortOrder == SWT.NONE) { - width -= RIGHT_MARGIN; - } else { - width -= ARROW_MARGIN + sortBounds.width; - } - if (hasFilters) { - width -= filterBounds.width; - } - //gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND)); - - int y = bounds.y + TOP_MARGIN; - - { - // Column name - String text = getColumnText(element); - text = TextUtils.getShortString(grid.fontMetrics, text, width); - gc.setFont(grid.normalFont); - gc.drawString(text, bounds.x + x + pushedDrawingOffset, y + pushedDrawingOffset, true); - } - - if (sortOrder != SWT.NONE) { - if (drawSelected) { - sortBounds.x = bounds.x + bounds.width - ARROW_MARGIN - sortBounds.width + 1; - sortBounds.y = y; - } else { - sortBounds.x = bounds.x + bounds.width - ARROW_MARGIN - sortBounds.width; - sortBounds.y = y; - } - sortBounds.x += IMAGE_SPACING; - paintSort(gc, sortBounds, sortOrder); - } - - if (hasFilters) { - gc.drawImage(IMAGE_FILTER, - bounds.x + bounds.width - filterBounds.width - - (sortOrder != SWT.NONE ? IMAGE_SPACING + sortBounds.width + 1 : ARROW_MARGIN), - y); - } - - { - // Draw column description - String text = getColumnDescription(element); - if (!CommonUtils.isEmpty(text)) { - y += TOP_MARGIN + grid.fontMetrics.getHeight(); - text = TextUtils.getShortString(grid.fontMetrics, text, width); - gc.setFont(grid.normalFont); - gc.drawString(text, bounds.x + x + pushedDrawingOffset, y + pushedDrawingOffset, true); - } - } - - // Draw border - if (!flat) { - - if (drawSelected) { - gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); - } else { - gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW)); - } - - gc.drawLine(bounds.x, bounds.y, bounds.x + bounds.width - 1, - bounds.y); - gc.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height - - 1); - - if (!drawSelected) { - gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW)); - gc.drawLine(bounds.x + 1, bounds.y + 1, - bounds.x + bounds.width - 2, bounds.y + 1); - gc.drawLine(bounds.x + 1, bounds.y + 1, bounds.x + 1, - bounds.y + bounds.height - 2); - } - - if (drawSelected) { - gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); - } else { - gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW)); - } - gc.drawLine(bounds.x + bounds.width - 1, bounds.y, bounds.x - + bounds.width - 1, - bounds.y + bounds.height - 1); - gc.drawLine(bounds.x, bounds.y + bounds.height - 1, bounds.x - + bounds.width - 1, - bounds.y + bounds.height - 1); - - if (!drawSelected) { - gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); - gc.drawLine(bounds.x + bounds.width - 2, bounds.y + 1, - bounds.x + bounds.width - 2, bounds.y + bounds.height - - 2); - gc.drawLine(bounds.x + 1, bounds.y + bounds.height - 2, - bounds.x + bounds.width - 2, bounds.y + bounds.height - - 2); - } - - } else { - gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW)); - - gc.drawLine(bounds.x + bounds.width - 1, bounds.y, bounds.x - + bounds.width - 1, - bounds.y + bounds.height - 1); - gc.drawLine(bounds.x, bounds.y + bounds.height - 1, bounds.x - + bounds.width - 1, - bounds.y + bounds.height - 1); - } - - gc.setFont(grid.normalFont); - } - - public static void paintSort(GC gc, Rectangle bounds, int sort) - { - switch (sort) { - case SWT.DEFAULT: - gc.drawImage(IMAGE_ASTERISK, bounds.x, bounds.y); - break; - case SWT.UP: - gc.drawImage(IMAGE_ASC, bounds.x, bounds.y); - break; - case SWT.DOWN: - gc.drawImage(IMAGE_DESC, bounds.x, bounds.y); - break; - } -/* - if (isSelected()) { - gc.drawLine(bounds.x, bounds.y, bounds.x + 6, bounds.y); - gc.drawLine(bounds.x + 1, bounds.y + 1, bounds.x + 5, bounds.y + 1); - gc.drawLine(bounds.x + 2, bounds.y + 2, bounds.x + 4, bounds.y + 2); - gc.drawPoint(bounds.x + 3, bounds.y + 3); - } else { - gc.drawPoint(bounds.x + 3, bounds.y); - gc.drawLine(bounds.x + 2, bounds.y + 1, bounds.x + 4, bounds.y + 1); - gc.drawLine(bounds.x + 1, bounds.y + 2, bounds.x + 5, bounds.y + 2); - gc.drawLine(bounds.x, bounds.y + 3, bounds.x + 6, bounds.y + 3); - } -*/ - } -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.ui.DBeaverIcons; +import org.jkiss.dbeaver.ui.TextUtils; +import org.jkiss.dbeaver.ui.UIIcon; +import org.jkiss.utils.CommonUtils; + +/** + * Grid column renderer + */ +class GridColumnRenderer extends AbstractRenderer +{ + public static final int LEFT_MARGIN = 6; + public static final int RIGHT_MARGIN = 6; + public static final int BOTTOM_MARGIN = 6; + public static final int TOP_MARGIN = 6; + public static final int ARROW_MARGIN = 6; + public static final int IMAGE_SPACING = 3; + + public static final Image IMAGE_ASTERISK = DBeaverIcons.getImage(UIIcon.SORT_UNKNOWN); + public static final Image IMAGE_DESC = DBeaverIcons.getImage(UIIcon.SORT_DECREASE); + public static final Image IMAGE_ASC = DBeaverIcons.getImage(UIIcon.SORT_INCREASE); + public static final Image IMAGE_FILTER = DBeaverIcons.getImage(UIIcon.FILTER_SMALL); + + public static final int SORT_WIDTH = IMAGE_DESC.getBounds().width; + public static final int FILTER_WIDTH = IMAGE_FILTER.getBounds().width; + + + + public GridColumnRenderer(LightGrid grid) { + super(grid); + } + + public static Rectangle getSortControlBounds() { + return IMAGE_DESC.getBounds(); + } + + public static Rectangle getFilterControlBounds() { + return IMAGE_FILTER.getBounds(); + } + + @Nullable + protected Image getColumnImage(Object element) { + return grid.getLabelProvider().getImage(element); + } + + protected String getColumnText(Object element) + { + return grid.getLabelProvider().getText(element); + } + + protected String getColumnDescription(Object element) + { + return grid.getLabelProvider().getDescription(element); + } + + protected Font getColumnFont(Object element) { + Font font = grid.getLabelProvider().getFont(element); + return font != null ? font : grid.normalFont; + } + + public void paint(GC gc, Rectangle bounds, boolean selected, boolean hovering, Object element) { + + boolean hasFilters = grid.getContentProvider().isElementSupportsFilter(element); + + //GridColumn col = grid.getColumnByElement(cell.col); + //AbstractRenderer arrowRenderer = col.getSortRenderer(); + int sortOrder = grid.getContentProvider().getSortOrder(element); + final Rectangle sortBounds = getSortControlBounds(); + final Rectangle filterBounds = getFilterControlBounds(); + + // set the font to be used to display the text. + gc.setFont(getColumnFont(element)); + + boolean flat = true; + + boolean drawSelected = false; + + if (flat && (selected || hovering)) { + gc.setBackground(grid.getContentProvider().getCellHeaderSelectionBackground(element)); + } else { + gc.setBackground(grid.getContentProvider().getCellHeaderBackground(element)); + } + gc.setForeground(grid.getContentProvider().getCellHeaderForeground(element)); + + gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height); + + int pushedDrawingOffset = 0; + if (drawSelected) { + pushedDrawingOffset = 1; + } + + int x = LEFT_MARGIN; + + Image columnImage = getColumnImage(element); + if (columnImage != null) { + int y = bounds.y + pushedDrawingOffset + TOP_MARGIN; + + gc.drawImage(columnImage, bounds.x + x + pushedDrawingOffset, y); + x += columnImage.getBounds().width + IMAGE_SPACING; + } + + int width = bounds.width - x; + + if (sortOrder == SWT.NONE) { + width -= RIGHT_MARGIN; + } else { + width -= ARROW_MARGIN + sortBounds.width; + } + if (hasFilters) { + width -= filterBounds.width; + } + //gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND)); + + int y = bounds.y + TOP_MARGIN; + + { + // Column name + String text = getColumnText(element); + text = TextUtils.getShortString(grid.fontMetrics, text, width); + gc.setFont(grid.normalFont); + gc.drawString(text, bounds.x + x + pushedDrawingOffset, y + pushedDrawingOffset, true); + } + + if (sortOrder != SWT.NONE) { + if (drawSelected) { + sortBounds.x = bounds.x + bounds.width - ARROW_MARGIN - sortBounds.width + 1; + sortBounds.y = y; + } else { + sortBounds.x = bounds.x + bounds.width - ARROW_MARGIN - sortBounds.width; + sortBounds.y = y; + } + sortBounds.x += IMAGE_SPACING; + paintSort(gc, sortBounds, sortOrder); + } + + if (hasFilters) { + gc.drawImage(IMAGE_FILTER, + bounds.x + bounds.width - filterBounds.width - + (sortOrder != SWT.NONE ? IMAGE_SPACING + sortBounds.width + 1 : ARROW_MARGIN), + y); + } + + { + // Draw column description + String text = getColumnDescription(element); + if (!CommonUtils.isEmpty(text)) { + y += TOP_MARGIN + grid.fontMetrics.getHeight(); + text = TextUtils.getShortString(grid.fontMetrics, text, width); + gc.setFont(grid.normalFont); + gc.drawString(text, bounds.x + x + pushedDrawingOffset, y + pushedDrawingOffset, true); + } + } + + // Draw border + if (!flat) { + + if (drawSelected) { + gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); + } else { + gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW)); + } + + gc.drawLine(bounds.x, bounds.y, bounds.x + bounds.width - 1, + bounds.y); + gc.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height + - 1); + + if (!drawSelected) { + gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW)); + gc.drawLine(bounds.x + 1, bounds.y + 1, + bounds.x + bounds.width - 2, bounds.y + 1); + gc.drawLine(bounds.x + 1, bounds.y + 1, bounds.x + 1, + bounds.y + bounds.height - 2); + } + + if (drawSelected) { + gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); + } else { + gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW)); + } + gc.drawLine(bounds.x + bounds.width - 1, bounds.y, bounds.x + + bounds.width - 1, + bounds.y + bounds.height - 1); + gc.drawLine(bounds.x, bounds.y + bounds.height - 1, bounds.x + + bounds.width - 1, + bounds.y + bounds.height - 1); + + if (!drawSelected) { + gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); + gc.drawLine(bounds.x + bounds.width - 2, bounds.y + 1, + bounds.x + bounds.width - 2, bounds.y + bounds.height + - 2); + gc.drawLine(bounds.x + 1, bounds.y + bounds.height - 2, + bounds.x + bounds.width - 2, bounds.y + bounds.height + - 2); + } + + } else { + gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW)); + + gc.drawLine(bounds.x + bounds.width - 1, bounds.y, bounds.x + + bounds.width - 1, + bounds.y + bounds.height - 1); + gc.drawLine(bounds.x, bounds.y + bounds.height - 1, bounds.x + + bounds.width - 1, + bounds.y + bounds.height - 1); + } + + gc.setFont(grid.normalFont); + } + + public static void paintSort(GC gc, Rectangle bounds, int sort) + { + switch (sort) { + case SWT.DEFAULT: + gc.drawImage(IMAGE_ASTERISK, bounds.x, bounds.y); + break; + case SWT.UP: + gc.drawImage(IMAGE_ASC, bounds.x, bounds.y); + break; + case SWT.DOWN: + gc.drawImage(IMAGE_DESC, bounds.x, bounds.y); + break; + } +/* + if (isSelected()) { + gc.drawLine(bounds.x, bounds.y, bounds.x + 6, bounds.y); + gc.drawLine(bounds.x + 1, bounds.y + 1, bounds.x + 5, bounds.y + 1); + gc.drawLine(bounds.x + 2, bounds.y + 2, bounds.x + 4, bounds.y + 2); + gc.drawPoint(bounds.x + 3, bounds.y + 3); + } else { + gc.drawPoint(bounds.x + 3, bounds.y); + gc.drawLine(bounds.x + 2, bounds.y + 1, bounds.x + 4, bounds.y + 1); + gc.drawLine(bounds.x + 1, bounds.y + 2, bounds.x + 5, bounds.y + 2); + gc.drawLine(bounds.x, bounds.y + 3, bounds.x + 6, bounds.y + 3); + } +*/ + } +} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridPos.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridPos.java similarity index 96% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridPos.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridPos.java index 294458e73793b7f661654e20274f1fc52e5e4a52..468306f4c2f2a6ed390370a2d40de12b0f4a589e 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridPos.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridPos.java @@ -1,83 +1,83 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.jkiss.dbeaver.model.data.DBDCellValue; - -import java.util.Comparator; - -/** - * Grid position. Tuple of x/y coordinates. - * Pos comparator orders positions in tree in natural order (first ordered by rows then by columns). - */ -public class GridPos implements DBDCellValue -{ - public int col; - public int row; - - public GridPos(int col, int row) - { - this.col = col; - this.row = row; - } - - public GridPos(GridPos copy) - { - this.col = copy.col; - this.row = copy.row; - } - - public boolean isValid() - { - return col >= 0 && row >= 0; - } - - public boolean equals(Object object) - { - return object instanceof GridPos && equalsTo((GridPos) object); - } - - public boolean equals(int col, int row) - { - return this.col == col && this.row == row; - } - - public boolean equalsTo(GridPos pos) - { - return this.col == pos.col && this.row == pos.row; - } - - public String toString() - { - return col + ":" + row; - } - - public int hashCode() - { - return col ^ row; - } - - public static class PosComparator implements Comparator { - @Override - public int compare(GridPos pos1, GridPos pos2) - { - int res = pos1.row - pos2.row; - return res != 0 ? res : pos1.col - pos2.col; - } - } -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.jkiss.dbeaver.model.data.DBDCellValue; + +import java.util.Comparator; + +/** + * Grid position. Tuple of x/y coordinates. + * Pos comparator orders positions in tree in natural order (first ordered by rows then by columns). + */ +public class GridPos implements DBDCellValue +{ + public int col; + public int row; + + public GridPos(int col, int row) + { + this.col = col; + this.row = row; + } + + public GridPos(GridPos copy) + { + this.col = copy.col; + this.row = copy.row; + } + + public boolean isValid() + { + return col >= 0 && row >= 0; + } + + public boolean equals(Object object) + { + return object instanceof GridPos && equalsTo((GridPos) object); + } + + public boolean equals(int col, int row) + { + return this.col == col && this.row == row; + } + + public boolean equalsTo(GridPos pos) + { + return this.col == pos.col && this.row == pos.row; + } + + public String toString() + { + return col + ":" + row; + } + + public int hashCode() + { + return col ^ row; + } + + public static class PosComparator implements Comparator { + @Override + public int compare(GridPos pos1, GridPos pos2) + { + int res = pos1.row - pos2.row; + return res != 0 ? res : pos1.col - pos2.col; + } + } +} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridRowRenderer.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridRowRenderer.java similarity index 97% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridRowRenderer.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridRowRenderer.java index 355a1634616e16ce2207dbf685b930eaf257c373..980407741018f449e3b0dd9f130921bf03f8a175 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridRowRenderer.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/GridRowRenderer.java @@ -1,139 +1,139 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.*; -import org.jkiss.dbeaver.ui.DBeaverIcons; -import org.jkiss.dbeaver.ui.TextUtils; -import org.jkiss.dbeaver.ui.UIIcon; - -/** - * Grid row header renderer. - */ -class GridRowRenderer extends AbstractRenderer { - - static final Image IMG_EXPAND = DBeaverIcons.getImage(UIIcon.TREE_EXPAND); - static final Image IMG_COLLAPSE = DBeaverIcons.getImage(UIIcon.TREE_COLLAPSE); - static final Rectangle EXPANDED_BOUNDS = IMG_EXPAND.getBounds(); - - public static final int LEFT_MARGIN = 4; - public static final int RIGHT_MARGIN = 4; - public static final int IMAGE_SPACING = 5; - public static final int EXPANDER_SPACING = 2; - public static final int LEVEL_SPACING = EXPANDED_BOUNDS.width; - - final Color DEFAULT_BACKGROUND; - final Color DEFAULT_FOREGROUND; - final Color DEFAULT_FOREGROUND_TEXT; - - public GridRowRenderer(LightGrid grid) { - super(grid); - DEFAULT_BACKGROUND = getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); - DEFAULT_FOREGROUND = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW); - DEFAULT_FOREGROUND_TEXT = getDisplay().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND); - } - - public void paint(GC gc, Rectangle bounds, boolean selected, int level, IGridContentProvider.ElementState state, Object element) { - String text = grid.getLabelProvider().getText(element); - - gc.setFont(getDisplay().getSystemFont()); - - Color background = selected ? grid.getContentProvider().getCellHeaderSelectionBackground(element) : grid.getContentProvider().getCellHeaderBackground(element); - if (background == null) { - background = DEFAULT_BACKGROUND; - } - gc.setBackground(background); - - gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height + 1); - - { - gc.setForeground(grid.getContentProvider().getCellHeaderForeground(element)); - - gc.drawLine( - bounds.x + bounds.width - 1, - bounds.y, - bounds.x + bounds.width - 1, - bounds.y + bounds.height - 1); - gc.drawLine( - bounds.x, - bounds.y + bounds.height - 1, - bounds.x + bounds.width - 1, - bounds.y + bounds.height - 1); - } - - int x = LEFT_MARGIN; - if (level > 0) { - x += level * LEVEL_SPACING; - } - if (state != IGridContentProvider.ElementState.NONE) { - Image expandImage = state == IGridContentProvider.ElementState.EXPANDED ? IMG_COLLAPSE : IMG_EXPAND; - gc.drawImage(expandImage, x, bounds.y + (bounds.height - EXPANDED_BOUNDS.height) / 2); - x += EXPANDED_BOUNDS.width + EXPANDER_SPACING; - } else if (grid.hasNodes()) { - x += EXPANDED_BOUNDS.width + EXPANDER_SPACING; - } - - Image image = grid.getLabelProvider().getImage(element); - - if (image != null) { - gc.drawImage(image, x, bounds.y + (bounds.height - image.getBounds().height) / 2); - x += image.getBounds().width + IMAGE_SPACING; - } - - int width = bounds.width - x; - - width -= RIGHT_MARGIN; - - Color foreground = grid.getContentProvider().getCellHeaderForeground(element); - if (foreground == null) { - foreground = grid.getLabelProvider().getForeground(element); - } - - gc.setForeground(foreground); - - int y = bounds.y; - int selectionOffset = 0; - - y += (bounds.height - gc.stringExtent(text).y) / 2; - gc.drawString(TextUtils.getShortString(grid.fontMetrics, text, width), bounds.x + x + selectionOffset, y + selectionOffset, true); - } - - public int computeHeaderWidth(Object element, int level) { - int width = GridRowRenderer.LEFT_MARGIN + GridRowRenderer.RIGHT_MARGIN; - if (grid.hasNodes()) { - width += GridRowRenderer.EXPANDED_BOUNDS.width + EXPANDER_SPACING; - } - Image rowImage = grid.getLabelProvider().getImage(element); - if (rowImage != null) { - width += rowImage.getBounds().width; - width += GridRowRenderer.IMAGE_SPACING; - } - String rowText = grid.getLabelProvider().getText(element); - Point ext = grid.sizingGC.stringExtent(rowText); - width += ext.x; - width += level * GridRowRenderer.LEVEL_SPACING; - return width; - } - - public static boolean isOverExpander(int x, int level) - { - int expandBegin = LEFT_MARGIN + level * LEVEL_SPACING; - int expandEnd = expandBegin + EXPANDED_BOUNDS.width; - return x >= expandBegin && x <= expandEnd; - } -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.*; +import org.jkiss.dbeaver.ui.DBeaverIcons; +import org.jkiss.dbeaver.ui.TextUtils; +import org.jkiss.dbeaver.ui.UIIcon; + +/** + * Grid row header renderer. + */ +class GridRowRenderer extends AbstractRenderer { + + static final Image IMG_EXPAND = DBeaverIcons.getImage(UIIcon.TREE_EXPAND); + static final Image IMG_COLLAPSE = DBeaverIcons.getImage(UIIcon.TREE_COLLAPSE); + static final Rectangle EXPANDED_BOUNDS = IMG_EXPAND.getBounds(); + + public static final int LEFT_MARGIN = 4; + public static final int RIGHT_MARGIN = 4; + public static final int IMAGE_SPACING = 5; + public static final int EXPANDER_SPACING = 2; + public static final int LEVEL_SPACING = EXPANDED_BOUNDS.width; + + final Color DEFAULT_BACKGROUND; + final Color DEFAULT_FOREGROUND; + final Color DEFAULT_FOREGROUND_TEXT; + + public GridRowRenderer(LightGrid grid) { + super(grid); + DEFAULT_BACKGROUND = getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + DEFAULT_FOREGROUND = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW); + DEFAULT_FOREGROUND_TEXT = getDisplay().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND); + } + + public void paint(GC gc, Rectangle bounds, boolean selected, int level, IGridContentProvider.ElementState state, Object element) { + String text = grid.getLabelProvider().getText(element); + + gc.setFont(getDisplay().getSystemFont()); + + Color background = selected ? grid.getContentProvider().getCellHeaderSelectionBackground(element) : grid.getContentProvider().getCellHeaderBackground(element); + if (background == null) { + background = DEFAULT_BACKGROUND; + } + gc.setBackground(background); + + gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height + 1); + + { + gc.setForeground(grid.getContentProvider().getCellHeaderForeground(element)); + + gc.drawLine( + bounds.x + bounds.width - 1, + bounds.y, + bounds.x + bounds.width - 1, + bounds.y + bounds.height - 1); + gc.drawLine( + bounds.x, + bounds.y + bounds.height - 1, + bounds.x + bounds.width - 1, + bounds.y + bounds.height - 1); + } + + int x = LEFT_MARGIN; + if (level > 0) { + x += level * LEVEL_SPACING; + } + if (state != IGridContentProvider.ElementState.NONE) { + Image expandImage = state == IGridContentProvider.ElementState.EXPANDED ? IMG_COLLAPSE : IMG_EXPAND; + gc.drawImage(expandImage, x, bounds.y + (bounds.height - EXPANDED_BOUNDS.height) / 2); + x += EXPANDED_BOUNDS.width + EXPANDER_SPACING; + } else if (grid.hasNodes()) { + x += EXPANDED_BOUNDS.width + EXPANDER_SPACING; + } + + Image image = grid.getLabelProvider().getImage(element); + + if (image != null) { + gc.drawImage(image, x, bounds.y + (bounds.height - image.getBounds().height) / 2); + x += image.getBounds().width + IMAGE_SPACING; + } + + int width = bounds.width - x; + + width -= RIGHT_MARGIN; + + Color foreground = grid.getContentProvider().getCellHeaderForeground(element); + if (foreground == null) { + foreground = grid.getLabelProvider().getForeground(element); + } + + gc.setForeground(foreground); + + int y = bounds.y; + int selectionOffset = 0; + + y += (bounds.height - gc.stringExtent(text).y) / 2; + gc.drawString(TextUtils.getShortString(grid.fontMetrics, text, width), bounds.x + x + selectionOffset, y + selectionOffset, true); + } + + public int computeHeaderWidth(Object element, int level) { + int width = GridRowRenderer.LEFT_MARGIN + GridRowRenderer.RIGHT_MARGIN; + if (grid.hasNodes()) { + width += GridRowRenderer.EXPANDED_BOUNDS.width + EXPANDER_SPACING; + } + Image rowImage = grid.getLabelProvider().getImage(element); + if (rowImage != null) { + width += rowImage.getBounds().width; + width += GridRowRenderer.IMAGE_SPACING; + } + String rowText = grid.getLabelProvider().getText(element); + Point ext = grid.sizingGC.stringExtent(rowText); + width += ext.x; + width += level * GridRowRenderer.LEVEL_SPACING; + return width; + } + + public static boolean isOverExpander(int x, int level) + { + int expandBegin = LEFT_MARGIN + level * LEVEL_SPACING; + int expandEnd = expandBegin + EXPANDED_BOUNDS.width; + return x >= expandBegin && x <= expandEnd; + } +} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridContentProvider.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridContentProvider.java similarity index 96% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridContentProvider.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridContentProvider.java index ad0b19f9ab6798c172c460bcf31f5ab327540740..0a6181c094a2dcde4e38ac3788799998ea0f396b 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridContentProvider.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridContentProvider.java @@ -1,92 +1,92 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.jface.viewers.IContentProvider; -import org.eclipse.swt.graphics.Color; -import org.jkiss.code.NotNull; -import org.jkiss.code.Nullable; -import org.jkiss.dbeaver.model.DBPImage; - -public interface IGridContentProvider extends IContentProvider { - - enum ElementState { - NONE, - EXPANDED, - COLLAPSED - } - - int STATE_NONE = 0; - int STATE_LINK = 1; - int STATE_HYPER_LINK = 2; - int STATE_TRANSFORMED = 4; - - int ALIGN_LEFT = 0; - int ALIGN_CENTER = 1; - int ALIGN_RIGHT = 2; - - @NotNull - Object[] getElements(boolean horizontal); - - @Nullable - Object[] getChildren(Object element); - - int getSortOrder(@Nullable Object element); - - ElementState getDefaultState(@NotNull Object element); - - int getColumnAlign(@Nullable Object element); - - boolean isElementSupportsFilter(@Nullable Object element); - - /** - * - * @param cellText pre-rendered cell text. Used for cache purposes. - */ - int getCellState(Object colElement, Object rowElement, @Nullable String cellText); - - Object getCellValue(Object colElement, Object rowElement, boolean formatString); - - @NotNull - String getCellText(Object colElement, Object rowElement); - - @Nullable - DBPImage getCellImage(Object colElement, Object rowElement); - - @Nullable - Color getCellForeground(Object colElement, Object rowElement, boolean selected); - - @Nullable - Color getCellBackground(Object colElement, Object rowElement, boolean selected); - - @Nullable - Color getCellHeaderForeground(Object element); - - @Nullable - Color getCellHeaderBackground(Object element); - - @Nullable - Color getCellHeaderSelectionBackground(Object element); - - @NotNull - String getCellLinkText(Object colElement, Object rowElement); - - // Resets all cached colors - void resetColors(); - -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.swt.graphics.Color; +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.model.DBPImage; + +public interface IGridContentProvider extends IContentProvider { + + enum ElementState { + NONE, + EXPANDED, + COLLAPSED + } + + int STATE_NONE = 0; + int STATE_LINK = 1; + int STATE_HYPER_LINK = 2; + int STATE_TRANSFORMED = 4; + + int ALIGN_LEFT = 0; + int ALIGN_CENTER = 1; + int ALIGN_RIGHT = 2; + + @NotNull + Object[] getElements(boolean horizontal); + + @Nullable + Object[] getChildren(Object element); + + int getSortOrder(@Nullable Object element); + + ElementState getDefaultState(@NotNull Object element); + + int getColumnAlign(@Nullable Object element); + + boolean isElementSupportsFilter(@Nullable Object element); + + /** + * + * @param cellText pre-rendered cell text. Used for cache purposes. + */ + int getCellState(Object colElement, Object rowElement, @Nullable String cellText); + + Object getCellValue(Object colElement, Object rowElement, boolean formatString); + + @NotNull + String getCellText(Object colElement, Object rowElement); + + @Nullable + DBPImage getCellImage(Object colElement, Object rowElement); + + @Nullable + Color getCellForeground(Object colElement, Object rowElement, boolean selected); + + @Nullable + Color getCellBackground(Object colElement, Object rowElement, boolean selected); + + @Nullable + Color getCellHeaderForeground(Object element); + + @Nullable + Color getCellHeaderBackground(Object element); + + @Nullable + Color getCellHeaderSelectionBackground(Object element); + + @NotNull + String getCellLinkText(Object colElement, Object rowElement); + + // Resets all cached colors + void resetColors(); + +} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridController.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridController.java similarity index 100% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridController.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridController.java diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridLabelProvider.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridLabelProvider.java similarity index 97% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridLabelProvider.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridLabelProvider.java index 6e313ef6148e42ebef1a6d381c872de4cef85cdf..3363cdf0dd5d7e5edf9b90d31342fa2d0f6960b9 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridLabelProvider.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridLabelProvider.java @@ -1,42 +1,42 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.jface.viewers.IColorProvider; -import org.eclipse.jface.viewers.IFontProvider; -import org.eclipse.jface.viewers.IToolTipProvider; -import org.eclipse.swt.graphics.Image; -import org.jkiss.code.NotNull; -import org.jkiss.code.Nullable; - -public interface IGridLabelProvider extends IColorProvider, IFontProvider, IToolTipProvider { - - String OPTION_EXCLUDE_COLUMN_NAME_FOR_WIDTH_CALC = "OPTION_EXCLUDE_COLUMN_NAME_FOR_WIDTH_CALC"; - - @NotNull - String getText(Object element); - - @Nullable - String getDescription(Object element); - - @Nullable - Image getImage(Object element); - - Object getGridOption(String option); - -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.IFontProvider; +import org.eclipse.jface.viewers.IToolTipProvider; +import org.eclipse.swt.graphics.Image; +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; + +public interface IGridLabelProvider extends IColorProvider, IFontProvider, IToolTipProvider { + + String OPTION_EXCLUDE_COLUMN_NAME_FOR_WIDTH_CALC = "OPTION_EXCLUDE_COLUMN_NAME_FOR_WIDTH_CALC"; + + @NotNull + String getText(Object element); + + @Nullable + String getDescription(Object element); + + @Nullable + Image getImage(Object element); + + Object getGridOption(String option); + +} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridScrollBar.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridScrollBar.java similarity index 96% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridScrollBar.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridScrollBar.java index 5012b9696a5e9bce8754024a11f8d010a01f100a..900427b588f5e26a49da7273230f95a0a7c96f48 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridScrollBar.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/IGridScrollBar.java @@ -1,80 +1,80 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Event; - -/** - * Used by Grid to externalize the scrollbars from the table itself. - * - * @author chris.gross@us.ibm.com - * @version 1.0.0 - */ -public interface IGridScrollBar -{ - - public int getWidth(); - - public boolean getVisible(); - - public void setVisible(boolean visible); - - public int getSelection(); - - public void setSelection(int selection); - - /** - * Sets the receiver's selection, minimum value, maximum value, thumb, - * increment and page increment all at once. - * - * @param selection selection - * @param min minimum - * @param max maximum - * @param thumb thumb - * @param increment increment - * @param pageIncrement page increment - */ - public void setValues(int selection, int min, int max, int thumb, int increment, int pageIncrement); - - public void handleMouseWheel(Event e); - - public void setMinimum(int min); - - public int getMinimum(); - - public void setMaximum(int max); - - public int getMaximum(); - - public void setThumb(int thumb); - - public int getThumb(); - - public void setIncrement(int increment); - - public int getIncrement(); - - public void setPageIncrement(int page); - - public int getPageIncrement(); - - public void addSelectionListener(SelectionListener listener); - - public void removeSelectionListener(SelectionListener listener); -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Event; + +/** + * Used by Grid to externalize the scrollbars from the table itself. + * + * @author chris.gross@us.ibm.com + * @version 1.0.0 + */ +public interface IGridScrollBar +{ + + public int getWidth(); + + public boolean getVisible(); + + public void setVisible(boolean visible); + + public int getSelection(); + + public void setSelection(int selection); + + /** + * Sets the receiver's selection, minimum value, maximum value, thumb, + * increment and page increment all at once. + * + * @param selection selection + * @param min minimum + * @param max maximum + * @param thumb thumb + * @param increment increment + * @param pageIncrement page increment + */ + public void setValues(int selection, int min, int max, int thumb, int increment, int pageIncrement); + + public void handleMouseWheel(Event e); + + public void setMinimum(int min); + + public int getMinimum(); + + public void setMaximum(int max); + + public int getMaximum(); + + public void setThumb(int thumb); + + public int getThumb(); + + public void setIncrement(int increment); + + public int getIncrement(); + + public void setPageIncrement(int page); + + public int getPageIncrement(); + + public void addSelectionListener(SelectionListener listener); + + public void removeSelectionListener(SelectionListener listener); +} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/LightGrid.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/LightGrid.java similarity index 96% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/LightGrid.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/LightGrid.java index 01d0cbbb06c2eba50166622d2bdcd5283a303e67..51e631c6c9808258eaae1437863a06b368e76403 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/LightGrid.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/LightGrid.java @@ -1,4617 +1,4617 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.jface.resource.JFaceColors; -import org.eclipse.osgi.util.NLS; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.*; -import org.eclipse.swt.events.*; -import org.eclipse.swt.graphics.*; -import org.eclipse.swt.widgets.*; -import org.eclipse.ui.progress.UIJob; -import org.jkiss.code.NotNull; -import org.jkiss.code.Nullable; -import org.jkiss.dbeaver.Log; -import org.jkiss.dbeaver.core.CoreMessages; -import org.jkiss.dbeaver.model.DBPImage; -import org.jkiss.dbeaver.ui.UIUtils; -import org.jkiss.dbeaver.ui.dnd.LocalObjectTransfer; -import org.jkiss.utils.ArrayUtils; -import org.jkiss.utils.CommonUtils; -import org.jkiss.utils.IntKeyMap; - -import java.util.*; -import java.util.List; - -/** - * LightGrid - * initially based on Nebula grid. Refactored and mostly redone. - * - * @author serge@jkiss.org - * @author chris.gross@us.ibm.com - */ -public abstract class LightGrid extends Canvas { - - private static final Log log = Log.getLog(LightGrid.class); - - private static final int MAX_TOOLTIP_LENGTH = 1000; - - protected static final int Event_ChangeSort = 1000; - protected static final int Event_NavigateLink = 1001; - protected static final int Event_FilterColumn = 1002; - - /** - * Horizontal scrolling increment, in pixels. - */ - private static final int HORZ_SCROLL_INCREMENT = 5; - - /** - * The area to the left and right of the column boundary/resizer that is - * still considered the resizer area. This prevents a user from having to be - * *exactly* over the resizer. - */ - private static final int COLUMN_RESIZER_THRESHOLD = 4; - private static final int DEFAULT_ROW_HEADER_WIDTH = 30; - private static final int MAX_ROW_HEADER_WIDTH = 400; - - - /** - * The minimum width of a column header. - */ - private static final int MIN_COLUMN_HEADER_WIDTH = 32; - - /** - * Threshold for the selection border used for drag n drop - * in mode. - */ - private static final int SELECTION_DRAG_BORDER_THRESHOLD = 2; - private static final boolean MAXIMIZE_SINGLE_COLUMN = false; - // Indicates that last time refreshData was called grid control was hidden (had zero size) - // In that case columns will be repacked even if keepState is true - private boolean controlWasHidden; - - public enum EventSource { - MOUSE, - KEYBOARD, - } - - static class GridNode { - GridNode parent; - Object[] rows; - IGridContentProvider.ElementState state; - int level; - - private GridNode(GridNode parent, Object[] rows, IGridContentProvider.ElementState state, int level) { - this.parent = parent; - this.rows = rows; - this.state = state; - this.level = level; - } - - boolean isParentOf(GridNode node) { - for (GridNode p = node; p != null; p = p.parent) { - if (p == this) { - return true; - } - } - return false; - } - } - - // Tooltips - - private class ToolTipHandler extends UIJob { - private String toolTip; - ToolTipHandler() { - super("ToolTip handler"); - setSystem(true); - } - - @Override - public IStatus runInUIThread(IProgressMonitor monitor) { - if (!monitor.isCanceled() && !LightGrid.this.isDisposed()) { - LightGrid.this.setToolTipText(toolTip); - } - toolTipHandler = null; - return Status.OK_STATUS; - } - } - - // Last calculated client area - private volatile static Rectangle lastClientArea; - - private volatile String prevToolTip; - private volatile ToolTipHandler toolTipHandler; - - /** - * Tracks whether the scroll values are correct. If not they will be - * recomputed in onPaint. This allows us to get a free ride on top of the - * OS's paint event merging to assure that we don't perform this expensive - * operation when unnecessary. - */ - private boolean scrollValuesObsolete = false; - - /** - * Reference to the item in focus. - */ - private int focusItem = -1; - - private final Set selectedCells = new TreeSet<>(new GridPos.PosComparator()); - private final List selectedCellsBeforeRangeSelect = new ArrayList<>(); - private final List selectedColumns = new ArrayList<>(); - private final IntKeyMap selectedRows = new IntKeyMap<>(); - - private boolean cellDragSelectionOccurring = false; - private boolean cellRowDragSelectionOccurring = false; - private boolean cellColumnDragSelectionOccurring = false; - private boolean cellDragCTRL = false; - private boolean followupCellSelectionEventOwed = false; - - private boolean cellSelectedOnLastMouseDown; - private boolean cellRowSelectedOnLastMouseDown; - private boolean cellColumnSelectedOnLastMouseDown; - - private boolean headerColumnDragStarted, rowHeaderDragStarted; - - private GridColumn shiftSelectionAnchorColumn; - - private GridColumn focusColumn; - private final GridPos focusCell = new GridPos(-1, -1); - - /** - * List of table columns in creation/index order. - */ - private final List topColumns = new ArrayList<>(); - private final List columns = new ArrayList<>(); - private int maxColumnDepth = 0; - protected Object[] columnElements = new Object[0]; - protected Object[] rowElements = new Object[0]; - private GridNode[] parentNodes = new GridNode[0]; - private final Map rowNodes = new IdentityHashMap<>(); - - private int maxColumnDefWidth = 1000; - - private GridColumnRenderer columnHeaderRenderer; - private GridRowRenderer rowHeaderRenderer; - private GridCellRenderer cellRenderer; - - /** - * Are row headers visible? - */ - private boolean rowHeaderVisible = false; - - /** - * Are column headers visible? - */ - private boolean columnHeadersVisible = false; - - /** - * Type of selection behavior. Valid values are SWT.SINGLE and SWT.MULTI. - */ - private int selectionType = SWT.SINGLE; - - /** - * Default height of items. - */ - private int itemHeight = 1; - - /** - * Width of each row header. - */ - private int rowHeaderWidth = 0; - - /** - * Height of each column header. - */ - private int headerHeight = 0; - - private boolean hoveringOnHeader = false; - private boolean hoveringOnColumnIcon = false; - private boolean hoveringOnColumnSorter = false; - private boolean hoveringOnColumnFilter = false; - private boolean hoveringOnLink = false; - private boolean hoveringOnRowHeader = false; - - private GridColumn columnBeingSorted; - private GridColumn columnBeingFiltered; - private boolean hoveringOnColumnResizer = false; - private GridColumn columnBeingResized; - private boolean resizingColumn = false; - private int resizingStartX = 0; - private int resizingColumnStartWidth = 0; - private int hoveringItem; - private GridColumn hoveringColumn; - private GridColumn draggingColumn; - private Integer hoveringRow; - private Integer draggingRow; - - /** - * String-based detail of what is being hovered over in a cell. This allows - * a renderer to differentiate between hovering over different parts of the - * cell. For example, hovering over a checkbox in the cell or hovering over - * a tree node in the cell. The table does nothing with this string except - * to set it back in the renderer when its painted. The renderer sets this - * during its notify method (InternalWidget.HOVER) and the table pulls it - * back and maintains it so it can be set back when the cell is painted. The - * renderer determines what the hover detail means and how it affects - * painting. - */ - private Object hoveringDetail = null; - - /** - * Are the grid lines visible? - */ - private boolean linesVisible = true; - - @NotNull - private final IGridScrollBar vScroll; - @NotNull - private final IGridScrollBar hScroll; - - /** - * Item selected when a multiple selection using shift+click first occurs. - * This item anchors all further shift+click selections. - */ - private int shiftSelectionAnchorItem; - - private boolean columnScrolling = false; - - /** - * Dispose listener. This listener is removed during the dispose event to allow re-firing of - * the event. - */ - private Listener disposeListener; - - GC sizingGC; - FontMetrics fontMetrics; - Font normalFont; - - @NotNull - private Color lineColor; - private Color lineSelectedColor; - private Color backgroundColor; - private Color foregroundColor; - @NotNull - private Cursor sortCursor; - - /** - * Index of first visible item. The value must never be read directly. It is cached and - * updated when appropriate. #getTopIndex should be called for every client (even internal - * callers). A value of -1 indicates that the value is old and will be recomputed. - * - * @see #bottomIndex - */ - private int topIndex = -1; - /** - * Index of last visible item. The value must never be read directly. It is cached and - * updated when appropriate. #getBottomIndex() should be called for every client (even internal - * callers). A value of -1 indicates that the value is old and will be recomputed. - *

- * Note that the item with this index is often only partly visible; maybe only - * a single line of pixels is visible. In extreme cases, bottomIndex may be the - * same as topIndex. - * - * @see #topIndex - */ - private int bottomIndex = -1; - - /** - * True if the last visible item is completely visible. The value must never be read directly. It is cached and - * updated when appropriate. #isShown() should be called for every client (even internal - * callers). - * - * @see #bottomIndex - */ - private boolean bottomIndexShownCompletely = false; - - /** - * This is the tooltip text currently used. This could be the tooltip text for the currently - * hovered cell, or the general grid tooltip. See handleCellHover. - */ - private String displayedToolTipText; - - private boolean hoveringOnHeaderDragArea = false; - - /** - * A range of rows in a Grid. - *

- * A row in this sense exists only for visible items - * Therefore, the items at 'startIndex' and 'endIndex' - * are always visible. - * - * @see LightGrid#getRowRange(int, int, boolean, boolean) - */ - private static class RowRange { - /** - * index of first item in range - */ - public int startIndex; - /** - * index of last item in range - */ - public int endIndex; - /** - * number of rows (i.e. visible items) in this range - */ - public int rows; - /** - * height in pixels of this range (including horizontal separator between rows) - */ - public int height; - } - - /** - * Filters out unnecessary styles, adds mandatory styles and generally - * manages the style to pass to the super class. - * - * @param style user specified style. - * @return style to pass to the super class. - */ - private static int checkStyle(int style) - { - int mask = SWT.BORDER | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT | SWT.H_SCROLL | SWT.V_SCROLL - | SWT.SINGLE | SWT.MULTI | SWT.NO_FOCUS | SWT.CHECK | SWT.VIRTUAL; - int newStyle = style & mask; - newStyle |= SWT.DOUBLE_BUFFERED; - return newStyle; - } - - /** - * Constructs a new instance of this class given its parent and a style - * value describing its behavior and appearance. - *

- * - * @param parent a composite control which will be the parent of the new - * instance (cannot be null) - * @param style the style of control to construct - * @see SWT#SINGLE - * @see SWT#MULTI - */ - public LightGrid(Composite parent, int style) - { - super(parent, checkStyle(style)); - - sizingGC = new GC(this); - fontMetrics = sizingGC.getFontMetrics(); - normalFont = getFont(); - columnHeaderRenderer = new GridColumnRenderer(this); - rowHeaderRenderer = new GridRowRenderer(this); - cellRenderer = new GridCellRenderer(this); - - final Display display = getDisplay(); - lineColor = JFaceColors.getErrorBackground(display); - lineSelectedColor = JFaceColors.getErrorBorder(display);//SWT.COLOR_WIDGET_DARK_SHADOW; - //setForeground(JFaceColors.getBannerForeground(display)); - //setBackground(JFaceColors.getBannerBackground(display)); -/* - ColorRegistry colorRegistry = UIUtils.getColorRegistry(); - setLineColor(colorRegistry.get(JFacePreferences.QUALIFIER_COLOR)); - setForeground(colorRegistry.get(JFacePreferences.CONTENT_ASSIST_FOREGROUND_COLOR)); - setBackground(colorRegistry.get(JFacePreferences.CONTENT_ASSIST_BACKGROUND_COLOR)); -*/ - sortCursor = display.getSystemCursor(SWT.CURSOR_HAND); - - if ((style & SWT.MULTI) != 0) { - selectionType = SWT.MULTI; - } - - if (getVerticalBar() != null) { - getVerticalBar().setVisible(false); - vScroll = new ScrollBarAdapter(getVerticalBar()); - } else { - vScroll = new NullScrollBar(); - } - - if (getHorizontalBar() != null) { - getHorizontalBar().setVisible(false); - hScroll = new ScrollBarAdapter(getHorizontalBar()); - } else { - hScroll = new NullScrollBar(); - } - - scrollValuesObsolete = true; - - initListeners(); - - recalculateSizes(); - - addDragAndDropSupport(); - setDragDetect(true); - } - - @NotNull - public abstract IGridContentProvider getContentProvider(); - - @NotNull - public abstract IGridLabelProvider getLabelProvider(); - - @Nullable - public abstract IGridController getGridController(); - - public boolean hasNodes() { - return !rowNodes.isEmpty(); - } - - public void setMaxColumnDefWidth(int maxColumnDefWidth) { - this.maxColumnDefWidth = maxColumnDefWidth; - } - - private void collectRows(List result, List parents, @Nullable GridNode parent, Object[] rows, int level) - { - for (int i = 0; i < rows.length; i++) { - Object row = rows[i]; - if (row == null) { - continue; - } - result.add(row); - parents.add(parent); - Object[] children = getContentProvider().getChildren(row); - if (children != null) { - IGridContentProvider.ElementState state; - GridNode node = rowNodes.get(row); - if (node == null) { - state = getContentProvider().getDefaultState(row); - node = new GridNode(parent, children, state, level + 1); - } else { - state = node.state; - } - rowNodes.put(row, node); - if (state == IGridContentProvider.ElementState.EXPANDED) { - collectRows(result, parents, node, children, level + 1); - } - } - } - } - - /** - * Refresh grid data - */ - public void refreshData(boolean refreshColumns, boolean keepState, boolean fitValue) - { - GridPos savedFocus = keepState ? getFocusPos() : null; - int savedHSB = keepState ? hScroll.getSelection() : -1; - int savedVSB = keepState ? vScroll.getSelection() : -1; - - int[] oldWidths = null; - if (keepState && !controlWasHidden) { - // Save widths - oldWidths = new int[columns.size()]; - if (!columns.isEmpty()) { - for (int i = 0; i < columns.size(); i++) { - oldWidths[i] = columns.get(i).getWidth(); - } - } - } - controlWasHidden = getClientArea().height == 0; - - if (refreshColumns) { - this.removeAll(); - } else { - this.deselectAll(); - topIndex = -1; - bottomIndex = -1; - } - IGridContentProvider contentProvider = getContentProvider(); - refreshRowsData(); - this.displayedToolTipText = null; - - if (refreshColumns) { - this.maxColumnDepth = 0; - - // Add columns - this.columnElements = contentProvider.getElements(true); - for (Object columnElement : columnElements) { - GridColumn column = new GridColumn(this, columnElement); - createChildColumns(column); - } - // Invalidate columns structure - boolean hasChildColumns = false; - for (Iterator iter = columns.iterator(); iter.hasNext(); ) { - GridColumn column = iter.next(); - if (column.getParent() == null) { - topColumns.add(column); - } else { - hasChildColumns = true; - } - if (column.getChildren() != null) { - iter.remove(); - } - } - if (hasChildColumns) { - // Rebuild columns model - columnElements = new Object[columns.size()]; - for (int i = 0; i < columns.size(); i++) { - columnElements[i] = columns.get(i).getElement(); - } - } - - scrollValuesObsolete = true; - - if (getColumnCount() == 1 && (fitValue || MAXIMIZE_SINGLE_COLUMN)) { - // Here we going to maximize single column to entire grid's width - // Sometimes (when new grid created and filled with data very fast our client area size is zero - // So let's add a workaround for it and use column's width in this case - GridColumn column = getColumn(0); - int columnWidth = column.computeHeaderWidth(); - int gridWidth = getCurrentOrLastClientArea().width - getRowHeaderWidth() - getHScrollSelectionInPixels() - getVerticalBar().getSize().x; - if (gridWidth > columnWidth) { - columnWidth = gridWidth; - } - column.setWidth(columnWidth); - } else { - int totalWidth = 0; - for (GridColumn curColumn : topColumns) { - curColumn.pack(false); - totalWidth += curColumn.getWidth(); - } - if (!fitValue) { - // If grid width more than screen - lets narrow too long columns - int clientWidth = getCurrentOrLastClientArea().width; - if (totalWidth > clientWidth) { - int normalWidth = 0; - List fatColumns = new ArrayList<>(); - for (GridColumn curColumn : columns) { - if (curColumn.getWidth() > maxColumnDefWidth) { - fatColumns.add(curColumn); - } else { - normalWidth += curColumn.getWidth(); - } - } - if (!fatColumns.isEmpty()) { - // Narrow fat columns on decWidth - int freeSpace = (clientWidth - normalWidth - getBorderWidth() - rowHeaderWidth - vScroll.getWidth()) - / fatColumns.size(); - int newFatWidth = (freeSpace > maxColumnDefWidth ? freeSpace : maxColumnDefWidth); - for (GridColumn curColumn : fatColumns) { - curColumn.setWidth(newFatWidth); - } - } - } - } - } - - if (oldWidths != null) { - // Restore widths - if (oldWidths.length == columns.size()) { - for (int i = 0; i < oldWidths.length; i++) { - columns.get(i).setWidth(oldWidths[i]); - } - } - } - } - // Recalculate indexes, sizes and update scrollbars - topIndex = -1; - bottomIndex = -1; - recalculateSizes(); - updateScrollbars(); - - // Restore state - if (savedFocus != null) { - savedFocus.row = Math.min(savedFocus.row, getItemCount() - 1); - savedFocus.col = Math.min(savedFocus.col, getColumnCount() - 1); - if (savedFocus.row >= 0) setFocusItem(savedFocus.row); - if (savedFocus.col >= 0) setFocusColumn(savedFocus.col); - if (savedFocus.isValid()) selectCell(savedFocus); - } - if (savedHSB >= 0) { - hScroll.setSelection(Math.min(hScroll.getMaximum(), savedHSB)); - } - if (savedVSB >= 0) { - vScroll.setSelection(Math.min(vScroll.getMaximum(), savedVSB)); - } -// // Add focus cell to selection -// GridPos focusPos = getFocusPos(); -// if (focusPos.isValid()) { -// selectCell(focusPos); -// } - } - - public void refreshRowsData() { - // Prepare rows - Object[] initialElements = getContentProvider().getElements(false); - this.rowNodes.clear(); - List realRows = new ArrayList<>(initialElements.length); - List parents = new ArrayList<>(initialElements.length); - collectRows(realRows, parents, null, initialElements, 0); - this.rowElements = realRows.toArray(); - this.parentNodes = parents.toArray(new GridNode[parents.size()]); - } - - /** - * Returns current or last client area. - * If Grid controls are stacked then only the top is visible and has real client area. - * So we cache it - all stack has the same client area - */ - private Rectangle getCurrentOrLastClientArea() { - Rectangle clientArea = getClientArea(); - if (clientArea.width == 0) { - if (lastClientArea == null) { - return clientArea; - } - return lastClientArea; - } - lastClientArea = clientArea; - return clientArea; - } - - private void createChildColumns(GridColumn parent) { - Object[] children = getContentProvider().getChildren(parent.getElement()); - if (children != null) { - for (Object child : children) { - GridColumn column = new GridColumn(parent, child); - createChildColumns(column); - } - } - this.maxColumnDepth = Math.max(this.maxColumnDepth, parent.getLevel()); - } - - @Nullable - public GridCell posToCell(GridPos pos) - { - if (pos.col < 0 || pos.row < 0) { - return null; - } - return new GridCell(columnElements[pos.col], rowElements[pos.row]); - } - - @NotNull - public GridPos cellToPos(GridCell cell) - { - int colIndex = ArrayUtils.indexOf(columnElements, cell.col); - int rowIndex = ArrayUtils.indexOf(rowElements, cell.row); - return new GridPos(colIndex, rowIndex); - } - - public Object getColumnElement(int col) { - return columnElements[col]; - } - - public Rectangle getColumnBounds(int col) { - return getColumn(col).getBounds(); - } - - public Object getRowElement(int row) { - return rowElements[row]; - } - - @Override - public Color getBackground() - { - if (backgroundColor == null) { - backgroundColor = super.getBackground(); - } - return backgroundColor; - } - - @Override - public void setBackground(Color color) - { - super.setBackground(backgroundColor = color); - } - - /////////////////////////////////// - // Just caching because native impl creates new objects and called too often - - @Override - public Color getForeground() { - if (foregroundColor == null) { - foregroundColor = super.getForeground(); - } - return foregroundColor; - } - - @Override - public void setForeground(Color color) { - super.setForeground(foregroundColor = color); - getContentProvider().resetColors(); - } - - /** - * Adds the listener to the collection of listeners who will be notified - * when the receiver's selection changes, by sending it one of the messages - * defined in the {@code SelectionListener} interface. - *

- * Cell selection events may have Event.detail = SWT.DRAG when the - * user is drag selecting multiple cells. A follow up selection event will be generated - * when the drag is complete. - * - * @param listener the listener which should be notified - */ - public void addSelectionListener(SelectionListener listener) - { - checkWidget(); - if (listener == null) { - SWT.error(SWT.ERROR_NULL_ARGUMENT); - } - addListener(SWT.Selection, new TypedListener(listener)); - addListener(SWT.DefaultSelection, new TypedListener(listener)); - } - - /** - * Removes the listener from the collection of listeners who will be - * notified when the receiver's selection changes. - * - * @param listener the listener which should no longer be notified - * @see SelectionListener - * @see #addSelectionListener(SelectionListener) - */ - public void removeSelectionListener(SelectionListener listener) - { - checkWidget(); - removeListener(SWT.Selection, listener); - removeListener(SWT.DefaultSelection, listener); - } - - - - @Override - public Point computeSize(int wHint, int hHint, boolean changed) - { - checkWidget(); - - Point prefSize = null; - if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) { - prefSize = getTableSize(); - prefSize.x += 2 * getBorderWidth(); - prefSize.y += 2 * getBorderWidth(); - } - - int x = 0; - int y = 0; - - if (wHint == SWT.DEFAULT) { - x += prefSize.x; - if (getVerticalBar() != null) { - x += getVerticalBar().getSize().x; - } - } else { - x = wHint; - } - - if (hHint == SWT.DEFAULT) { - y += prefSize.y; - if (getHorizontalBar() != null) { - y += getHorizontalBar().getSize().y; - } - } else { - y = hHint; - } - - return new Point(x, y); - } - - /** - * Deselects all selected items in the receiver. If cell selection is enabled, - * all cells are deselected. - */ - public void deselectAll() - { - checkWidget(); - - selectedCells.clear(); - updateSelectionCache(); - redraw(); - } - - @NotNull - private GridColumn getColumn(int index) - { - return columns.get(index); - } - - /** - * Returns the column at the given point and a known item in the receiver or null if no such - * column exists. The point is in the coordinate system of the receiver. - * - * @param point the point used to locate the column - * @return the column at the given point - */ - @Nullable - private GridColumn getColumn(Point point) - { - checkWidget(); - if (point == null) { - SWT.error(SWT.ERROR_NULL_ARGUMENT); - return null; - } - - GridColumn overThis = null; - - int x2 = 0; - - if (rowHeaderVisible) { - if (point.x <= rowHeaderWidth) { - return null; - } - - x2 += rowHeaderWidth; - } - - x2 -= getHScrollSelectionInPixels(); - - for (GridColumn column : columns) { - if (point.x >= x2 && point.x < x2 + column.getWidth()) { - for (GridColumn parent = column.getParent(); parent != null; parent = parent.getParent()) { - Point parentLoc = getOrigin(parent, -1); - if (point.y >= parentLoc.y && point.y <= parentLoc.y + parent.getHeaderHeight(false, false)) { - column = parent; - break; - } - } - overThis = column; - break; - } - - x2 += column.getWidth(); - } - - if (overThis == null) { - return null; - } - - return overThis; - } - - /** - * Returns the number of columns contained in the receiver. If no - * {@code GridColumn}s were created by the programmer, this value is - * zero, despite the fact that visually, one column of items may be visible. - * This occurs when the programmer uses the table like a list, adding items - * but never creating a column. - * - * @return the number of columns - */ - public int getColumnCount() - { - return columns.size(); - } - - Collection getColumns() - { - return columns; - } - - public IGridScrollBar getHorizontalScrollBarProxy() - { - return hScroll; - } - - public IGridScrollBar getVerticalScrollBarProxy() - { - return vScroll; - } - - /** - * Returns the height of the column headers. If this table has column - * groups, the returned value includes the height of group headers. - * - * @return height of the column header row - */ - public int getHeaderHeight() - { - return headerHeight; - } - - private int getRowHeaderWidth() - { - return rowHeaderWidth; - } - - public int getRow(Point point) - { - checkWidget(); - - if (point == null) { - SWT.error(SWT.ERROR_NULL_ARGUMENT); - return -1; - } - - final Rectangle clientArea = getClientArea(); - if (point.x < 0 || point.x > clientArea.width) return -1; - - Point p = new Point(point.x, point.y); - - int y2 = 0; - - if (columnHeadersVisible) { - if (p.y <= headerHeight) { - return -1; - } - y2 += headerHeight; - } - - int row = getTopIndex(); - int currItemHeight = getItemHeight(); - - int itemCount = getItemCount(); - while (row < itemCount && y2 <= clientArea.height) { - if (p.y >= y2 && p.y < y2 + currItemHeight + 1) { - return row; - } - - y2 += currItemHeight + 1; - - row++; - } - - return -1; - } - - /** - * Returns the number of items contained in the receiver. - * - * @return the number of items - */ - public int getItemCount() - { - return rowElements.length; - } - - /** - * Returns the default height of the items - * - * @return default height of items - * @see #setItemHeight(int) - */ - public int getItemHeight() - { - return itemHeight; - } - - /** - * Sets the default height for this Grid's items. When - * this method is called, all existing items are resized - * to the specified height and items created afterwards will be - * initially sized to this height. - *

- * As long as no default height was set by the client through this method, - * the preferred height of the first item in this Grid is - * used as a default for all items (and is returned by {@link #getItemHeight()}). - * - * @param height default height in pixels - */ - private void setItemHeight(int height) - { - checkWidget(); - if (height < 1) - SWT.error(SWT.ERROR_INVALID_ARGUMENT); - itemHeight = height; - setScrollValuesObsolete(); - redraw(); - } - - /** - * Returns the line color. - * - * @return Returns the lineColor. - */ - @NotNull - public Color getLineColor() { - return lineColor; - } - - public void setLineColor(@NotNull Color lineColor) { - this.lineColor = lineColor; - } - - public Color getLineSelectedColor() { - return lineSelectedColor; - } - - public void setLineSelectedColor(Color lineSelectedColor) { - this.lineSelectedColor = lineSelectedColor; - } - - /** - * Returns true if the lines are visible. - * - * @return Returns the linesVisible. - */ - public boolean isLinesVisible() - { - return linesVisible; - } - - /** - * Returns the next visible item in the table. - * - * @return next visible item or null - */ - private int getNextVisibleItem(int index) - { - if (index >= getItemCount()) { - return -1; - } - if (index == getItemCount() - 1) { - return index; - } else { - return index + 1; - } - } - - /** - * Returns the previous visible item in the table. Passing null for the item - * will return the last visible item in the table. - * - * @return previous visible item or if item==null last visible item - */ - private int getPreviousVisibleItem(int index) - { - if (index == 0) { - return -1; - } - return index - 1; - } - - /** - * Returns the previous visible column in the table. - * - * @param column column - * @return previous visible column or null - */ - @Nullable - private GridColumn getPreviousVisibleColumn(GridColumn column) - { - int index = indexOf(column); - if (index <= 0) - return null; - - return columns.get(index - 1); - } - - /** - * Returns the next visible column in the table. - * - * @param column column - * @return next visible column or null - */ - @Nullable - private GridColumn getNextVisibleColumn(GridColumn column) - { - int index = indexOf(column); - - if (index < 0 || index >= columns.size() - 1) - return null; - - return columns.get(index + 1); - } - - /** - * Returns the number of selected cells contained in the receiver. - * - * @return the number of selected cells - */ - private int getCellSelectionCount() - { - return selectedCells.size(); - } - - /** - * Returns the zero-relative index of the item which is currently selected - * in the receiver, or -1 if no item is selected. If cell selection is enabled, - * returns the index of first item that contains at least one selected cell. - * - * @return the index of the selected item - */ - public int getSelectionIndex() - { - if (selectedCells.isEmpty()) - return -1; - - return selectedCells.iterator().next().row; - } - - /** - * Returns the zero-relative index of the item which is currently at the top - * of the receiver. This index can change when items are scrolled or new - * items are added or removed. - * - * @return the index of the top item - */ - public int getTopIndex() - { - if (topIndex != -1) - return topIndex; - - if (!vScroll.getVisible()) { - topIndex = 0; - } else { - // figure out first visible row and last visible row - topIndex = vScroll.getSelection(); - } - - return topIndex; - } - - /** - * Returns the zero-relative index of the item which is currently at the bottom - * of the receiver. This index can change when items are scrolled, expanded - * or collapsed or new items are added or removed. - *

- * Note that the item with this index is often only partly visible; maybe only - * a single line of pixels is visible. Use {@link #isShown(int)} to find - * out. - *

- * In extreme cases, getBottomIndex() may return the same value as - * {@link #getTopIndex()}. - * - * @return the index of the bottom item - */ - public int getBottomIndex() - { - if (bottomIndex != -1) - return bottomIndex; - - if (getItemCount() == 0) { - bottomIndex = 0; - } else if (getVisibleGridHeight() < 1) { - bottomIndex = getTopIndex(); - } else { - RowRange range = getRowRange(getTopIndex(), getVisibleGridHeight(), false, false); - - bottomIndex = range.endIndex; - bottomIndexShownCompletely = range.height <= getVisibleGridHeight(); - } - - return bottomIndex; - } - - /** - * Returns a {@link RowRange} ranging from - * the grid item at startIndex to that at endIndex. - *

- * This is primarily used to measure the height - * in pixel of such a range and to count the number - * of visible grid items within the range. - * - * @param startIndex index of the first item in the range or -1 to the first visible item in this grid - * @param endIndex index of the last item in the range or -1 to use the last visible item in this grid - */ - @Nullable - private RowRange getRowRange(int startIndex, int endIndex) - { - - // parameter preparation - int itemCount = getItemCount(); - if (startIndex == -1) { - // search first visible item - startIndex = 0; - if (startIndex == itemCount) return null; - } - if (endIndex == -1) { - // search last visible item - endIndex = itemCount - 1; - if (endIndex <= 0) return null; - } - - // fail fast - if (startIndex < 0 || endIndex < 0 || startIndex >= itemCount || endIndex >= itemCount - || endIndex < startIndex) - SWT.error(SWT.ERROR_INVALID_ARGUMENT); - RowRange range = new RowRange(); - range.startIndex = startIndex; - range.endIndex = endIndex; - - range.rows = range.endIndex - range.startIndex + 1; - range.height = (getItemHeight() + 1) * range.rows - 1; - - return range; - } - - /** - * This method can be used to build a range of grid rows - * that is allowed to span a certain height in pixels. - *

- * It returns a {@link RowRange} that contains information - * about the range, especially the index of the last - * element in the range (or if inverse == true, then the - * index of the first element). - *

- * Note: Even if 'forceEndCompletelyInside' is set to - * true, the last item will not lie completely within - * the availableHeight, if (height of item at startIndex < availableHeight). - * - * @param startIndex index of the first (if inverse==false) or - * last (if inverse==true) item in the range - * @param availableHeight height in pixels - * @param forceEndCompletelyInside if true, the last item in the range will lie completely - * within the availableHeight, otherwise it may lie partly outside this range - * @param inverse if true, then the first item in the range will be searched, not the last - * @return range of grid rows - * @see RowRange - */ - private RowRange getRowRange(int startIndex, int availableHeight, - boolean forceEndCompletelyInside, boolean inverse) - { - // parameter preparation - if (startIndex == -1) { - if (!inverse) { - // search first visible item - startIndex = 0; - } else { - // search last visible item - startIndex = getItemCount() - 1; - } - } - - RowRange range = new RowRange(); - - if (startIndex < 0 || startIndex >= getItemCount()) { - // something is broken - range.startIndex = 0; - range.endIndex = 0; - range.height = 0; - range.rows = 0; - return range; - } - - if (availableHeight <= 0) { - // special case: empty range - range.startIndex = startIndex; - range.endIndex = startIndex; - range.rows = 0; - range.height = 0; - return range; - } - - int availableRows = (availableHeight + 1) / (getItemHeight() + 1); - - if (((getItemHeight() + 1) * range.rows - 1) + 1 < availableHeight) { - // not all available space used yet - // - so add another row if it need not be completely within availableHeight - if (!forceEndCompletelyInside) - availableRows++; - } - - int otherIndex = startIndex + ((availableRows - 1) * (!inverse ? 1 : -1)); - if (otherIndex < 0) otherIndex = 0; - if (otherIndex >= getItemCount()) otherIndex = getItemCount() - 1; - - range.startIndex = !inverse ? startIndex : otherIndex; - range.endIndex = !inverse ? otherIndex : startIndex; - range.rows = range.endIndex - range.startIndex + 1; - range.height = (getItemHeight() + 1) * range.rows - 1; - - return range; - } - - /** - * Returns the height of the plain grid in pixels. - * This does not include the height of the column headers. - * - * @return height of plain grid - */ - private int getGridHeight() - { - RowRange range = getRowRange(-1, -1); - return range != null ? range.height : 0; - } - - /** - * Returns the height of the on-screen area that is available - * for showing the grid's rows, i.e. the client area of the - * scrollable minus the height of the column headers (if shown). - * - * @return height of visible grid in pixels - */ - private int getVisibleGridHeight() - { - return getClientArea().height - (columnHeadersVisible ? headerHeight : 0); - } - - /** - * Searches the receiver's list starting at the first column (index 0) until - * a column is found that is equal to the argument, and returns the index of - * that column. If no column is found, returns -1. - * - * @param column the search column - * @return the index of the column - */ - int indexOf(GridColumn column) - { - column = column.getFirstLeaf(); - int index = columns.indexOf(column); - if (index < 0) { - log.warn("Bad column [" + column.getElement() + "]"); - } - return index; - } - - /** - * Returns {@code true} if the receiver's row header is visible, and - * {@code false} otherwise. - *

- * - * @return the receiver's row header's visibility state - */ - private boolean isRowHeaderVisible() - { - return rowHeaderVisible; - } - - /** - * Returns true if the given cell is selected. - * - * @param cell cell - * @return true if the cell is selected. - */ - private boolean isCellSelected(GridPos cell) - { - if (cell == null) - SWT.error(SWT.ERROR_NULL_ARGUMENT); - - return selectedCells.contains(cell); - } - - /** - * Removes all of the items from the receiver. - */ - public void removeAll() - { - checkWidget(); - deselectAll(); - vScroll.setSelection(0); - hScroll.setSelection(0); - - focusItem = -1; - focusColumn = null; - topIndex = -1; - bottomIndex = -1; - shiftSelectionAnchorColumn = null; - - topColumns.clear(); - columns.clear(); - columnElements = new Object[0]; - rowElements = new Object[0]; - } - - /** - * Selects the item at the given zero-relative index in the receiver. If the - * item at the index was already selected, it remains selected. Indices that - * are out of range are ignored. - *

- * If cell selection is enabled, selects all cells at the given index. - * - * @param index the index of the item to select - */ - public void select(int index) - { - checkWidget(); - - if (index < 0 || index >= getItemCount()) return; - - selectCells(getCells(index)); - - redraw(); - } - - /** - * Selects the items in the range specified by the given zero-relative - * indices in the receiver. The range of indices is inclusive. The current - * selection is not cleared before the new items are selected. - *

- * If an item in the given range is not selected, it is selected. If an item - * in the given range was already selected, it remains selected. Indices - * that are out of range are ignored and no items will be selected if start - * is greater than end. If the receiver is single-select and there is more - * than one item in the given range, then all indices are ignored. - *

- * If cell selection is enabled, all cells within the given range are selected. - * - * @param start the start of the range - * @param end the end of the range - * @see LightGrid#setSelection(int,int) - */ - public void select(int start, int end) - { - checkWidget(); - - if (selectionType == SWT.SINGLE && start != end) return; - - for (int i = start; i <= end; i++) { - if (i < 0) { - continue; - } - if (i > getItemCount() - 1) { - break; - } - - selectCells(getCells(i)); - } - - redraw(); - } - - /** - * Selects the items at the given zero-relative indices in the receiver. The - * current selection is not cleared before the new items are selected. - *

- * If the item at a given index is not selected, it is selected. If the item - * at a given index was already selected, it remains selected. Indices that - * are out of range and duplicate indices are ignored. If the receiver is - * single-select and multiple indices are specified, then all indices are - * ignored. - *

- * If cell selection is enabled, all cells within the given indices are - * selected. - * - * @param indices the array of indices for the items to select - * @see LightGrid#setSelection(int[]) - */ - public void select(int[] indices) - { - checkWidget(); - - if (indices == null) { - SWT.error(SWT.ERROR_NULL_ARGUMENT); - return; - } - - if (selectionType == SWT.SINGLE && indices.length > 1) return; - - for (int j : indices) { - if (j >= 0 && j < getItemCount()) { - selectCells(getCells(j)); - } - } - redraw(); - } - - /** - * Selects all of the items in the receiver. - *

- * If the receiver is single-select, do nothing. If cell selection is enabled, - * all cells are selected. - */ - public void selectAll() - { - checkWidget(); - - if (selectionType == SWT.SINGLE) return; - - selectAllCells(); - } - - /** - * Marks the receiver's header as visible if the argument is {@code true}, - * and marks it invisible otherwise. - * - * @param show the new visibility state - */ - public void setHeaderVisible(boolean show) - { - checkWidget(); - this.columnHeadersVisible = show; - redraw(); - } - - /** - * Sets the line visibility. - * - * @param linesVisible Te linesVisible to set. - */ - public void setLinesVisible(boolean linesVisible) - { - checkWidget(); - this.linesVisible = linesVisible; - redraw(); - } - - /** - * Marks the receiver's row header as visible if the argument is - * {@code true}, and marks it invisible otherwise. When row headers are - * visible, horizontal scrolling is always done by column rather than by - * pixel. - * - * @param show the new visibility state - */ - public void setRowHeaderVisible(boolean show) - { - checkWidget(); - this.rowHeaderVisible = show; - setColumnScrolling(true); - - redraw(); - } - - /** - * Selects the item at the given zero-relative index in the receiver. The - * current selection is first cleared, then the new item is selected. - *

- * If cell selection is enabled, all cells within the item at the given index - * are selected. - * - * @param index the index of the item to select - */ - public void setSelection(int index) - { - checkWidget(); - - if (index >= 0 && index < getItemCount()) { - selectedCells.clear(); - selectCells(getCells(index)); - } - } - - /** - * Selects the items in the range specified by the given zero-relative - * indices in the receiver. The range of indices is inclusive. The current - * selection is cleared before the new items are selected. - *

- * Indices that are out of range are ignored and no items will be selected - * if start is greater than end. If the receiver is single-select and there - * is more than one item in the given range, then all indices are ignored. - *

- * If cell selection is enabled, all cells within the given range are selected. - * - * @param start the start index of the items to select - * @param end the end index of the items to select - * @see LightGrid#deselectAll() - * @see LightGrid#select(int,int) - */ - public void setSelection(int start, int end) - { - checkWidget(); - - if (selectionType == SWT.SINGLE && start != end) return; - - selectedCells.clear(); - - for (int i = start; i <= end; i++) { - if (i < 0) { - continue; - } - if (i > getItemCount() - 1) { - break; - } - - selectCells(getCells(i)); - } - redraw(); - } - - /** - * Selects the items at the given zero-relative indices in the receiver. The - * current selection is cleared before the new items are selected. - *

- * Indices that are out of range and duplicate indices are ignored. If the - * receiver is single-select and multiple indices are specified, then all - * indices are ignored. - *

- * If cell selection is enabled, all cells within the given indices are selected. - * - * @param indices the indices of the items to select - * @see LightGrid#deselectAll() - * @see LightGrid#select(int[]) - */ - public void setSelection(int[] indices) - { - checkWidget(); - - if (selectionType == SWT.SINGLE && indices.length > 1) return; - - selectedCells.clear(); - - for (int j : indices) { - if (j < 0) { - continue; - } - if (j > getItemCount() - 1) { - break; - } - - selectCells(getCells(j)); - } - redraw(); - } - - /** - * Sets the zero-relative index of the item which is currently at the top of - * the receiver. This index can change when items are scrolled or new items - * are added and removed. - * - * @param index the index of the top item - */ - private void setTopIndex(int index) - { - checkWidget(); - if (index < 0 || index >= getItemCount()) { - return; - } - - if (!vScroll.getVisible()) { - return; - } - - vScroll.setSelection(index); - topIndex = -1; - bottomIndex = -1; - redraw(); - } - - /** - * Shows the column. If the column is already showing in the receiver, this - * method simply returns. Otherwise, the columns are scrolled until the - * column is visible. - * - */ - public void showColumn(int column) - { - GridColumn col = getColumn(column); - showColumn(col); - } - - public void showColumn(Object element) - { - for (GridColumn column : columns) { - if (column.getElement() == element) { - showColumn(column); - break; - } - } - } - - private void showColumn(@NotNull GridColumn col) - { - checkWidget(); - - if (!hScroll.getVisible()) { - return; - } - - int x = getColumnHeaderXPosition(col); - - int firstVisibleX = 0; - if (rowHeaderVisible) { - firstVisibleX = rowHeaderWidth; - } - - // if its visible just return - final Rectangle clientArea = getClientArea(); - if (x >= firstVisibleX - && (x + col.getWidth()) <= (firstVisibleX + (clientArea.width - firstVisibleX))) { - return; - } - - if (!getColumnScrolling()) { - if (x < firstVisibleX) { - hScroll.setSelection(getHScrollSelectionInPixels() - (firstVisibleX - x)); - } else { - if (col.getWidth() > clientArea.width - firstVisibleX) { - hScroll.setSelection(getHScrollSelectionInPixels() + (x - firstVisibleX)); - } else { - x -= clientArea.width - firstVisibleX - col.getWidth(); - hScroll.setSelection(getHScrollSelectionInPixels() + (x - firstVisibleX)); - } - } - } else { - if (x < firstVisibleX || col.getWidth() > clientArea.width - firstVisibleX) { - int sel = indexOf(col); - hScroll.setSelection(sel); - } else { - int availableWidth = clientArea.width - firstVisibleX - col.getWidth(); - - GridColumn prevCol = getPreviousVisibleColumn(col); - GridColumn currentScrollTo = col; - - while (true) { - if (prevCol == null || prevCol.getWidth() > availableWidth) { - int sel = indexOf(currentScrollTo); - hScroll.setSelection(sel); - break; - } else { - availableWidth -= prevCol.getWidth(); - currentScrollTo = prevCol; - prevCol = getPreviousVisibleColumn(prevCol); - } - } - } - } - - redraw(); - } - - /** - * Returns true if 'item' is currently being completely - * shown in this Grid's visible on-screen area. - *

- *

Here, "completely" only refers to the item's height, not its - * width. This means this method returns true also if some cells - * are horizontally scrolled away. - * - * @param row row number - * @return true if 'item' is shown - */ - private boolean isShown(int row) - { - checkWidget(); - - if (row == -1) - SWT.error(SWT.ERROR_INVALID_ARGUMENT); - - int firstVisibleIndex = getTopIndex(); - int lastVisibleIndex = getBottomIndex(); - - return (row >= firstVisibleIndex && row < lastVisibleIndex) - || - (row == lastVisibleIndex && bottomIndexShownCompletely); - } - - /** - * Shows the item. If the item is already showing in the receiver, this - * method simply returns. Otherwise, the items are scrolled until the item - * is visible. - * - * @param item the item to be shown - */ - public void showItem(int item) - { - checkWidget(); - - updateScrollbars(); - - // if no items are visible on screen then abort - if (getVisibleGridHeight() < 1) { - return; - } - - // if its visible just return - if (isShown(item)) { - return; - } - - int newTopIndex = item; - - if (newTopIndex >= getBottomIndex()) { - RowRange range = getRowRange(newTopIndex, getVisibleGridHeight(), true, true); // note: inverse==true - newTopIndex = range.startIndex; // note: use startIndex because of inverse==true - } - - setTopIndex(newTopIndex); - } - - /** - * Shows the selection. If the selection is already showing in the receiver, - * this method simply returns. Otherwise, the items are scrolled until the - * selection is visible. - * - */ - public void showSelection() - { - checkWidget(); - - if (scrollValuesObsolete) - updateScrollbars(); - - if (selectedCells.isEmpty()) return; - - GridPos cell = selectedCells.iterator().next(); - showItem(cell.row); - showColumn(cell.col); - } - - /** - * Computes and sets the height of the header row. This method will ask for - * the preferred size of all the column headers and use the max. - */ - private void computeHeaderSizes() - { - // Item height - itemHeight = fontMetrics.getHeight() + 3; - - // Column header height - int colHeaderHeight = 0; - for (GridColumn column : topColumns) { - colHeaderHeight = Math.max(column.getHeaderHeight(true, true), colHeaderHeight); - } - headerHeight = colHeaderHeight; - - // Row header width - rowHeaderWidth = DEFAULT_ROW_HEADER_WIDTH; - for (int i = 0; i < rowElements.length; i++) { - Object row = rowElements[i]; - GridNode parentNode = parentNodes[i]; - GridNode nr = rowNodes.get(row); - int width = rowHeaderRenderer.computeHeaderWidth( - row, nr != null ? nr.level : parentNode == null ? 0 : parentNode.level + 1); - rowHeaderWidth = Math.max(rowHeaderWidth, width); - } - if (rowHeaderWidth > MAX_ROW_HEADER_WIDTH) { - rowHeaderWidth = MAX_ROW_HEADER_WIDTH; - } - } - - /** - * Returns the x position of the given column. Takes into account scroll - * position. - * - * @param column given column - * @return x position - */ - private int getColumnHeaderXPosition(@NotNull GridColumn column) - { - int x = 0; - - x -= getHScrollSelectionInPixels(); - - if (rowHeaderVisible) { - x += rowHeaderWidth; - } - column = column.getFirstLeaf(); - for (GridColumn column2 : columns) { - if (column2 == column) { - break; - } - - x += column2.getWidth(); - } - - return x; - } - - /** - * Returns the hscroll selection in pixels. This method abstracts away the - * differences between column by column scrolling and pixel based scrolling. - * - * @return the horizontal scroll selection in pixels - */ - private int getHScrollSelectionInPixels() - { - int selection = hScroll.getSelection(); - if (columnScrolling) { - int pixels = 0; - for (int i = 0; i < selection && i < columns.size(); i++) { - pixels += columns.get(i).getWidth(); - } - selection = pixels; - } - return selection; - } - - /** - * Returns the size of the preferred size of the inner table. - * - * @return the preferred size of the table. - */ - private Point getTableSize() - { - int x = 0; - int y = 0; - - if (columnHeadersVisible) { - y += headerHeight; - } - - y += getGridHeight(); - - if (rowHeaderVisible) { - x += rowHeaderWidth; - } - - for (GridColumn column : columns) { - x += column.getWidth(); - } - - return new Point(x, y); - } - - /** - * Sets the new width of the column being resized and fires the appropriate - * listeners. - * - * @param x mouse x - */ - private void handleColumnResizerDragging(int x) - { - int newWidth = resizingColumnStartWidth + (x - resizingStartX); - if (newWidth < MIN_COLUMN_HEADER_WIDTH) { - newWidth = MIN_COLUMN_HEADER_WIDTH; - } - - Rectangle clientArea = getClientArea(); - if (columnScrolling) { - int maxWidth = clientArea.width; - if (rowHeaderVisible) - maxWidth -= rowHeaderWidth; - if (newWidth > maxWidth) - newWidth = maxWidth; - } - - if (newWidth == columnBeingResized.getWidth()) { - return; - } - - columnBeingResized.setWidth(newWidth, false); - scrollValuesObsolete = true; - - redraw(clientArea.x, clientArea.y, clientArea.width, clientArea.height, false); - } - - /** - * Determines if the mouse is hovering on a column resizer and changes the - * pointer and sets field appropriately. - * Also checks if mouse if hovering on a column sorter control. - * - * @param x mouse x - * @param y mouse y - */ - private void handleHoverOnColumnHeader(int x, int y) - { - boolean overSorter = false, overResizer = false, overFilter = false; - hoveringOnHeader = false; - boolean overIcon = false; - - if (y <= headerHeight) { - int x2 = 0; - - if (rowHeaderVisible) { - x2 += rowHeaderWidth; - } - - x2 -= getHScrollSelectionInPixels(); - - if (x < x2) { - int ltSort = getContentProvider().getSortOrder(null); - if (ltSort != SWT.NONE - && x > x2 - GridColumnRenderer.SORT_WIDTH - GridColumnRenderer.ARROW_MARGIN - && x < x2 - GridColumnRenderer.ARROW_MARGIN - && y > GridColumnRenderer.TOP_MARGIN) - { - columnBeingSorted = null; - overSorter = true; - } - - } else { - if (x > getRowHeaderWidth()) { - for (GridColumn column : columns) { - if (x >= x2 && x <= x2 + column.getWidth()) { - hoveringOnHeader = true; - if (column.isOverSortArrow(x - x2, y)) { - overSorter = true; - columnBeingSorted = column; - break; - } - - if(column.isOverFilterButton(x - x2, y)) { - columnBeingFiltered = column; - overFilter = true; - break; - } - - if (column.isOverIcon(x, y)) { - overIcon = true; - break; - } - - x2 += column.getWidth(); - if (x2 >= (x - COLUMN_RESIZER_THRESHOLD) && x2 <= (x + COLUMN_RESIZER_THRESHOLD)) { - overResizer = true; - - columnBeingResized = column; - break; - } - } else { - x2 += column.getWidth(); - } - } - } - } - // Redraw header -// GC gc = new GC(this); -// try { -// paintHeader(gc); -// } catch (Exception e) { -// gc.dispose(); -// } - - } else if (x <= rowHeaderWidth) { - // Hover in row header - } - if (overSorter != hoveringOnColumnSorter) { - if (overSorter) { - setCursor(sortCursor); - } else { - columnBeingSorted = null; - setCursor(null); - } - hoveringOnColumnSorter = overSorter; - } else if (overIcon != hoveringOnColumnIcon) { - setCursor(overIcon ? sortCursor : null); - hoveringOnColumnIcon = overIcon; - } - - if(overFilter != hoveringOnColumnFilter) { - if(overFilter) - setCursor(sortCursor); - else if(!overSorter) { - columnBeingFiltered = null; - setCursor(null); - } - - hoveringOnColumnFilter = overFilter; - } - - if (overResizer != hoveringOnColumnResizer) { - if (overResizer) { - setCursor(getDisplay().getSystemCursor(SWT.CURSOR_SIZEWE)); - } else { - columnBeingResized = null; - if (!hoveringOnColumnSorter) { - setCursor(null); - } - } - hoveringOnColumnResizer = overResizer; - } - - if (hoveringOnHeader && !overSorter && !overResizer && !overFilter) { - hoveringOnHeaderDragArea = true; - } else { - hoveringOnHeaderDragArea = false; - } - } - - private void handleHoverOnRowHeader(int x, int y) { - hoveringRow = null; - draggingRow = null; - - if ((!rowHeaderVisible || y > headerHeight) && x <= rowHeaderWidth) { - hoveringOnRowHeader = true; - int row = getRow(new Point(x, y)); - if (row != -1) { - hoveringRow = row; - } - } else { - hoveringOnRowHeader = false; - } - } - - /** - * Returns the cell at the given point in the receiver or null if no such - * cell exists. The point is in the coordinate system of the receiver. - * - * @param point the point used to locate the item - * @return the cell at the given point - */ - @Nullable - public GridPos getCell(Point point) - { - checkWidget(); - - if (point == null) { - SWT.error(SWT.ERROR_NULL_ARGUMENT); - return null; - } - - if (point.x < 0 || point.x > getClientArea().width) return null; - - int item = getRow(point); - GridColumn column = getColumn(point); - - if (item >= 0 && column != null) { - return new GridPos(indexOf(column), item); - } else { - return null; - } - } - - /** - * Paints. - * - * @param e paint event - */ - private void onPaint(@NotNull PaintEvent e) - { - final GC gc = e.gc; - gc.setBackground(getBackground()); - - //this.drawBackground(gc, 0, 0, getSize().x, getSize().y); - - if (scrollValuesObsolete) { - updateScrollbars(); - scrollValuesObsolete = false; - } - - int y = 0; - - if (columnHeadersVisible) { - paintHeader(gc); - y += headerHeight; - } - - final Rectangle clientArea = getClientArea(); - int availableHeight = clientArea.height - y; - int visibleRows = availableHeight / getItemHeight() + 1; - if (getItemCount() > 0 && availableHeight > 0) { - RowRange range = getRowRange(getTopIndex(), availableHeight, false, false); - if (range.height >= availableHeight) - visibleRows = range.rows; - else - visibleRows = range.rows + (availableHeight - range.height) / getItemHeight() + 1; - } - - int firstVisibleIndex = getTopIndex(); - - int row = firstVisibleIndex; - final int hScrollSelectionInPixels = getHScrollSelectionInPixels(); - final GridPos testPos = new GridPos(-1, -1); - final Rectangle cellBounds = new Rectangle(0, 0, 0, 0); - - for (int i = 0; i < visibleRows; i++) { - - int x = 0; - - x -= hScrollSelectionInPixels; - - // get the item to draw - if (row >= 0 && row < getItemCount()) { - - boolean cellInRowSelected = selectedRows.containsKey(row); - - if (rowHeaderVisible) { - - // row header is actually painted later - x += rowHeaderWidth; - } - - // draw regular cells for each column - for (int k = 0, columnsSize = columns.size(); k < columnsSize; k++) { - GridColumn column = columns.get(k); - - int width = column.getWidth(); - - if (x + width >= 0 && x < clientArea.width) { - - cellBounds.x = x; - cellBounds.y = y; - cellBounds.width = width; - cellBounds.height = getItemHeight(); - - testPos.col = k; - testPos.row = row; - cellRenderer.paint( - gc, - cellBounds, - selectedCells.contains(testPos), - focusItem == row && focusColumn == column, - column.getElement(), - rowElements[row]); - - //gc.setClipping((Rectangle) null); - } - - x += column.getWidth(); - } - - if (x < clientArea.width) { - drawEmptyCell(gc, x, y, clientArea.width - x + 1, getItemHeight()); - } - - x = 0; - - GridNode rowNode = this.rowNodes.get(rowElements[row]); - GridNode parentNode = this.parentNodes[row]; - if (rowHeaderVisible) { - - if (y >= headerHeight) { - cellBounds.x = 0; - cellBounds.y = y; - cellBounds.width = rowHeaderWidth; - cellBounds.height = getItemHeight() + 1; - - gc.setClipping(cellBounds); - try { - rowHeaderRenderer.paint( - gc, - cellBounds, - cellInRowSelected, - parentNode == null ? 0 : parentNode.level, - rowNode == null ? IGridContentProvider.ElementState.NONE : rowNode.state, - rowElements[row]); - } finally { - gc.setClipping((Rectangle)null); - } - } - x += rowHeaderWidth; - } - - y += getItemHeight() + 1; - - } else { - - if (rowHeaderVisible) { - //row header is actually painted later - x += rowHeaderWidth; - } - - for (GridColumn column : columns) { - drawEmptyCell(gc, x, y, column.getWidth(), getItemHeight()); - x += column.getWidth(); - } - if (x < clientArea.width) { - drawEmptyCell(gc, x, y, clientArea.width - x + 1, getItemHeight()); - } - - x = 0; - - if (rowHeaderVisible) { - drawEmptyRowHeader(gc, x, y, rowHeaderWidth, getItemHeight() + 1); - x += rowHeaderWidth; - } - - y += getItemHeight() + 1; - } - - row++; - } - } - - /** - * Paints the header. - * - * @param gc gc from paint event - */ - private void paintHeader(@NotNull GC gc) - { - int x = 0; - int y; - - x -= getHScrollSelectionInPixels(); - - if (rowHeaderVisible) { - // skip left corner - x += rowHeaderWidth; - } - - final Rectangle clientArea = getClientArea(); - for (int i = 0, columnsSize = topColumns.size(); i < columnsSize; i++) { - GridColumn column = topColumns.get(i); - if (x > clientArea.width) - break; - - int columnHeight = column.getHeaderHeight(false, false); - y = 0; - if (x + column.getWidth() >= 0) { - paintColumnsHeader(gc, column, x, y, columnHeight, 0); - } - - x += column.getWidth(); - } - - if (x < clientArea.width) { - drawEmptyColumnHeader(gc, x, 0, clientArea.width - x, headerHeight); - } - - x = 0; - - if (rowHeaderVisible) { - // paint left corner - drawTopLeftCell(gc, 0, 0, rowHeaderWidth, headerHeight); - x += rowHeaderWidth; - } - } - - private void paintColumnsHeader(GC gc, @NotNull GridColumn column, int x, int y, int columnHeight, int level) { - List children = column.getChildren(); - int paintHeight = columnHeight; - if (CommonUtils.isEmpty(children)) { - paintHeight = columnHeight + (headerHeight - y - columnHeight); - } - Rectangle bounds = new Rectangle(x, y, column.getWidth(), paintHeight); - boolean hover = hoveringOnHeader && hoveringColumn == column; - columnHeaderRenderer.paint(gc, bounds, selectedColumns.contains(column), hover, column.getElement()); - if (!CommonUtils.isEmpty(children)) { - // Draw child columns - level++; - int childX = x; - for (GridColumn child : children) { - paintColumnsHeader(gc, child, childX, y + columnHeight, columnHeight, level); - childX += child.getWidth(); - } - } - } - - /** - * Manages the state of the scrollbars when new items are added or the - * bounds are changed. - */ - public void updateScrollbars() - { - Point preferredSize = getTableSize(); - - Rectangle clientArea = getClientArea(); - - // First, figure out if the scrollbars should be visible and turn them - // on right away - // this will allow the computations further down to accommodate the - // correct client - // area - - // Turn the scrollbars on if necessary and do it all over again if - // necessary. This ensures - // that if a scrollbar is turned on/off, the other scrollbars - // visibility may be affected (more - // area may have been added/removed. - for (int doublePass = 1; doublePass <= 2; doublePass++) { - - if (preferredSize.y > clientArea.height) { - vScroll.setVisible(true); - } else { - vScroll.setVisible(false); - vScroll.setValues(0, 0, 1, 1, 1, 1); - } - if (preferredSize.x > clientArea.width) { - hScroll.setVisible(true); - } else { - hScroll.setVisible(false); - hScroll.setValues(0, 0, 1, 1, 1, 1); - } - - // get the clientArea again with the now visible/invisible - // scrollbars - clientArea = getClientArea(); - } - - // if the scrollbar is visible set its values - if (vScroll.getVisible()) { - int max = getItemCount(); - int thumb = (getVisibleGridHeight() + 1) / (getItemHeight() + 1); - - // if possible, remember selection, if selection is too large, just - // make it the max you can - int selection = Math.min(vScroll.getSelection(), max); - - vScroll.setValues(selection, 0, max, thumb, 1, thumb); - } - - // if the scrollbar is visible set its values - if (hScroll.getVisible()) { - - if (!columnScrolling) { - // horizontal scrolling works pixel by pixel - - int hiddenArea = preferredSize.x - clientArea.width + 1; - - // if possible, remember selection, if selection is too large, - // just - // make it the max you can - int selection = Math.min(hScroll.getSelection(), hiddenArea - 1); - - hScroll.setValues(selection, 0, hiddenArea + clientArea.width - 1, clientArea.width, - HORZ_SCROLL_INCREMENT, clientArea.width); - } else { - // horizontal scrolling is column by column - - int hiddenArea = preferredSize.x - clientArea.width + 1; - - int max = 0; - int i = 0; - - while (hiddenArea > 0 && i < getColumnCount()) { - GridColumn col = columns.get(i); - - i++; - - hiddenArea -= col.getWidth(); - max++; - } - - max++; - - // max should never be greater than the number of visible cols - int visCols = columns.size(); - max = Math.min(visCols, max); - - // if possible, remember selection, if selection is too large, - // just - // make it the max you can - int selection = Math.min(hScroll.getSelection(), max); - - hScroll.setValues(selection, 0, max, 1, 1, 1); - } - } - - } - - /** - * Updates cell selection. - * - * @param newCell newly clicked, navigated to cell. - * @param stateMask state mask during preceeding mouse or key event. - * @param dragging true if the user is dragging. - * @param reverseDuplicateSelections true if the user is reversing selection rather than adding to. - * @return selection event that will need to be fired or null. - */ - @Nullable - private Event updateCellSelection( - @NotNull GridPos newCell, - int stateMask, - boolean dragging, - boolean reverseDuplicateSelections, - EventSource eventSource) - { - return updateCellSelection(Collections.singletonList(newCell), stateMask, dragging, reverseDuplicateSelections, eventSource); - } - - /** - * Updates cell selection. - * - * @param newCells newly clicked, navigated to cells. - * @param stateMask state mask during preceeding mouse or key event. - * @param dragging true if the user is dragging. - * @param reverseDuplicateSelections true if the user is reversing selection rather than adding to. - * @return selection event that will need to be fired or null. - */ - @Nullable - private Event updateCellSelection( - @NotNull List newCells, - int stateMask, - boolean dragging, - boolean reverseDuplicateSelections, - EventSource eventSource) - { - boolean shift = (stateMask & SWT.MOD2) == SWT.MOD2; - boolean ctrl = (stateMask & SWT.MOD1) == SWT.MOD1; - if (eventSource == EventSource.KEYBOARD) { - ctrl = false; - } - - if (!shift) { - shiftSelectionAnchorColumn = null; - shiftSelectionAnchorItem = -1; - } - - List oldSelection = null; - if (!shift && !ctrl) { - if (newCells.size() == 1 && - newCells.size() == selectedCells.size() && - newCells.get(0).equals(selectedCells.iterator().next())) - { - return null; - } - - selectedCells.clear(); - for (GridPos newCell : newCells) { - addToCellSelection(newCell); - } - - } else if (shift) { - - GridPos newCell = newCells.get(0); //shift selection should only occur with one cell, ignoring others - oldSelection = new ArrayList<>(selectedCells); - - if ((focusColumn == null) || (focusItem < 0)) { - return null; - } - - shiftSelectionAnchorColumn = getColumn(newCell.col); - shiftSelectionAnchorItem = newCell.row; - - if (ctrl) { - selectedCells.clear(); - selectedCells.addAll(selectedCellsBeforeRangeSelect); - } else { - selectedCells.clear(); - } - - - GridColumn currentColumn = focusColumn; - int currentItem = focusItem; - - GridColumn endColumn = getColumn(newCell.col); - int endItem = newCell.row; - - Point newRange = getSelectionRange(currentItem, currentColumn, endItem, endColumn); - - currentColumn = getColumn(newRange.x); - endColumn = getColumn(newRange.y); - - GridColumn startCol = currentColumn; - - if (currentItem > endItem) { - int temp = currentItem; - currentItem = endItem; - endItem = temp; - } - - boolean firstLoop = true; - do { - if (!firstLoop) { - currentItem++; - } - - firstLoop = false; - - boolean firstLoop2 = true; - - currentColumn = startCol; - - do { - if (!firstLoop2) { - int index = indexOf(currentColumn) + 1; - - if (index < columns.size()) { - currentColumn = columns.get(index); - } else { - currentColumn = null; - } - - if (currentColumn != null) - if (indexOf(currentColumn) > indexOf(endColumn)) - currentColumn = null; - } - - firstLoop2 = false; - - if (currentColumn != null) { - GridPos cell = new GridPos(indexOf(currentColumn), currentItem); - addToCellSelection(cell); - } - } while (currentColumn != endColumn && currentColumn != null); - } while (currentItem != endItem); - - if (selectedCells.equals(newCells)) { - return null; - } - - } else /*if (eventSource == EventSource.MOUSE)*/ { - // Ctrl selection works only for mouse events - boolean reverse = reverseDuplicateSelections; - if (!selectedCells.containsAll(newCells)) - reverse = false; - - if (dragging) { - selectedCells.clear(); - selectedCells.addAll(selectedCellsBeforeRangeSelect); - } - - if (reverse) { - selectedCells.removeAll(newCells); - } else { - for (GridPos newCell : newCells) { - addToCellSelection(newCell); - } - } - } - if (oldSelection != null && oldSelection.size() == selectedCells.size() && selectedCells.containsAll(oldSelection)) { - return null; - } - - updateSelectionCache(); - - Event e = new Event(); - if (dragging) { - e.detail = SWT.DRAG; - followupCellSelectionEventOwed = true; - } - - Rectangle clientArea = getClientArea(); - redraw(clientArea.x, clientArea.y, clientArea.width, clientArea.height, false); - - return e; - } - - private boolean addToCellSelection(GridPos newCell) - { - if (newCell.col < 0 || newCell.col >= columns.size()) - return false; - - if (newCell.row < 0 || newCell.row >= getItemCount()) - return false; - - return selectedCells.add(newCell); - } - - private void updateSelectionCache() - { - //Update the list of which columns have all their cells selected - selectedColumns.clear(); - selectedRows.clear(); - - IntKeyMap columnIndices = new IntKeyMap<>(); - for (GridPos cell : selectedCells) { - columnIndices.put(cell.col, Boolean.TRUE); - selectedRows.put(cell.row, Boolean.TRUE); - } - for (Integer columnIndex : columnIndices.keySet()) { - selectedColumns.add(columns.get(columnIndex)); - } - selectedColumns.sort(Comparator.comparingInt(GridColumn::getIndex)); - } - - /** - * Initialize all listeners. - */ - private void initListeners() - { - disposeListener = this::onDispose; - addListener(SWT.Dispose, disposeListener); - - addPaintListener(this::onPaint); - addListener(SWT.Resize, e -> onResize()); - - if (getVerticalBar() != null) { - getVerticalBar().addListener(SWT.Selection, e -> onScrollSelection()); - } - - if (getHorizontalBar() != null) { - getHorizontalBar().addListener(SWT.Selection, e -> onScrollSelection()); - } - - addListener(SWT.KeyDown, this::onKeyDown); - - addTraverseListener(e -> e.doit = true); - - addMouseListener(new MouseListener() { - @Override - public void mouseDoubleClick(MouseEvent e) - { - onMouseDoubleClick(e); - } - - @Override - public void mouseDown(MouseEvent e) - { - onMouseDown(e); - } - - @Override - public void mouseUp(MouseEvent e) - { - onMouseUp(e); - } - }); - - addMouseMoveListener(this::onMouseMove); - - addMouseTrackListener(new MouseTrackListener() { - @Override - public void mouseEnter(MouseEvent e) - { - } - - @Override - public void mouseExit(MouseEvent e) - { - onMouseExit(e); - } - - @Override - public void mouseHover(MouseEvent e) - { - } - }); - - addFocusListener(new FocusListener() { - @Override - public void focusGained(FocusEvent e) - { - onFocusIn(); - redraw(); - } - - @Override - public void focusLost(FocusEvent e) - { - redraw(); - } - }); - - // Special code to reflect mouse wheel events if using an external - // scroller - addListener(SWT.MouseWheel, this::onMouseWheel); - } - - private void onFocusIn() - { - if (getItemCount() > 0 && focusItem < 0) { - focusItem = 0; - } - } - - private void onDispose(Event event) - { - removeAll(); - //We only want to dispose of our items and such *after* anybody else who may have been - //listening to the dispose has had a chance to do whatever. - removeListener(SWT.Dispose, disposeListener); - notifyListeners(SWT.Dispose, event); - event.type = SWT.None; - } - - /** - * Mouse wheel event handler. - * - * @param e event - */ - private void onMouseWheel(Event e) - { - if (vScroll.getVisible()) { - vScroll.handleMouseWheel(e); - if (getVerticalBar() == null) - e.doit = false; - } else if (hScroll.getVisible()) { - hScroll.handleMouseWheel(e); - if (getHorizontalBar() == null) - e.doit = false; - } - } - - /** - * Mouse down event handler. - * - * @param e event - */ - private void onMouseDown(MouseEvent e) - { - // for some reason, SWT prefers the children to get focus if - // there are any children - // the setFocus method on Composite will not set focus to the - // Composite if one of its - // children can get focus instead. This only affects the grid - // when an editor is open - // and therefore the grid has a child. The solution is to - // forceFocus() - if ((getStyle() & SWT.NO_FOCUS) != SWT.NO_FOCUS) { - forceFocus(); - } - - //if populated will be fired at end of method. - Event selectionEvent = null; - - cellSelectedOnLastMouseDown = false; - cellRowSelectedOnLastMouseDown = false; - cellColumnSelectedOnLastMouseDown = false; - - if (hoveringOnColumnSorter) { - handleHoverOnColumnHeader(e.x, e.y); - if (hoveringOnColumnSorter) { - return; - } - } - - if(hoveringOnColumnFilter) { - handleHoverOnColumnHeader(e.x, e.y); - if (hoveringOnColumnFilter) { - return; - } - } - - if (hoveringOnColumnResizer) { - if (e.button == 1) { - resizingColumn = true; - resizingStartX = e.x; - resizingColumnStartWidth = columnBeingResized.getWidth(); - } - return; - } - - Point point = new Point(e.x, e.y); - int row = getRow(point); - - if (isListening(SWT.DragDetect)) { - - if (hoveringOnHeaderDragArea && hoveringColumn != null) { - if (e.button == 1 && (hoveringColumn.isOverIcon(e.x, e.y) || selectedColumns.contains(hoveringColumn))) { - if (dragDetect(e)) { - // Drag and drop started - headerColumnDragStarted = true; - return; - } - } - } else if (hoveringOnRowHeader && hoveringRow != null) { - if (e.button == 1 && selectedRows.containsKey(hoveringRow) && dragDetect(e)) { - rowHeaderDragStarted = true; - return; - } - } - } - headerColumnDragStarted = false; - rowHeaderDragStarted = false; - - GridColumn col = null; - if (row >= 0) { - col = getColumn(point); - boolean isSelectedCell = false; - if (col != null) { - isSelectedCell = selectedCells.contains(new GridPos(col.getIndex(), row)); - } - - if (col == null && rowHeaderVisible && e.x <= rowHeaderWidth) { - boolean shift = ((e.stateMask & SWT.MOD2) != 0); - boolean ctrl = false; - if (!shift) { - ctrl = ((e.stateMask & SWT.MOD1) != 0); - } - - if (e.button == 1 && !shift && !ctrl) { - GridNode node = rowNodes.get(rowElements[row]); - GridNode parentNode = parentNodes[row]; - if (node != null && node.state != IGridContentProvider.ElementState.NONE) { - if (GridRowRenderer.isOverExpander(e.x, parentNode == null ? 0 : parentNode.level)) - { - toggleRowState(row); - return; - } - } - } - List cells = new ArrayList<>(); - - if (shift) { - getCells(row, focusItem, cells); - } else { - getCells(row, cells); - } - - int newStateMask = SWT.NONE; - if (ctrl) newStateMask = SWT.MOD1; - - selectionEvent = updateCellSelection(cells, newStateMask, shift, ctrl, EventSource.MOUSE); - cellRowSelectedOnLastMouseDown = (getCellSelectionCount() > 0); - - if (!shift) { - //set focus back to the first visible column - focusColumn = getColumn(new Point(rowHeaderWidth + 1, e.y)); - - focusItem = row; - } - showItem(row); - redraw(); - //return; - } else if (e.button == 1 || (e.button == 3 && col != null && !isSelectedCell)) { - if (col != null) { - selectionEvent = updateCellSelection(new GridPos(col.getIndex(), row), e.stateMask, false, true, EventSource.MOUSE); - // Trigger selection event always! - // It makes sense if grid content was changed but selection remains the same - // If user clicks on the same selected cell value - selection event will trigger value redraw in panels - selectionEvent = new Event(); - cellSelectedOnLastMouseDown = (getCellSelectionCount() > 0); - - if (e.stateMask != SWT.MOD2) { - focusColumn = col; - focusItem = row; - } - //showColumn(col); - showItem(row); - redraw(); - } - } else { - return; - } - } else if (e.button == 1 && rowHeaderVisible && e.x <= rowHeaderWidth && e.y < headerHeight) { - // Nothing to select - if (getItemCount() == 0) { - return; - } - - //click on the top left corner means select everything - selectionEvent = selectAllCellsInternal(e.stateMask); - - focusColumn = getColumn(new Point(rowHeaderWidth + 1, 1)); - focusItem = getTopIndex(); - } else if (e.button == 1 && columnHeadersVisible && e.y <= headerHeight) { - //column cell selection - col = getColumn(point); - - if (col == null) return; - - if (getItemCount() == 0) - return; - - List cells = new ArrayList<>(); - getCells(col, cells); - - selectionEvent = updateCellSelection(cells, e.stateMask, false, true, EventSource.MOUSE); - cellColumnSelectedOnLastMouseDown = (getCellSelectionCount() > 0); - - if (getItemCount() > 0) { - focusColumn = col; - focusItem = 0; - } - - showColumn(col); - redraw(); - } else { - // Change focus column anyway - GridColumn column = getColumn(point); - if (column == null) { - // Clicked on top-left cell or outside of grid - return; - } - focusColumn = column; - } - - if (selectionEvent != null) { - selectionEvent.stateMask = e.stateMask; - selectionEvent.button = e.button; - selectionEvent.data = new GridCell(col == null ? null : col.getElement(), row < 0 ? null : rowElements[row]); - selectionEvent.x = e.x; - selectionEvent.y = e.y; - notifyListeners(SWT.Selection, selectionEvent); - } - - - } - - private void toggleRowState(int row) { - GridNode node = rowNodes.get(rowElements[row]); - if (node == null || node.state == IGridContentProvider.ElementState.NONE) { - log.error("Row [" + row + "] state can't be toggled"); - return; - } - if (node.state == IGridContentProvider.ElementState.EXPANDED) { - // Collapse node. Remove all elements with different parent - int deleteTo; - for (deleteTo = row + 1; deleteTo < rowElements.length; deleteTo++) { - if (!node.isParentOf(parentNodes[deleteTo])) { - break; - } - } - rowElements = ArrayUtils.deleteArea(Object.class, rowElements, row + 1, deleteTo - 1); - parentNodes = ArrayUtils.deleteArea(GridNode.class, parentNodes, row + 1, deleteTo - 1); - node.state = IGridContentProvider.ElementState.COLLAPSED; - } else { - // Expand node - List result = new ArrayList<>(); - List parents = new ArrayList<>(); - collectRows(result, parents, node, node.rows, node.level); - rowElements = ArrayUtils.insertArea(Object.class, rowElements, row + 1, result.toArray()); - parentNodes = ArrayUtils.insertArea(GridNode.class, parentNodes, row + 1, parents.toArray()); - node.state = IGridContentProvider.ElementState.EXPANDED; - } - - if (focusItem > row) { - focusItem = row; - } - selectedCells.removeIf(pos -> pos.row > row); - updateSelectionCache(); - computeHeaderSizes(); - this.scrollValuesObsolete = true; - redraw(); - } - - /** - * Mouse double click event handler. - * - * @param e event - */ - private void onMouseDoubleClick(MouseEvent e) - { - if (e.button == 1) { - - if (hoveringOnColumnResizer) { - columnBeingResized.pack(true); - resizingColumn = false; - handleHoverOnColumnHeader(e.x, e.y); - redraw(); - return; - } - - Point point = new Point(e.x, e.y); - int row = getRow(point); - GridColumn col = getColumn(point); - if (row >= 0) { - if (col != null) { - if (isListening(SWT.DefaultSelection)) { - Event newEvent = new Event(); - newEvent.data = new GridCell(col.getElement(), rowElements[row]); - - notifyListeners(SWT.DefaultSelection, newEvent); - } - } else { - GridNode node = rowNodes.get(rowElements[row]); - GridNode parentNode = parentNodes[row]; - if (node != null && node.state != IGridContentProvider.ElementState.NONE) { - if (!GridRowRenderer.isOverExpander(e.x, parentNode == null ? 0 : parentNode.level)) - { - toggleRowState(row); - } - } - } - } - } - } - - /** - * Mouse up handler. - * - * @param e event - */ - private void onMouseUp(MouseEvent e) - { - if (focusColumn != null && focusItem >= 0) { - if (e.button == 1 && cellRenderer.isOverLink(focusColumn, focusItem, e.x, e.y)) { - // Navigate link - Event event = new Event(); - event.x = e.x; - event.y = e.y; - event.stateMask = e.stateMask; - event.data = new GridCell(focusColumn.getElement(), rowElements[focusItem]); - notifyListeners(Event_NavigateLink, event); - return; - } - } - - cellSelectedOnLastMouseDown = false; - - if (hoveringOnColumnSorter) { - handleHoverOnColumnHeader(e.x, e.y); - if (hoveringOnColumnSorter) { - if (e.button == 1) { - Event event = new Event(); - event.x = e.x; - event.y = e.y; - event.data = columnBeingSorted == null ? null : columnBeingSorted.getElement(); - event.stateMask = e.stateMask; - notifyListeners(Event_ChangeSort, event); - return; - } - } - } - - if (hoveringOnColumnFilter) { - handleHoverOnColumnHeader(e.x, e.y); - if (hoveringOnColumnFilter) { - if (e.button == 1) { - Event event = new Event(); - event.x = e.x; - event.y = e.y; - event.data = columnBeingFiltered == null ? null : columnBeingFiltered.getElement(); - event.stateMask = e.stateMask; - notifyListeners(Event_FilterColumn, event); - return; - } - - } - } - - if (resizingColumn) { - resizingColumn = false; - handleHoverOnColumnHeader(e.x, e.y); // resets cursor if - // necessary - return; - } - - if (cellDragSelectionOccurring || cellRowDragSelectionOccurring || cellColumnDragSelectionOccurring) { - cellDragSelectionOccurring = false; - cellRowDragSelectionOccurring = false; - cellColumnDragSelectionOccurring = false; - setCursor(null); - - if (followupCellSelectionEventOwed) { - Event se = new Event(); - se.button = e.button; - Point point = new Point(e.x, e.y); - GridColumn column = getColumn(point); - int rowIndex = getRow(point); - if (column != null && rowIndex >= 0) { - se.data = new GridCell(column.getElement(), rowElements[rowIndex]); - } - se.stateMask = e.stateMask; - se.x = e.x; - se.y = e.y; - se.detail = SWT.DROP_DOWN; - - notifyListeners(SWT.Selection, se); - followupCellSelectionEventOwed = false; - } - } - - } - - /** - * Mouse move event handler. - * - * @param e event - */ - private void onMouseMove(MouseEvent e) - { - //if populated will be fired at end of method. - Event selectionEvent = null; - - if ((e.stateMask & SWT.BUTTON1) == 0) { - - handleHovering(e.x, e.y); - - } else { - - if (resizingColumn) { - handleColumnResizerDragging(e.x); - return; - } - { - if (!cellDragSelectionOccurring && cellSelectedOnLastMouseDown) { - cellDragSelectionOccurring = true; - //XXX: make this user definable - setCursor(getDisplay().getSystemCursor(SWT.CURSOR_CROSS)); - cellDragCTRL = ((e.stateMask & SWT.MOD1) != 0); - if (cellDragCTRL) { - selectedCellsBeforeRangeSelect.clear(); - selectedCellsBeforeRangeSelect.addAll(selectedCells); - } - } - if (!cellRowDragSelectionOccurring && cellRowSelectedOnLastMouseDown) { - cellRowDragSelectionOccurring = true; - setCursor(getDisplay().getSystemCursor(SWT.CURSOR_CROSS)); - cellDragCTRL = ((e.stateMask & SWT.MOD1) != 0); - if (cellDragCTRL) { - selectedCellsBeforeRangeSelect.clear(); - selectedCellsBeforeRangeSelect.addAll(selectedCells); - } - } - - if (!cellColumnDragSelectionOccurring && cellColumnSelectedOnLastMouseDown) { - cellColumnDragSelectionOccurring = true; - setCursor(getDisplay().getSystemCursor(SWT.CURSOR_CROSS)); - cellDragCTRL = ((e.stateMask & SWT.MOD1) != 0); - if (cellDragCTRL) { - selectedCellsBeforeRangeSelect.clear(); - selectedCellsBeforeRangeSelect.addAll(selectedCells); - } - } - - int ctrlFlag = (cellDragCTRL ? SWT.MOD1 : SWT.NONE); - - if (cellDragSelectionOccurring && handleCellHover(e.x, e.y)) { - GridColumn intentColumn = hoveringColumn; - int intentItem = hoveringItem; - - if (hoveringItem < 0) { - if (e.y > headerHeight) { - //then we must be hovering way to the bottom - intentItem = Math.min(getItemCount() - 1, getBottomIndex() + 1); - } else { - intentItem = Math.max(0, getTopIndex() - 1); - } - } - - - if (hoveringColumn == null) { - if (e.x > rowHeaderWidth) { - //then we must be hovering way to the right - intentColumn = columns.get(columns.size() - 1); - } else { - intentColumn = columns.get(0); - } - } - - showColumn(intentColumn); - showItem(intentItem); - GridPos newCell = new GridPos(intentColumn.getIndex(), intentItem); - selectionEvent = updateCellSelection(newCell, ctrlFlag | SWT.MOD2, true, false, EventSource.MOUSE); - } - if (cellRowDragSelectionOccurring && handleCellHover(e.x, e.y)) { - int intentItem = hoveringItem; - - if (hoveringItem < 0) { - if (e.y > headerHeight) { - //then we must be hovering way to the bottom - intentItem = getTopIndex() + 1; - } else { - if (getTopIndex() > 0) { - intentItem = getTopIndex() - 1; - } else { - intentItem = 0; - } - } - } - - List cells = new ArrayList<>(); - - getCells(intentItem, focusItem, cells); - - showItem(intentItem); - selectionEvent = updateCellSelection(cells, ctrlFlag, true, false, EventSource.MOUSE); - } - final GridColumn prevHoveringColumn = hoveringColumn; - if (cellColumnDragSelectionOccurring && handleCellHover(e.x, e.y)) { - boolean dragging; - List newSelected = new ArrayList<>(); - - GridColumn iterCol = hoveringColumn; - if (iterCol != null) { - boolean decreasing = (indexOf(iterCol) > indexOf(focusColumn)); - dragging = true; - - while (iterCol != null) { - getCells(iterCol, newSelected); - - if (iterCol == focusColumn) { - break; - } - - if (decreasing) { - iterCol = getPreviousVisibleColumn(iterCol); - } else { - iterCol = getNextVisibleColumn(iterCol); - } - - } - } else { - dragging = false; - if (e.x <= rowHeaderWidth) { - GridColumn prev = prevHoveringColumn == null ? null : getPreviousVisibleColumn(prevHoveringColumn); - if (prev != null) { - showColumn(prev); - getCells(prev, newSelected); - ctrlFlag = SWT.MOD1; - } - } else { - - } - - } - - selectionEvent = updateCellSelection(newSelected, ctrlFlag, dragging, false, EventSource.MOUSE); - } - - } - } - - if (selectionEvent != null) { - selectionEvent.stateMask = e.stateMask; - selectionEvent.button = e.button; - Point point = new Point(e.x, e.y); - GridColumn column = getColumn(point); - int rowIndex = getRow(point); - if (column != null && rowIndex >= 0) { - selectionEvent.data = new GridCell(column.getElement(), rowElements[rowIndex]); - } - selectionEvent.x = e.x; - selectionEvent.y = e.y; - notifyListeners(SWT.Selection, selectionEvent); - } - } - - /** - * Handles the assignment of the correct values to the hover* field - * variables that let the painting code now what to paint as hovered. - * - * @param x mouse x coordinate - * @param y mouse y coordinate - */ - private void handleHovering(int x, int y) - { - handleCellHover(x, y); - - if (columnHeadersVisible) { - handleHoverOnColumnHeader(x, y); - } - if (!hoveringOnHeader && rowHeaderVisible) { - handleHoverOnRowHeader(x, y); - } - } - - /** - * Refreshes the hover* variables according to the mouse location and - * current state of the table. This is useful is some method call, caused - * the state of the table to change and therefore the hover effects may have - * become out of date. - */ - private void refreshHoverState() - { - Point p = getDisplay().map(null, this, getDisplay().getCursorLocation()); - handleHovering(p.x, p.y); - } - - /** - * Mouse exit event handler. - * - * @param e event - */ - private void onMouseExit(MouseEvent e) - { - hoveringItem = -1; - hoveringDetail = null; - hoveringColumn = null; - redraw(); - } - - public void scrollHorizontally(int count) { - Rectangle clientArea = getClientArea(); - GridColumn leftColumn = null, rightColumn = null; - for (GridColumn column : columns) { - Rectangle bounds = column.getBounds(); - if (leftColumn == null) { - if (bounds.x + bounds.width > 0) { - leftColumn = column; - } - } else { - if (bounds.x + bounds.width > clientArea.width) { - rightColumn = column; - break; - } - } - } - GridColumn scrollTo = null; - if (count > 0) { - if (leftColumn != null) { - scrollTo = getPreviousVisibleColumn(leftColumn); - if (scrollTo == null) { - scrollTo = leftColumn; - } - } - } else { - if (rightColumn != null) { - scrollTo = getNextVisibleColumn(rightColumn); - if (scrollTo == null) { - scrollTo = rightColumn; - } - } - - } - if (scrollTo != null) { - showColumn(scrollTo); - } - } - - /** - * Key down event handler. - * - * @param e event - */ - private void onKeyDown(Event e) - { - if (focusColumn == null) { - if (columns.size() == 0) - return; - - focusColumn = getColumn(0); - } - - if (e.character == '\r' && focusItem >= 0 && focusItem < rowElements.length) { - Event newEvent = new Event(); - newEvent.data = new GridCell(focusColumn.getElement(), rowElements[focusItem]); - - notifyListeners(SWT.DefaultSelection, newEvent); - return; - } - - int newSelection = -1; - GridColumn newColumnFocus = null; - - //These two variables are used because the key navigation when the shift key is down is - //based, not off the focus item/column, but rather off the implied focus (i.e. where the - //keyboard has extended focus to). - int impliedFocusItem = focusItem; - GridColumn impliedFocusColumn = focusColumn; - - boolean ctrlPressed = ((e.stateMask & SWT.MOD1) != 0); - boolean shiftPressed = ((e.stateMask & SWT.MOD2) != 0); - - //if (shiftPressed) { - if (shiftSelectionAnchorColumn != null) { - impliedFocusItem = shiftSelectionAnchorItem; - impliedFocusColumn = shiftSelectionAnchorColumn; - } - //} - switch (e.keyCode) { - case SWT.ARROW_RIGHT: - { - if (impliedFocusItem >= 0) { - newSelection = impliedFocusItem; - - int index = indexOf(impliedFocusColumn) + 1; - - if (index < columns.size()) { - newColumnFocus = columns.get(index); - } else { - newColumnFocus = impliedFocusColumn; - } - } - } - break; - case SWT.ARROW_LEFT: - { - if (impliedFocusItem >= 0) { - newSelection = impliedFocusItem; - - int index = indexOf(impliedFocusColumn); - - if (index > 0) { - newColumnFocus = columns.get(index - 1); - } else { - newColumnFocus = impliedFocusColumn; - } - } - } - break; - case SWT.ARROW_UP: - if (impliedFocusItem >= 0) { - newSelection = getPreviousVisibleItem(impliedFocusItem); - } - - newColumnFocus = impliedFocusColumn; - - break; - case SWT.ARROW_DOWN: - if (impliedFocusItem >= 0) { - newSelection = getNextVisibleItem(impliedFocusItem); - } else { - if (getItemCount() > 0) { - newSelection = 0; - } - } - - newColumnFocus = impliedFocusColumn; - break; - case SWT.HOME: - if (ctrlPressed || columns.size() == 1) { - newSelection = 0; - } else { - newSelection = impliedFocusItem; - } - newColumnFocus = columns.get(0); - - break; - case SWT.END: - { - if ((ctrlPressed || columns.size() == 1) && getItemCount() > 0) { - newSelection = getItemCount() - 1; - } else { - newSelection = impliedFocusItem; - } - newColumnFocus = columns.get(columns.size() - 1); - } - - break; - case SWT.PAGE_UP: - int topIndex = getTopIndex(); - - newSelection = topIndex; - - if ((impliedFocusItem >= 0 && impliedFocusItem == topIndex) || focusItem == topIndex) { - RowRange range = getRowRange(getTopIndex(), getVisibleGridHeight(), false, true); - newSelection = range.startIndex; - } - - newColumnFocus = impliedFocusColumn; - //newColumnFocus = focusColumn; - break; - case SWT.PAGE_DOWN: - int bottomIndex = getBottomIndex(); - - newSelection = bottomIndex; - - if (!isShown(bottomIndex)) { - // the item at bottom index is not shown completely - int tmpItem = getPreviousVisibleItem(newSelection); - if (tmpItem >= 0) - newSelection = tmpItem; - } - - if ((impliedFocusItem >= 0 && impliedFocusItem >= bottomIndex - 1) || focusItem == bottomIndex - 1) { - RowRange range = getRowRange(getBottomIndex(), getVisibleGridHeight(), true, false); - newSelection = range.endIndex; - } - - newColumnFocus = impliedFocusColumn; - //newColumnFocus = focusColumn; - break; - case '+': - case '-': - case '=': - case SWT.KEYPAD_ADD: - case SWT.KEYPAD_SUBTRACT: - if (focusItem >= 0) { - GridNode node = rowNodes.get(rowElements[focusItem]); - if (node != null) { - boolean isPlus = (e.keyCode == '+' || e.keyCode == '=' || e.keyCode == SWT.KEYPAD_ADD); - if ((node.state == IGridContentProvider.ElementState.EXPANDED && !isPlus) || - (node.state == IGridContentProvider.ElementState.COLLAPSED && isPlus)) - { - toggleRowState(focusItem); - } - } - } - break; - default: - break; - } - - if (newSelection < 0) { - return; - } - - if (newColumnFocus != null) { - //if (e.stateMask != SWT.MOD1) { - Event selEvent = updateCellSelection( - new GridPos(newColumnFocus.getIndex(), newSelection), - e.stateMask, - false, - false, - EventSource.KEYBOARD); - //} - - if (!shiftPressed) - focusColumn = newColumnFocus; - showColumn(newColumnFocus); - - if (!shiftPressed) { - if (newSelection < 0) { - focusItem = -1; - } else { - focusItem = newSelection; - } - } - showItem(newSelection); - - GridCell newPos; - if (newSelection >= 0 && newSelection < rowElements.length) { - newPos = new GridCell(newColumnFocus.getElement(), rowElements[newSelection]); - } else { - newPos = null; - } - if (selEvent != null) { - selEvent.stateMask = e.stateMask; - selEvent.character = e.character; - selEvent.keyCode = e.keyCode; - selEvent.data = newPos; - notifyListeners(SWT.Selection, selEvent); - } - - redraw(); - } - } - - /** - * Resize event handler. - */ - private void onResize() - { - - //CGross 1/2/08 - I don't really want to be doing this.... - //I shouldn't be changing something you user configured... - //leaving out for now -// if (columnScrolling) -// { -// int maxWidth = getClientArea().width; -// if (rowHeaderVisible) -// maxWidth -= rowHeaderWidth; -// -// for (Iterator cols = columns.iterator(); cols.hasNext();) { -// GridColumn col = (GridColumn) cols.next(); -// if (col.getWidth() > maxWidth) -// col.setWidth(maxWidth); -// } -// } - - scrollValuesObsolete = true; - topIndex = -1; - bottomIndex = -1; - } - - /** - * Scrollbar selection event handler. - */ - private void onScrollSelection() - { - topIndex = -1; - bottomIndex = -1; - refreshHoverState(); - final Rectangle clientArea = getClientArea(); - redraw(clientArea.x, clientArea.y, clientArea.width, clientArea.height, false); - } - - /** - * Returns the intersection of the given column and given item. - * - * @param column column - * @param item item - * @return x,y of top left corner of the cell - */ - Point getOrigin(GridColumn column, int item) - { - int x = 0; - - if (rowHeaderVisible) { - x += rowHeaderWidth; - } - - x -= getHScrollSelectionInPixels(); - - for (int i = 0; i < columns.size(); i++) { - GridColumn colIter = columns.get(i); - if (colIter == column) { - break; - } - x += colIter.getWidth(); - } - - int y = 0; - if (item >= 0) { - if (columnHeadersVisible) { - y += headerHeight; - } - - int currIndex = getTopIndex(); - - if (item == -1) { - SWT.error(SWT.ERROR_INVALID_ARGUMENT); - } - - while (currIndex != item) { - if (currIndex < item) { - y += getItemHeight() + 1; - currIndex++; - } else if (currIndex > item) { - currIndex--; - y -= getItemHeight() + 1; - } - } - } else if (columnHeadersVisible && column.getParent() != null) { - for (GridColumn parent = column.getParent(); parent != null; parent = parent.getParent()) { - y += parent.getHeaderHeight(false, false); - } - } - - return new Point(x, y); - } - - /** - * Sets the hovering variables (hoverItem,hoveringColumn) as well as - * hoverDetail by talking to the cell renderers. Triggers a redraw if - * necessary. - * - * @param x mouse x - * @param y mouse y - * @return true if a new section of the table is now being hovered - */ - private boolean handleCellHover(int x, int y) - { - Point point = new Point(x, y); - final GridColumn col = getColumn(point); - final int row = getRow(point); - Integer detail = - (hoveringOnColumnSorter ? 1000000 : 0) + - (hoveringOnColumnFilter ? 1000000 : 0) + - y; - - boolean hoverChange = false; - - if (hoveringItem != row || !CommonUtils.equalObjects(hoveringDetail, detail) || hoveringColumn != col || y <= headerHeight) { - hoveringItem = row; - hoveringDetail = detail; - hoveringColumn = col; - - hoverChange = true; - } - - // Check for link - boolean overLink = false; - if (col != null && row >= 0) { - if (cellRenderer.isOverLink(col, row, x, y)) { - overLink = true; - } - } - - if (overLink) { - if (!hoveringOnLink) { - setCursor(sortCursor); - } - } else if (hoveringOnLink) { - setCursor(null); - } - hoveringOnLink = overLink; - - //do normal cell specific tooltip stuff - if (hoverChange) { - - // Check tooltip - String newTip = null; - if ((hoveringItem >= 0) && (hoveringColumn != null)) { - if (overLink) { - newTip = getContentProvider().getCellLinkText(columnElements[col.getIndex()], rowElements[row]); - } - if (CommonUtils.isEmpty(newTip)) { - // get cell specific tooltip - newTip = getCellToolTip(hoveringColumn, hoveringItem); - } - } else if (columnHeadersVisible && hoveringColumn != null && y <= headerHeight) { - // get column header specific tooltip - if (hoveringOnColumnSorter) { - newTip = NLS.bind(CoreMessages.grid_tooltip_sort_by_column, getLabelProvider().getText(hoveringColumn.getElement())); - } else if (hoveringOnColumnFilter) { - newTip = NLS.bind(CoreMessages.grid_tooltip_filter_by_column, getLabelProvider().getText(hoveringColumn.getElement())); - } else { - newTip = hoveringColumn.getHeaderTooltip(); - } - } else if (rowHeaderVisible && hoveringItem >= 0 && x <= rowHeaderWidth) { - newTip = getLabelProvider().getToolTipText(getRowElement(hoveringItem)); - } - - //Avoid unnecessarily resetting tooltip - this will cause the tooltip to jump around - if (newTip != null && !newTip.equals(displayedToolTipText)) { - updateToolTipText(newTip); - } else if (newTip == null && displayedToolTipText != null) { - updateToolTipText(null); - } - displayedToolTipText = newTip; - } - - return hoverChange; - } - - /** - * Sets the tooltip for the whole Grid to the given text. This method is made available - * for subclasses to override, when a subclass wants to display a different than the standard - * SWT/OS tooltip. Generally, those subclasses would override this event and use this tooltip - * text in their own tooltip or just override this method to prevent the SWT/OS tooltip from - * displaying. - * - * @param text tooltip text - */ - private void updateToolTipText(@Nullable String text) - { - ToolTipHandler curHandler = this.toolTipHandler; - if (!CommonUtils.equalObjects(prevToolTip, text)) { - // New tooltip - if (curHandler != null) { - curHandler.cancel(); - } - prevToolTip = text; - this.setToolTipText(""); - this.toolTipHandler = new ToolTipHandler(); - this.toolTipHandler.toolTip = text; - this.toolTipHandler.schedule(500); - } - } - - /** - * Marks the scroll values obsolete so they will be recalculated. - */ - protected void setScrollValuesObsolete() - { - this.scrollValuesObsolete = true; - redraw(); - } - - /** - * Inserts a new column into the table. - * - * @param column new column - * @param index index to insert new column - */ - void newColumn(GridColumn column, int index) - { - - if (index == -1) { - columns.add(column); - } else { - columns.add(index, column); - } - } - - public void recalculateSizes() { - int oldHeaderHeight = headerHeight; - computeHeaderSizes(); - if (oldHeaderHeight != headerHeight) { - scrollValuesObsolete = true; - } - } - - /** - * Returns the current cell in focus. If cell selection is disabled, this method returns null. - * - * @return cell in focus or {@code null}. x represents the column and y the row the cell is in - */ - public GridPos getFocusPos() - { - checkWidget(); - - int x = -1; - - if (focusColumn != null) - x = focusColumn.getIndex(); - focusCell.col = x; - focusCell.row = focusItem; - return focusCell; - } - - @Nullable - public Object getFocusColumnElement() { - return focusColumn == null ? null : focusColumn.getElement(); - } - - @Nullable - public Object getFocusRowElement() { - if (focusItem < 0 || focusItem >= rowElements.length) { - return null; - } - return rowElements[focusItem]; - } - - @Nullable - public GridCell getFocusCell() - { - return posToCell(getFocusPos()); - } - /** - * Sets the focused item to the given item. - * - * @param item item to focus. - */ - public void setFocusItem(int item) - { - checkWidget(); - focusItem = item; - } - - /** - * Sets the focused item to the given column. Column focus is only applicable when cell - * selection is enabled. - * - * @param col column to focus. - */ - public void setFocusColumn(int col) - { - checkWidget(); - GridColumn column = getColumn(col); - if (column == null || column.getGrid() != this) { - SWT.error(SWT.ERROR_INVALID_ARGUMENT); - return; - } - - focusColumn = column; - } - - public void resetFocus() { - focusColumn = null; - focusItem = -1; - } - - /** - * Returns true if the table is set to horizontally scroll column-by-column - * rather than pixel-by-pixel. - * - * @return true if the table is scrolled horizontally by column - */ - private boolean getColumnScrolling() - { - checkWidget(); - return columnScrolling; - } - - /** - * Sets the table scrolling method to either scroll column-by-column (true) - * or pixel-by-pixel (false). - * - * @param columnScrolling true to horizontally scroll by column, false to - * scroll by pixel - */ - private void setColumnScrolling(boolean columnScrolling) - { - checkWidget(); - if (rowHeaderVisible && !columnScrolling) { - return; - } - - this.columnScrolling = columnScrolling; - scrollValuesObsolete = true; - redraw(); - } - - /** - * Selects the given cell. Invalid cells are ignored. - * - * @param cell point whose x values is a column index and y value is an item index - */ - public void selectCell(@NotNull GridPos cell) - { - checkWidget(); - - addToCellSelection(cell); - updateSelectionCache(); - redraw(); - } - - /** - * Selects the given cells. Invalid cells are ignored. - * - * @param cells an array of points whose x value is a column index and y value is an item index - */ - public void selectCells(@NotNull Collection cells) - { - checkWidget(); - - for (GridPos cell : cells) { - addToCellSelection(cell); - } - - updateSelectionCache(); - redraw(); - } - - /** - * Selects all cells in the receiver. - */ - public void selectAllCells() - { - checkWidget(); - selectAllCellsInternal(0); - } - - /** - * Selects all cells in the receiver. - * - * @return An Event object - */ - @Nullable - private Event selectAllCellsInternal(int stateMask) - { - if (columns.size() == 0) - return null; - - if (getItemCount() == 0) - return null; - - GridColumn oldFocusColumn = focusColumn; - int oldFocusItem = focusItem; - - focusColumn = columns.get(0); - focusItem = 0; - - List cells = new ArrayList<>(); - getAllCells(cells); - Event selectionEvent = updateCellSelection(cells, stateMask, false, true, EventSource.KEYBOARD); - - focusColumn = oldFocusColumn; - focusItem = oldFocusItem; - - updateSelectionCache(); - - redraw(); - - return selectionEvent; - } - - /** - * Selects the selection to the given cell. The existing selection is cleared before - * selecting the given cell. - * - * @param cell point whose x values is a column index and y value is an item index - */ - public void setCellSelection(@NotNull GridPos cell) - { - checkWidget(); - - if (!isValidCell(cell)) - SWT.error(SWT.ERROR_INVALID_ARGUMENT); - - selectedCells.clear(); - addToCellSelection(cell); - updateSelectionCache(); - redraw(); - } - - /** - * Returns an array of cells that are currently selected in the - * receiver. The order of the items is unspecified. An empty array indicates - * that no items are selected. - *

- * Note: This is not the actual structure used by the receiver to maintain - * its selection, so modifying the array will not affect the receiver. - *

- * - * @return an array representing the cell selection - */ - @NotNull - public Collection getSelection() - { - if (isDisposed()) { - return Collections.emptyList(); - } - return Collections.unmodifiableCollection(selectedCells); - } - - public List getCellSelection() - { - if (isDisposed() || selectedCells.isEmpty()) { - return Collections.emptyList(); - } - List cells = new ArrayList<>(selectedCells.size()); - for (GridPos pos : selectedCells) { - cells.add(posToCell(pos)); - } - return cells; - } - - @NotNull - public List getColumnSelection() - { - if (selectedColumns.isEmpty()) { - return Collections.emptyList(); - } - List selection = new ArrayList<>(); - for (GridColumn col : selectedColumns) { - selection.add(col.getElement()); - } - return selection; - } - - /** - * Returns selected rows indexes - * @return indexes of selected rows - */ - public Collection getRowSelection() - { - return Collections.unmodifiableCollection(selectedRows.keySet()); - } - - private void getCells(GridColumn col, List cells) - { - if (col.getChildren() != null) { - // Get cells for all leafs - for (int i = 0; i < columns.size(); i++) { - if (columns.get(i).isParent(col)) { - for (int k = 0; k < getItemCount(); k++) { - cells.add(new GridPos(i, k)); - } - } - } - } else { - int colIndex = col.getIndex(); - - for (int i = 0; i < getItemCount(); i++) { - cells.add(new GridPos(colIndex, i)); - } - } - } - - private void getCells(int row, List cells) - { - for (int i = 0; i < columns.size(); i++) { - cells.add(new GridPos(i, row)); - } - } - - private void getAllCells(List cells) - { - for (int i = 0; i < getItemCount(); i++) { - for (int k = 0; k < columns.size(); k++) { - cells.add(new GridPos(k, i)); - } - } - } - - private List getCells(int row) - { - List cells = new ArrayList<>(); - getCells(row, cells); - return cells; - } - - - private void getCells(int startRow, int endRow, List cells) - { - boolean descending = (startRow < endRow); - - int iterItem = endRow; - - do { - getCells(iterItem, cells); - - if (iterItem == startRow) break; - - if (descending) { - iterItem--; - } else { - iterItem++; - } - } while (true); - } - - /** - * Returns a point whose x and y values are the to and from column indexes of the new selection - * range inclusive of all spanned columns. - */ - private Point getSelectionRange(int fromItem, GridColumn fromColumn, int toItem, GridColumn toColumn) - { - if (indexOf(fromColumn) > indexOf(toColumn)) { - GridColumn temp = fromColumn; - fromColumn = toColumn; - toColumn = temp; - } - - if (fromItem > toItem) { - int temp = fromItem; - fromItem = toItem; - toItem = temp; - } - - boolean firstTime = true; - int iterItem = fromItem; - - int fromIndex = fromColumn.getIndex(); - int toIndex = toColumn.getIndex(); - - do { - if (!firstTime) { - iterItem++; - } else { - firstTime = false; - } - - Point cols = getRowSelectionRange(fromColumn, toColumn); - - //check and see if column spanning means that the range increased - if (cols.x != fromIndex || cols.y != toIndex) { - GridColumn newFrom = getColumn(cols.x); - GridColumn newTo = getColumn(cols.y); - - //Unfortunately we have to start all over again from the top with the new range - return getSelectionRange(fromItem, newFrom, toItem, newTo); - } - } while (iterItem != toItem); - - return new Point(fromColumn.getIndex(), toColumn.getIndex()); - } - - /** - * Returns a point whose x and y value are the to and from column indexes of the new selection - * range inclusive of all spanned columns. - */ - private Point getRowSelectionRange(GridColumn fromColumn, GridColumn toColumn) - { - int newFrom = fromColumn.getIndex(); - int newTo = toColumn.getIndex(); - return new Point(newFrom, newTo); - } - - /** - * Returns true if the given cell's x and y values are valid column and - * item indexes respectively. - * - * @param cell cell - */ - private boolean isValidCell(GridPos cell) - { - if (cell.col < 0 || cell.col >= columns.size()) - return false; - - if (cell.row < 0 || cell.row >= getItemCount()) { - return false; - } - // Valid - return true; - } - - - - @Override - public void setFont(Font font) - { - super.setFont(font); - sizingGC.setFont(font); - fontMetrics = sizingGC.getFontMetrics(); - normalFont = font; - } - - public String getCellText(Object colElement, Object rowElement) - { - String text = getContentProvider().getCellText(colElement, rowElement); - // Truncate too long texts (they are really bad for performance) - if (text.length() > MAX_TOOLTIP_LENGTH) { - text = text.substring(0, MAX_TOOLTIP_LENGTH) + " ..."; - } - - return text; - } - - @Nullable - private String getCellToolTip(GridColumn col, int row) - { - String toolTip = getCellText(columnElements[col.getIndex()], rowElements[row]); - if (toolTip == null) { - return null; - } - // Show tooltip only if it's larger than column width - Point ttSize = sizingGC.textExtent(toolTip); - if (ttSize.x > col.getWidth() || ttSize.y > getItemHeight()) { - int gridHeight = getBounds().height; - if (ttSize.y > gridHeight) { - // Too big tool tip - larger than entire grid - // Lets chop it - StringBuilder newToolTip = new StringBuilder(); - StringTokenizer st = new StringTokenizer(toolTip, "'\n"); - int maxLineNumbers = gridHeight / getItemHeight(), lineNumber = 0; - while (st.hasMoreTokens()) { - newToolTip.append(st.nextToken()).append('\n'); - lineNumber++; - if (lineNumber >= maxLineNumbers) { - break; - } - } - toolTip = newToolTip.toString(); - } - return toolTip; - } else { - return ""; - } - } - - @Nullable - public DBPImage getCellImage(Object colElement, Object rowElement) - { - return getContentProvider().getCellImage(colElement, rowElement); - } - - public Color getCellBackground(Object colElement, Object rowElement, boolean selected) - { - Color color = getContentProvider().getCellBackground(colElement, rowElement, selected); - return color != null ? color : getBackground(); - } - - public Color getCellForeground(Object colElement, Object rowElement, boolean selected) - { - Color color = getContentProvider().getCellForeground(colElement, rowElement, selected); - return color != null ? color : getForeground(); - } - - public Rectangle getCellBounds(int columnIndex, int rowIndex) { - if (!isShown(rowIndex)) - return new Rectangle(-1000, -1000, 0, 0); - - GridColumn column = getColumn(columnIndex); - Point origin = getOrigin(column, rowIndex); - - if (origin.x < 0 && isRowHeaderVisible()) - return new Rectangle(-1000, -1000, 0, 0); - - return new Rectangle(origin.x, origin.y, column.getWidth(), getItemHeight()); - } - - void setDefaultBackground(GC gc) { - Color background = getLabelProvider().getBackground(null); - if (background != null) { - gc.setBackground(background); - } - } - - private void drawEmptyColumnHeader(GC gc, int x, int y, int width, int height) - { - gc.setBackground(getContentProvider().getCellHeaderBackground(null)); - - gc.fillRectangle( - x, - y, - width + 1, - height + 1); - } - - private void drawEmptyRowHeader(GC gc, int x, int y, int width, int height) - { - gc.setBackground(getContentProvider().getCellHeaderBackground(null)); - - gc.fillRectangle(x, y, width, height + 1); - - gc.setForeground(getContentProvider().getCellHeaderForeground(null)); - - gc.drawLine( - x + width - 1, - y, - x + width - 1, - y + height - 1); - gc.drawLine( - x, - y + height - 1, - x + width - 1, - y + height - 1); - } - - private void drawEmptyCell(GC gc, int x, int y, int width, int height) { - IGridLabelProvider labelProvider = getLabelProvider(); - Color foreground = labelProvider.getForeground(null); - setDefaultBackground(gc); - gc.setForeground(foreground); - - gc.fillRectangle(x, y, width + 1, height); - - if (isLinesVisible()) { - gc.setForeground(getLineColor()); - gc.drawLine( - x, - y + height, - x + width, - y + height); - gc.drawLine(x + width - 1, - y, - x + width - 1, - y + height); - } - } - - private void drawTopLeftCell(GC gc, int x, int y, int width, int height) { - int sortOrder = getContentProvider().getSortOrder(null); - gc.setBackground(getContentProvider().getCellHeaderBackground(null)); - - gc.fillRectangle( - x, - y, - width - 1, - height + 1); - - gc.setForeground(getContentProvider().getCellHeaderForeground(null)); - - gc.drawLine( - x + width - 1, - y, - x + width - 1, - y + height); - - gc.drawLine( - x, - y + height - 1, - x + width, - y + height - 1); - - if (sortOrder != SWT.NONE) { - int arrowWidth = GridColumnRenderer.SORT_WIDTH; - Rectangle sortBounds = new Rectangle( - x + width - GridColumnRenderer.ARROW_MARGIN - arrowWidth, - y + GridColumnRenderer.TOP_MARGIN, - arrowWidth, - height); - GridColumnRenderer.paintSort(gc, sortBounds, sortOrder); - } - } - - ///////////////////////////////////////////////////////////////////////////////// - // DnD - ///////////////////////////////////////////////////////////////////////////////// - - private void addDragAndDropSupport() - { - final int operations = DND.DROP_MOVE | DND.DROP_COPY;// | DND.DROP_MOVE | DND.DROP_LINK | DND.DROP_DEFAULT; - - final DragSource source = new DragSource(this, operations); - source.setTransfer(GridColumnTransfer.INSTANCE, TextTransfer.getInstance()); - source.addDragListener (new DragSourceListener() { - - private Image dragImage; - private long lastDragEndTime; - - @Override - public void dragStart(DragSourceEvent event) { - if (lastDragEndTime > 0 && System.currentTimeMillis() - lastDragEndTime < 100) { - event.doit = false; - } else { - Rectangle columnBounds; - if (headerColumnDragStarted && hoveringColumn != null) { - draggingColumn = hoveringColumn; - columnBounds = hoveringColumn.getBounds(); - } else if (rowHeaderDragStarted && hoveringRow != null) { - draggingRow = hoveringRow; - int rowFromTop = hoveringRow - getTopIndex(); - columnBounds = new Rectangle(0, rowFromTop * getItemHeight(), getRowHeaderWidth(), getItemHeight()); - } else { - event.doit = false; - return; - } - GC gc = new GC(LightGrid.this); - dragImage = new Image(Display.getCurrent(), columnBounds.width, columnBounds.height); - gc.copyArea( - dragImage, - columnBounds.x, - columnBounds.y); - event.image = dragImage; - gc.dispose(); - } - } - - @Override - public void dragSetData (DragSourceEvent event) { - if (draggingColumn != null) { - if (GridColumnTransfer.INSTANCE.isSupportedType(event.dataType)) { - List elements = new ArrayList<>(); - if (isDragSingleColumn()) { - elements.add(draggingColumn.getElement()); - } else { - for (GridColumn col : selectedColumns) { - elements.add(col.getElement()); - } - } - event.data = elements; - } else if (TextTransfer.getInstance().isSupportedType(event.dataType)) { - // Copy all selected columns - if (selectedColumns.size() > 1 && !isDragSingleColumn()) { - StringBuilder text = new StringBuilder(); - for (GridColumn column : selectedColumns) { - if (text.length() > 0) text.append(", "); - text.append(getLabelProvider().getText(column.getElement())); - } - event.data = text.toString(); - } else { - event.data = getLabelProvider().getText(draggingColumn.getElement()); - } - } - } else if (draggingRow != null) { - if (GridColumnTransfer.INSTANCE.isSupportedType(event.dataType)) { - List elements = new ArrayList<>(); - if (isDragSingleRow()) { - elements.add(getRowElement(draggingRow)); - } else { - for (Integer row : selectedRows.keySet()) { - elements.add(getRowElement(row)); - } - } - event.data = elements; - } else if (TextTransfer.getInstance().isSupportedType(event.dataType)) { - List columns = selectedColumns; - if (columns.isEmpty()) { - columns = LightGrid.this.columns; - } - Set rows = selectedRows.keySet(); - if (rows.isEmpty()) { - rows = Collections.singleton(draggingRow); - } - - StringBuilder text = new StringBuilder(); - for (Integer row : rows) { - if (text.length() > 0) text.append("\n"); - for (int i = 0; i < columns.size(); i++) { - GridColumn column = columns.get(i); - String cellText = getContentProvider().getCellText(column.getElement(), getRowElement(row)); - if (i > 0) text.append(", "); - text.append(cellText); - } - } - event.data = text.toString(); - } - } - } - @Override - public void dragFinished(DragSourceEvent event) { - draggingColumn = null; - draggingRow = null; - if (dragImage != null) { - UIUtils.dispose(dragImage); - dragImage = null; - } - lastDragEndTime = System.currentTimeMillis(); - } - }); - - DropTarget dropTarget = new DropTarget(this, operations); - dropTarget.setTransfer(GridColumnTransfer.INSTANCE, TextTransfer.getInstance()); - dropTarget.addDropListener(new DropTargetListener() { - @Override - public void dragEnter(DropTargetEvent event) - { - handleDragEvent(event); - } - - @Override - public void dragLeave(DropTargetEvent event) - { - handleDragEvent(event); - } - - @Override - public void dragOperationChanged(DropTargetEvent event) - { - handleDragEvent(event); - } - - @Override - public void dragOver(DropTargetEvent event) - { - handleDragEvent(event); - } - - @Override - public void drop(DropTargetEvent event) - { - handleDragEvent(event); - if (event.detail == DND.DROP_MOVE) { - moveColumns(event); - } - } - - @Override - public void dropAccept(DropTargetEvent event) - { - handleDragEvent(event); - } - - private void handleDragEvent(DropTargetEvent event) - { - if (!isDropSupported(event)) { - event.detail = DND.DROP_NONE; - } else { - event.detail = DND.DROP_MOVE; - } - event.feedback = DND.FEEDBACK_SELECT; - } - - private boolean isDropSupported(DropTargetEvent event) - { - if (!hoveringOnHeaderDragArea) { - return false; - } - if (draggingColumn == null || draggingColumn.getGrid() != LightGrid.this) { - return false; - } - GridColumn overColumn = getOverColumn(event); - return draggingColumn != overColumn; - } - - private GridColumn getOverColumn(DropTargetEvent event) { - Point dragPoint = getDisplay().map(null, LightGrid.this, new Point(event.x, event.y)); - return getColumn(dragPoint); - } - - private void moveColumns(DropTargetEvent event) - { - GridColumn overColumn = getOverColumn(event); - if (draggingColumn == null || draggingColumn == overColumn) { - return; - } - IGridController gridController = getGridController(); - if (gridController != null) { - IGridController.DropLocation location;// = IGridController.DropLocation.SWAP; - - Point dropPoint = getDisplay().map(null, LightGrid.this, new Point(event.x, event.y)); - Rectangle columnBounds = overColumn.getBounds(); - if (dropPoint.x > columnBounds.x + columnBounds.width / 2) { - location = IGridController.DropLocation.DROP_AFTER; - } else { - location = IGridController.DropLocation.DROP_BEFORE; - } - gridController.moveColumn(draggingColumn.getElement(), overColumn.getElement(), location); - } - draggingColumn = null; - } - }); - } - - private boolean isDragSingleColumn() { - return draggingColumn != null && !selectedColumns.contains(draggingColumn); - } - - private boolean isDragSingleRow() { - return draggingRow != null && !selectedRows.containsKey(draggingRow); - } - - public final static class GridColumnTransfer extends LocalObjectTransfer> { - - public static final GridColumnTransfer INSTANCE = new GridColumnTransfer(); - private static final String TYPE_NAME = "LighGrid.GridColumn Transfer" + System.currentTimeMillis() + ":" + INSTANCE.hashCode();//$NON-NLS-1$ - private static final int TYPEID = registerType(TYPE_NAME); - - private GridColumnTransfer() { - } - - @Override - protected int[] getTypeIds() { - return new int[] { TYPEID }; - } - - @Override - protected String[] getTypeNames() { - return new String[] { TYPE_NAME }; - } - - } - - -} - - +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.resource.JFaceColors; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.ui.progress.UIJob; +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.Log; +import org.jkiss.dbeaver.model.DBPImage; +import org.jkiss.dbeaver.ui.UIUtils; +import org.jkiss.dbeaver.ui.dnd.LocalObjectTransfer; +import org.jkiss.dbeaver.ui.editors.data.internal.DataEditorsMessages; +import org.jkiss.utils.ArrayUtils; +import org.jkiss.utils.CommonUtils; +import org.jkiss.utils.IntKeyMap; + +import java.util.*; +import java.util.List; + +/** + * LightGrid + * initially based on Nebula grid. Refactored and mostly redone. + * + * @author serge@jkiss.org + * @author chris.gross@us.ibm.com + */ +public abstract class LightGrid extends Canvas { + + private static final Log log = Log.getLog(LightGrid.class); + + private static final int MAX_TOOLTIP_LENGTH = 1000; + + protected static final int Event_ChangeSort = 1000; + protected static final int Event_NavigateLink = 1001; + protected static final int Event_FilterColumn = 1002; + + /** + * Horizontal scrolling increment, in pixels. + */ + private static final int HORZ_SCROLL_INCREMENT = 5; + + /** + * The area to the left and right of the column boundary/resizer that is + * still considered the resizer area. This prevents a user from having to be + * *exactly* over the resizer. + */ + private static final int COLUMN_RESIZER_THRESHOLD = 4; + private static final int DEFAULT_ROW_HEADER_WIDTH = 30; + private static final int MAX_ROW_HEADER_WIDTH = 400; + + + /** + * The minimum width of a column header. + */ + private static final int MIN_COLUMN_HEADER_WIDTH = 32; + + /** + * Threshold for the selection border used for drag n drop + * in mode. + */ + private static final int SELECTION_DRAG_BORDER_THRESHOLD = 2; + private static final boolean MAXIMIZE_SINGLE_COLUMN = false; + // Indicates that last time refreshData was called grid control was hidden (had zero size) + // In that case columns will be repacked even if keepState is true + private boolean controlWasHidden; + + public enum EventSource { + MOUSE, + KEYBOARD, + } + + static class GridNode { + GridNode parent; + Object[] rows; + IGridContentProvider.ElementState state; + int level; + + private GridNode(GridNode parent, Object[] rows, IGridContentProvider.ElementState state, int level) { + this.parent = parent; + this.rows = rows; + this.state = state; + this.level = level; + } + + boolean isParentOf(GridNode node) { + for (GridNode p = node; p != null; p = p.parent) { + if (p == this) { + return true; + } + } + return false; + } + } + + // Tooltips + + private class ToolTipHandler extends UIJob { + private String toolTip; + ToolTipHandler() { + super("ToolTip handler"); + setSystem(true); + } + + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + if (!monitor.isCanceled() && !LightGrid.this.isDisposed()) { + LightGrid.this.setToolTipText(toolTip); + } + toolTipHandler = null; + return Status.OK_STATUS; + } + } + + // Last calculated client area + private volatile static Rectangle lastClientArea; + + private volatile String prevToolTip; + private volatile ToolTipHandler toolTipHandler; + + /** + * Tracks whether the scroll values are correct. If not they will be + * recomputed in onPaint. This allows us to get a free ride on top of the + * OS's paint event merging to assure that we don't perform this expensive + * operation when unnecessary. + */ + private boolean scrollValuesObsolete = false; + + /** + * Reference to the item in focus. + */ + private int focusItem = -1; + + private final Set selectedCells = new TreeSet<>(new GridPos.PosComparator()); + private final List selectedCellsBeforeRangeSelect = new ArrayList<>(); + private final List selectedColumns = new ArrayList<>(); + private final IntKeyMap selectedRows = new IntKeyMap<>(); + + private boolean cellDragSelectionOccurring = false; + private boolean cellRowDragSelectionOccurring = false; + private boolean cellColumnDragSelectionOccurring = false; + private boolean cellDragCTRL = false; + private boolean followupCellSelectionEventOwed = false; + + private boolean cellSelectedOnLastMouseDown; + private boolean cellRowSelectedOnLastMouseDown; + private boolean cellColumnSelectedOnLastMouseDown; + + private boolean headerColumnDragStarted, rowHeaderDragStarted; + + private GridColumn shiftSelectionAnchorColumn; + + private GridColumn focusColumn; + private final GridPos focusCell = new GridPos(-1, -1); + + /** + * List of table columns in creation/index order. + */ + private final List topColumns = new ArrayList<>(); + private final List columns = new ArrayList<>(); + private int maxColumnDepth = 0; + protected Object[] columnElements = new Object[0]; + protected Object[] rowElements = new Object[0]; + private GridNode[] parentNodes = new GridNode[0]; + private final Map rowNodes = new IdentityHashMap<>(); + + private int maxColumnDefWidth = 1000; + + private GridColumnRenderer columnHeaderRenderer; + private GridRowRenderer rowHeaderRenderer; + private GridCellRenderer cellRenderer; + + /** + * Are row headers visible? + */ + private boolean rowHeaderVisible = false; + + /** + * Are column headers visible? + */ + private boolean columnHeadersVisible = false; + + /** + * Type of selection behavior. Valid values are SWT.SINGLE and SWT.MULTI. + */ + private int selectionType = SWT.SINGLE; + + /** + * Default height of items. + */ + private int itemHeight = 1; + + /** + * Width of each row header. + */ + private int rowHeaderWidth = 0; + + /** + * Height of each column header. + */ + private int headerHeight = 0; + + private boolean hoveringOnHeader = false; + private boolean hoveringOnColumnIcon = false; + private boolean hoveringOnColumnSorter = false; + private boolean hoveringOnColumnFilter = false; + private boolean hoveringOnLink = false; + private boolean hoveringOnRowHeader = false; + + private GridColumn columnBeingSorted; + private GridColumn columnBeingFiltered; + private boolean hoveringOnColumnResizer = false; + private GridColumn columnBeingResized; + private boolean resizingColumn = false; + private int resizingStartX = 0; + private int resizingColumnStartWidth = 0; + private int hoveringItem; + private GridColumn hoveringColumn; + private GridColumn draggingColumn; + private Integer hoveringRow; + private Integer draggingRow; + + /** + * String-based detail of what is being hovered over in a cell. This allows + * a renderer to differentiate between hovering over different parts of the + * cell. For example, hovering over a checkbox in the cell or hovering over + * a tree node in the cell. The table does nothing with this string except + * to set it back in the renderer when its painted. The renderer sets this + * during its notify method (InternalWidget.HOVER) and the table pulls it + * back and maintains it so it can be set back when the cell is painted. The + * renderer determines what the hover detail means and how it affects + * painting. + */ + private Object hoveringDetail = null; + + /** + * Are the grid lines visible? + */ + private boolean linesVisible = true; + + @NotNull + private final IGridScrollBar vScroll; + @NotNull + private final IGridScrollBar hScroll; + + /** + * Item selected when a multiple selection using shift+click first occurs. + * This item anchors all further shift+click selections. + */ + private int shiftSelectionAnchorItem; + + private boolean columnScrolling = false; + + /** + * Dispose listener. This listener is removed during the dispose event to allow re-firing of + * the event. + */ + private Listener disposeListener; + + GC sizingGC; + FontMetrics fontMetrics; + Font normalFont; + + @NotNull + private Color lineColor; + private Color lineSelectedColor; + private Color backgroundColor; + private Color foregroundColor; + @NotNull + private Cursor sortCursor; + + /** + * Index of first visible item. The value must never be read directly. It is cached and + * updated when appropriate. #getTopIndex should be called for every client (even internal + * callers). A value of -1 indicates that the value is old and will be recomputed. + * + * @see #bottomIndex + */ + private int topIndex = -1; + /** + * Index of last visible item. The value must never be read directly. It is cached and + * updated when appropriate. #getBottomIndex() should be called for every client (even internal + * callers). A value of -1 indicates that the value is old and will be recomputed. + *

+ * Note that the item with this index is often only partly visible; maybe only + * a single line of pixels is visible. In extreme cases, bottomIndex may be the + * same as topIndex. + * + * @see #topIndex + */ + private int bottomIndex = -1; + + /** + * True if the last visible item is completely visible. The value must never be read directly. It is cached and + * updated when appropriate. #isShown() should be called for every client (even internal + * callers). + * + * @see #bottomIndex + */ + private boolean bottomIndexShownCompletely = false; + + /** + * This is the tooltip text currently used. This could be the tooltip text for the currently + * hovered cell, or the general grid tooltip. See handleCellHover. + */ + private String displayedToolTipText; + + private boolean hoveringOnHeaderDragArea = false; + + /** + * A range of rows in a Grid. + *

+ * A row in this sense exists only for visible items + * Therefore, the items at 'startIndex' and 'endIndex' + * are always visible. + * + * @see LightGrid#getRowRange(int, int, boolean, boolean) + */ + private static class RowRange { + /** + * index of first item in range + */ + public int startIndex; + /** + * index of last item in range + */ + public int endIndex; + /** + * number of rows (i.e. visible items) in this range + */ + public int rows; + /** + * height in pixels of this range (including horizontal separator between rows) + */ + public int height; + } + + /** + * Filters out unnecessary styles, adds mandatory styles and generally + * manages the style to pass to the super class. + * + * @param style user specified style. + * @return style to pass to the super class. + */ + private static int checkStyle(int style) + { + int mask = SWT.BORDER | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT | SWT.H_SCROLL | SWT.V_SCROLL + | SWT.SINGLE | SWT.MULTI | SWT.NO_FOCUS | SWT.CHECK | SWT.VIRTUAL; + int newStyle = style & mask; + newStyle |= SWT.DOUBLE_BUFFERED; + return newStyle; + } + + /** + * Constructs a new instance of this class given its parent and a style + * value describing its behavior and appearance. + *

+ * + * @param parent a composite control which will be the parent of the new + * instance (cannot be null) + * @param style the style of control to construct + * @see SWT#SINGLE + * @see SWT#MULTI + */ + public LightGrid(Composite parent, int style) + { + super(parent, checkStyle(style)); + + sizingGC = new GC(this); + fontMetrics = sizingGC.getFontMetrics(); + normalFont = getFont(); + columnHeaderRenderer = new GridColumnRenderer(this); + rowHeaderRenderer = new GridRowRenderer(this); + cellRenderer = new GridCellRenderer(this); + + final Display display = getDisplay(); + lineColor = JFaceColors.getErrorBackground(display); + lineSelectedColor = JFaceColors.getErrorBorder(display);//SWT.COLOR_WIDGET_DARK_SHADOW; + //setForeground(JFaceColors.getBannerForeground(display)); + //setBackground(JFaceColors.getBannerBackground(display)); +/* + ColorRegistry colorRegistry = UIUtils.getColorRegistry(); + setLineColor(colorRegistry.get(JFacePreferences.QUALIFIER_COLOR)); + setForeground(colorRegistry.get(JFacePreferences.CONTENT_ASSIST_FOREGROUND_COLOR)); + setBackground(colorRegistry.get(JFacePreferences.CONTENT_ASSIST_BACKGROUND_COLOR)); +*/ + sortCursor = display.getSystemCursor(SWT.CURSOR_HAND); + + if ((style & SWT.MULTI) != 0) { + selectionType = SWT.MULTI; + } + + if (getVerticalBar() != null) { + getVerticalBar().setVisible(false); + vScroll = new ScrollBarAdapter(getVerticalBar()); + } else { + vScroll = new NullScrollBar(); + } + + if (getHorizontalBar() != null) { + getHorizontalBar().setVisible(false); + hScroll = new ScrollBarAdapter(getHorizontalBar()); + } else { + hScroll = new NullScrollBar(); + } + + scrollValuesObsolete = true; + + initListeners(); + + recalculateSizes(); + + addDragAndDropSupport(); + setDragDetect(true); + } + + @NotNull + public abstract IGridContentProvider getContentProvider(); + + @NotNull + public abstract IGridLabelProvider getLabelProvider(); + + @Nullable + public abstract IGridController getGridController(); + + public boolean hasNodes() { + return !rowNodes.isEmpty(); + } + + public void setMaxColumnDefWidth(int maxColumnDefWidth) { + this.maxColumnDefWidth = maxColumnDefWidth; + } + + private void collectRows(List result, List parents, @Nullable GridNode parent, Object[] rows, int level) + { + for (int i = 0; i < rows.length; i++) { + Object row = rows[i]; + if (row == null) { + continue; + } + result.add(row); + parents.add(parent); + Object[] children = getContentProvider().getChildren(row); + if (children != null) { + IGridContentProvider.ElementState state; + GridNode node = rowNodes.get(row); + if (node == null) { + state = getContentProvider().getDefaultState(row); + node = new GridNode(parent, children, state, level + 1); + } else { + state = node.state; + } + rowNodes.put(row, node); + if (state == IGridContentProvider.ElementState.EXPANDED) { + collectRows(result, parents, node, children, level + 1); + } + } + } + } + + /** + * Refresh grid data + */ + public void refreshData(boolean refreshColumns, boolean keepState, boolean fitValue) + { + GridPos savedFocus = keepState ? getFocusPos() : null; + int savedHSB = keepState ? hScroll.getSelection() : -1; + int savedVSB = keepState ? vScroll.getSelection() : -1; + + int[] oldWidths = null; + if (keepState && !controlWasHidden) { + // Save widths + oldWidths = new int[columns.size()]; + if (!columns.isEmpty()) { + for (int i = 0; i < columns.size(); i++) { + oldWidths[i] = columns.get(i).getWidth(); + } + } + } + controlWasHidden = getClientArea().height == 0; + + if (refreshColumns) { + this.removeAll(); + } else { + this.deselectAll(); + topIndex = -1; + bottomIndex = -1; + } + IGridContentProvider contentProvider = getContentProvider(); + refreshRowsData(); + this.displayedToolTipText = null; + + if (refreshColumns) { + this.maxColumnDepth = 0; + + // Add columns + this.columnElements = contentProvider.getElements(true); + for (Object columnElement : columnElements) { + GridColumn column = new GridColumn(this, columnElement); + createChildColumns(column); + } + // Invalidate columns structure + boolean hasChildColumns = false; + for (Iterator iter = columns.iterator(); iter.hasNext(); ) { + GridColumn column = iter.next(); + if (column.getParent() == null) { + topColumns.add(column); + } else { + hasChildColumns = true; + } + if (column.getChildren() != null) { + iter.remove(); + } + } + if (hasChildColumns) { + // Rebuild columns model + columnElements = new Object[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + columnElements[i] = columns.get(i).getElement(); + } + } + + scrollValuesObsolete = true; + + if (getColumnCount() == 1 && (fitValue || MAXIMIZE_SINGLE_COLUMN)) { + // Here we going to maximize single column to entire grid's width + // Sometimes (when new grid created and filled with data very fast our client area size is zero + // So let's add a workaround for it and use column's width in this case + GridColumn column = getColumn(0); + int columnWidth = column.computeHeaderWidth(); + int gridWidth = getCurrentOrLastClientArea().width - getRowHeaderWidth() - getHScrollSelectionInPixels() - getVerticalBar().getSize().x; + if (gridWidth > columnWidth) { + columnWidth = gridWidth; + } + column.setWidth(columnWidth); + } else { + int totalWidth = 0; + for (GridColumn curColumn : topColumns) { + curColumn.pack(false); + totalWidth += curColumn.getWidth(); + } + if (!fitValue) { + // If grid width more than screen - lets narrow too long columns + int clientWidth = getCurrentOrLastClientArea().width; + if (totalWidth > clientWidth) { + int normalWidth = 0; + List fatColumns = new ArrayList<>(); + for (GridColumn curColumn : columns) { + if (curColumn.getWidth() > maxColumnDefWidth) { + fatColumns.add(curColumn); + } else { + normalWidth += curColumn.getWidth(); + } + } + if (!fatColumns.isEmpty()) { + // Narrow fat columns on decWidth + int freeSpace = (clientWidth - normalWidth - getBorderWidth() - rowHeaderWidth - vScroll.getWidth()) + / fatColumns.size(); + int newFatWidth = (freeSpace > maxColumnDefWidth ? freeSpace : maxColumnDefWidth); + for (GridColumn curColumn : fatColumns) { + curColumn.setWidth(newFatWidth); + } + } + } + } + } + + if (oldWidths != null) { + // Restore widths + if (oldWidths.length == columns.size()) { + for (int i = 0; i < oldWidths.length; i++) { + columns.get(i).setWidth(oldWidths[i]); + } + } + } + } + // Recalculate indexes, sizes and update scrollbars + topIndex = -1; + bottomIndex = -1; + recalculateSizes(); + updateScrollbars(); + + // Restore state + if (savedFocus != null) { + savedFocus.row = Math.min(savedFocus.row, getItemCount() - 1); + savedFocus.col = Math.min(savedFocus.col, getColumnCount() - 1); + if (savedFocus.row >= 0) setFocusItem(savedFocus.row); + if (savedFocus.col >= 0) setFocusColumn(savedFocus.col); + if (savedFocus.isValid()) selectCell(savedFocus); + } + if (savedHSB >= 0) { + hScroll.setSelection(Math.min(hScroll.getMaximum(), savedHSB)); + } + if (savedVSB >= 0) { + vScroll.setSelection(Math.min(vScroll.getMaximum(), savedVSB)); + } +// // Add focus cell to selection +// GridPos focusPos = getFocusPos(); +// if (focusPos.isValid()) { +// selectCell(focusPos); +// } + } + + public void refreshRowsData() { + // Prepare rows + Object[] initialElements = getContentProvider().getElements(false); + this.rowNodes.clear(); + List realRows = new ArrayList<>(initialElements.length); + List parents = new ArrayList<>(initialElements.length); + collectRows(realRows, parents, null, initialElements, 0); + this.rowElements = realRows.toArray(); + this.parentNodes = parents.toArray(new GridNode[parents.size()]); + } + + /** + * Returns current or last client area. + * If Grid controls are stacked then only the top is visible and has real client area. + * So we cache it - all stack has the same client area + */ + private Rectangle getCurrentOrLastClientArea() { + Rectangle clientArea = getClientArea(); + if (clientArea.width == 0) { + if (lastClientArea == null) { + return clientArea; + } + return lastClientArea; + } + lastClientArea = clientArea; + return clientArea; + } + + private void createChildColumns(GridColumn parent) { + Object[] children = getContentProvider().getChildren(parent.getElement()); + if (children != null) { + for (Object child : children) { + GridColumn column = new GridColumn(parent, child); + createChildColumns(column); + } + } + this.maxColumnDepth = Math.max(this.maxColumnDepth, parent.getLevel()); + } + + @Nullable + public GridCell posToCell(GridPos pos) + { + if (pos.col < 0 || pos.row < 0) { + return null; + } + return new GridCell(columnElements[pos.col], rowElements[pos.row]); + } + + @NotNull + public GridPos cellToPos(GridCell cell) + { + int colIndex = ArrayUtils.indexOf(columnElements, cell.col); + int rowIndex = ArrayUtils.indexOf(rowElements, cell.row); + return new GridPos(colIndex, rowIndex); + } + + public Object getColumnElement(int col) { + return columnElements[col]; + } + + public Rectangle getColumnBounds(int col) { + return getColumn(col).getBounds(); + } + + public Object getRowElement(int row) { + return rowElements[row]; + } + + @Override + public Color getBackground() + { + if (backgroundColor == null) { + backgroundColor = super.getBackground(); + } + return backgroundColor; + } + + @Override + public void setBackground(Color color) + { + super.setBackground(backgroundColor = color); + } + + /////////////////////////////////// + // Just caching because native impl creates new objects and called too often + + @Override + public Color getForeground() { + if (foregroundColor == null) { + foregroundColor = super.getForeground(); + } + return foregroundColor; + } + + @Override + public void setForeground(Color color) { + super.setForeground(foregroundColor = color); + getContentProvider().resetColors(); + } + + /** + * Adds the listener to the collection of listeners who will be notified + * when the receiver's selection changes, by sending it one of the messages + * defined in the {@code SelectionListener} interface. + *

+ * Cell selection events may have Event.detail = SWT.DRAG when the + * user is drag selecting multiple cells. A follow up selection event will be generated + * when the drag is complete. + * + * @param listener the listener which should be notified + */ + public void addSelectionListener(SelectionListener listener) + { + checkWidget(); + if (listener == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + addListener(SWT.Selection, new TypedListener(listener)); + addListener(SWT.DefaultSelection, new TypedListener(listener)); + } + + /** + * Removes the listener from the collection of listeners who will be + * notified when the receiver's selection changes. + * + * @param listener the listener which should no longer be notified + * @see SelectionListener + * @see #addSelectionListener(SelectionListener) + */ + public void removeSelectionListener(SelectionListener listener) + { + checkWidget(); + removeListener(SWT.Selection, listener); + removeListener(SWT.DefaultSelection, listener); + } + + + + @Override + public Point computeSize(int wHint, int hHint, boolean changed) + { + checkWidget(); + + Point prefSize = null; + if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) { + prefSize = getTableSize(); + prefSize.x += 2 * getBorderWidth(); + prefSize.y += 2 * getBorderWidth(); + } + + int x = 0; + int y = 0; + + if (wHint == SWT.DEFAULT) { + x += prefSize.x; + if (getVerticalBar() != null) { + x += getVerticalBar().getSize().x; + } + } else { + x = wHint; + } + + if (hHint == SWT.DEFAULT) { + y += prefSize.y; + if (getHorizontalBar() != null) { + y += getHorizontalBar().getSize().y; + } + } else { + y = hHint; + } + + return new Point(x, y); + } + + /** + * Deselects all selected items in the receiver. If cell selection is enabled, + * all cells are deselected. + */ + public void deselectAll() + { + checkWidget(); + + selectedCells.clear(); + updateSelectionCache(); + redraw(); + } + + @NotNull + private GridColumn getColumn(int index) + { + return columns.get(index); + } + + /** + * Returns the column at the given point and a known item in the receiver or null if no such + * column exists. The point is in the coordinate system of the receiver. + * + * @param point the point used to locate the column + * @return the column at the given point + */ + @Nullable + private GridColumn getColumn(Point point) + { + checkWidget(); + if (point == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + return null; + } + + GridColumn overThis = null; + + int x2 = 0; + + if (rowHeaderVisible) { + if (point.x <= rowHeaderWidth) { + return null; + } + + x2 += rowHeaderWidth; + } + + x2 -= getHScrollSelectionInPixels(); + + for (GridColumn column : columns) { + if (point.x >= x2 && point.x < x2 + column.getWidth()) { + for (GridColumn parent = column.getParent(); parent != null; parent = parent.getParent()) { + Point parentLoc = getOrigin(parent, -1); + if (point.y >= parentLoc.y && point.y <= parentLoc.y + parent.getHeaderHeight(false, false)) { + column = parent; + break; + } + } + overThis = column; + break; + } + + x2 += column.getWidth(); + } + + if (overThis == null) { + return null; + } + + return overThis; + } + + /** + * Returns the number of columns contained in the receiver. If no + * {@code GridColumn}s were created by the programmer, this value is + * zero, despite the fact that visually, one column of items may be visible. + * This occurs when the programmer uses the table like a list, adding items + * but never creating a column. + * + * @return the number of columns + */ + public int getColumnCount() + { + return columns.size(); + } + + Collection getColumns() + { + return columns; + } + + public IGridScrollBar getHorizontalScrollBarProxy() + { + return hScroll; + } + + public IGridScrollBar getVerticalScrollBarProxy() + { + return vScroll; + } + + /** + * Returns the height of the column headers. If this table has column + * groups, the returned value includes the height of group headers. + * + * @return height of the column header row + */ + public int getHeaderHeight() + { + return headerHeight; + } + + private int getRowHeaderWidth() + { + return rowHeaderWidth; + } + + public int getRow(Point point) + { + checkWidget(); + + if (point == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + return -1; + } + + final Rectangle clientArea = getClientArea(); + if (point.x < 0 || point.x > clientArea.width) return -1; + + Point p = new Point(point.x, point.y); + + int y2 = 0; + + if (columnHeadersVisible) { + if (p.y <= headerHeight) { + return -1; + } + y2 += headerHeight; + } + + int row = getTopIndex(); + int currItemHeight = getItemHeight(); + + int itemCount = getItemCount(); + while (row < itemCount && y2 <= clientArea.height) { + if (p.y >= y2 && p.y < y2 + currItemHeight + 1) { + return row; + } + + y2 += currItemHeight + 1; + + row++; + } + + return -1; + } + + /** + * Returns the number of items contained in the receiver. + * + * @return the number of items + */ + public int getItemCount() + { + return rowElements.length; + } + + /** + * Returns the default height of the items + * + * @return default height of items + * @see #setItemHeight(int) + */ + public int getItemHeight() + { + return itemHeight; + } + + /** + * Sets the default height for this Grid's items. When + * this method is called, all existing items are resized + * to the specified height and items created afterwards will be + * initially sized to this height. + *

+ * As long as no default height was set by the client through this method, + * the preferred height of the first item in this Grid is + * used as a default for all items (and is returned by {@link #getItemHeight()}). + * + * @param height default height in pixels + */ + private void setItemHeight(int height) + { + checkWidget(); + if (height < 1) + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + itemHeight = height; + setScrollValuesObsolete(); + redraw(); + } + + /** + * Returns the line color. + * + * @return Returns the lineColor. + */ + @NotNull + public Color getLineColor() { + return lineColor; + } + + public void setLineColor(@NotNull Color lineColor) { + this.lineColor = lineColor; + } + + public Color getLineSelectedColor() { + return lineSelectedColor; + } + + public void setLineSelectedColor(Color lineSelectedColor) { + this.lineSelectedColor = lineSelectedColor; + } + + /** + * Returns true if the lines are visible. + * + * @return Returns the linesVisible. + */ + public boolean isLinesVisible() + { + return linesVisible; + } + + /** + * Returns the next visible item in the table. + * + * @return next visible item or null + */ + private int getNextVisibleItem(int index) + { + if (index >= getItemCount()) { + return -1; + } + if (index == getItemCount() - 1) { + return index; + } else { + return index + 1; + } + } + + /** + * Returns the previous visible item in the table. Passing null for the item + * will return the last visible item in the table. + * + * @return previous visible item or if item==null last visible item + */ + private int getPreviousVisibleItem(int index) + { + if (index == 0) { + return -1; + } + return index - 1; + } + + /** + * Returns the previous visible column in the table. + * + * @param column column + * @return previous visible column or null + */ + @Nullable + private GridColumn getPreviousVisibleColumn(GridColumn column) + { + int index = indexOf(column); + if (index <= 0) + return null; + + return columns.get(index - 1); + } + + /** + * Returns the next visible column in the table. + * + * @param column column + * @return next visible column or null + */ + @Nullable + private GridColumn getNextVisibleColumn(GridColumn column) + { + int index = indexOf(column); + + if (index < 0 || index >= columns.size() - 1) + return null; + + return columns.get(index + 1); + } + + /** + * Returns the number of selected cells contained in the receiver. + * + * @return the number of selected cells + */ + private int getCellSelectionCount() + { + return selectedCells.size(); + } + + /** + * Returns the zero-relative index of the item which is currently selected + * in the receiver, or -1 if no item is selected. If cell selection is enabled, + * returns the index of first item that contains at least one selected cell. + * + * @return the index of the selected item + */ + public int getSelectionIndex() + { + if (selectedCells.isEmpty()) + return -1; + + return selectedCells.iterator().next().row; + } + + /** + * Returns the zero-relative index of the item which is currently at the top + * of the receiver. This index can change when items are scrolled or new + * items are added or removed. + * + * @return the index of the top item + */ + public int getTopIndex() + { + if (topIndex != -1) + return topIndex; + + if (!vScroll.getVisible()) { + topIndex = 0; + } else { + // figure out first visible row and last visible row + topIndex = vScroll.getSelection(); + } + + return topIndex; + } + + /** + * Returns the zero-relative index of the item which is currently at the bottom + * of the receiver. This index can change when items are scrolled, expanded + * or collapsed or new items are added or removed. + *

+ * Note that the item with this index is often only partly visible; maybe only + * a single line of pixels is visible. Use {@link #isShown(int)} to find + * out. + *

+ * In extreme cases, getBottomIndex() may return the same value as + * {@link #getTopIndex()}. + * + * @return the index of the bottom item + */ + public int getBottomIndex() + { + if (bottomIndex != -1) + return bottomIndex; + + if (getItemCount() == 0) { + bottomIndex = 0; + } else if (getVisibleGridHeight() < 1) { + bottomIndex = getTopIndex(); + } else { + RowRange range = getRowRange(getTopIndex(), getVisibleGridHeight(), false, false); + + bottomIndex = range.endIndex; + bottomIndexShownCompletely = range.height <= getVisibleGridHeight(); + } + + return bottomIndex; + } + + /** + * Returns a {@link RowRange} ranging from + * the grid item at startIndex to that at endIndex. + *

+ * This is primarily used to measure the height + * in pixel of such a range and to count the number + * of visible grid items within the range. + * + * @param startIndex index of the first item in the range or -1 to the first visible item in this grid + * @param endIndex index of the last item in the range or -1 to use the last visible item in this grid + */ + @Nullable + private RowRange getRowRange(int startIndex, int endIndex) + { + + // parameter preparation + int itemCount = getItemCount(); + if (startIndex == -1) { + // search first visible item + startIndex = 0; + if (startIndex == itemCount) return null; + } + if (endIndex == -1) { + // search last visible item + endIndex = itemCount - 1; + if (endIndex <= 0) return null; + } + + // fail fast + if (startIndex < 0 || endIndex < 0 || startIndex >= itemCount || endIndex >= itemCount + || endIndex < startIndex) + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + RowRange range = new RowRange(); + range.startIndex = startIndex; + range.endIndex = endIndex; + + range.rows = range.endIndex - range.startIndex + 1; + range.height = (getItemHeight() + 1) * range.rows - 1; + + return range; + } + + /** + * This method can be used to build a range of grid rows + * that is allowed to span a certain height in pixels. + *

+ * It returns a {@link RowRange} that contains information + * about the range, especially the index of the last + * element in the range (or if inverse == true, then the + * index of the first element). + *

+ * Note: Even if 'forceEndCompletelyInside' is set to + * true, the last item will not lie completely within + * the availableHeight, if (height of item at startIndex < availableHeight). + * + * @param startIndex index of the first (if inverse==false) or + * last (if inverse==true) item in the range + * @param availableHeight height in pixels + * @param forceEndCompletelyInside if true, the last item in the range will lie completely + * within the availableHeight, otherwise it may lie partly outside this range + * @param inverse if true, then the first item in the range will be searched, not the last + * @return range of grid rows + * @see RowRange + */ + private RowRange getRowRange(int startIndex, int availableHeight, + boolean forceEndCompletelyInside, boolean inverse) + { + // parameter preparation + if (startIndex == -1) { + if (!inverse) { + // search first visible item + startIndex = 0; + } else { + // search last visible item + startIndex = getItemCount() - 1; + } + } + + RowRange range = new RowRange(); + + if (startIndex < 0 || startIndex >= getItemCount()) { + // something is broken + range.startIndex = 0; + range.endIndex = 0; + range.height = 0; + range.rows = 0; + return range; + } + + if (availableHeight <= 0) { + // special case: empty range + range.startIndex = startIndex; + range.endIndex = startIndex; + range.rows = 0; + range.height = 0; + return range; + } + + int availableRows = (availableHeight + 1) / (getItemHeight() + 1); + + if (((getItemHeight() + 1) * range.rows - 1) + 1 < availableHeight) { + // not all available space used yet + // - so add another row if it need not be completely within availableHeight + if (!forceEndCompletelyInside) + availableRows++; + } + + int otherIndex = startIndex + ((availableRows - 1) * (!inverse ? 1 : -1)); + if (otherIndex < 0) otherIndex = 0; + if (otherIndex >= getItemCount()) otherIndex = getItemCount() - 1; + + range.startIndex = !inverse ? startIndex : otherIndex; + range.endIndex = !inverse ? otherIndex : startIndex; + range.rows = range.endIndex - range.startIndex + 1; + range.height = (getItemHeight() + 1) * range.rows - 1; + + return range; + } + + /** + * Returns the height of the plain grid in pixels. + * This does not include the height of the column headers. + * + * @return height of plain grid + */ + private int getGridHeight() + { + RowRange range = getRowRange(-1, -1); + return range != null ? range.height : 0; + } + + /** + * Returns the height of the on-screen area that is available + * for showing the grid's rows, i.e. the client area of the + * scrollable minus the height of the column headers (if shown). + * + * @return height of visible grid in pixels + */ + private int getVisibleGridHeight() + { + return getClientArea().height - (columnHeadersVisible ? headerHeight : 0); + } + + /** + * Searches the receiver's list starting at the first column (index 0) until + * a column is found that is equal to the argument, and returns the index of + * that column. If no column is found, returns -1. + * + * @param column the search column + * @return the index of the column + */ + int indexOf(GridColumn column) + { + column = column.getFirstLeaf(); + int index = columns.indexOf(column); + if (index < 0) { + log.warn("Bad column [" + column.getElement() + "]"); + } + return index; + } + + /** + * Returns {@code true} if the receiver's row header is visible, and + * {@code false} otherwise. + *

+ * + * @return the receiver's row header's visibility state + */ + private boolean isRowHeaderVisible() + { + return rowHeaderVisible; + } + + /** + * Returns true if the given cell is selected. + * + * @param cell cell + * @return true if the cell is selected. + */ + private boolean isCellSelected(GridPos cell) + { + if (cell == null) + SWT.error(SWT.ERROR_NULL_ARGUMENT); + + return selectedCells.contains(cell); + } + + /** + * Removes all of the items from the receiver. + */ + public void removeAll() + { + checkWidget(); + deselectAll(); + vScroll.setSelection(0); + hScroll.setSelection(0); + + focusItem = -1; + focusColumn = null; + topIndex = -1; + bottomIndex = -1; + shiftSelectionAnchorColumn = null; + + topColumns.clear(); + columns.clear(); + columnElements = new Object[0]; + rowElements = new Object[0]; + } + + /** + * Selects the item at the given zero-relative index in the receiver. If the + * item at the index was already selected, it remains selected. Indices that + * are out of range are ignored. + *

+ * If cell selection is enabled, selects all cells at the given index. + * + * @param index the index of the item to select + */ + public void select(int index) + { + checkWidget(); + + if (index < 0 || index >= getItemCount()) return; + + selectCells(getCells(index)); + + redraw(); + } + + /** + * Selects the items in the range specified by the given zero-relative + * indices in the receiver. The range of indices is inclusive. The current + * selection is not cleared before the new items are selected. + *

+ * If an item in the given range is not selected, it is selected. If an item + * in the given range was already selected, it remains selected. Indices + * that are out of range are ignored and no items will be selected if start + * is greater than end. If the receiver is single-select and there is more + * than one item in the given range, then all indices are ignored. + *

+ * If cell selection is enabled, all cells within the given range are selected. + * + * @param start the start of the range + * @param end the end of the range + * @see LightGrid#setSelection(int,int) + */ + public void select(int start, int end) + { + checkWidget(); + + if (selectionType == SWT.SINGLE && start != end) return; + + for (int i = start; i <= end; i++) { + if (i < 0) { + continue; + } + if (i > getItemCount() - 1) { + break; + } + + selectCells(getCells(i)); + } + + redraw(); + } + + /** + * Selects the items at the given zero-relative indices in the receiver. The + * current selection is not cleared before the new items are selected. + *

+ * If the item at a given index is not selected, it is selected. If the item + * at a given index was already selected, it remains selected. Indices that + * are out of range and duplicate indices are ignored. If the receiver is + * single-select and multiple indices are specified, then all indices are + * ignored. + *

+ * If cell selection is enabled, all cells within the given indices are + * selected. + * + * @param indices the array of indices for the items to select + * @see LightGrid#setSelection(int[]) + */ + public void select(int[] indices) + { + checkWidget(); + + if (indices == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + return; + } + + if (selectionType == SWT.SINGLE && indices.length > 1) return; + + for (int j : indices) { + if (j >= 0 && j < getItemCount()) { + selectCells(getCells(j)); + } + } + redraw(); + } + + /** + * Selects all of the items in the receiver. + *

+ * If the receiver is single-select, do nothing. If cell selection is enabled, + * all cells are selected. + */ + public void selectAll() + { + checkWidget(); + + if (selectionType == SWT.SINGLE) return; + + selectAllCells(); + } + + /** + * Marks the receiver's header as visible if the argument is {@code true}, + * and marks it invisible otherwise. + * + * @param show the new visibility state + */ + public void setHeaderVisible(boolean show) + { + checkWidget(); + this.columnHeadersVisible = show; + redraw(); + } + + /** + * Sets the line visibility. + * + * @param linesVisible Te linesVisible to set. + */ + public void setLinesVisible(boolean linesVisible) + { + checkWidget(); + this.linesVisible = linesVisible; + redraw(); + } + + /** + * Marks the receiver's row header as visible if the argument is + * {@code true}, and marks it invisible otherwise. When row headers are + * visible, horizontal scrolling is always done by column rather than by + * pixel. + * + * @param show the new visibility state + */ + public void setRowHeaderVisible(boolean show) + { + checkWidget(); + this.rowHeaderVisible = show; + setColumnScrolling(true); + + redraw(); + } + + /** + * Selects the item at the given zero-relative index in the receiver. The + * current selection is first cleared, then the new item is selected. + *

+ * If cell selection is enabled, all cells within the item at the given index + * are selected. + * + * @param index the index of the item to select + */ + public void setSelection(int index) + { + checkWidget(); + + if (index >= 0 && index < getItemCount()) { + selectedCells.clear(); + selectCells(getCells(index)); + } + } + + /** + * Selects the items in the range specified by the given zero-relative + * indices in the receiver. The range of indices is inclusive. The current + * selection is cleared before the new items are selected. + *

+ * Indices that are out of range are ignored and no items will be selected + * if start is greater than end. If the receiver is single-select and there + * is more than one item in the given range, then all indices are ignored. + *

+ * If cell selection is enabled, all cells within the given range are selected. + * + * @param start the start index of the items to select + * @param end the end index of the items to select + * @see LightGrid#deselectAll() + * @see LightGrid#select(int,int) + */ + public void setSelection(int start, int end) + { + checkWidget(); + + if (selectionType == SWT.SINGLE && start != end) return; + + selectedCells.clear(); + + for (int i = start; i <= end; i++) { + if (i < 0) { + continue; + } + if (i > getItemCount() - 1) { + break; + } + + selectCells(getCells(i)); + } + redraw(); + } + + /** + * Selects the items at the given zero-relative indices in the receiver. The + * current selection is cleared before the new items are selected. + *

+ * Indices that are out of range and duplicate indices are ignored. If the + * receiver is single-select and multiple indices are specified, then all + * indices are ignored. + *

+ * If cell selection is enabled, all cells within the given indices are selected. + * + * @param indices the indices of the items to select + * @see LightGrid#deselectAll() + * @see LightGrid#select(int[]) + */ + public void setSelection(int[] indices) + { + checkWidget(); + + if (selectionType == SWT.SINGLE && indices.length > 1) return; + + selectedCells.clear(); + + for (int j : indices) { + if (j < 0) { + continue; + } + if (j > getItemCount() - 1) { + break; + } + + selectCells(getCells(j)); + } + redraw(); + } + + /** + * Sets the zero-relative index of the item which is currently at the top of + * the receiver. This index can change when items are scrolled or new items + * are added and removed. + * + * @param index the index of the top item + */ + private void setTopIndex(int index) + { + checkWidget(); + if (index < 0 || index >= getItemCount()) { + return; + } + + if (!vScroll.getVisible()) { + return; + } + + vScroll.setSelection(index); + topIndex = -1; + bottomIndex = -1; + redraw(); + } + + /** + * Shows the column. If the column is already showing in the receiver, this + * method simply returns. Otherwise, the columns are scrolled until the + * column is visible. + * + */ + public void showColumn(int column) + { + GridColumn col = getColumn(column); + showColumn(col); + } + + public void showColumn(Object element) + { + for (GridColumn column : columns) { + if (column.getElement() == element) { + showColumn(column); + break; + } + } + } + + private void showColumn(@NotNull GridColumn col) + { + checkWidget(); + + if (!hScroll.getVisible()) { + return; + } + + int x = getColumnHeaderXPosition(col); + + int firstVisibleX = 0; + if (rowHeaderVisible) { + firstVisibleX = rowHeaderWidth; + } + + // if its visible just return + final Rectangle clientArea = getClientArea(); + if (x >= firstVisibleX + && (x + col.getWidth()) <= (firstVisibleX + (clientArea.width - firstVisibleX))) { + return; + } + + if (!getColumnScrolling()) { + if (x < firstVisibleX) { + hScroll.setSelection(getHScrollSelectionInPixels() - (firstVisibleX - x)); + } else { + if (col.getWidth() > clientArea.width - firstVisibleX) { + hScroll.setSelection(getHScrollSelectionInPixels() + (x - firstVisibleX)); + } else { + x -= clientArea.width - firstVisibleX - col.getWidth(); + hScroll.setSelection(getHScrollSelectionInPixels() + (x - firstVisibleX)); + } + } + } else { + if (x < firstVisibleX || col.getWidth() > clientArea.width - firstVisibleX) { + int sel = indexOf(col); + hScroll.setSelection(sel); + } else { + int availableWidth = clientArea.width - firstVisibleX - col.getWidth(); + + GridColumn prevCol = getPreviousVisibleColumn(col); + GridColumn currentScrollTo = col; + + while (true) { + if (prevCol == null || prevCol.getWidth() > availableWidth) { + int sel = indexOf(currentScrollTo); + hScroll.setSelection(sel); + break; + } else { + availableWidth -= prevCol.getWidth(); + currentScrollTo = prevCol; + prevCol = getPreviousVisibleColumn(prevCol); + } + } + } + } + + redraw(); + } + + /** + * Returns true if 'item' is currently being completely + * shown in this Grid's visible on-screen area. + *

+ *

Here, "completely" only refers to the item's height, not its + * width. This means this method returns true also if some cells + * are horizontally scrolled away. + * + * @param row row number + * @return true if 'item' is shown + */ + private boolean isShown(int row) + { + checkWidget(); + + if (row == -1) + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + + int firstVisibleIndex = getTopIndex(); + int lastVisibleIndex = getBottomIndex(); + + return (row >= firstVisibleIndex && row < lastVisibleIndex) + || + (row == lastVisibleIndex && bottomIndexShownCompletely); + } + + /** + * Shows the item. If the item is already showing in the receiver, this + * method simply returns. Otherwise, the items are scrolled until the item + * is visible. + * + * @param item the item to be shown + */ + public void showItem(int item) + { + checkWidget(); + + updateScrollbars(); + + // if no items are visible on screen then abort + if (getVisibleGridHeight() < 1) { + return; + } + + // if its visible just return + if (isShown(item)) { + return; + } + + int newTopIndex = item; + + if (newTopIndex >= getBottomIndex()) { + RowRange range = getRowRange(newTopIndex, getVisibleGridHeight(), true, true); // note: inverse==true + newTopIndex = range.startIndex; // note: use startIndex because of inverse==true + } + + setTopIndex(newTopIndex); + } + + /** + * Shows the selection. If the selection is already showing in the receiver, + * this method simply returns. Otherwise, the items are scrolled until the + * selection is visible. + * + */ + public void showSelection() + { + checkWidget(); + + if (scrollValuesObsolete) + updateScrollbars(); + + if (selectedCells.isEmpty()) return; + + GridPos cell = selectedCells.iterator().next(); + showItem(cell.row); + showColumn(cell.col); + } + + /** + * Computes and sets the height of the header row. This method will ask for + * the preferred size of all the column headers and use the max. + */ + private void computeHeaderSizes() + { + // Item height + itemHeight = fontMetrics.getHeight() + 3; + + // Column header height + int colHeaderHeight = 0; + for (GridColumn column : topColumns) { + colHeaderHeight = Math.max(column.getHeaderHeight(true, true), colHeaderHeight); + } + headerHeight = colHeaderHeight; + + // Row header width + rowHeaderWidth = DEFAULT_ROW_HEADER_WIDTH; + for (int i = 0; i < rowElements.length; i++) { + Object row = rowElements[i]; + GridNode parentNode = parentNodes[i]; + GridNode nr = rowNodes.get(row); + int width = rowHeaderRenderer.computeHeaderWidth( + row, nr != null ? nr.level : parentNode == null ? 0 : parentNode.level + 1); + rowHeaderWidth = Math.max(rowHeaderWidth, width); + } + if (rowHeaderWidth > MAX_ROW_HEADER_WIDTH) { + rowHeaderWidth = MAX_ROW_HEADER_WIDTH; + } + } + + /** + * Returns the x position of the given column. Takes into account scroll + * position. + * + * @param column given column + * @return x position + */ + private int getColumnHeaderXPosition(@NotNull GridColumn column) + { + int x = 0; + + x -= getHScrollSelectionInPixels(); + + if (rowHeaderVisible) { + x += rowHeaderWidth; + } + column = column.getFirstLeaf(); + for (GridColumn column2 : columns) { + if (column2 == column) { + break; + } + + x += column2.getWidth(); + } + + return x; + } + + /** + * Returns the hscroll selection in pixels. This method abstracts away the + * differences between column by column scrolling and pixel based scrolling. + * + * @return the horizontal scroll selection in pixels + */ + private int getHScrollSelectionInPixels() + { + int selection = hScroll.getSelection(); + if (columnScrolling) { + int pixels = 0; + for (int i = 0; i < selection && i < columns.size(); i++) { + pixels += columns.get(i).getWidth(); + } + selection = pixels; + } + return selection; + } + + /** + * Returns the size of the preferred size of the inner table. + * + * @return the preferred size of the table. + */ + private Point getTableSize() + { + int x = 0; + int y = 0; + + if (columnHeadersVisible) { + y += headerHeight; + } + + y += getGridHeight(); + + if (rowHeaderVisible) { + x += rowHeaderWidth; + } + + for (GridColumn column : columns) { + x += column.getWidth(); + } + + return new Point(x, y); + } + + /** + * Sets the new width of the column being resized and fires the appropriate + * listeners. + * + * @param x mouse x + */ + private void handleColumnResizerDragging(int x) + { + int newWidth = resizingColumnStartWidth + (x - resizingStartX); + if (newWidth < MIN_COLUMN_HEADER_WIDTH) { + newWidth = MIN_COLUMN_HEADER_WIDTH; + } + + Rectangle clientArea = getClientArea(); + if (columnScrolling) { + int maxWidth = clientArea.width; + if (rowHeaderVisible) + maxWidth -= rowHeaderWidth; + if (newWidth > maxWidth) + newWidth = maxWidth; + } + + if (newWidth == columnBeingResized.getWidth()) { + return; + } + + columnBeingResized.setWidth(newWidth, false); + scrollValuesObsolete = true; + + redraw(clientArea.x, clientArea.y, clientArea.width, clientArea.height, false); + } + + /** + * Determines if the mouse is hovering on a column resizer and changes the + * pointer and sets field appropriately. + * Also checks if mouse if hovering on a column sorter control. + * + * @param x mouse x + * @param y mouse y + */ + private void handleHoverOnColumnHeader(int x, int y) + { + boolean overSorter = false, overResizer = false, overFilter = false; + hoveringOnHeader = false; + boolean overIcon = false; + + if (y <= headerHeight) { + int x2 = 0; + + if (rowHeaderVisible) { + x2 += rowHeaderWidth; + } + + x2 -= getHScrollSelectionInPixels(); + + if (x < x2) { + int ltSort = getContentProvider().getSortOrder(null); + if (ltSort != SWT.NONE + && x > x2 - GridColumnRenderer.SORT_WIDTH - GridColumnRenderer.ARROW_MARGIN + && x < x2 - GridColumnRenderer.ARROW_MARGIN + && y > GridColumnRenderer.TOP_MARGIN) + { + columnBeingSorted = null; + overSorter = true; + } + + } else { + if (x > getRowHeaderWidth()) { + for (GridColumn column : columns) { + if (x >= x2 && x <= x2 + column.getWidth()) { + hoveringOnHeader = true; + if (column.isOverSortArrow(x - x2, y)) { + overSorter = true; + columnBeingSorted = column; + break; + } + + if(column.isOverFilterButton(x - x2, y)) { + columnBeingFiltered = column; + overFilter = true; + break; + } + + if (column.isOverIcon(x, y)) { + overIcon = true; + break; + } + + x2 += column.getWidth(); + if (x2 >= (x - COLUMN_RESIZER_THRESHOLD) && x2 <= (x + COLUMN_RESIZER_THRESHOLD)) { + overResizer = true; + + columnBeingResized = column; + break; + } + } else { + x2 += column.getWidth(); + } + } + } + } + // Redraw header +// GC gc = new GC(this); +// try { +// paintHeader(gc); +// } catch (Exception e) { +// gc.dispose(); +// } + + } else if (x <= rowHeaderWidth) { + // Hover in row header + } + if (overSorter != hoveringOnColumnSorter) { + if (overSorter) { + setCursor(sortCursor); + } else { + columnBeingSorted = null; + setCursor(null); + } + hoveringOnColumnSorter = overSorter; + } else if (overIcon != hoveringOnColumnIcon) { + setCursor(overIcon ? sortCursor : null); + hoveringOnColumnIcon = overIcon; + } + + if(overFilter != hoveringOnColumnFilter) { + if(overFilter) + setCursor(sortCursor); + else if(!overSorter) { + columnBeingFiltered = null; + setCursor(null); + } + + hoveringOnColumnFilter = overFilter; + } + + if (overResizer != hoveringOnColumnResizer) { + if (overResizer) { + setCursor(getDisplay().getSystemCursor(SWT.CURSOR_SIZEWE)); + } else { + columnBeingResized = null; + if (!hoveringOnColumnSorter) { + setCursor(null); + } + } + hoveringOnColumnResizer = overResizer; + } + + if (hoveringOnHeader && !overSorter && !overResizer && !overFilter) { + hoveringOnHeaderDragArea = true; + } else { + hoveringOnHeaderDragArea = false; + } + } + + private void handleHoverOnRowHeader(int x, int y) { + hoveringRow = null; + draggingRow = null; + + if ((!rowHeaderVisible || y > headerHeight) && x <= rowHeaderWidth) { + hoveringOnRowHeader = true; + int row = getRow(new Point(x, y)); + if (row != -1) { + hoveringRow = row; + } + } else { + hoveringOnRowHeader = false; + } + } + + /** + * Returns the cell at the given point in the receiver or null if no such + * cell exists. The point is in the coordinate system of the receiver. + * + * @param point the point used to locate the item + * @return the cell at the given point + */ + @Nullable + public GridPos getCell(Point point) + { + checkWidget(); + + if (point == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + return null; + } + + if (point.x < 0 || point.x > getClientArea().width) return null; + + int item = getRow(point); + GridColumn column = getColumn(point); + + if (item >= 0 && column != null) { + return new GridPos(indexOf(column), item); + } else { + return null; + } + } + + /** + * Paints. + * + * @param e paint event + */ + private void onPaint(@NotNull PaintEvent e) + { + final GC gc = e.gc; + gc.setBackground(getBackground()); + + //this.drawBackground(gc, 0, 0, getSize().x, getSize().y); + + if (scrollValuesObsolete) { + updateScrollbars(); + scrollValuesObsolete = false; + } + + int y = 0; + + if (columnHeadersVisible) { + paintHeader(gc); + y += headerHeight; + } + + final Rectangle clientArea = getClientArea(); + int availableHeight = clientArea.height - y; + int visibleRows = availableHeight / getItemHeight() + 1; + if (getItemCount() > 0 && availableHeight > 0) { + RowRange range = getRowRange(getTopIndex(), availableHeight, false, false); + if (range.height >= availableHeight) + visibleRows = range.rows; + else + visibleRows = range.rows + (availableHeight - range.height) / getItemHeight() + 1; + } + + int firstVisibleIndex = getTopIndex(); + + int row = firstVisibleIndex; + final int hScrollSelectionInPixels = getHScrollSelectionInPixels(); + final GridPos testPos = new GridPos(-1, -1); + final Rectangle cellBounds = new Rectangle(0, 0, 0, 0); + + for (int i = 0; i < visibleRows; i++) { + + int x = 0; + + x -= hScrollSelectionInPixels; + + // get the item to draw + if (row >= 0 && row < getItemCount()) { + + boolean cellInRowSelected = selectedRows.containsKey(row); + + if (rowHeaderVisible) { + + // row header is actually painted later + x += rowHeaderWidth; + } + + // draw regular cells for each column + for (int k = 0, columnsSize = columns.size(); k < columnsSize; k++) { + GridColumn column = columns.get(k); + + int width = column.getWidth(); + + if (x + width >= 0 && x < clientArea.width) { + + cellBounds.x = x; + cellBounds.y = y; + cellBounds.width = width; + cellBounds.height = getItemHeight(); + + testPos.col = k; + testPos.row = row; + cellRenderer.paint( + gc, + cellBounds, + selectedCells.contains(testPos), + focusItem == row && focusColumn == column, + column.getElement(), + rowElements[row]); + + //gc.setClipping((Rectangle) null); + } + + x += column.getWidth(); + } + + if (x < clientArea.width) { + drawEmptyCell(gc, x, y, clientArea.width - x + 1, getItemHeight()); + } + + x = 0; + + GridNode rowNode = this.rowNodes.get(rowElements[row]); + GridNode parentNode = this.parentNodes[row]; + if (rowHeaderVisible) { + + if (y >= headerHeight) { + cellBounds.x = 0; + cellBounds.y = y; + cellBounds.width = rowHeaderWidth; + cellBounds.height = getItemHeight() + 1; + + gc.setClipping(cellBounds); + try { + rowHeaderRenderer.paint( + gc, + cellBounds, + cellInRowSelected, + parentNode == null ? 0 : parentNode.level, + rowNode == null ? IGridContentProvider.ElementState.NONE : rowNode.state, + rowElements[row]); + } finally { + gc.setClipping((Rectangle)null); + } + } + x += rowHeaderWidth; + } + + y += getItemHeight() + 1; + + } else { + + if (rowHeaderVisible) { + //row header is actually painted later + x += rowHeaderWidth; + } + + for (GridColumn column : columns) { + drawEmptyCell(gc, x, y, column.getWidth(), getItemHeight()); + x += column.getWidth(); + } + if (x < clientArea.width) { + drawEmptyCell(gc, x, y, clientArea.width - x + 1, getItemHeight()); + } + + x = 0; + + if (rowHeaderVisible) { + drawEmptyRowHeader(gc, x, y, rowHeaderWidth, getItemHeight() + 1); + x += rowHeaderWidth; + } + + y += getItemHeight() + 1; + } + + row++; + } + } + + /** + * Paints the header. + * + * @param gc gc from paint event + */ + private void paintHeader(@NotNull GC gc) + { + int x = 0; + int y; + + x -= getHScrollSelectionInPixels(); + + if (rowHeaderVisible) { + // skip left corner + x += rowHeaderWidth; + } + + final Rectangle clientArea = getClientArea(); + for (int i = 0, columnsSize = topColumns.size(); i < columnsSize; i++) { + GridColumn column = topColumns.get(i); + if (x > clientArea.width) + break; + + int columnHeight = column.getHeaderHeight(false, false); + y = 0; + if (x + column.getWidth() >= 0) { + paintColumnsHeader(gc, column, x, y, columnHeight, 0); + } + + x += column.getWidth(); + } + + if (x < clientArea.width) { + drawEmptyColumnHeader(gc, x, 0, clientArea.width - x, headerHeight); + } + + x = 0; + + if (rowHeaderVisible) { + // paint left corner + drawTopLeftCell(gc, 0, 0, rowHeaderWidth, headerHeight); + x += rowHeaderWidth; + } + } + + private void paintColumnsHeader(GC gc, @NotNull GridColumn column, int x, int y, int columnHeight, int level) { + List children = column.getChildren(); + int paintHeight = columnHeight; + if (CommonUtils.isEmpty(children)) { + paintHeight = columnHeight + (headerHeight - y - columnHeight); + } + Rectangle bounds = new Rectangle(x, y, column.getWidth(), paintHeight); + boolean hover = hoveringOnHeader && hoveringColumn == column; + columnHeaderRenderer.paint(gc, bounds, selectedColumns.contains(column), hover, column.getElement()); + if (!CommonUtils.isEmpty(children)) { + // Draw child columns + level++; + int childX = x; + for (GridColumn child : children) { + paintColumnsHeader(gc, child, childX, y + columnHeight, columnHeight, level); + childX += child.getWidth(); + } + } + } + + /** + * Manages the state of the scrollbars when new items are added or the + * bounds are changed. + */ + public void updateScrollbars() + { + Point preferredSize = getTableSize(); + + Rectangle clientArea = getClientArea(); + + // First, figure out if the scrollbars should be visible and turn them + // on right away + // this will allow the computations further down to accommodate the + // correct client + // area + + // Turn the scrollbars on if necessary and do it all over again if + // necessary. This ensures + // that if a scrollbar is turned on/off, the other scrollbars + // visibility may be affected (more + // area may have been added/removed. + for (int doublePass = 1; doublePass <= 2; doublePass++) { + + if (preferredSize.y > clientArea.height) { + vScroll.setVisible(true); + } else { + vScroll.setVisible(false); + vScroll.setValues(0, 0, 1, 1, 1, 1); + } + if (preferredSize.x > clientArea.width) { + hScroll.setVisible(true); + } else { + hScroll.setVisible(false); + hScroll.setValues(0, 0, 1, 1, 1, 1); + } + + // get the clientArea again with the now visible/invisible + // scrollbars + clientArea = getClientArea(); + } + + // if the scrollbar is visible set its values + if (vScroll.getVisible()) { + int max = getItemCount(); + int thumb = (getVisibleGridHeight() + 1) / (getItemHeight() + 1); + + // if possible, remember selection, if selection is too large, just + // make it the max you can + int selection = Math.min(vScroll.getSelection(), max); + + vScroll.setValues(selection, 0, max, thumb, 1, thumb); + } + + // if the scrollbar is visible set its values + if (hScroll.getVisible()) { + + if (!columnScrolling) { + // horizontal scrolling works pixel by pixel + + int hiddenArea = preferredSize.x - clientArea.width + 1; + + // if possible, remember selection, if selection is too large, + // just + // make it the max you can + int selection = Math.min(hScroll.getSelection(), hiddenArea - 1); + + hScroll.setValues(selection, 0, hiddenArea + clientArea.width - 1, clientArea.width, + HORZ_SCROLL_INCREMENT, clientArea.width); + } else { + // horizontal scrolling is column by column + + int hiddenArea = preferredSize.x - clientArea.width + 1; + + int max = 0; + int i = 0; + + while (hiddenArea > 0 && i < getColumnCount()) { + GridColumn col = columns.get(i); + + i++; + + hiddenArea -= col.getWidth(); + max++; + } + + max++; + + // max should never be greater than the number of visible cols + int visCols = columns.size(); + max = Math.min(visCols, max); + + // if possible, remember selection, if selection is too large, + // just + // make it the max you can + int selection = Math.min(hScroll.getSelection(), max); + + hScroll.setValues(selection, 0, max, 1, 1, 1); + } + } + + } + + /** + * Updates cell selection. + * + * @param newCell newly clicked, navigated to cell. + * @param stateMask state mask during preceeding mouse or key event. + * @param dragging true if the user is dragging. + * @param reverseDuplicateSelections true if the user is reversing selection rather than adding to. + * @return selection event that will need to be fired or null. + */ + @Nullable + private Event updateCellSelection( + @NotNull GridPos newCell, + int stateMask, + boolean dragging, + boolean reverseDuplicateSelections, + EventSource eventSource) + { + return updateCellSelection(Collections.singletonList(newCell), stateMask, dragging, reverseDuplicateSelections, eventSource); + } + + /** + * Updates cell selection. + * + * @param newCells newly clicked, navigated to cells. + * @param stateMask state mask during preceeding mouse or key event. + * @param dragging true if the user is dragging. + * @param reverseDuplicateSelections true if the user is reversing selection rather than adding to. + * @return selection event that will need to be fired or null. + */ + @Nullable + private Event updateCellSelection( + @NotNull List newCells, + int stateMask, + boolean dragging, + boolean reverseDuplicateSelections, + EventSource eventSource) + { + boolean shift = (stateMask & SWT.MOD2) == SWT.MOD2; + boolean ctrl = (stateMask & SWT.MOD1) == SWT.MOD1; + if (eventSource == EventSource.KEYBOARD) { + ctrl = false; + } + + if (!shift) { + shiftSelectionAnchorColumn = null; + shiftSelectionAnchorItem = -1; + } + + List oldSelection = null; + if (!shift && !ctrl) { + if (newCells.size() == 1 && + newCells.size() == selectedCells.size() && + newCells.get(0).equals(selectedCells.iterator().next())) + { + return null; + } + + selectedCells.clear(); + for (GridPos newCell : newCells) { + addToCellSelection(newCell); + } + + } else if (shift) { + + GridPos newCell = newCells.get(0); //shift selection should only occur with one cell, ignoring others + oldSelection = new ArrayList<>(selectedCells); + + if ((focusColumn == null) || (focusItem < 0)) { + return null; + } + + shiftSelectionAnchorColumn = getColumn(newCell.col); + shiftSelectionAnchorItem = newCell.row; + + if (ctrl) { + selectedCells.clear(); + selectedCells.addAll(selectedCellsBeforeRangeSelect); + } else { + selectedCells.clear(); + } + + + GridColumn currentColumn = focusColumn; + int currentItem = focusItem; + + GridColumn endColumn = getColumn(newCell.col); + int endItem = newCell.row; + + Point newRange = getSelectionRange(currentItem, currentColumn, endItem, endColumn); + + currentColumn = getColumn(newRange.x); + endColumn = getColumn(newRange.y); + + GridColumn startCol = currentColumn; + + if (currentItem > endItem) { + int temp = currentItem; + currentItem = endItem; + endItem = temp; + } + + boolean firstLoop = true; + do { + if (!firstLoop) { + currentItem++; + } + + firstLoop = false; + + boolean firstLoop2 = true; + + currentColumn = startCol; + + do { + if (!firstLoop2) { + int index = indexOf(currentColumn) + 1; + + if (index < columns.size()) { + currentColumn = columns.get(index); + } else { + currentColumn = null; + } + + if (currentColumn != null) + if (indexOf(currentColumn) > indexOf(endColumn)) + currentColumn = null; + } + + firstLoop2 = false; + + if (currentColumn != null) { + GridPos cell = new GridPos(indexOf(currentColumn), currentItem); + addToCellSelection(cell); + } + } while (currentColumn != endColumn && currentColumn != null); + } while (currentItem != endItem); + + if (selectedCells.equals(newCells)) { + return null; + } + + } else /*if (eventSource == EventSource.MOUSE)*/ { + // Ctrl selection works only for mouse events + boolean reverse = reverseDuplicateSelections; + if (!selectedCells.containsAll(newCells)) + reverse = false; + + if (dragging) { + selectedCells.clear(); + selectedCells.addAll(selectedCellsBeforeRangeSelect); + } + + if (reverse) { + selectedCells.removeAll(newCells); + } else { + for (GridPos newCell : newCells) { + addToCellSelection(newCell); + } + } + } + if (oldSelection != null && oldSelection.size() == selectedCells.size() && selectedCells.containsAll(oldSelection)) { + return null; + } + + updateSelectionCache(); + + Event e = new Event(); + if (dragging) { + e.detail = SWT.DRAG; + followupCellSelectionEventOwed = true; + } + + Rectangle clientArea = getClientArea(); + redraw(clientArea.x, clientArea.y, clientArea.width, clientArea.height, false); + + return e; + } + + private boolean addToCellSelection(GridPos newCell) + { + if (newCell.col < 0 || newCell.col >= columns.size()) + return false; + + if (newCell.row < 0 || newCell.row >= getItemCount()) + return false; + + return selectedCells.add(newCell); + } + + private void updateSelectionCache() + { + //Update the list of which columns have all their cells selected + selectedColumns.clear(); + selectedRows.clear(); + + IntKeyMap columnIndices = new IntKeyMap<>(); + for (GridPos cell : selectedCells) { + columnIndices.put(cell.col, Boolean.TRUE); + selectedRows.put(cell.row, Boolean.TRUE); + } + for (Integer columnIndex : columnIndices.keySet()) { + selectedColumns.add(columns.get(columnIndex)); + } + selectedColumns.sort(Comparator.comparingInt(GridColumn::getIndex)); + } + + /** + * Initialize all listeners. + */ + private void initListeners() + { + disposeListener = this::onDispose; + addListener(SWT.Dispose, disposeListener); + + addPaintListener(this::onPaint); + addListener(SWT.Resize, e -> onResize()); + + if (getVerticalBar() != null) { + getVerticalBar().addListener(SWT.Selection, e -> onScrollSelection()); + } + + if (getHorizontalBar() != null) { + getHorizontalBar().addListener(SWT.Selection, e -> onScrollSelection()); + } + + addListener(SWT.KeyDown, this::onKeyDown); + + addTraverseListener(e -> e.doit = true); + + addMouseListener(new MouseListener() { + @Override + public void mouseDoubleClick(MouseEvent e) + { + onMouseDoubleClick(e); + } + + @Override + public void mouseDown(MouseEvent e) + { + onMouseDown(e); + } + + @Override + public void mouseUp(MouseEvent e) + { + onMouseUp(e); + } + }); + + addMouseMoveListener(this::onMouseMove); + + addMouseTrackListener(new MouseTrackListener() { + @Override + public void mouseEnter(MouseEvent e) + { + } + + @Override + public void mouseExit(MouseEvent e) + { + onMouseExit(e); + } + + @Override + public void mouseHover(MouseEvent e) + { + } + }); + + addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) + { + onFocusIn(); + redraw(); + } + + @Override + public void focusLost(FocusEvent e) + { + redraw(); + } + }); + + // Special code to reflect mouse wheel events if using an external + // scroller + addListener(SWT.MouseWheel, this::onMouseWheel); + } + + private void onFocusIn() + { + if (getItemCount() > 0 && focusItem < 0) { + focusItem = 0; + } + } + + private void onDispose(Event event) + { + removeAll(); + //We only want to dispose of our items and such *after* anybody else who may have been + //listening to the dispose has had a chance to do whatever. + removeListener(SWT.Dispose, disposeListener); + notifyListeners(SWT.Dispose, event); + event.type = SWT.None; + } + + /** + * Mouse wheel event handler. + * + * @param e event + */ + private void onMouseWheel(Event e) + { + if (vScroll.getVisible()) { + vScroll.handleMouseWheel(e); + if (getVerticalBar() == null) + e.doit = false; + } else if (hScroll.getVisible()) { + hScroll.handleMouseWheel(e); + if (getHorizontalBar() == null) + e.doit = false; + } + } + + /** + * Mouse down event handler. + * + * @param e event + */ + private void onMouseDown(MouseEvent e) + { + // for some reason, SWT prefers the children to get focus if + // there are any children + // the setFocus method on Composite will not set focus to the + // Composite if one of its + // children can get focus instead. This only affects the grid + // when an editor is open + // and therefore the grid has a child. The solution is to + // forceFocus() + if ((getStyle() & SWT.NO_FOCUS) != SWT.NO_FOCUS) { + forceFocus(); + } + + //if populated will be fired at end of method. + Event selectionEvent = null; + + cellSelectedOnLastMouseDown = false; + cellRowSelectedOnLastMouseDown = false; + cellColumnSelectedOnLastMouseDown = false; + + if (hoveringOnColumnSorter) { + handleHoverOnColumnHeader(e.x, e.y); + if (hoveringOnColumnSorter) { + return; + } + } + + if(hoveringOnColumnFilter) { + handleHoverOnColumnHeader(e.x, e.y); + if (hoveringOnColumnFilter) { + return; + } + } + + if (hoveringOnColumnResizer) { + if (e.button == 1) { + resizingColumn = true; + resizingStartX = e.x; + resizingColumnStartWidth = columnBeingResized.getWidth(); + } + return; + } + + Point point = new Point(e.x, e.y); + int row = getRow(point); + + if (isListening(SWT.DragDetect)) { + + if (hoveringOnHeaderDragArea && hoveringColumn != null) { + if (e.button == 1 && (hoveringColumn.isOverIcon(e.x, e.y) || selectedColumns.contains(hoveringColumn))) { + if (dragDetect(e)) { + // Drag and drop started + headerColumnDragStarted = true; + return; + } + } + } else if (hoveringOnRowHeader && hoveringRow != null) { + if (e.button == 1 && selectedRows.containsKey(hoveringRow) && dragDetect(e)) { + rowHeaderDragStarted = true; + return; + } + } + } + headerColumnDragStarted = false; + rowHeaderDragStarted = false; + + GridColumn col = null; + if (row >= 0) { + col = getColumn(point); + boolean isSelectedCell = false; + if (col != null) { + isSelectedCell = selectedCells.contains(new GridPos(col.getIndex(), row)); + } + + if (col == null && rowHeaderVisible && e.x <= rowHeaderWidth) { + boolean shift = ((e.stateMask & SWT.MOD2) != 0); + boolean ctrl = false; + if (!shift) { + ctrl = ((e.stateMask & SWT.MOD1) != 0); + } + + if (e.button == 1 && !shift && !ctrl) { + GridNode node = rowNodes.get(rowElements[row]); + GridNode parentNode = parentNodes[row]; + if (node != null && node.state != IGridContentProvider.ElementState.NONE) { + if (GridRowRenderer.isOverExpander(e.x, parentNode == null ? 0 : parentNode.level)) + { + toggleRowState(row); + return; + } + } + } + List cells = new ArrayList<>(); + + if (shift) { + getCells(row, focusItem, cells); + } else { + getCells(row, cells); + } + + int newStateMask = SWT.NONE; + if (ctrl) newStateMask = SWT.MOD1; + + selectionEvent = updateCellSelection(cells, newStateMask, shift, ctrl, EventSource.MOUSE); + cellRowSelectedOnLastMouseDown = (getCellSelectionCount() > 0); + + if (!shift) { + //set focus back to the first visible column + focusColumn = getColumn(new Point(rowHeaderWidth + 1, e.y)); + + focusItem = row; + } + showItem(row); + redraw(); + //return; + } else if (e.button == 1 || (e.button == 3 && col != null && !isSelectedCell)) { + if (col != null) { + selectionEvent = updateCellSelection(new GridPos(col.getIndex(), row), e.stateMask, false, true, EventSource.MOUSE); + // Trigger selection event always! + // It makes sense if grid content was changed but selection remains the same + // If user clicks on the same selected cell value - selection event will trigger value redraw in panels + selectionEvent = new Event(); + cellSelectedOnLastMouseDown = (getCellSelectionCount() > 0); + + if (e.stateMask != SWT.MOD2) { + focusColumn = col; + focusItem = row; + } + //showColumn(col); + showItem(row); + redraw(); + } + } else { + return; + } + } else if (e.button == 1 && rowHeaderVisible && e.x <= rowHeaderWidth && e.y < headerHeight) { + // Nothing to select + if (getItemCount() == 0) { + return; + } + + //click on the top left corner means select everything + selectionEvent = selectAllCellsInternal(e.stateMask); + + focusColumn = getColumn(new Point(rowHeaderWidth + 1, 1)); + focusItem = getTopIndex(); + } else if (e.button == 1 && columnHeadersVisible && e.y <= headerHeight) { + //column cell selection + col = getColumn(point); + + if (col == null) return; + + if (getItemCount() == 0) + return; + + List cells = new ArrayList<>(); + getCells(col, cells); + + selectionEvent = updateCellSelection(cells, e.stateMask, false, true, EventSource.MOUSE); + cellColumnSelectedOnLastMouseDown = (getCellSelectionCount() > 0); + + if (getItemCount() > 0) { + focusColumn = col; + focusItem = 0; + } + + showColumn(col); + redraw(); + } else { + // Change focus column anyway + GridColumn column = getColumn(point); + if (column == null) { + // Clicked on top-left cell or outside of grid + return; + } + focusColumn = column; + } + + if (selectionEvent != null) { + selectionEvent.stateMask = e.stateMask; + selectionEvent.button = e.button; + selectionEvent.data = new GridCell(col == null ? null : col.getElement(), row < 0 ? null : rowElements[row]); + selectionEvent.x = e.x; + selectionEvent.y = e.y; + notifyListeners(SWT.Selection, selectionEvent); + } + + + } + + private void toggleRowState(int row) { + GridNode node = rowNodes.get(rowElements[row]); + if (node == null || node.state == IGridContentProvider.ElementState.NONE) { + log.error("Row [" + row + "] state can't be toggled"); + return; + } + if (node.state == IGridContentProvider.ElementState.EXPANDED) { + // Collapse node. Remove all elements with different parent + int deleteTo; + for (deleteTo = row + 1; deleteTo < rowElements.length; deleteTo++) { + if (!node.isParentOf(parentNodes[deleteTo])) { + break; + } + } + rowElements = ArrayUtils.deleteArea(Object.class, rowElements, row + 1, deleteTo - 1); + parentNodes = ArrayUtils.deleteArea(GridNode.class, parentNodes, row + 1, deleteTo - 1); + node.state = IGridContentProvider.ElementState.COLLAPSED; + } else { + // Expand node + List result = new ArrayList<>(); + List parents = new ArrayList<>(); + collectRows(result, parents, node, node.rows, node.level); + rowElements = ArrayUtils.insertArea(Object.class, rowElements, row + 1, result.toArray()); + parentNodes = ArrayUtils.insertArea(GridNode.class, parentNodes, row + 1, parents.toArray()); + node.state = IGridContentProvider.ElementState.EXPANDED; + } + + if (focusItem > row) { + focusItem = row; + } + selectedCells.removeIf(pos -> pos.row > row); + updateSelectionCache(); + computeHeaderSizes(); + this.scrollValuesObsolete = true; + redraw(); + } + + /** + * Mouse double click event handler. + * + * @param e event + */ + private void onMouseDoubleClick(MouseEvent e) + { + if (e.button == 1) { + + if (hoveringOnColumnResizer) { + columnBeingResized.pack(true); + resizingColumn = false; + handleHoverOnColumnHeader(e.x, e.y); + redraw(); + return; + } + + Point point = new Point(e.x, e.y); + int row = getRow(point); + GridColumn col = getColumn(point); + if (row >= 0) { + if (col != null) { + if (isListening(SWT.DefaultSelection)) { + Event newEvent = new Event(); + newEvent.data = new GridCell(col.getElement(), rowElements[row]); + + notifyListeners(SWT.DefaultSelection, newEvent); + } + } else { + GridNode node = rowNodes.get(rowElements[row]); + GridNode parentNode = parentNodes[row]; + if (node != null && node.state != IGridContentProvider.ElementState.NONE) { + if (!GridRowRenderer.isOverExpander(e.x, parentNode == null ? 0 : parentNode.level)) + { + toggleRowState(row); + } + } + } + } + } + } + + /** + * Mouse up handler. + * + * @param e event + */ + private void onMouseUp(MouseEvent e) + { + if (focusColumn != null && focusItem >= 0) { + if (e.button == 1 && cellRenderer.isOverLink(focusColumn, focusItem, e.x, e.y)) { + // Navigate link + Event event = new Event(); + event.x = e.x; + event.y = e.y; + event.stateMask = e.stateMask; + event.data = new GridCell(focusColumn.getElement(), rowElements[focusItem]); + notifyListeners(Event_NavigateLink, event); + return; + } + } + + cellSelectedOnLastMouseDown = false; + + if (hoveringOnColumnSorter) { + handleHoverOnColumnHeader(e.x, e.y); + if (hoveringOnColumnSorter) { + if (e.button == 1) { + Event event = new Event(); + event.x = e.x; + event.y = e.y; + event.data = columnBeingSorted == null ? null : columnBeingSorted.getElement(); + event.stateMask = e.stateMask; + notifyListeners(Event_ChangeSort, event); + return; + } + } + } + + if (hoveringOnColumnFilter) { + handleHoverOnColumnHeader(e.x, e.y); + if (hoveringOnColumnFilter) { + if (e.button == 1) { + Event event = new Event(); + event.x = e.x; + event.y = e.y; + event.data = columnBeingFiltered == null ? null : columnBeingFiltered.getElement(); + event.stateMask = e.stateMask; + notifyListeners(Event_FilterColumn, event); + return; + } + + } + } + + if (resizingColumn) { + resizingColumn = false; + handleHoverOnColumnHeader(e.x, e.y); // resets cursor if + // necessary + return; + } + + if (cellDragSelectionOccurring || cellRowDragSelectionOccurring || cellColumnDragSelectionOccurring) { + cellDragSelectionOccurring = false; + cellRowDragSelectionOccurring = false; + cellColumnDragSelectionOccurring = false; + setCursor(null); + + if (followupCellSelectionEventOwed) { + Event se = new Event(); + se.button = e.button; + Point point = new Point(e.x, e.y); + GridColumn column = getColumn(point); + int rowIndex = getRow(point); + if (column != null && rowIndex >= 0) { + se.data = new GridCell(column.getElement(), rowElements[rowIndex]); + } + se.stateMask = e.stateMask; + se.x = e.x; + se.y = e.y; + se.detail = SWT.DROP_DOWN; + + notifyListeners(SWT.Selection, se); + followupCellSelectionEventOwed = false; + } + } + + } + + /** + * Mouse move event handler. + * + * @param e event + */ + private void onMouseMove(MouseEvent e) + { + //if populated will be fired at end of method. + Event selectionEvent = null; + + if ((e.stateMask & SWT.BUTTON1) == 0) { + + handleHovering(e.x, e.y); + + } else { + + if (resizingColumn) { + handleColumnResizerDragging(e.x); + return; + } + { + if (!cellDragSelectionOccurring && cellSelectedOnLastMouseDown) { + cellDragSelectionOccurring = true; + //XXX: make this user definable + setCursor(getDisplay().getSystemCursor(SWT.CURSOR_CROSS)); + cellDragCTRL = ((e.stateMask & SWT.MOD1) != 0); + if (cellDragCTRL) { + selectedCellsBeforeRangeSelect.clear(); + selectedCellsBeforeRangeSelect.addAll(selectedCells); + } + } + if (!cellRowDragSelectionOccurring && cellRowSelectedOnLastMouseDown) { + cellRowDragSelectionOccurring = true; + setCursor(getDisplay().getSystemCursor(SWT.CURSOR_CROSS)); + cellDragCTRL = ((e.stateMask & SWT.MOD1) != 0); + if (cellDragCTRL) { + selectedCellsBeforeRangeSelect.clear(); + selectedCellsBeforeRangeSelect.addAll(selectedCells); + } + } + + if (!cellColumnDragSelectionOccurring && cellColumnSelectedOnLastMouseDown) { + cellColumnDragSelectionOccurring = true; + setCursor(getDisplay().getSystemCursor(SWT.CURSOR_CROSS)); + cellDragCTRL = ((e.stateMask & SWT.MOD1) != 0); + if (cellDragCTRL) { + selectedCellsBeforeRangeSelect.clear(); + selectedCellsBeforeRangeSelect.addAll(selectedCells); + } + } + + int ctrlFlag = (cellDragCTRL ? SWT.MOD1 : SWT.NONE); + + if (cellDragSelectionOccurring && handleCellHover(e.x, e.y)) { + GridColumn intentColumn = hoveringColumn; + int intentItem = hoveringItem; + + if (hoveringItem < 0) { + if (e.y > headerHeight) { + //then we must be hovering way to the bottom + intentItem = Math.min(getItemCount() - 1, getBottomIndex() + 1); + } else { + intentItem = Math.max(0, getTopIndex() - 1); + } + } + + + if (hoveringColumn == null) { + if (e.x > rowHeaderWidth) { + //then we must be hovering way to the right + intentColumn = columns.get(columns.size() - 1); + } else { + intentColumn = columns.get(0); + } + } + + showColumn(intentColumn); + showItem(intentItem); + GridPos newCell = new GridPos(intentColumn.getIndex(), intentItem); + selectionEvent = updateCellSelection(newCell, ctrlFlag | SWT.MOD2, true, false, EventSource.MOUSE); + } + if (cellRowDragSelectionOccurring && handleCellHover(e.x, e.y)) { + int intentItem = hoveringItem; + + if (hoveringItem < 0) { + if (e.y > headerHeight) { + //then we must be hovering way to the bottom + intentItem = getTopIndex() + 1; + } else { + if (getTopIndex() > 0) { + intentItem = getTopIndex() - 1; + } else { + intentItem = 0; + } + } + } + + List cells = new ArrayList<>(); + + getCells(intentItem, focusItem, cells); + + showItem(intentItem); + selectionEvent = updateCellSelection(cells, ctrlFlag, true, false, EventSource.MOUSE); + } + final GridColumn prevHoveringColumn = hoveringColumn; + if (cellColumnDragSelectionOccurring && handleCellHover(e.x, e.y)) { + boolean dragging; + List newSelected = new ArrayList<>(); + + GridColumn iterCol = hoveringColumn; + if (iterCol != null) { + boolean decreasing = (indexOf(iterCol) > indexOf(focusColumn)); + dragging = true; + + while (iterCol != null) { + getCells(iterCol, newSelected); + + if (iterCol == focusColumn) { + break; + } + + if (decreasing) { + iterCol = getPreviousVisibleColumn(iterCol); + } else { + iterCol = getNextVisibleColumn(iterCol); + } + + } + } else { + dragging = false; + if (e.x <= rowHeaderWidth) { + GridColumn prev = prevHoveringColumn == null ? null : getPreviousVisibleColumn(prevHoveringColumn); + if (prev != null) { + showColumn(prev); + getCells(prev, newSelected); + ctrlFlag = SWT.MOD1; + } + } else { + + } + + } + + selectionEvent = updateCellSelection(newSelected, ctrlFlag, dragging, false, EventSource.MOUSE); + } + + } + } + + if (selectionEvent != null) { + selectionEvent.stateMask = e.stateMask; + selectionEvent.button = e.button; + Point point = new Point(e.x, e.y); + GridColumn column = getColumn(point); + int rowIndex = getRow(point); + if (column != null && rowIndex >= 0) { + selectionEvent.data = new GridCell(column.getElement(), rowElements[rowIndex]); + } + selectionEvent.x = e.x; + selectionEvent.y = e.y; + notifyListeners(SWT.Selection, selectionEvent); + } + } + + /** + * Handles the assignment of the correct values to the hover* field + * variables that let the painting code now what to paint as hovered. + * + * @param x mouse x coordinate + * @param y mouse y coordinate + */ + private void handleHovering(int x, int y) + { + handleCellHover(x, y); + + if (columnHeadersVisible) { + handleHoverOnColumnHeader(x, y); + } + if (!hoveringOnHeader && rowHeaderVisible) { + handleHoverOnRowHeader(x, y); + } + } + + /** + * Refreshes the hover* variables according to the mouse location and + * current state of the table. This is useful is some method call, caused + * the state of the table to change and therefore the hover effects may have + * become out of date. + */ + private void refreshHoverState() + { + Point p = getDisplay().map(null, this, getDisplay().getCursorLocation()); + handleHovering(p.x, p.y); + } + + /** + * Mouse exit event handler. + * + * @param e event + */ + private void onMouseExit(MouseEvent e) + { + hoveringItem = -1; + hoveringDetail = null; + hoveringColumn = null; + redraw(); + } + + public void scrollHorizontally(int count) { + Rectangle clientArea = getClientArea(); + GridColumn leftColumn = null, rightColumn = null; + for (GridColumn column : columns) { + Rectangle bounds = column.getBounds(); + if (leftColumn == null) { + if (bounds.x + bounds.width > 0) { + leftColumn = column; + } + } else { + if (bounds.x + bounds.width > clientArea.width) { + rightColumn = column; + break; + } + } + } + GridColumn scrollTo = null; + if (count > 0) { + if (leftColumn != null) { + scrollTo = getPreviousVisibleColumn(leftColumn); + if (scrollTo == null) { + scrollTo = leftColumn; + } + } + } else { + if (rightColumn != null) { + scrollTo = getNextVisibleColumn(rightColumn); + if (scrollTo == null) { + scrollTo = rightColumn; + } + } + + } + if (scrollTo != null) { + showColumn(scrollTo); + } + } + + /** + * Key down event handler. + * + * @param e event + */ + private void onKeyDown(Event e) + { + if (focusColumn == null) { + if (columns.size() == 0) + return; + + focusColumn = getColumn(0); + } + + if (e.character == '\r' && focusItem >= 0 && focusItem < rowElements.length) { + Event newEvent = new Event(); + newEvent.data = new GridCell(focusColumn.getElement(), rowElements[focusItem]); + + notifyListeners(SWT.DefaultSelection, newEvent); + return; + } + + int newSelection = -1; + GridColumn newColumnFocus = null; + + //These two variables are used because the key navigation when the shift key is down is + //based, not off the focus item/column, but rather off the implied focus (i.e. where the + //keyboard has extended focus to). + int impliedFocusItem = focusItem; + GridColumn impliedFocusColumn = focusColumn; + + boolean ctrlPressed = ((e.stateMask & SWT.MOD1) != 0); + boolean shiftPressed = ((e.stateMask & SWT.MOD2) != 0); + + //if (shiftPressed) { + if (shiftSelectionAnchorColumn != null) { + impliedFocusItem = shiftSelectionAnchorItem; + impliedFocusColumn = shiftSelectionAnchorColumn; + } + //} + switch (e.keyCode) { + case SWT.ARROW_RIGHT: + { + if (impliedFocusItem >= 0) { + newSelection = impliedFocusItem; + + int index = indexOf(impliedFocusColumn) + 1; + + if (index < columns.size()) { + newColumnFocus = columns.get(index); + } else { + newColumnFocus = impliedFocusColumn; + } + } + } + break; + case SWT.ARROW_LEFT: + { + if (impliedFocusItem >= 0) { + newSelection = impliedFocusItem; + + int index = indexOf(impliedFocusColumn); + + if (index > 0) { + newColumnFocus = columns.get(index - 1); + } else { + newColumnFocus = impliedFocusColumn; + } + } + } + break; + case SWT.ARROW_UP: + if (impliedFocusItem >= 0) { + newSelection = getPreviousVisibleItem(impliedFocusItem); + } + + newColumnFocus = impliedFocusColumn; + + break; + case SWT.ARROW_DOWN: + if (impliedFocusItem >= 0) { + newSelection = getNextVisibleItem(impliedFocusItem); + } else { + if (getItemCount() > 0) { + newSelection = 0; + } + } + + newColumnFocus = impliedFocusColumn; + break; + case SWT.HOME: + if (ctrlPressed || columns.size() == 1) { + newSelection = 0; + } else { + newSelection = impliedFocusItem; + } + newColumnFocus = columns.get(0); + + break; + case SWT.END: + { + if ((ctrlPressed || columns.size() == 1) && getItemCount() > 0) { + newSelection = getItemCount() - 1; + } else { + newSelection = impliedFocusItem; + } + newColumnFocus = columns.get(columns.size() - 1); + } + + break; + case SWT.PAGE_UP: + int topIndex = getTopIndex(); + + newSelection = topIndex; + + if ((impliedFocusItem >= 0 && impliedFocusItem == topIndex) || focusItem == topIndex) { + RowRange range = getRowRange(getTopIndex(), getVisibleGridHeight(), false, true); + newSelection = range.startIndex; + } + + newColumnFocus = impliedFocusColumn; + //newColumnFocus = focusColumn; + break; + case SWT.PAGE_DOWN: + int bottomIndex = getBottomIndex(); + + newSelection = bottomIndex; + + if (!isShown(bottomIndex)) { + // the item at bottom index is not shown completely + int tmpItem = getPreviousVisibleItem(newSelection); + if (tmpItem >= 0) + newSelection = tmpItem; + } + + if ((impliedFocusItem >= 0 && impliedFocusItem >= bottomIndex - 1) || focusItem == bottomIndex - 1) { + RowRange range = getRowRange(getBottomIndex(), getVisibleGridHeight(), true, false); + newSelection = range.endIndex; + } + + newColumnFocus = impliedFocusColumn; + //newColumnFocus = focusColumn; + break; + case '+': + case '-': + case '=': + case SWT.KEYPAD_ADD: + case SWT.KEYPAD_SUBTRACT: + if (focusItem >= 0) { + GridNode node = rowNodes.get(rowElements[focusItem]); + if (node != null) { + boolean isPlus = (e.keyCode == '+' || e.keyCode == '=' || e.keyCode == SWT.KEYPAD_ADD); + if ((node.state == IGridContentProvider.ElementState.EXPANDED && !isPlus) || + (node.state == IGridContentProvider.ElementState.COLLAPSED && isPlus)) + { + toggleRowState(focusItem); + } + } + } + break; + default: + break; + } + + if (newSelection < 0) { + return; + } + + if (newColumnFocus != null) { + //if (e.stateMask != SWT.MOD1) { + Event selEvent = updateCellSelection( + new GridPos(newColumnFocus.getIndex(), newSelection), + e.stateMask, + false, + false, + EventSource.KEYBOARD); + //} + + if (!shiftPressed) + focusColumn = newColumnFocus; + showColumn(newColumnFocus); + + if (!shiftPressed) { + if (newSelection < 0) { + focusItem = -1; + } else { + focusItem = newSelection; + } + } + showItem(newSelection); + + GridCell newPos; + if (newSelection >= 0 && newSelection < rowElements.length) { + newPos = new GridCell(newColumnFocus.getElement(), rowElements[newSelection]); + } else { + newPos = null; + } + if (selEvent != null) { + selEvent.stateMask = e.stateMask; + selEvent.character = e.character; + selEvent.keyCode = e.keyCode; + selEvent.data = newPos; + notifyListeners(SWT.Selection, selEvent); + } + + redraw(); + } + } + + /** + * Resize event handler. + */ + private void onResize() + { + + //CGross 1/2/08 - I don't really want to be doing this.... + //I shouldn't be changing something you user configured... + //leaving out for now +// if (columnScrolling) +// { +// int maxWidth = getClientArea().width; +// if (rowHeaderVisible) +// maxWidth -= rowHeaderWidth; +// +// for (Iterator cols = columns.iterator(); cols.hasNext();) { +// GridColumn col = (GridColumn) cols.next(); +// if (col.getWidth() > maxWidth) +// col.setWidth(maxWidth); +// } +// } + + scrollValuesObsolete = true; + topIndex = -1; + bottomIndex = -1; + } + + /** + * Scrollbar selection event handler. + */ + private void onScrollSelection() + { + topIndex = -1; + bottomIndex = -1; + refreshHoverState(); + final Rectangle clientArea = getClientArea(); + redraw(clientArea.x, clientArea.y, clientArea.width, clientArea.height, false); + } + + /** + * Returns the intersection of the given column and given item. + * + * @param column column + * @param item item + * @return x,y of top left corner of the cell + */ + Point getOrigin(GridColumn column, int item) + { + int x = 0; + + if (rowHeaderVisible) { + x += rowHeaderWidth; + } + + x -= getHScrollSelectionInPixels(); + + for (int i = 0; i < columns.size(); i++) { + GridColumn colIter = columns.get(i); + if (colIter == column) { + break; + } + x += colIter.getWidth(); + } + + int y = 0; + if (item >= 0) { + if (columnHeadersVisible) { + y += headerHeight; + } + + int currIndex = getTopIndex(); + + if (item == -1) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + while (currIndex != item) { + if (currIndex < item) { + y += getItemHeight() + 1; + currIndex++; + } else if (currIndex > item) { + currIndex--; + y -= getItemHeight() + 1; + } + } + } else if (columnHeadersVisible && column.getParent() != null) { + for (GridColumn parent = column.getParent(); parent != null; parent = parent.getParent()) { + y += parent.getHeaderHeight(false, false); + } + } + + return new Point(x, y); + } + + /** + * Sets the hovering variables (hoverItem,hoveringColumn) as well as + * hoverDetail by talking to the cell renderers. Triggers a redraw if + * necessary. + * + * @param x mouse x + * @param y mouse y + * @return true if a new section of the table is now being hovered + */ + private boolean handleCellHover(int x, int y) + { + Point point = new Point(x, y); + final GridColumn col = getColumn(point); + final int row = getRow(point); + Integer detail = + (hoveringOnColumnSorter ? 1000000 : 0) + + (hoveringOnColumnFilter ? 1000000 : 0) + + y; + + boolean hoverChange = false; + + if (hoveringItem != row || !CommonUtils.equalObjects(hoveringDetail, detail) || hoveringColumn != col || y <= headerHeight) { + hoveringItem = row; + hoveringDetail = detail; + hoveringColumn = col; + + hoverChange = true; + } + + // Check for link + boolean overLink = false; + if (col != null && row >= 0) { + if (cellRenderer.isOverLink(col, row, x, y)) { + overLink = true; + } + } + + if (overLink) { + if (!hoveringOnLink) { + setCursor(sortCursor); + } + } else if (hoveringOnLink) { + setCursor(null); + } + hoveringOnLink = overLink; + + //do normal cell specific tooltip stuff + if (hoverChange) { + + // Check tooltip + String newTip = null; + if ((hoveringItem >= 0) && (hoveringColumn != null)) { + if (overLink) { + newTip = getContentProvider().getCellLinkText(columnElements[col.getIndex()], rowElements[row]); + } + if (CommonUtils.isEmpty(newTip)) { + // get cell specific tooltip + newTip = getCellToolTip(hoveringColumn, hoveringItem); + } + } else if (columnHeadersVisible && hoveringColumn != null && y <= headerHeight) { + // get column header specific tooltip + if (hoveringOnColumnSorter) { + newTip = NLS.bind(DataEditorsMessages.grid_tooltip_sort_by_column, getLabelProvider().getText(hoveringColumn.getElement())); + } else if (hoveringOnColumnFilter) { + newTip = NLS.bind(DataEditorsMessages.grid_tooltip_filter_by_column, getLabelProvider().getText(hoveringColumn.getElement())); + } else { + newTip = hoveringColumn.getHeaderTooltip(); + } + } else if (rowHeaderVisible && hoveringItem >= 0 && x <= rowHeaderWidth) { + newTip = getLabelProvider().getToolTipText(getRowElement(hoveringItem)); + } + + //Avoid unnecessarily resetting tooltip - this will cause the tooltip to jump around + if (newTip != null && !newTip.equals(displayedToolTipText)) { + updateToolTipText(newTip); + } else if (newTip == null && displayedToolTipText != null) { + updateToolTipText(null); + } + displayedToolTipText = newTip; + } + + return hoverChange; + } + + /** + * Sets the tooltip for the whole Grid to the given text. This method is made available + * for subclasses to override, when a subclass wants to display a different than the standard + * SWT/OS tooltip. Generally, those subclasses would override this event and use this tooltip + * text in their own tooltip or just override this method to prevent the SWT/OS tooltip from + * displaying. + * + * @param text tooltip text + */ + private void updateToolTipText(@Nullable String text) + { + ToolTipHandler curHandler = this.toolTipHandler; + if (!CommonUtils.equalObjects(prevToolTip, text)) { + // New tooltip + if (curHandler != null) { + curHandler.cancel(); + } + prevToolTip = text; + this.setToolTipText(""); + this.toolTipHandler = new ToolTipHandler(); + this.toolTipHandler.toolTip = text; + this.toolTipHandler.schedule(500); + } + } + + /** + * Marks the scroll values obsolete so they will be recalculated. + */ + protected void setScrollValuesObsolete() + { + this.scrollValuesObsolete = true; + redraw(); + } + + /** + * Inserts a new column into the table. + * + * @param column new column + * @param index index to insert new column + */ + void newColumn(GridColumn column, int index) + { + + if (index == -1) { + columns.add(column); + } else { + columns.add(index, column); + } + } + + public void recalculateSizes() { + int oldHeaderHeight = headerHeight; + computeHeaderSizes(); + if (oldHeaderHeight != headerHeight) { + scrollValuesObsolete = true; + } + } + + /** + * Returns the current cell in focus. If cell selection is disabled, this method returns null. + * + * @return cell in focus or {@code null}. x represents the column and y the row the cell is in + */ + public GridPos getFocusPos() + { + checkWidget(); + + int x = -1; + + if (focusColumn != null) + x = focusColumn.getIndex(); + focusCell.col = x; + focusCell.row = focusItem; + return focusCell; + } + + @Nullable + public Object getFocusColumnElement() { + return focusColumn == null ? null : focusColumn.getElement(); + } + + @Nullable + public Object getFocusRowElement() { + if (focusItem < 0 || focusItem >= rowElements.length) { + return null; + } + return rowElements[focusItem]; + } + + @Nullable + public GridCell getFocusCell() + { + return posToCell(getFocusPos()); + } + /** + * Sets the focused item to the given item. + * + * @param item item to focus. + */ + public void setFocusItem(int item) + { + checkWidget(); + focusItem = item; + } + + /** + * Sets the focused item to the given column. Column focus is only applicable when cell + * selection is enabled. + * + * @param col column to focus. + */ + public void setFocusColumn(int col) + { + checkWidget(); + GridColumn column = getColumn(col); + if (column == null || column.getGrid() != this) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + return; + } + + focusColumn = column; + } + + public void resetFocus() { + focusColumn = null; + focusItem = -1; + } + + /** + * Returns true if the table is set to horizontally scroll column-by-column + * rather than pixel-by-pixel. + * + * @return true if the table is scrolled horizontally by column + */ + private boolean getColumnScrolling() + { + checkWidget(); + return columnScrolling; + } + + /** + * Sets the table scrolling method to either scroll column-by-column (true) + * or pixel-by-pixel (false). + * + * @param columnScrolling true to horizontally scroll by column, false to + * scroll by pixel + */ + private void setColumnScrolling(boolean columnScrolling) + { + checkWidget(); + if (rowHeaderVisible && !columnScrolling) { + return; + } + + this.columnScrolling = columnScrolling; + scrollValuesObsolete = true; + redraw(); + } + + /** + * Selects the given cell. Invalid cells are ignored. + * + * @param cell point whose x values is a column index and y value is an item index + */ + public void selectCell(@NotNull GridPos cell) + { + checkWidget(); + + addToCellSelection(cell); + updateSelectionCache(); + redraw(); + } + + /** + * Selects the given cells. Invalid cells are ignored. + * + * @param cells an array of points whose x value is a column index and y value is an item index + */ + public void selectCells(@NotNull Collection cells) + { + checkWidget(); + + for (GridPos cell : cells) { + addToCellSelection(cell); + } + + updateSelectionCache(); + redraw(); + } + + /** + * Selects all cells in the receiver. + */ + public void selectAllCells() + { + checkWidget(); + selectAllCellsInternal(0); + } + + /** + * Selects all cells in the receiver. + * + * @return An Event object + */ + @Nullable + private Event selectAllCellsInternal(int stateMask) + { + if (columns.size() == 0) + return null; + + if (getItemCount() == 0) + return null; + + GridColumn oldFocusColumn = focusColumn; + int oldFocusItem = focusItem; + + focusColumn = columns.get(0); + focusItem = 0; + + List cells = new ArrayList<>(); + getAllCells(cells); + Event selectionEvent = updateCellSelection(cells, stateMask, false, true, EventSource.KEYBOARD); + + focusColumn = oldFocusColumn; + focusItem = oldFocusItem; + + updateSelectionCache(); + + redraw(); + + return selectionEvent; + } + + /** + * Selects the selection to the given cell. The existing selection is cleared before + * selecting the given cell. + * + * @param cell point whose x values is a column index and y value is an item index + */ + public void setCellSelection(@NotNull GridPos cell) + { + checkWidget(); + + if (!isValidCell(cell)) + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + + selectedCells.clear(); + addToCellSelection(cell); + updateSelectionCache(); + redraw(); + } + + /** + * Returns an array of cells that are currently selected in the + * receiver. The order of the items is unspecified. An empty array indicates + * that no items are selected. + *

+ * Note: This is not the actual structure used by the receiver to maintain + * its selection, so modifying the array will not affect the receiver. + *

+ * + * @return an array representing the cell selection + */ + @NotNull + public Collection getSelection() + { + if (isDisposed()) { + return Collections.emptyList(); + } + return Collections.unmodifiableCollection(selectedCells); + } + + public List getCellSelection() + { + if (isDisposed() || selectedCells.isEmpty()) { + return Collections.emptyList(); + } + List cells = new ArrayList<>(selectedCells.size()); + for (GridPos pos : selectedCells) { + cells.add(posToCell(pos)); + } + return cells; + } + + @NotNull + public List getColumnSelection() + { + if (selectedColumns.isEmpty()) { + return Collections.emptyList(); + } + List selection = new ArrayList<>(); + for (GridColumn col : selectedColumns) { + selection.add(col.getElement()); + } + return selection; + } + + /** + * Returns selected rows indexes + * @return indexes of selected rows + */ + public Collection getRowSelection() + { + return Collections.unmodifiableCollection(selectedRows.keySet()); + } + + private void getCells(GridColumn col, List cells) + { + if (col.getChildren() != null) { + // Get cells for all leafs + for (int i = 0; i < columns.size(); i++) { + if (columns.get(i).isParent(col)) { + for (int k = 0; k < getItemCount(); k++) { + cells.add(new GridPos(i, k)); + } + } + } + } else { + int colIndex = col.getIndex(); + + for (int i = 0; i < getItemCount(); i++) { + cells.add(new GridPos(colIndex, i)); + } + } + } + + private void getCells(int row, List cells) + { + for (int i = 0; i < columns.size(); i++) { + cells.add(new GridPos(i, row)); + } + } + + private void getAllCells(List cells) + { + for (int i = 0; i < getItemCount(); i++) { + for (int k = 0; k < columns.size(); k++) { + cells.add(new GridPos(k, i)); + } + } + } + + private List getCells(int row) + { + List cells = new ArrayList<>(); + getCells(row, cells); + return cells; + } + + + private void getCells(int startRow, int endRow, List cells) + { + boolean descending = (startRow < endRow); + + int iterItem = endRow; + + do { + getCells(iterItem, cells); + + if (iterItem == startRow) break; + + if (descending) { + iterItem--; + } else { + iterItem++; + } + } while (true); + } + + /** + * Returns a point whose x and y values are the to and from column indexes of the new selection + * range inclusive of all spanned columns. + */ + private Point getSelectionRange(int fromItem, GridColumn fromColumn, int toItem, GridColumn toColumn) + { + if (indexOf(fromColumn) > indexOf(toColumn)) { + GridColumn temp = fromColumn; + fromColumn = toColumn; + toColumn = temp; + } + + if (fromItem > toItem) { + int temp = fromItem; + fromItem = toItem; + toItem = temp; + } + + boolean firstTime = true; + int iterItem = fromItem; + + int fromIndex = fromColumn.getIndex(); + int toIndex = toColumn.getIndex(); + + do { + if (!firstTime) { + iterItem++; + } else { + firstTime = false; + } + + Point cols = getRowSelectionRange(fromColumn, toColumn); + + //check and see if column spanning means that the range increased + if (cols.x != fromIndex || cols.y != toIndex) { + GridColumn newFrom = getColumn(cols.x); + GridColumn newTo = getColumn(cols.y); + + //Unfortunately we have to start all over again from the top with the new range + return getSelectionRange(fromItem, newFrom, toItem, newTo); + } + } while (iterItem != toItem); + + return new Point(fromColumn.getIndex(), toColumn.getIndex()); + } + + /** + * Returns a point whose x and y value are the to and from column indexes of the new selection + * range inclusive of all spanned columns. + */ + private Point getRowSelectionRange(GridColumn fromColumn, GridColumn toColumn) + { + int newFrom = fromColumn.getIndex(); + int newTo = toColumn.getIndex(); + return new Point(newFrom, newTo); + } + + /** + * Returns true if the given cell's x and y values are valid column and + * item indexes respectively. + * + * @param cell cell + */ + private boolean isValidCell(GridPos cell) + { + if (cell.col < 0 || cell.col >= columns.size()) + return false; + + if (cell.row < 0 || cell.row >= getItemCount()) { + return false; + } + // Valid + return true; + } + + + + @Override + public void setFont(Font font) + { + super.setFont(font); + sizingGC.setFont(font); + fontMetrics = sizingGC.getFontMetrics(); + normalFont = font; + } + + public String getCellText(Object colElement, Object rowElement) + { + String text = getContentProvider().getCellText(colElement, rowElement); + // Truncate too long texts (they are really bad for performance) + if (text.length() > MAX_TOOLTIP_LENGTH) { + text = text.substring(0, MAX_TOOLTIP_LENGTH) + " ..."; + } + + return text; + } + + @Nullable + private String getCellToolTip(GridColumn col, int row) + { + String toolTip = getCellText(columnElements[col.getIndex()], rowElements[row]); + if (toolTip == null) { + return null; + } + // Show tooltip only if it's larger than column width + Point ttSize = sizingGC.textExtent(toolTip); + if (ttSize.x > col.getWidth() || ttSize.y > getItemHeight()) { + int gridHeight = getBounds().height; + if (ttSize.y > gridHeight) { + // Too big tool tip - larger than entire grid + // Lets chop it + StringBuilder newToolTip = new StringBuilder(); + StringTokenizer st = new StringTokenizer(toolTip, "'\n"); + int maxLineNumbers = gridHeight / getItemHeight(), lineNumber = 0; + while (st.hasMoreTokens()) { + newToolTip.append(st.nextToken()).append('\n'); + lineNumber++; + if (lineNumber >= maxLineNumbers) { + break; + } + } + toolTip = newToolTip.toString(); + } + return toolTip; + } else { + return ""; + } + } + + @Nullable + public DBPImage getCellImage(Object colElement, Object rowElement) + { + return getContentProvider().getCellImage(colElement, rowElement); + } + + public Color getCellBackground(Object colElement, Object rowElement, boolean selected) + { + Color color = getContentProvider().getCellBackground(colElement, rowElement, selected); + return color != null ? color : getBackground(); + } + + public Color getCellForeground(Object colElement, Object rowElement, boolean selected) + { + Color color = getContentProvider().getCellForeground(colElement, rowElement, selected); + return color != null ? color : getForeground(); + } + + public Rectangle getCellBounds(int columnIndex, int rowIndex) { + if (!isShown(rowIndex)) + return new Rectangle(-1000, -1000, 0, 0); + + GridColumn column = getColumn(columnIndex); + Point origin = getOrigin(column, rowIndex); + + if (origin.x < 0 && isRowHeaderVisible()) + return new Rectangle(-1000, -1000, 0, 0); + + return new Rectangle(origin.x, origin.y, column.getWidth(), getItemHeight()); + } + + void setDefaultBackground(GC gc) { + Color background = getLabelProvider().getBackground(null); + if (background != null) { + gc.setBackground(background); + } + } + + private void drawEmptyColumnHeader(GC gc, int x, int y, int width, int height) + { + gc.setBackground(getContentProvider().getCellHeaderBackground(null)); + + gc.fillRectangle( + x, + y, + width + 1, + height + 1); + } + + private void drawEmptyRowHeader(GC gc, int x, int y, int width, int height) + { + gc.setBackground(getContentProvider().getCellHeaderBackground(null)); + + gc.fillRectangle(x, y, width, height + 1); + + gc.setForeground(getContentProvider().getCellHeaderForeground(null)); + + gc.drawLine( + x + width - 1, + y, + x + width - 1, + y + height - 1); + gc.drawLine( + x, + y + height - 1, + x + width - 1, + y + height - 1); + } + + private void drawEmptyCell(GC gc, int x, int y, int width, int height) { + IGridLabelProvider labelProvider = getLabelProvider(); + Color foreground = labelProvider.getForeground(null); + setDefaultBackground(gc); + gc.setForeground(foreground); + + gc.fillRectangle(x, y, width + 1, height); + + if (isLinesVisible()) { + gc.setForeground(getLineColor()); + gc.drawLine( + x, + y + height, + x + width, + y + height); + gc.drawLine(x + width - 1, + y, + x + width - 1, + y + height); + } + } + + private void drawTopLeftCell(GC gc, int x, int y, int width, int height) { + int sortOrder = getContentProvider().getSortOrder(null); + gc.setBackground(getContentProvider().getCellHeaderBackground(null)); + + gc.fillRectangle( + x, + y, + width - 1, + height + 1); + + gc.setForeground(getContentProvider().getCellHeaderForeground(null)); + + gc.drawLine( + x + width - 1, + y, + x + width - 1, + y + height); + + gc.drawLine( + x, + y + height - 1, + x + width, + y + height - 1); + + if (sortOrder != SWT.NONE) { + int arrowWidth = GridColumnRenderer.SORT_WIDTH; + Rectangle sortBounds = new Rectangle( + x + width - GridColumnRenderer.ARROW_MARGIN - arrowWidth, + y + GridColumnRenderer.TOP_MARGIN, + arrowWidth, + height); + GridColumnRenderer.paintSort(gc, sortBounds, sortOrder); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + // DnD + ///////////////////////////////////////////////////////////////////////////////// + + private void addDragAndDropSupport() + { + final int operations = DND.DROP_MOVE | DND.DROP_COPY;// | DND.DROP_MOVE | DND.DROP_LINK | DND.DROP_DEFAULT; + + final DragSource source = new DragSource(this, operations); + source.setTransfer(GridColumnTransfer.INSTANCE, TextTransfer.getInstance()); + source.addDragListener (new DragSourceListener() { + + private Image dragImage; + private long lastDragEndTime; + + @Override + public void dragStart(DragSourceEvent event) { + if (lastDragEndTime > 0 && System.currentTimeMillis() - lastDragEndTime < 100) { + event.doit = false; + } else { + Rectangle columnBounds; + if (headerColumnDragStarted && hoveringColumn != null) { + draggingColumn = hoveringColumn; + columnBounds = hoveringColumn.getBounds(); + } else if (rowHeaderDragStarted && hoveringRow != null) { + draggingRow = hoveringRow; + int rowFromTop = hoveringRow - getTopIndex(); + columnBounds = new Rectangle(0, rowFromTop * getItemHeight(), getRowHeaderWidth(), getItemHeight()); + } else { + event.doit = false; + return; + } + GC gc = new GC(LightGrid.this); + dragImage = new Image(Display.getCurrent(), columnBounds.width, columnBounds.height); + gc.copyArea( + dragImage, + columnBounds.x, + columnBounds.y); + event.image = dragImage; + gc.dispose(); + } + } + + @Override + public void dragSetData (DragSourceEvent event) { + if (draggingColumn != null) { + if (GridColumnTransfer.INSTANCE.isSupportedType(event.dataType)) { + List elements = new ArrayList<>(); + if (isDragSingleColumn()) { + elements.add(draggingColumn.getElement()); + } else { + for (GridColumn col : selectedColumns) { + elements.add(col.getElement()); + } + } + event.data = elements; + } else if (TextTransfer.getInstance().isSupportedType(event.dataType)) { + // Copy all selected columns + if (selectedColumns.size() > 1 && !isDragSingleColumn()) { + StringBuilder text = new StringBuilder(); + for (GridColumn column : selectedColumns) { + if (text.length() > 0) text.append(", "); + text.append(getLabelProvider().getText(column.getElement())); + } + event.data = text.toString(); + } else { + event.data = getLabelProvider().getText(draggingColumn.getElement()); + } + } + } else if (draggingRow != null) { + if (GridColumnTransfer.INSTANCE.isSupportedType(event.dataType)) { + List elements = new ArrayList<>(); + if (isDragSingleRow()) { + elements.add(getRowElement(draggingRow)); + } else { + for (Integer row : selectedRows.keySet()) { + elements.add(getRowElement(row)); + } + } + event.data = elements; + } else if (TextTransfer.getInstance().isSupportedType(event.dataType)) { + List columns = selectedColumns; + if (columns.isEmpty()) { + columns = LightGrid.this.columns; + } + Set rows = selectedRows.keySet(); + if (rows.isEmpty()) { + rows = Collections.singleton(draggingRow); + } + + StringBuilder text = new StringBuilder(); + for (Integer row : rows) { + if (text.length() > 0) text.append("\n"); + for (int i = 0; i < columns.size(); i++) { + GridColumn column = columns.get(i); + String cellText = getContentProvider().getCellText(column.getElement(), getRowElement(row)); + if (i > 0) text.append(", "); + text.append(cellText); + } + } + event.data = text.toString(); + } + } + } + @Override + public void dragFinished(DragSourceEvent event) { + draggingColumn = null; + draggingRow = null; + if (dragImage != null) { + UIUtils.dispose(dragImage); + dragImage = null; + } + lastDragEndTime = System.currentTimeMillis(); + } + }); + + DropTarget dropTarget = new DropTarget(this, operations); + dropTarget.setTransfer(GridColumnTransfer.INSTANCE, TextTransfer.getInstance()); + dropTarget.addDropListener(new DropTargetListener() { + @Override + public void dragEnter(DropTargetEvent event) + { + handleDragEvent(event); + } + + @Override + public void dragLeave(DropTargetEvent event) + { + handleDragEvent(event); + } + + @Override + public void dragOperationChanged(DropTargetEvent event) + { + handleDragEvent(event); + } + + @Override + public void dragOver(DropTargetEvent event) + { + handleDragEvent(event); + } + + @Override + public void drop(DropTargetEvent event) + { + handleDragEvent(event); + if (event.detail == DND.DROP_MOVE) { + moveColumns(event); + } + } + + @Override + public void dropAccept(DropTargetEvent event) + { + handleDragEvent(event); + } + + private void handleDragEvent(DropTargetEvent event) + { + if (!isDropSupported(event)) { + event.detail = DND.DROP_NONE; + } else { + event.detail = DND.DROP_MOVE; + } + event.feedback = DND.FEEDBACK_SELECT; + } + + private boolean isDropSupported(DropTargetEvent event) + { + if (!hoveringOnHeaderDragArea) { + return false; + } + if (draggingColumn == null || draggingColumn.getGrid() != LightGrid.this) { + return false; + } + GridColumn overColumn = getOverColumn(event); + return draggingColumn != overColumn; + } + + private GridColumn getOverColumn(DropTargetEvent event) { + Point dragPoint = getDisplay().map(null, LightGrid.this, new Point(event.x, event.y)); + return getColumn(dragPoint); + } + + private void moveColumns(DropTargetEvent event) + { + GridColumn overColumn = getOverColumn(event); + if (draggingColumn == null || draggingColumn == overColumn) { + return; + } + IGridController gridController = getGridController(); + if (gridController != null) { + IGridController.DropLocation location;// = IGridController.DropLocation.SWAP; + + Point dropPoint = getDisplay().map(null, LightGrid.this, new Point(event.x, event.y)); + Rectangle columnBounds = overColumn.getBounds(); + if (dropPoint.x > columnBounds.x + columnBounds.width / 2) { + location = IGridController.DropLocation.DROP_AFTER; + } else { + location = IGridController.DropLocation.DROP_BEFORE; + } + gridController.moveColumn(draggingColumn.getElement(), overColumn.getElement(), location); + } + draggingColumn = null; + } + }); + } + + private boolean isDragSingleColumn() { + return draggingColumn != null && !selectedColumns.contains(draggingColumn); + } + + private boolean isDragSingleRow() { + return draggingRow != null && !selectedRows.containsKey(draggingRow); + } + + public final static class GridColumnTransfer extends LocalObjectTransfer> { + + public static final GridColumnTransfer INSTANCE = new GridColumnTransfer(); + private static final String TYPE_NAME = "LighGrid.GridColumn Transfer" + System.currentTimeMillis() + ":" + INSTANCE.hashCode();//$NON-NLS-1$ + private static final int TYPEID = registerType(TYPE_NAME); + + private GridColumnTransfer() { + } + + @Override + protected int[] getTypeIds() { + return new int[] { TYPEID }; + } + + @Override + protected String[] getTypeNames() { + return new String[] { TYPE_NAME }; + } + + } + + +} + + diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/NullScrollBar.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/NullScrollBar.java similarity index 94% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/NullScrollBar.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/NullScrollBar.java index 82e1f3cbd7935d70e9525faa21224ad0ba7348c1..1b6b2a4854fef1ce37d8e42da12f2282f9beee89 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/NullScrollBar.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/NullScrollBar.java @@ -1,135 +1,135 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Event; - -/** - * A null-op scrollbar proxy. Used when the grid is not showing scrollbars. - * - * @author chris.gross@us.ibm.com - * @since 2.0.0 - */ -class NullScrollBar implements IGridScrollBar -{ - - @Override - public int getWidth() - { - return 0; - } - - @Override - public boolean getVisible() - { - return false; - } - - @Override - public void setVisible(boolean visible) - { - } - - @Override - public int getSelection() - { - return 0; - } - - @Override - public void setSelection(int selection) - { - } - - @Override - public void setValues(int selection, int min, int max, int thumb, int increment, int pageIncrement) - { - } - - @Override - public void handleMouseWheel(Event e) - { - } - - @Override - public void setMinimum(int min) - { - } - - @Override - public int getMinimum() - { - return 0; - } - - @Override - public void setMaximum(int max) - { - } - - @Override - public int getMaximum() - { - return 0; - } - - @Override - public void setThumb(int thumb) - { - } - - @Override - public int getThumb() - { - return 0; - } - - @Override - public void setIncrement(int increment) - { - } - - @Override - public int getIncrement() - { - return 0; - } - - @Override - public void setPageIncrement(int page) - { - } - - @Override - public int getPageIncrement() - { - return 0; - } - - @Override - public void addSelectionListener(SelectionListener listener) - { - } - - @Override - public void removeSelectionListener(SelectionListener listener) - { - - } - -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Event; + +/** + * A null-op scrollbar proxy. Used when the grid is not showing scrollbars. + * + * @author chris.gross@us.ibm.com + * @since 2.0.0 + */ +class NullScrollBar implements IGridScrollBar +{ + + @Override + public int getWidth() + { + return 0; + } + + @Override + public boolean getVisible() + { + return false; + } + + @Override + public void setVisible(boolean visible) + { + } + + @Override + public int getSelection() + { + return 0; + } + + @Override + public void setSelection(int selection) + { + } + + @Override + public void setValues(int selection, int min, int max, int thumb, int increment, int pageIncrement) + { + } + + @Override + public void handleMouseWheel(Event e) + { + } + + @Override + public void setMinimum(int min) + { + } + + @Override + public int getMinimum() + { + return 0; + } + + @Override + public void setMaximum(int max) + { + } + + @Override + public int getMaximum() + { + return 0; + } + + @Override + public void setThumb(int thumb) + { + } + + @Override + public int getThumb() + { + return 0; + } + + @Override + public void setIncrement(int increment) + { + } + + @Override + public int getIncrement() + { + return 0; + } + + @Override + public void setPageIncrement(int page) + { + } + + @Override + public int getPageIncrement() + { + return 0; + } + + @Override + public void addSelectionListener(SelectionListener listener) + { + } + + @Override + public void removeSelectionListener(SelectionListener listener) + { + + } + +} diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/ScrollBarAdapter.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/ScrollBarAdapter.java similarity index 95% rename from plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/ScrollBarAdapter.java rename to plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/ScrollBarAdapter.java index da7bbe72ded52b1a6827489d0c9845ea2d4b2ec7..d0a52a3f021c89338257c5486769de7bd49d7acb 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/lightgrid/ScrollBarAdapter.java +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/controls/lightgrid/ScrollBarAdapter.java @@ -1,163 +1,163 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jkiss.dbeaver.ui.controls.lightgrid; - -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.ScrollBar; - -/** - * Adapts a normal scrollbar to the IScrollBar proxy. - * - * @author chris.gross@us.ibm.com - * @since 2.0.0 - */ -class ScrollBarAdapter implements IGridScrollBar -{ - /** - * Delegates to this scrollbar. - */ - private ScrollBar scrollBar; - - /** - * Contructs this adapter by delegating to the given scroll bar. - * - * @param scrollBar delegate - */ - public ScrollBarAdapter(ScrollBar scrollBar) - { - super(); - this.scrollBar = scrollBar; - } - - @Override - public int getIncrement() - { - return scrollBar.getIncrement(); - } - - @Override - public int getMaximum() - { - return scrollBar.getMaximum(); - } - - @Override - public int getMinimum() - { - return scrollBar.getMinimum(); - } - - @Override - public int getPageIncrement() - { - return scrollBar.getPageIncrement(); - } - - @Override - public int getSelection() - { - return scrollBar.getSelection(); - } - - @Override - public int getThumb() - { - return scrollBar.getThumb(); - } - - @Override - public int getWidth() - { - return scrollBar.getSize().x; - } - - - - @Override - public boolean getVisible() - { - return scrollBar.getVisible(); - } - - @Override - public void setIncrement(int value) - { - scrollBar.setIncrement(value); - } - - @Override - public void setMaximum(int value) - { - scrollBar.setMaximum(value); - } - - @Override - public void setMinimum(int value) - { - scrollBar.setMinimum(value); - } - - @Override - public void setPageIncrement(int value) - { - scrollBar.setPageIncrement(value); - } - - @Override - public void setSelection(int selection) - { - scrollBar.setSelection(selection); - } - - @Override - public void setThumb(int value) - { - scrollBar.setThumb(value); - } - - @Override - public void setValues(int selection, int minimum, int maximum, int thumb, int increment, - int pageIncrement) - { - scrollBar.setValues(selection, minimum, maximum, thumb, increment, pageIncrement); - } - - @Override - public void setVisible(boolean visible) - { - scrollBar.setVisible(visible); - } - - @Override - public void handleMouseWheel(Event e) - { - //do nothing - } - - @Override - public void addSelectionListener(SelectionListener listener) - { - scrollBar.addSelectionListener(listener); - } - - @Override - public void removeSelectionListener(SelectionListener listener) - { - scrollBar.removeSelectionListener(listener); - } -} +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jkiss.dbeaver.ui.controls.lightgrid; + +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.ScrollBar; + +/** + * Adapts a normal scrollbar to the IScrollBar proxy. + * + * @author chris.gross@us.ibm.com + * @since 2.0.0 + */ +class ScrollBarAdapter implements IGridScrollBar +{ + /** + * Delegates to this scrollbar. + */ + private ScrollBar scrollBar; + + /** + * Contructs this adapter by delegating to the given scroll bar. + * + * @param scrollBar delegate + */ + public ScrollBarAdapter(ScrollBar scrollBar) + { + super(); + this.scrollBar = scrollBar; + } + + @Override + public int getIncrement() + { + return scrollBar.getIncrement(); + } + + @Override + public int getMaximum() + { + return scrollBar.getMaximum(); + } + + @Override + public int getMinimum() + { + return scrollBar.getMinimum(); + } + + @Override + public int getPageIncrement() + { + return scrollBar.getPageIncrement(); + } + + @Override + public int getSelection() + { + return scrollBar.getSelection(); + } + + @Override + public int getThumb() + { + return scrollBar.getThumb(); + } + + @Override + public int getWidth() + { + return scrollBar.getSize().x; + } + + + + @Override + public boolean getVisible() + { + return scrollBar.getVisible(); + } + + @Override + public void setIncrement(int value) + { + scrollBar.setIncrement(value); + } + + @Override + public void setMaximum(int value) + { + scrollBar.setMaximum(value); + } + + @Override + public void setMinimum(int value) + { + scrollBar.setMinimum(value); + } + + @Override + public void setPageIncrement(int value) + { + scrollBar.setPageIncrement(value); + } + + @Override + public void setSelection(int selection) + { + scrollBar.setSelection(selection); + } + + @Override + public void setThumb(int value) + { + scrollBar.setThumb(value); + } + + @Override + public void setValues(int selection, int minimum, int maximum, int thumb, int increment, + int pageIncrement) + { + scrollBar.setValues(selection, minimum, maximum, thumb, increment, pageIncrement); + } + + @Override + public void setVisible(boolean visible) + { + scrollBar.setVisible(visible); + } + + @Override + public void handleMouseWheel(Event e) + { + //do nothing + } + + @Override + public void addSelectionListener(SelectionListener listener) + { + scrollBar.addSelectionListener(listener); + } + + @Override + public void removeSelectionListener(SelectionListener listener) + { + scrollBar.removeSelectionListener(listener); + } +} diff --git a/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsActivator.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsActivator.java new file mode 100644 index 0000000000000000000000000000000000000000..2a7677ecd42ca4677bdd377caf212e242bb0a1dd --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsActivator.java @@ -0,0 +1,55 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jkiss.dbeaver.ui.editors.data.internal; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.jkiss.dbeaver.runtime.DBeaverNotifications; +import org.jkiss.dbeaver.ui.notifications.NotificationUtils; +import org.osgi.framework.BundleContext; + +public class DataEditorsActivator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.jkiss.dbeaver.ui.editors.data"; + + // The shared instance + private static DataEditorsActivator plugin; + + public DataEditorsActivator() { + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + public static DataEditorsActivator getDefault() { + return plugin; + } + + public static ImageDescriptor getImageDescriptor(String path) { + return imageDescriptorFromPlugin(PLUGIN_ID, path); + } +} diff --git a/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsMessages.java b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsMessages.java new file mode 100644 index 0000000000000000000000000000000000000000..65ab404b41e965c3dabdb94e04d801374460f6ec --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsMessages.java @@ -0,0 +1,35 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) + * Copyright (C) 2011-2012 Eugene Fradkin (eugene.fradkin@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jkiss.dbeaver.ui.editors.data.internal; + +import org.eclipse.osgi.util.NLS; + +public class DataEditorsMessages extends NLS { + static final String BUNDLE_NAME = "org.jkiss.dbeaver.ui.editors.data.internal.DataEditorsResources"; //$NON-NLS-1$ + + public static String grid_tooltip_sort_by_column; + public static String grid_tooltip_filter_by_column; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, DataEditorsMessages.class); + } + + private DataEditorsMessages() { + } +} diff --git a/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources.properties b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources.properties new file mode 100644 index 0000000000000000000000000000000000000000..2c99e5d42f8becf1f0b50826ba7db8fff0034c52 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources.properties @@ -0,0 +1,3 @@ +grid_tooltip_sort_by_column = Sort by column +grid_tooltip_filter_by_column = Filter by column value + diff --git a/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_de.properties b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_de.properties new file mode 100644 index 0000000000000000000000000000000000000000..e1aadd16d15e3b7579e6fe4bf77d6ebc5adb0eff --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_de.properties @@ -0,0 +1,2 @@ +grid_tooltip_filter_by_column = Filtern mit Spaltenwert +grid_tooltip_sort_by_column = Sortieren mit Spalte diff --git a/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_fr.properties b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_fr.properties new file mode 100644 index 0000000000000000000000000000000000000000..c51f02cdd22f679bbcd62b39c5a961c2e991b105 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_fr.properties @@ -0,0 +1,2 @@ +grid_tooltip_sort_by_column = Trier par colonne +grid_tooltip_filter_by_column = Filtrer par la valeur de colonne diff --git a/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_ru.properties b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_ru.properties new file mode 100644 index 0000000000000000000000000000000000000000..3b65ba0c5d7132e6ae1c12a1fa6dc5b1a1072bb8 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_ru.properties @@ -0,0 +1,2 @@ +grid_tooltip_sort_by_column = \u0421\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0430 \u043F\u043E \u043A\u043E\u043B\u043E\u043D\u043A\u0435 +grid_tooltip_filter_by_column = \u0424\u0438\u043B\u044C\u0442\u0440 \u043F\u043E \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044E \u043A\u043E\u043B\u043E\u043D\u043A\u0438 diff --git a/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_zh.properties b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_zh.properties new file mode 100644 index 0000000000000000000000000000000000000000..a643d648d9ad007d833a9b58b6b3a49e9046d6b4 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ui.editors.data/src/org/jkiss/dbeaver/ui/editors/data/internal/DataEditorsResources_zh.properties @@ -0,0 +1,3 @@ +grid_tooltip_filter_by_column = \u6309\u5217\u503C\u8FC7\u6EE4 +grid_tooltip_sort_by_column = \u6309\u5217\u8FC7\u6EE4 + diff --git a/plugins/org.jkiss.dbeaver.ui/META-INF/MANIFEST.MF b/plugins/org.jkiss.dbeaver.ui/META-INF/MANIFEST.MF index e2da1d7aae6b53b114c8b31ccdc9bf61e2936792..3b6b0c7d59d62fbd08b14341dfe4f1ef196dfa32 100644 --- a/plugins/org.jkiss.dbeaver.ui/META-INF/MANIFEST.MF +++ b/plugins/org.jkiss.dbeaver.ui/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-SymbolicName: org.jkiss.dbeaver.ui;singleton:=true Bundle-Version: 5.1.15.qualifier Bundle-Release-Date: 20190107 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-Activator: org.jkiss.dbeaver.bundle.UIActivator +Bundle-Activator: org.jkiss.dbeaver.ui.internal.UIActivator Bundle-ActivationPolicy: lazy Export-Package: org.jkiss.dbeaver.runtime, org.jkiss.dbeaver.ui, diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/ContextSearchAction.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/ContextSearchAction.java index cd32b7ac12ad2cbeed57ddae23afb145c6ac88f5..d4e19c1274386f24fa5c0c91ba5d5317e1d0957c 100644 --- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/ContextSearchAction.java +++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/ContextSearchAction.java @@ -17,10 +17,7 @@ package org.jkiss.dbeaver.ui; import org.eclipse.jface.action.Action; -import org.jkiss.dbeaver.bundle.UIMessages; -import org.jkiss.dbeaver.ui.DBeaverIcons; -import org.jkiss.dbeaver.ui.ISearchContextProvider; -import org.jkiss.dbeaver.ui.UIIcon; +import org.jkiss.dbeaver.ui.internal.UIMessages; /** * Context search action diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/LoadingJob.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/LoadingJob.java index dc76368d6df019d8d6e7fc5e73354a0d0cb801cb..1a6835c7564d3213e00c5a2231b2dab1ec4c56b4 100644 --- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/LoadingJob.java +++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/LoadingJob.java @@ -19,7 +19,7 @@ package org.jkiss.dbeaver.ui; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.jkiss.dbeaver.Log; -import org.jkiss.dbeaver.bundle.UIActivator; +import org.jkiss.dbeaver.ui.internal.UIActivator; import org.jkiss.dbeaver.model.runtime.AbstractJob; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/UIUtils.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/UIUtils.java index cb08a55b862789724e45a4c81ed17f33dfe5bc46..78313641337adab329a172ba95413db055a50304 100644 --- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/UIUtils.java +++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/UIUtils.java @@ -63,7 +63,7 @@ import org.eclipse.ui.swt.IFocusService; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.Log; -import org.jkiss.dbeaver.bundle.UIActivator; +import org.jkiss.dbeaver.ui.internal.UIActivator; import org.jkiss.dbeaver.model.DBIcon; import org.jkiss.dbeaver.model.DBPImage; import org.jkiss.dbeaver.model.DBPNamedObject; diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/autorefresh/AutoRefreshControl.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/autorefresh/AutoRefreshControl.java index b86be8958712422b1389998a7e79ef262c2b73a4..19ad89aedcb2a8a14045d46c3b668eef4955758b 100644 --- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/autorefresh/AutoRefreshControl.java +++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/autorefresh/AutoRefreshControl.java @@ -26,7 +26,7 @@ import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.*; -import org.jkiss.dbeaver.bundle.UIMessages; +import org.jkiss.dbeaver.ui.internal.UIMessages; import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress; import org.jkiss.dbeaver.ui.DBeaverIcons; import org.jkiss.dbeaver.ui.UIIcon; diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/dialogs/DialogUtils.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/dialogs/DialogUtils.java index 858e552cbd8eb059a7b54930b1d3c9e3746e3b0b..1f1f9c02e8e72a600b3a6d2fd8843ab21b6a8e2f 100644 --- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/dialogs/DialogUtils.java +++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/dialogs/DialogUtils.java @@ -23,7 +23,7 @@ import org.eclipse.swt.widgets.*; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.Log; -import org.jkiss.dbeaver.bundle.UIMessages; +import org.jkiss.dbeaver.ui.internal.UIMessages; import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.dbeaver.runtime.ui.DBUserInterface; import org.jkiss.dbeaver.ui.UIUtils; diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIActivator.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIActivator.java similarity index 97% rename from plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIActivator.java rename to plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIActivator.java index ab183067023ae4f6c0222dd99cdd36d18189ca10..fefaa1b290d7d34f8ae139b93568d31b4e3b0d97 100644 --- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIActivator.java +++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIActivator.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jkiss.dbeaver.bundle; +package org.jkiss.dbeaver.ui.internal; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.plugin.AbstractUIPlugin; diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages.java similarity index 93% rename from plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages.java rename to plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages.java index 89ce02a5b5666d7cd75a0e91425ad2a8405fa8e0..86ab7f518b48b71c535cc13d0ce0cfa8e0cf069e 100644 --- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages.java +++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages.java @@ -15,12 +15,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jkiss.dbeaver.bundle; +package org.jkiss.dbeaver.ui.internal; import org.eclipse.osgi.util.NLS; public class UIMessages extends NLS { - static final String BUNDLE_NAME = "org.jkiss.dbeaver.bundle.UIMessages"; //$NON-NLS-1$ + static final String BUNDLE_NAME = "org.jkiss.dbeaver.ui.internal.UIMessages"; //$NON-NLS-1$ public static String output_label_directory; diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages.properties b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages.properties similarity index 100% rename from plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages.properties rename to plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages.properties diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages_de.properties b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages_de.properties similarity index 100% rename from plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages_de.properties rename to plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages_de.properties diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages_fr.properties b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages_fr.properties similarity index 100% rename from plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages_fr.properties rename to plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages_fr.properties diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages_ja.properties b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages_ja.properties similarity index 100% rename from plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages_ja.properties rename to plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages_ja.properties diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages_ru.properties b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages_ru.properties similarity index 100% rename from plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages_ru.properties rename to plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages_ru.properties diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages_zh.properties b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages_zh.properties similarity index 100% rename from plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/bundle/UIMessages_zh.properties rename to plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/internal/UIMessages_zh.properties diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/properties/PropertyTreeViewer.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/properties/PropertyTreeViewer.java index 7a64892993c1330406c89e85f5a883feebba2fa7..ff95287b2baf2b1b5f7e34259562bc41c52978f1 100644 --- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/properties/PropertyTreeViewer.java +++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/properties/PropertyTreeViewer.java @@ -36,7 +36,7 @@ import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.eclipse.ui.views.properties.IPropertySource2; import org.jkiss.code.Nullable; -import org.jkiss.dbeaver.bundle.UIMessages; +import org.jkiss.dbeaver.ui.internal.UIMessages; import org.jkiss.dbeaver.model.DBPObject; import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;