OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 177 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.ui.grid;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class GridPanel extends ScrollablePanel {
    private static final Color SELECTION_BORDER_COLOR = new Color(200, 210, 220, 250);
    private static final Color SELECTION_COLOR = new Color(230, 240, 250, 180);
    private static final Color CREATION_COLOR = new Color(250, 254, 30, 100);
    private static final Color CREATION_BORDER_COLOR = new Color(250, 254, 60, 180);

    private int cellWidth = 40;
    private final int cellHeight;
    private int gridLineWidth = 1;
    private Color gridVerticalColor = Color.LIGHT_GRAY;
    private Color gridHorizontalColor = Color.DARK_GRAY;
    private int columnCount;
    private int rowCount;
    private List<GridItem> items = new ArrayList<>();
    private Map<Integer, List<GridItem>> itemsByRow = new HashMap<>();

    protected GridItem onCreationItem;
    protected int onCreationX;
    protected boolean[] onCreationAllowedColumns;
    private boolean editMode;
    protected GridListener gridListener;
    private Set<GridItem> selectedItems = new HashSet<>();

    public GridPanel(int columnCount, int rowCount) {
        this(columnCount, rowCount, 40);
    }

    public GridPanel(int columnCount, int rowCount, int rowHeight) {
        this.setBackground(Color.WHITE);
        this.rowCount = rowCount;
        this.columnCount = columnCount;
        this.cellHeight = rowHeight;
        this.addMouseMotionListener(new MouseMotionListener() {

            @Override
            public void mouseMoved(MouseEvent e) {
                // nothing
            }

            @Override
            public void mouseDragged(MouseEvent e) {
                if (GridPanel.this.onCreationItem != null) {

                    int x = getColumnFromMouseX(e.getX());
                    if (x >= 0 && x < columnCount && GridPanel.this.onCreationAllowedColumns[x]) {
                        if (x > GridPanel.this.onCreationX) {
                            GridPanel.this.onCreationItem.setW(x - GridPanel.this.onCreationX + 1);
                        } else {
                            GridPanel.this.onCreationItem.setX(x);
                            GridPanel.this.onCreationItem.setW(GridPanel.this.onCreationX - x + 1);
                        }
                    }
                    repaint();

                }

            }
        });
        this.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseReleased(MouseEvent e) {
                final GridItem selectedItem = getItemAtMouse(e.getX(), e.getY());
                if (!e.isControlDown()) {
                    GridPanel.this.selectedItems.clear();
                }
                if (selectedItem != null) {
                    if (GridPanel.this.selectedItems.contains(selectedItem) && !e.isPopupTrigger()) {
                        GridPanel.this.selectedItems.remove(selectedItem);
                    } else {
                        GridPanel.this.selectedItems.add(selectedItem);
                    }
                }

                if (GridPanel.this.onCreationItem != null) {
                    // Add the item
                    GridItem newItem = new GridItem(GridPanel.this.onCreationItem.getX(), GridPanel.this.onCreationItem.getY(), GridPanel.this.onCreationItem.getW(),
                            GridPanel.this.onCreationItem.getH());
                    if (GridPanel.this.gridListener != null) {
                        boolean added = GridPanel.this.gridListener.add(GridPanel.this, newItem);
                        if (added) {
                            add(newItem);
                        }
                    }
                    //
                    GridPanel.this.onCreationItem = null;

                }
                repaint();
                if (GridPanel.this.gridListener != null && e.isPopupTrigger()) {
                    GridPanel.this.gridListener.triggerPopup(GridPanel.this, e.getX(), e.getY());
                }
            }

            @Override
            public void mousePressed(MouseEvent e) {
                final GridItem selectedItem = getItemAtMouse(e.getX(), e.getY());
                if (GridPanel.this.editMode && selectedItem == null && getRowFromMouseY(e.getY()) < rowCount) {
                    GridPanel.this.onCreationX = getColumnFromMouseX(e.getX());
                    GridPanel.this.onCreationAllowedColumns = new boolean[columnCount];
                    GridPanel.this.onCreationAllowedColumns[GridPanel.this.onCreationX] = true;
                    final int onCreationY = getRowFromMouseY(e.getY());
                    for (int i = GridPanel.this.onCreationX + 1; i < columnCount; i++) {
                        if (getItem(onCreationY, i) == null) {
                            GridPanel.this.onCreationAllowedColumns[i] = true;
                        } else {
                            break;
                        }
                    }
                    for (int i = GridPanel.this.onCreationX - 1; i >= 0; i--) {
                        if (getItem(onCreationY, i) == null) {
                            GridPanel.this.onCreationAllowedColumns[i] = true;
                        } else {
                            break;
                        }
                    }
                    GridPanel.this.onCreationItem = new GridItem(getColumnFromMouseX(e.getX()), onCreationY, 1, 1);
                    GridPanel.this.onCreationItem.setColor(CREATION_COLOR);
                    GridPanel.this.onCreationItem.setBorderColor(CREATION_BORDER_COLOR);

                }
                repaint();
                if (GridPanel.this.gridListener != null) {
                    GridPanel.this.gridListener.selectionChanged(GridPanel.this);
                    if (e.isPopupTrigger()) {
                        GridPanel.this.gridListener.triggerPopup(GridPanel.this, e.getX(), e.getY());
                    }
                }
            }

        });

    }

    public void add(GridItem item) {
        this.items.add(item);
        for (int n = 0; n < item.getH(); n++) {
            final int y = item.getY() + n;
            List<GridItem> list = this.itemsByRow.get(y);
            if (list == null) {
                list = new ArrayList<>();
                list.add(item);
                this.itemsByRow.put(Integer.valueOf(y), list);
            }
            list.add(item);
        }
    }

    public int getCellHeight() {
        return this.cellHeight;
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(this.columnCount * this.cellWidth + (1 + this.columnCount) * this.gridLineWidth, this.rowCount * this.cellWidth + (1 + this.rowCount) * this.gridLineWidth);
    }

    @Override
    public Dimension getMinimumSize() {
        return getPreferredSize();
    }

    @Override
    public Dimension getMaximumSize() {
        return getPreferredSize();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        int x = 0;
        final int h = this.getHeight();
        Graphics2D g2 = (Graphics2D) g;
        g2.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] { 4 }, 0));
        // Vertical lines
        g.setColor(this.gridVerticalColor);
        for (int i = 0; i <= this.columnCount; i++) {
            g.drawLine(x, 0, x, h);
            x += this.gridLineWidth + this.cellWidth;
        }
        // Horizontal lines
        g.setColor(this.gridHorizontalColor);
        int w = this.getWidth();
        int y = 0;
        for (int i = 0; i <= this.rowCount; i++) {
            g.drawLine(0, y, w, y);
            y += this.gridLineWidth + this.cellHeight;
        }
        g2.setStroke(new BasicStroke(1));
        for (GridItem item : this.items) {
            drawItem(g, item);
        }
        if (this.onCreationItem != null) {
            drawItem(g, this.onCreationItem);
        }

    }

    private void drawItem(Graphics g, GridItem item) {
        final boolean isSelected = this.selectedItems.contains(item);
        if (isSelected) {
            g.setColor(SELECTION_COLOR);
        } else {
            g.setColor(item.getColor());
        }
        int width = item.getW() * (this.gridLineWidth + this.cellWidth);
        int height = item.getH() * (this.gridLineWidth + this.cellHeight);
        final int itemX = item.getX() * (this.gridLineWidth + this.cellWidth);
        final int itemY = item.getY() * (this.gridLineWidth + this.cellHeight);
        g.fillRect(itemX + 1, itemY + 1, width - 2, height - 2);
        if (isSelected) {
            g.setColor(SELECTION_BORDER_COLOR);
        } else {
            g.setColor(item.getBorderColor());
        }
        g.drawRect(itemX + 1, itemY + 1, width - 2, height - 2);
        g.drawRect(itemX, itemY, width, height);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
                    //
                }
                JFrame f = new JFrame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                final GridPanel contentPane = new GridPanel(24, 4);
                contentPane.add(new GridItem(0, 0, 1, 1));
                contentPane.add(new GridItem(4, 0, 3, 1));
                contentPane.add(new GridItem(1, 1, 2, 2));
                f.setContentPane(contentPane);
                f.pack();
                f.setLocationRelativeTo(null);
                f.setVisible(true);
            }
        });
    }

    GridItem getItemAtMouse(int x, int y) {
        int row = getRowFromMouseY(y);
        int col = getColumnFromMouseX(x);
        return getItem(row, col);
    }

    private GridItem getItem(int row, int col) {
        List<GridItem> rItems = this.itemsByRow.get(row);
        if (rItems == null) {
            return null;
        }
        for (GridItem item : rItems) {
            if (item.getX() <= col && (item.getX() + item.getW()) > col) {
                return item;
            }
        }
        return null;
    }

    private int getColumnFromMouseX(int x) {
        return x / (this.gridLineWidth + this.cellWidth);
    }

    private int getRowFromMouseY(int y) {
        return y / (this.gridLineWidth + this.cellHeight);
    }

    public void setEnableEditMode(boolean enable) {
        this.editMode = enable;
    }

    public void setGridListener(GridListener gridListener) {
        this.gridListener = gridListener;
    }

    public List<GridItem> getSelectedItems() {
        return new ArrayList<>(this.selectedItems);
    }

    @Override
    public Dimension getPreferredScrollableViewportSize() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
        // TODO Auto-generated method stub
        return 40;
    }

    @Override
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
        // TODO Auto-generated method stub
        return 40;
    }

    @Override
    public boolean getScrollableTracksViewportWidth() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean getScrollableTracksViewportHeight() {
        // TODO Auto-generated method stub
        return false;
    }
}