/*
 * @(#)PDFSplitPaneDivider.java  1.0  November 27, 2005
 *
 * Copyright (c) 2005 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */

package ch.randelshofer.pdf.plaf;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.basic.*;
/**
 * PDFSplitPaneDivider.
 *
 * @author  Werner Randelshofer
 * @version 1.0 November 27, 2005 Created.
 */
public class PDFSplitPaneDivider extends BasicSplitPaneDivider {
    static final Cursor defaultCursor =
    Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
    
    /**
     * Width or height of the divider based on orientation
     * BasicSplitPaneUI adds two to this.
     */
    protected static final int ONE_TOUCH_SIZE_ = 4;
    protected static final int ONE_TOUCH_OFFSET_ = 2;
    
    // private ActionListener doubleClickHandler = new DoubleClickActionHandler();
    
    /**
     * Creates a new instance.
     */
    public PDFSplitPaneDivider(BasicSplitPaneUI ui) {
        super(ui);
        setLayout(new PDFDividerLayout());
        PDFLookAndFeel plaf = PDFLookAndFeel.getInstance();
        setFocusable(plaf.getBoolean("SplitPaneDivider.focusable"));
        setBackground(plaf.getColor("SplitPane.background"));
    }
    
    /**
     * Sets the SplitPaneUI that is using the receiver.
     */
    public void setBasicSplitPaneUI(BasicSplitPaneUI newUI) {
        if (splitPane != null) {
            splitPane.removePropertyChangeListener(this);
            if (mouseHandler != null) {
                splitPane.removeMouseListener(mouseHandler);
                splitPane.removeMouseMotionListener(mouseHandler);
                removeMouseListener(mouseHandler);
                removeMouseMotionListener(mouseHandler);
                mouseHandler = null;
            }
        }
        splitPaneUI = newUI;
        if (newUI != null) {
            splitPane = newUI.getSplitPane();
            if (splitPane != null) {
                if (mouseHandler == null) mouseHandler = new Handler();
                splitPane.addMouseListener(mouseHandler);
                splitPane.addMouseMotionListener(mouseHandler);
                addMouseListener(mouseHandler);
                addMouseMotionListener(mouseHandler);
                splitPane.addPropertyChangeListener(this);
                if (splitPane.isOneTouchExpandable()) {
                    oneTouchExpandableChanged();
                }
            }
        }
        else {
            splitPane = null;
        }
    }
    
    /**
     * Paints the divider.
     */
    @Override
    public void paint(Graphics g) {
        PDFLookAndFeel plaf = PDFLookAndFeel.getInstance();
        Dimension size = getSize();
        Insets insets = getInsets();
        boolean isHorizontal = splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT;
        
        // We support the following styles:
        // "bar": The divider is a bar, that goes accross the split view.
        // "thumb" (this is the default): The divider is a thumb (dimple) on
        //        the regular background pattern.
        String style = (String) splitPane.getClientProperty("Quaqua.SplitPane.style");
        if (style == null) style = "thumb";
        
        if (style.equals("bar")) {
            Border border = plaf.getBorder(
            (isHorizontal) ? "SplitPane.vBar" : "SplitPane.hBar"
            );
            border.paintBorder(
            splitPane, g,
            insets.left, insets.top,
            size.width - insets.right - insets.left,
            size.height - insets.top - insets.bottom
            );
        }
        
        Icon dimple;
        boolean drawDimple;
        
        if (style.equals("thumb")) {
            dimple = plaf.getIcon("SplitPane.thumbDimple");
            drawDimple = true;
        } else {
            dimple = plaf.getIcon("SplitPane.barDimple");
            drawDimple = size.width >= dimple.getIconWidth() &&
            size.height >= dimple.getIconHeight();
        }
        
        if (drawDimple) {
            int x = (size.width - dimple.getIconWidth()) / 2;
            int y = (size.height - dimple.getIconHeight()) / 2;
            
            // Make sure, dimple does not intersect with the buttons
            if (splitPane.isOneTouchExpandable()) {
                if (isHorizontal) {
                    y = Math.min(y, leftButton.getY() - dimple.getIconHeight() - ONE_TOUCH_OFFSET_);
                } else {
                    x = Math.min(x, leftButton.getX() - dimple.getIconWidth() - ONE_TOUCH_OFFSET_);
                }
            }
            
            dimple.paintIcon(splitPane, g, x, y);
        }
        super.paint(g);
    }
    /**
     * Creates and return an instance of JButton that can be used to
     * collapse the left component in the split pane.
     */
    @Override
    protected JButton createLeftOneTouchButton() {
        final PDFLookAndFeel plaf = PDFLookAndFeel.getInstance();
        JButton b = new JButton() {
            public Icon getIcon() {
                return plaf.getIcon(
                (splitPane.getOrientation() == VERTICAL) ?
                "SplitPane.leftArrow" :
                    "SplitPane.upArrow"
                    );
            }
        };
        b.setBorder(null);
        b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE_, ONE_TOUCH_SIZE_));
        b.setCursor(defaultCursor);
        b.setFocusPainted(false);
        b.setBorderPainted(false);
        b.setRequestFocusEnabled(false);
        b.setFocusable(plaf.getBoolean("SplitPaneDivider.focusable"));
        return b;
    }
    
    
    /**
     * Creates and return an instance of JButton that can be used to
     * collapse the right component in the split pane.
     */
    protected JButton createRightOneTouchButton() {
        final PDFLookAndFeel plaf = PDFLookAndFeel.getInstance();
        JButton b = new JButton() {
            public Icon getIcon() {
                return plaf.getIcon(
                (splitPane.getOrientation() == VERTICAL) ?
                "SplitPane.rightArrow" :
                    "SplitPane.downArrow"
                    );
            }
        };
        b.setBorder(null);
        b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE_, ONE_TOUCH_SIZE_));
        b.setCursor(defaultCursor);
        b.setFocusPainted(false);
        b.setBorderPainted(false);
        b.setRequestFocusEnabled(false);
        b.setFocusable(plaf.getBoolean("SplitPaneDivider.focusable"));
        return b;
    }
    
    
    /**
     * Used to layout a <code>BasicSplitPaneDivider</code>.
     * Layout for the divider
     * involves appropriately moving the left/right buttons around.
     * <p>
     */
    protected class PDFDividerLayout implements LayoutManager {
        public void layoutContainer(Container c) {
            if (leftButton != null && rightButton != null &&
            c == PDFSplitPaneDivider.this) {
                if (splitPane.isOneTouchExpandable()) {
                    Insets insets = c.getInsets();
                    
                    if (orientation == JSplitPane.VERTICAL_SPLIT) {
                        //int extraX = (insets != null) ? insets.left : 0;
                        int blockSize = getHeight();
                        if (insets != null) {
                            blockSize -= (insets.top + insets.bottom);
                            blockSize = Math.max(blockSize, 0);
                        }
                        blockSize = Math.min(blockSize, ONE_TOUCH_SIZE_);
                        int extraX = c.getSize().width -
                        ((insets != null) ? insets.right : 0) -
                        ONE_TOUCH_OFFSET_ - blockSize * 4;
                        
                        int y = (c.getSize().height - blockSize) / 2;
                        
                        leftButton.setBounds(extraX, y,
                        blockSize * 2, blockSize);
                        rightButton.setBounds(extraX + blockSize * 2, y,
                        blockSize * 2, blockSize);
                    }
                    else {
                        //int extraY = (insets != null) ? insets.top : 0;
                        int blockSize = getWidth();
                        if (insets != null) {
                            blockSize -= (insets.left + insets.right);
                            blockSize = Math.max(blockSize, 0);
                        }
                        blockSize = Math.min(blockSize, ONE_TOUCH_SIZE_);
                        int extraY = c.getSize().height -
                        ((insets != null) ? insets.bottom : 0) -
                        ONE_TOUCH_OFFSET_ - blockSize * 4;
                        
                        int x = (c.getSize().width - blockSize) / 2;
                        
                        leftButton.setBounds(x, extraY,
                        blockSize, blockSize * 2);
                        rightButton.setBounds(x, extraY + blockSize * 2,
                        blockSize, blockSize * 2);
                    }
                }
                else {
                    leftButton.setBounds(-5, -5, 1, 1);
                    rightButton.setBounds(-5, -5, 1, 1);
                }
            }
        }
        
        
        public Dimension minimumLayoutSize(Container c) {
            // NOTE: This isn't really used, refer to
            // BasicSplitPaneDivider.getPreferredSize for the reason.
            // I leave it in hopes of having this used at some point.
            if (c != PDFSplitPaneDivider.this || splitPane == null) {
                return new Dimension(0,0);
            }
            Dimension buttonMinSize = null;
            
            if (splitPane.isOneTouchExpandable() && leftButton != null) {
                buttonMinSize = leftButton.getMinimumSize();
            }
            
            Insets insets = getInsets();
            int width = getDividerSize();
            int height = width;
            
            if (orientation == JSplitPane.VERTICAL_SPLIT) {
                if (buttonMinSize != null) {
                    int size = buttonMinSize.height;
                    if (insets != null) {
                        size += insets.top + insets.bottom;
                    }
                    height = Math.max(height, size);
                }
                width = 1;
            }
            else {
                if (buttonMinSize != null) {
                    int size = buttonMinSize.width;
                    if (insets != null) {
                        size += insets.left + insets.right;
                    }
                    width = Math.max(width, size);
                }
                height = 1;
            }
            return new Dimension(width, height);
        }
        
        
        public Dimension preferredLayoutSize(Container c) {
            return minimumLayoutSize(c);
        }
        
        
        public void removeLayoutComponent(Component c) {}
        
        public void addLayoutComponent(String string, Component c) {}
    } // End of class BasicSplitPaneDivider.DividerLayout
    /**
     * MouseHandler is responsible for converting mouse events
     * (released, dragged...) into the appropriate DragController
     * methods.
     * <p>
     */
    protected class Handler extends MouseHandler {
        /**
         * If dragger is not null it is messaged with completeDrag.
         */
        public void mouseReleased(MouseEvent e) {
            // The following code is needed, because the mouseReleased implementation
            // in the superclass changes the divider location even when the
            // user hasn't moved it.
            int lastLoc = splitPane.getLastDividerLocation();
            int currentLoc = splitPane.getDividerLocation();
            super.mouseReleased(e);
            if (splitPane.getDividerLocation() == currentLoc) {
                splitPane.setLastDividerLocation(lastLoc);
            }
        }
        
        /**
         * Double click on the split bar moves it to the bottom or to the left.
         * If it is already at the bottom most or leftmost position, it is moved
         * to its last location.
         */
        public void mouseClicked(MouseEvent evt) {
            if (evt.getClickCount() == 2) {
                boolean isHorizontal = splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT;
                
                Component  leftC = splitPane.getLeftComponent();
                Component  rightC = splitPane.getRightComponent();
                
                if (leftC == null || rightC == null) {
                    return;
                }
                Insets insets = splitPane.getInsets();
                
                int minLoc, maxLoc;
                if (isHorizontal) {
                    if (leftC.isVisible()) {
                        minLoc = leftC.getMinimumSize().width +
                        insets.left;
                    } else {
                        minLoc = insets.left;
                    }
                    if (rightC.isVisible()) {
                        maxLoc = splitPane.getWidth() -
                        rightC.getMinimumSize().width -
                        insets.right -
                        getSize().width;
                    } else {
                        maxLoc = splitPane.getWidth() - insets.right;
                    }
                } else {
                    if (leftC.isVisible()) {
                        minLoc = leftC.getMinimumSize().height +
                        insets.top;
                    } else {
                        minLoc = insets.top;
                    }
                    if (rightC.isVisible()) {
                        maxLoc = splitPane.getHeight() -
                        rightC.getMinimumSize().height -
                        insets.bottom -
                        getSize().height;
                    } else {
                        maxLoc = splitPane.getHeight() - insets.bottom;
                    }
                }
                maxLoc = Math.max(0, maxLoc);
                minLoc = Math.max(0, Math.min(minLoc, maxLoc));
                
                // FIXME We will provide a client property on the split
                // bar which can be used to specify into which direction the
                // split bar shall be moved on double click.
                if (isHorizontal) {
                    int helper = maxLoc;
                    maxLoc = minLoc;
                    minLoc = helper;
                }
                
                int lastLoc = splitPane.getLastDividerLocation();
                int currentLoc = splitPaneUI.getDividerLocation(splitPane);
                int newLoc;
                if (currentLoc == maxLoc) {
                    newLoc = lastLoc;
                } else {
                    newLoc = maxLoc;
                }
                
                if (currentLoc != newLoc) {
                    splitPane.setDividerLocation(newLoc);
                    // We do this in case the dividers notion of the location
                    // differs from the real location.
                    splitPane.setLastDividerLocation(currentLoc);
                }
            }
        }
    }
}
