<NME> AdvancedList.java
<BEF> /*
 * DBeaver - Universal Database Manager
 * Copyright (C) 2010-2022 DBeaver Corp and others
 *
 * 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.finder;

import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IToolTipProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.ui.UIStyles;
import org.jkiss.dbeaver.ui.UIUtils;
import org.jkiss.dbeaver.ui.controls.CustomToolTipHandler;
import org.jkiss.dbeaver.ui.css.CSSUtils;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * AdvancedList
 */
public class AdvancedList extends Canvas {

    private Point itemSize = new Point(64, 64);

    private final List<AdvancedListItem> items = new ArrayList<>();
    private AdvancedListItem selectedItem;
    private AdvancedListItem hoverItem;

    private final Color backgroundColor, selectionBackgroundColor, foregroundColor, selectionForegroundColor, hoverBackgroundColor;
    private final Point textSize;
    private final ScrollBar vScroll;
    private int topRowIndex;
    private int topRowOffset;

    private final CustomToolTipHandler toolTipHandler;

    public AdvancedList(Composite parent, int style) {
        super(parent, SWT.V_SCROLL | SWT.DOUBLE_BUFFERED | style);

        CSSUtils.setCSSClass(this, "List");
        this.backgroundColor = UIStyles.getDefaultTextBackground();
        this.foregroundColor = UIStyles.getDefaultTextForeground();
        this.selectionBackgroundColor = UIStyles.getDefaultTextSelectionBackground();
        this.selectionForegroundColor = UIStyles.getDefaultTextSelectionForeground();
        this.hoverBackgroundColor = UIUtils.getSharedTextColors().getColor(
            UIUtils.blend(this.selectionBackgroundColor.getRGB(), new RGB(255, 255, 255), 70));

        Font normalFont = getFont();
        FontData[] fontData = normalFont.getFontData();
        fontData[0].height -= 1.3;
        Font smallFont = new Font(normalFont.getDevice(), fontData[0]);
        setFont(smallFont);
        addDisposeListener(e -> {
            smallFont.dispose();
            for (AdvancedListItem item : items) {
                item.dispose();
            }
        });

        if (parent.getLayout() instanceof GridLayout) {
            setLayoutData(new GridData(GridData.FILL_BOTH));
        }

        this.setBackground(backgroundColor);

        vScroll = getVerticalBar();
        vScroll.setVisible(true);
        vScroll.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                onVerticalScroll();
            }
        });

        GC gc = new GC(getDisplay());
        textSize = gc.stringExtent("X");
        gc.dispose();

        addPaintListener(this::onPaint);

        addListener(SWT.Resize, event -> {
            updateMeasures();
            redraw();
        });

        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                switch (e.keyCode) {
                    case SWT.ARROW_LEFT:
                    case SWT.ARROW_RIGHT:
                    case SWT.ARROW_UP:
                    case SWT.ARROW_DOWN:
                    case SWT.PAGE_DOWN:
                    case SWT.PAGE_UP:
                    case SWT.HOME:
                    case SWT.END:
                    case SWT.CR:
                        if (getSelectedItem() != null) {
                            navigateByKey(e);
                        }
                        break;
                }
            }
        });

        this.addMouseMoveListener(this::onMouseMove);
        this.addMouseTrackListener(new MouseTrackAdapter() {
            @Override
            public void mouseEnter(MouseEvent e) {
            }

            @Override
            public void mouseExit(MouseEvent e) {
                onMouseMove(e);
            }

            @Override
            public void mouseHover(MouseEvent e) {
            }
        });
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDoubleClick(MouseEvent e) {
                notifyDefaultSelection();
            }

            @Override
            public void mouseDown(MouseEvent e) {
                AdvancedListItem item = getItemByPos(e.x, e.y);
                if (item != null) {
                    setSelection(item);
                    setFocus();
                }
            }
        });
        this.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                super.focusGained(e);
                if (getSelectedItem() == null && !items.isEmpty()) {
                    setSelection(items.get(0));
                }
            }
        });
        toolTipHandler = new CustomToolTipHandler(this);
    }

    public void refreshFilters() {
        selectedItem = null;
        hoverItem = null;
        updateMeasures();
    }

    private void onMouseMove(MouseEvent e) {
        AdvancedListItem item = getItemByPos(e.x, e.y);
        if (item == hoverItem) {
            return;
        }
        AdvancedListItem[] redrawItems = new AdvancedListItem[] { item, hoverItem };
        hoverItem = item;
        if (item == null) {
            toolTipHandler.updateToolTipText(null);
        } else {
            ILabelProvider labelProvider = item.getLabelProvider();
            if (labelProvider instanceof IToolTipProvider) {
                String toolTipText = ((IToolTipProvider) labelProvider).getToolTipText(item.getData());
                if (!CommonUtils.isEmpty(toolTipText)) {
                    toolTipHandler.updateToolTipText(toolTipText);
                }
            }
        }
        GC gc = new GC(this);
        try {
            paintList(gc, redrawItems);
        } finally {
            gc.dispose();
        }
    }

    private AdvancedListItem getItemByPos(int x, int y) {
        Point size = getSize();
        if (x < 0 || y < 0 || x >= size.x || y >= size.y) {
            return null;
        }
        Point itemSize = getItemSize();
        int row = topRowIndex + (y + topRowOffset) / itemSize.y;
        int col = x / itemSize.x;
        if (col < 0 || col >= getItemsPerRow()) {
            return null;
        }
        int itemIndex = row * getItemsPerRow() + col;
        return itemIndex < items.size() ? items.get(itemIndex) : null;
    }

    private void onVerticalScroll() {
        redraw();
    }

    private void updateMeasures() {
        int itemsPerRow = getItemsPerRow();
        int totalRows = itemsPerRow == 0 ? 0 : (items.size() / itemsPerRow) + 2;
        int itemHeight = getItemSize().y;
        int visibleRowCount = getVisibleRowCount();

        vScroll.setValues(0, 0, totalRows * itemHeight, visibleRowCount * itemHeight, itemHeight / 2, itemHeight);
        vScroll.setVisible(totalRows * itemHeight > getSize().y);
    }

    private void onPaint(PaintEvent e) {
        paintList(e.gc, null);
    }

    private void paintList(@NotNull GC gc, @Nullable AdvancedListItem[] redrawItems) {
        Point itemSize = getItemSize();
        int itemsPerRow = getItemsPerRow();
        int itemRowsVisible = getVisibleRowCount() + 1;

        topRowOffset = vScroll.getSelection();
        topRowIndex = topRowOffset / itemSize.y;
        topRowOffset = topRowOffset - topRowIndex * itemSize.y;

        int topItemIndex = topRowIndex * itemsPerRow;

        //int drawItemCount = itemRowsVisible * itemsPerRow;
        int x = 0, y = -topRowOffset;
        for (int i = 0; i < itemRowsVisible; i++) {
            if (topItemIndex + i * itemsPerRow >= items.size()) {
                break;
            }
            for (int k = 0; k < itemsPerRow; k++) {
                int itemIndex = topItemIndex + i * itemsPerRow + k;
                if (itemIndex >= items.size()) {
                    break;
                }
                AdvancedListItem item = items.get(itemIndex);
                if (redrawItems == null || ArrayUtils.contains(redrawItems, item)) {
                    item.painItem(gc, x, y);
                }

                x += itemSize.x;
            }
            y += itemSize.y;
            x = 0;
        }
    }

    private int getVisibleRowCount() {
        return getSize().y / getItemSize().y + 1;
    }

    Point getItemSize() {
        Point imageSize = getImageSize();
        int itemLength = imageSize.x + AdvancedListItem.BORDER_MARGIN * 4 + getTextSize().y;
        return new Point(itemLength, itemLength + AdvancedListItem.BORDER_MARGIN * 2);
    }

    private int getItemsPerRow() {
        Point itemSize = getItemSize();
        Point containerSize = getSize();
        return Math.floorDiv(containerSize.x, itemSize.x);
    }

    private void navigateByKey(KeyEvent e) {
        if (selectedItem == null) {
            return;
        }
        int itemIndex = items.indexOf(selectedItem);
        int itemsPerRow = getItemsPerRow();
        switch (e.keyCode) {
            case SWT.ARROW_LEFT:
                if (itemIndex > 0) {
                    setSelection(items.get(itemIndex - 1));
                }
                break;
            case SWT.ARROW_RIGHT:
                if (itemIndex < items.size() - 1) {
                    setSelection(items.get(itemIndex + 1));
                }
                break;
            case SWT.ARROW_UP:
                if (itemIndex >= itemsPerRow) {
                    setSelection(items.get(itemIndex - itemsPerRow));
                }
                break;
            case SWT.ARROW_DOWN:
                if (itemIndex < items.size() - 1) {
                    int nextIndex = itemIndex + itemsPerRow;
                    if (nextIndex >= items.size() - 1) {
                        nextIndex = items.size() - 1;
                    }
                    setSelection(items.get(nextIndex));
                }
                break;
            case SWT.PAGE_UP:
                itemIndex -= itemsPerRow * getVisibleRowCount();
                if (itemIndex < 0) itemIndex = 0;
                setSelection(items.get(itemIndex));
                break;
            case SWT.PAGE_DOWN:
                itemIndex += itemsPerRow * getVisibleRowCount();
                if (itemIndex > items.size() - 1) itemIndex = items.size() - 1;
                setSelection(items.get(itemIndex));
                break;
            case SWT.HOME:
                if (!items.isEmpty()) {
                    setSelection(items.get(0));
                }
                break;
            case SWT.END:
                if (!items.isEmpty()) {
                    setSelection(items.get(items.size() - 1));
                }
                break;
            case SWT.CR:
                notifyDefaultSelection();
                break;
        }
        showItem(selectedItem);
    }

    Color getBackgroundColor() {
        return backgroundColor;
    }

    Color getSelectionBackgroundColor() {
        return selectionBackgroundColor;
    }

    Color getForegroundColor() {
        return foregroundColor;
    }

    Color getSelectionForegroundColor() {
        return selectionForegroundColor;
    }

    Color getHoverBackgroundColor() {
        return hoverBackgroundColor;
            // Scroll down
            vScroll.setSelection(vScroll.getSelection() + vScroll.getPageIncrement());
        }
        int topOffset = itemRow * getItemSize().y;
        //if (vScroll.getSelection())
        //showControl(item);
    }

}
    public Point getImageSize() {
        return itemSize;
    }

    public void setItemSize(Point itemSize) {
        this.itemSize = itemSize;
    }

    void addItem(AdvancedListItem item) {
        items.add(item);
    }

    void removeItem(AdvancedListItem item) {
        this.items.remove(item);
    }

    public AdvancedListItem[] getItems() {
        return items.toArray(new AdvancedListItem[0]);
    }

    AdvancedListItem getHoverItem() {
        return hoverItem;
    }

    public AdvancedListItem getSelectedItem() {
        return selectedItem;
    }

    void setSelection(AdvancedListItem item) {
        if (this.selectedItem == item) {
            return;
        }
        this.selectedItem = item;

        Event event = new Event();
        event.widget = this;
        notifyListeners(SWT.Selection, event);

        redraw();
    }

    void notifyDefaultSelection() {
        Event event = new Event();
        event.widget = this;
        notifyListeners(SWT.DefaultSelection, event);
    }

    public void addSelectionListener(SelectionListener listener) {
        checkWidget ();
        if (listener == null) {
            return;
        }
        TypedListener typedListener = new TypedListener (listener);
        addListener (SWT.Selection,typedListener);
        addListener (SWT.DefaultSelection,typedListener);
    }

    public void removeAll() {
        checkWidget ();
        setSelection(null);
        items.clear();
    }

    private void showItem(AdvancedListItem item) {
        if (!vScroll.isVisible()) {
            return;
        }
        int itemIndex = items.indexOf(item);
        int itemRow = itemIndex / getItemsPerRow();
        if (itemRow < topRowIndex) {
            // Scroll up
            if (itemRow < topRowIndex - 1) {
                // To exact row
                vScroll.setSelection(itemRow * getItemSize().y);
            } else {
                // One row up
                vScroll.setSelection(Math.max(0, vScroll.getSelection() - vScroll.getPageIncrement()));
            }
        } else {
            int bottomRowIndex = topRowIndex + getVisibleRowCount();
            if (itemRow >= bottomRowIndex) {
                // Scroll down
                if (itemRow > bottomRowIndex + 1) {
                    // Scroll to exact row
                    vScroll.setSelection(itemRow * getItemSize().y);
                } else {
                    // Just one row down
                    vScroll.setSelection(vScroll.getSelection() + vScroll.getPageIncrement());
                }
            }
        }
    }

}

<MSG> #10677 Driver info visualization fix


Former-commit-id: 63e4b1116f698589407be977eec5a72f6bc8e416
<DFF> @@ -361,9 +361,6 @@ public class AdvancedList extends Canvas {
             // Scroll down
             vScroll.setSelection(vScroll.getSelection() + vScroll.getPageIncrement());
         }
-        int topOffset = itemRow * getItemSize().y;
-        //if (vScroll.getSelection())
-        //showControl(item);
     }
 
 }

{'commit': 'diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/dialogs/connection/ConnectionPageDriver.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/dialogs/connection/ConnectionPageDriver.java\nindex ff16471ab1..d800e2e0c9 100644\n--- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/dialogs/connection/ConnectionPageDriver.java\n+++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/dialogs/connection/ConnectionPageDriver.java\n@@ -216,21 +216,22 @@ class ConnectionPageDriver extends ActiveWizardPage implements ISelectionChanged\n     public void selectionChanged(SelectionChangedEvent event)\r\n     {\r\n         this.selectedDriver = null;\r\n-        this.setMessage("");\r\n         ISelection selection = event.getSelection();\r\n         if (selection instanceof IStructuredSelection) {\r\n+            // TODO: Show current driver info somehow. setMessage is super-slow (it re-layouts entire wizard dialog)\r\n             Object selectedObject = ((IStructuredSelection) selection).getFirstElement();\r\n             if (selectedObject instanceof DBPDriver) {\r\n                 selectedDriver = (DriverDescriptor) selectedObject;\r\n-                this.setMessage(selectedDriver.getDescription());\r\n+                //this.setMessage(selectedDriver.getDescription());\r\n             } else if (selectedObject instanceof DataSourceProviderDescriptor) {\r\n-                this.setMessage(((DataSourceProviderDescriptor) selectedObject).getDescription());\r\n+                //this.setMessage(((DataSourceProviderDescriptor) selectedObject).getDescription());\r\n             } else if (selectedObject instanceof DriverTreeViewer.DriverCategory) {\r\n-                this.setMessage(((DriverTreeViewer.DriverCategory) selectedObject).getName() + " drivers");\r\n+                //this.setMessage(((DriverTreeViewer.DriverCategory) selectedObject).getName() + " drivers");\r\n+            } else {\r\n+                //this.setMessage("");\r\n             }\r\n         }\r\n         getWizard().getContainer().updateButtons();\r\n-        getWizard().getContainer().updateMessage();\r\n     }\r\n \r\n     @Override\r\ndiff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/finder/AdvancedList.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/finder/AdvancedList.java\nindex 8cf2c21447..3c8c2da163 100644\n--- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/finder/AdvancedList.java\n+++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/finder/AdvancedList.java\n@@ -361,9 +361,6 @@ public class AdvancedList extends Canvas {\n             // Scroll down\n             vScroll.setSelection(vScroll.getSelection() + vScroll.getPageIncrement());\n         }\n-        int topOffset = itemRow * getItemSize().y;\n-        //if (vScroll.getSelection())\n-        //showControl(item);\n     }\n \n }\n', 'commit_subject': '#10677 Driver info visualization fix', 'repo_name': 'dbeaver/dbeaver', 'committer': {'name': 'serge-rider', 'email': 'c9ddb6c25a8f77bd31ba26bb8332c4e1aa2c6778@jkiss.org', 'time_sec': '1607813872', 'tz_offset': '180', 'date': {'seconds': '1607813872'}}, 'commit_tree': '01938d972bac8e42e26d819fecb86bf870a74049', 'commit_message': '#10677 Driver info visualization fix\n\n\nFormer-commit-id: 63e4b1116f698589407be977eec5a72f6bc8e416', 'language_name': 'Shell', 'license': 'apache-2.0', 'hunks': '@@ -361,9 +361,6 @@ public class AdvancedList extends Canvas {\n             // Scroll down\n             vScroll.setSelection(vScroll.getSelection() + vScroll.getPageIncrement());\n         }\n-        int topOffset = itemRow * getItemSize().y;\n-        //if (vScroll.getSelection())\n-        //showControl(item);\n     }\n \n }\n', 'addition_count': 0, 'deletion_count': 3, 'src_file': 'a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/finder/AdvancedList.java', 'tgt_file': 'b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/finder/AdvancedList.java', 'file_extension': '.java', 'before_file': '/*\n * DBeaver - Universal Database Manager\n * Copyright (C) 2010-2022 DBeaver Corp and others\n *\n * Licensed under the Apache License, Version 2.0 (the "License");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jkiss.dbeaver.ui.controls.finder;\n\nimport org.eclipse.jface.viewers.ILabelProvider;\nimport org.eclipse.jface.viewers.IToolTipProvider;\nimport org.eclipse.swt.SWT;\nimport org.eclipse.swt.events.*;\nimport org.eclipse.swt.graphics.*;\nimport org.eclipse.swt.layout.GridData;\nimport org.eclipse.swt.layout.GridLayout;\nimport org.eclipse.swt.widgets.*;\nimport org.jkiss.code.NotNull;\nimport org.jkiss.code.Nullable;\nimport org.jkiss.dbeaver.ui.UIStyles;\nimport org.jkiss.dbeaver.ui.UIUtils;\nimport org.jkiss.dbeaver.ui.controls.CustomToolTipHandler;\nimport org.jkiss.dbeaver.ui.css.CSSUtils;\nimport org.jkiss.utils.ArrayUtils;\nimport org.jkiss.utils.CommonUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * AdvancedList\n */\npublic class AdvancedList extends Canvas {\n\n    private Point itemSize = new Point(64, 64);\n\n    private final List<AdvancedListItem> items = new ArrayList<>();\n    private AdvancedListItem selectedItem;\n    private AdvancedListItem hoverItem;\n\n    private final Color backgroundColor, selectionBackgroundColor, foregroundColor, selectionForegroundColor, hoverBackgroundColor;\n    private final Point textSize;\n    private final ScrollBar vScroll;\n    private int topRowIndex;\n    private int topRowOffset;\n\n    private final CustomToolTipHandler toolTipHandler;\n\n    public AdvancedList(Composite parent, int style) {\n        super(parent, SWT.V_SCROLL | SWT.DOUBLE_BUFFERED | style);\n\n        CSSUtils.setCSSClass(this, "List");\n        this.backgroundColor = UIStyles.getDefaultTextBackground();\n        this.foregroundColor = UIStyles.getDefaultTextForeground();\n        this.selectionBackgroundColor = UIStyles.getDefaultTextSelectionBackground();\n        this.selectionForegroundColor = UIStyles.getDefaultTextSelectionForeground();\n        this.hoverBackgroundColor = UIUtils.getSharedTextColors().getColor(\n            UIUtils.blend(this.selectionBackgroundColor.getRGB(), new RGB(255, 255, 255), 70));\n\n        Font normalFont = getFont();\n        FontData[] fontData = normalFont.getFontData();\n        fontData[0].height -= 1.3;\n        Font smallFont = new Font(normalFont.getDevice(), fontData[0]);\n        setFont(smallFont);\n        addDisposeListener(e -> {\n            smallFont.dispose();\n            for (AdvancedListItem item : items) {\n                item.dispose();\n            }\n        });\n\n        if (parent.getLayout() instanceof GridLayout) {\n            setLayoutData(new GridData(GridData.FILL_BOTH));\n        }\n\n        this.setBackground(backgroundColor);\n\n        vScroll = getVerticalBar();\n        vScroll.setVisible(true);\n        vScroll.addSelectionListener(new SelectionAdapter() {\n            @Override\n            public void widgetSelected(SelectionEvent e) {\n                onVerticalScroll();\n            }\n        });\n\n        GC gc = new GC(getDisplay());\n        textSize = gc.stringExtent("X");\n        gc.dispose();\n\n        addPaintListener(this::onPaint);\n\n        addListener(SWT.Resize, event -> {\n            updateMeasures();\n            redraw();\n        });\n\n        this.addKeyListener(new KeyAdapter() {\n            @Override\n            public void keyPressed(KeyEvent e) {\n                switch (e.keyCode) {\n                    case SWT.ARROW_LEFT:\n                    case SWT.ARROW_RIGHT:\n                    case SWT.ARROW_UP:\n                    case SWT.ARROW_DOWN:\n                    case SWT.PAGE_DOWN:\n                    case SWT.PAGE_UP:\n                    case SWT.HOME:\n                    case SWT.END:\n                    case SWT.CR:\n                        if (getSelectedItem() != null) {\n                            navigateByKey(e);\n                        }\n                        break;\n                }\n            }\n        });\n\n        this.addMouseMoveListener(this::onMouseMove);\n        this.addMouseTrackListener(new MouseTrackAdapter() {\n            @Override\n            public void mouseEnter(MouseEvent e) {\n            }\n\n            @Override\n            public void mouseExit(MouseEvent e) {\n                onMouseMove(e);\n            }\n\n            @Override\n            public void mouseHover(MouseEvent e) {\n            }\n        });\n        this.addMouseListener(new MouseAdapter() {\n            @Override\n            public void mouseDoubleClick(MouseEvent e) {\n                notifyDefaultSelection();\n            }\n\n            @Override\n            public void mouseDown(MouseEvent e) {\n                AdvancedListItem item = getItemByPos(e.x, e.y);\n                if (item != null) {\n                    setSelection(item);\n                    setFocus();\n                }\n            }\n        });\n        this.addFocusListener(new FocusAdapter() {\n            @Override\n            public void focusGained(FocusEvent e) {\n                super.focusGained(e);\n                if (getSelectedItem() == null && !items.isEmpty()) {\n                    setSelection(items.get(0));\n                }\n            }\n        });\n        toolTipHandler = new CustomToolTipHandler(this);\n    }\n\n    public void refreshFilters() {\n        selectedItem = null;\n        hoverItem = null;\n        updateMeasures();\n    }\n\n    private void onMouseMove(MouseEvent e) {\n        AdvancedListItem item = getItemByPos(e.x, e.y);\n        if (item == hoverItem) {\n            return;\n        }\n        AdvancedListItem[] redrawItems = new AdvancedListItem[] { item, hoverItem };\n        hoverItem = item;\n        if (item == null) {\n            toolTipHandler.updateToolTipText(null);\n        } else {\n            ILabelProvider labelProvider = item.getLabelProvider();\n            if (labelProvider instanceof IToolTipProvider) {\n                String toolTipText = ((IToolTipProvider) labelProvider).getToolTipText(item.getData());\n                if (!CommonUtils.isEmpty(toolTipText)) {\n                    toolTipHandler.updateToolTipText(toolTipText);\n                }\n            }\n        }\n        GC gc = new GC(this);\n        try {\n            paintList(gc, redrawItems);\n        } finally {\n            gc.dispose();\n        }\n    }\n\n    private AdvancedListItem getItemByPos(int x, int y) {\n        Point size = getSize();\n        if (x < 0 || y < 0 || x >= size.x || y >= size.y) {\n            return null;\n        }\n        Point itemSize = getItemSize();\n        int row = topRowIndex + (y + topRowOffset) / itemSize.y;\n        int col = x / itemSize.x;\n        if (col < 0 || col >= getItemsPerRow()) {\n            return null;\n        }\n        int itemIndex = row * getItemsPerRow() + col;\n        return itemIndex < items.size() ? items.get(itemIndex) : null;\n    }\n\n    private void onVerticalScroll() {\n        redraw();\n    }\n\n    private void updateMeasures() {\n        int itemsPerRow = getItemsPerRow();\n        int totalRows = itemsPerRow == 0 ? 0 : (items.size() / itemsPerRow) + 2;\n        int itemHeight = getItemSize().y;\n        int visibleRowCount = getVisibleRowCount();\n\n        vScroll.setValues(0, 0, totalRows * itemHeight, visibleRowCount * itemHeight, itemHeight / 2, itemHeight);\n        vScroll.setVisible(totalRows * itemHeight > getSize().y);\n    }\n\n    private void onPaint(PaintEvent e) {\n        paintList(e.gc, null);\n    }\n\n    private void paintList(@NotNull GC gc, @Nullable AdvancedListItem[] redrawItems) {\n        Point itemSize = getItemSize();\n        int itemsPerRow = getItemsPerRow();\n        int itemRowsVisible = getVisibleRowCount() + 1;\n\n        topRowOffset = vScroll.getSelection();\n        topRowIndex = topRowOffset / itemSize.y;\n        topRowOffset = topRowOffset - topRowIndex * itemSize.y;\n\n        int topItemIndex = topRowIndex * itemsPerRow;\n\n        //int drawItemCount = itemRowsVisible * itemsPerRow;\n        int x = 0, y = -topRowOffset;\n        for (int i = 0; i < itemRowsVisible; i++) {\n            if (topItemIndex + i * itemsPerRow >= items.size()) {\n                break;\n            }\n            for (int k = 0; k < itemsPerRow; k++) {\n                int itemIndex = topItemIndex + i * itemsPerRow + k;\n                if (itemIndex >= items.size()) {\n                    break;\n                }\n                AdvancedListItem item = items.get(itemIndex);\n                if (redrawItems == null || ArrayUtils.contains(redrawItems, item)) {\n                    item.painItem(gc, x, y);\n                }\n\n                x += itemSize.x;\n            }\n            y += itemSize.y;\n            x = 0;\n        }\n    }\n\n    private int getVisibleRowCount() {\n        return getSize().y / getItemSize().y + 1;\n    }\n\n    Point getItemSize() {\n        Point imageSize = getImageSize();\n        int itemLength = imageSize.x + AdvancedListItem.BORDER_MARGIN * 4 + getTextSize().y;\n        return new Point(itemLength, itemLength + AdvancedListItem.BORDER_MARGIN * 2);\n    }\n\n    private int getItemsPerRow() {\n        Point itemSize = getItemSize();\n        Point containerSize = getSize();\n        return Math.floorDiv(containerSize.x, itemSize.x);\n    }\n\n    private void navigateByKey(KeyEvent e) {\n        if (selectedItem == null) {\n            return;\n        }\n        int itemIndex = items.indexOf(selectedItem);\n        int itemsPerRow = getItemsPerRow();\n        switch (e.keyCode) {\n            case SWT.ARROW_LEFT:\n                if (itemIndex > 0) {\n                    setSelection(items.get(itemIndex - 1));\n                }\n                break;\n            case SWT.ARROW_RIGHT:\n                if (itemIndex < items.size() - 1) {\n                    setSelection(items.get(itemIndex + 1));\n                }\n                break;\n            case SWT.ARROW_UP:\n                if (itemIndex >= itemsPerRow) {\n                    setSelection(items.get(itemIndex - itemsPerRow));\n                }\n                break;\n            case SWT.ARROW_DOWN:\n                if (itemIndex < items.size() - 1) {\n                    int nextIndex = itemIndex + itemsPerRow;\n                    if (nextIndex >= items.size() - 1) {\n                        nextIndex = items.size() - 1;\n                    }\n                    setSelection(items.get(nextIndex));\n                }\n                break;\n            case SWT.PAGE_UP:\n                itemIndex -= itemsPerRow * getVisibleRowCount();\n                if (itemIndex < 0) itemIndex = 0;\n                setSelection(items.get(itemIndex));\n                break;\n            case SWT.PAGE_DOWN:\n                itemIndex += itemsPerRow * getVisibleRowCount();\n                if (itemIndex > items.size() - 1) itemIndex = items.size() - 1;\n                setSelection(items.get(itemIndex));\n                break;\n            case SWT.HOME:\n                if (!items.isEmpty()) {\n                    setSelection(items.get(0));\n                }\n                break;\n            case SWT.END:\n                if (!items.isEmpty()) {\n                    setSelection(items.get(items.size() - 1));\n                }\n                break;\n            case SWT.CR:\n                notifyDefaultSelection();\n                break;\n        }\n        showItem(selectedItem);\n    }\n\n    Color getBackgroundColor() {\n        return backgroundColor;\n    }\n\n    Color getSelectionBackgroundColor() {\n        return selectionBackgroundColor;\n    }\n\n    Color getForegroundColor() {\n        return foregroundColor;\n    }\n\n    Color getSelectionForegroundColor() {\n        return selectionForegroundColor;\n    }\n\n    Color getHoverBackgroundColor() {\n        return hoverBackgroundColor;\n            // Scroll down\n            vScroll.setSelection(vScroll.getSelection() + vScroll.getPageIncrement());\n        }\n        int topOffset = itemRow * getItemSize().y;\n        //if (vScroll.getSelection())\n        //showControl(item);\n    }\n\n}\n    public Point getImageSize() {\n        return itemSize;\n    }\n\n    public void setItemSize(Point itemSize) {\n        this.itemSize = itemSize;\n    }\n\n    void addItem(AdvancedListItem item) {\n        items.add(item);\n    }\n\n    void removeItem(AdvancedListItem item) {\n        this.items.remove(item);\n    }\n\n    public AdvancedListItem[] getItems() {\n        return items.toArray(new AdvancedListItem[0]);\n    }\n\n    AdvancedListItem getHoverItem() {\n        return hoverItem;\n    }\n\n    public AdvancedListItem getSelectedItem() {\n        return selectedItem;\n    }\n\n    void setSelection(AdvancedListItem item) {\n        if (this.selectedItem == item) {\n            return;\n        }\n        this.selectedItem = item;\n\n        Event event = new Event();\n        event.widget = this;\n        notifyListeners(SWT.Selection, event);\n\n        redraw();\n    }\n\n    void notifyDefaultSelection() {\n        Event event = new Event();\n        event.widget = this;\n        notifyListeners(SWT.DefaultSelection, event);\n    }\n\n    public void addSelectionListener(SelectionListener listener) {\n        checkWidget ();\n        if (listener == null) {\n            return;\n        }\n        TypedListener typedListener = new TypedListener (listener);\n        addListener (SWT.Selection,typedListener);\n        addListener (SWT.DefaultSelection,typedListener);\n    }\n\n    public void removeAll() {\n        checkWidget ();\n        setSelection(null);\n        items.clear();\n    }\n\n    private void showItem(AdvancedListItem item) {\n        if (!vScroll.isVisible()) {\n            return;\n        }\n        int itemIndex = items.indexOf(item);\n        int itemRow = itemIndex / getItemsPerRow();\n        if (itemRow < topRowIndex) {\n            // Scroll up\n            if (itemRow < topRowIndex - 1) {\n                // To exact row\n                vScroll.setSelection(itemRow * getItemSize().y);\n            } else {\n                // One row up\n                vScroll.setSelection(Math.max(0, vScroll.getSelection() - vScroll.getPageIncrement()));\n            }\n        } else {\n            int bottomRowIndex = topRowIndex + getVisibleRowCount();\n            if (itemRow >= bottomRowIndex) {\n                // Scroll down\n                if (itemRow > bottomRowIndex + 1) {\n                    // Scroll to exact row\n                    vScroll.setSelection(itemRow * getItemSize().y);\n                } else {\n                    // Just one row down\n                    vScroll.setSelection(vScroll.getSelection() + vScroll.getPageIncrement());\n                }\n            }\n        }\n    }\n\n}\n', 'message': '#10677 Driver info visualization fix\n\n\nFormer-commit-id: 63e4b1116f698589407be977eec5a72f6bc8e416', 'author_name': 'serge-rider', 'lang': 'jkiss'}