com.jidesoft.swing.Contour Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jide-oss Show documentation
Show all versions of jide-oss Show documentation
JIDE Common Layer (Professional Swing Components)
/*
* @(#)Contour.java
*
* Copyright 2002 JIDE Software Inc. All rights reserved.
*/
package com.jidesoft.swing;
import com.jidesoft.plaf.UIDefaultsLookup;
import javax.swing.*;
import java.awt.*;
/**
* A Contour
is a lightweight component which only paints the outline of component when dragged. It is also
* used as a placeholder for some information during dragging.
*
* Notes: this class has to be public so that JIDE can use it in different packages, not meant to release to end user as
* a public API. JIDE will not guarantee the class will remain as it is.
*/
public class Contour extends JComponent implements IContour {
public static final int PARTIAL_OUTLINE_MODE = 0;
public static final int MIX_OUTLINE_MODE = 1;
public static final int FULL_OUTLINE_MODE = 2;
// Five values below should be configurable
/**
* width of the contour.
*/
private int _thickness = 4;
/**
* the width of tab if in tab-docked mode.
*/
private static final int TAB_WIDTH = 42;
/**
* the distance from the beginning to the tab if in tab-docked mode.
*/
private static final int TAB_LEADING = 8;
/**
* the color of contour.
*/
private Color _lineColor = new Color(136, 136, 136); // to fake the old contour color
// private static final Color LINE_COLOR = Color.BLACK;
/**
* the stroke to paint special effect of a contour.
*/
// private static final BasicStroke DOTTED_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE,
// BasicStroke.JOIN_ROUND, 1.0f, new float[]{0, 2, 0, 2}, 0);;
/**
* height of tab. It was used to draw contour. Application can set the height.
*/
private int _tabHeight = 22;
/**
* true
if docking is allowed; false
otherwise. Usually when user pressed ctrl key, dock
* is not allowed.
*/
private boolean _allowDocking = true;
/**
* true
if tab-docked; false
otherwise.
*/
private boolean _tabDocking;
/**
* Which side is the tab
*/
private int _tabSide;
/**
* true
if float; false
otherwise.
*/
private boolean _floating;
/**
* which component the dragged component will dock to.
*/
private Component _attachedComponent;
/**
* which side the dragged component will dock to.
*/
private int _attachedSide;
/**
* When you dragged a component, several other components could be dragged. For example, if user drags on title bar
* of FrameContainer, all components in the FrameContainer are considered as dragged. If user drags on tab, only
* selected one is dragged.
*
* true
if all dragged components are affected; false
otherwise.
*/
private boolean _single;
/**
* When user press and release ctrl key, the contour will toggle between dock and float mode. These three fields are
* used to remember previous state.
*/
private JComponent _saveDraggedComponent;
private int _saveX, _saveY;
private int _saveMouseModifier;
private Container _relativeContainer;
private int _outlineMode = 0;
private Outline _topOutline;
private Outline _bottomOutline;
private Outline _leftOutline;
private Outline _rightOutline;
/**
* If ghost is true, it will not paint itself at all but paint to underlying LayerPane.
*/
private boolean _ghost = false;
private Component _glassPane;
private boolean _changeCursor = false;
/**
* Default Constructor.
*/
public Contour() {
this(22);
setOpaque(false);
setDoubleBuffered(true);
}
/**
* Constructor with tab height.
*
* @param tabHeight the tab height
*/
public Contour(int tabHeight) {
_thickness = UIDefaultsLookup.getInt("Contour.thickness") == 0 ? 4 : UIDefaultsLookup.getInt("Contour.thickness");
_lineColor = UIDefaultsLookup.getColor("Contour.color") == null ? new Color(136, 136, 136) : UIDefaultsLookup.getColor("Contour.color");
setTabHeight(tabHeight);
if (getOutlineMode() != PARTIAL_OUTLINE_MODE) {
initOutline();
}
}
private void initOutline() {
// System.out.println("1");
_topOutline = new Outline();
_bottomOutline = new Outline();
_leftOutline = new Outline();
_rightOutline = new Outline();
}
/**
* Returns whether this component should use a buffer to paint.
*
* @return true if this component is double buffered, otherwise false
*/
@Override
public boolean isDoubleBuffered() {
return true;
}
/**
* Paint a rectangle or tab-shape using DOTTED_STROKE with width of WIDTH.
*
* @param g
*/
@Override
public void paint(Graphics g) {
if (!_ghost) {
paintOutline(g, false);
}
}
private void paintOutline(Graphics g, boolean xorMode) {
Rectangle bounds = getBounds();
g.translate(-bounds.x, -bounds.y);
if (xorMode) {
bounds = SwingUtilities.convertRectangle(this, bounds, (getRelativeContainer() != null ? getRelativeContainer() : getParent()));
// System.out.println(bounds);
}
else {
g.setColor(_lineColor);
}
// comment to make it draw faster
// ((Graphics2D)g).setStroke(DOTTED_STROKE);
if (getOutlineMode() != FULL_OUTLINE_MODE && isTabDocking()) {
drawTab(g, bounds.x, bounds.y, bounds.width, bounds.height - 1,
_tabHeight - 1, TAB_WIDTH, TAB_LEADING,
_thickness, getTabSide());
}
else {
drawRect(g, bounds.x, bounds.y, bounds.width - 1, bounds.height - 1, _thickness);
}
}
/**
* Draws vertical orhorizontall lines. Try to test if fillRect then drawLine four times. Other unit test show it's
* about fillRect is 75% faster than drawLine, so we decide to use fillRect to draw the contour.
*
* @param g
* @param x1
* @param y1
* @param x2
* @param y2
* @param thick
*/
private static void drawLine(Graphics g, int x1, int y1, int x2, int y2, int thick) {
if (x1 == x2) {
if (y2 > y1) {
g.fillRect(x1, y1, thick, y2 - y1);
}
else {
g.fillRect(x1, y2, thick, y1 - y2);
}
}
else if (y1 == y2) {
if (x2 > x1) {
g.fillRect(x1, y1, x2 - x1, thick);
}
else {
g.fillRect(x2, y1, x1 - x2, thick);
}
}
}
private static void drawRect(Graphics g, int x, int y, int width, int height, int thick) {
if (width <= thick) {
drawLine(g, x, y, x, y + height, thick);
}
else if (height <= thick) {
drawLine(g, x, y, x + width, y, thick);
}
else {
drawLine(g, x, y, x + width - thick, y, thick);
drawLine(g, x + width - thick, y, x + width - thick, y + height - thick, thick);
drawLine(g, x + width, y + height - thick, x + thick, y + height - thick, thick);
drawLine(g, x, y + height, x, y + thick, thick);
}
// commented because of using fillRect
// g.drawLine(x, y, x + width - 1, y);
// g.drawLine(x + width, y, x + width, y + height - 1);
// g.drawLine(x + width, y + height, x + 1, y + height);
// g.drawLine(x, y + height, x, y + 1);
}
private static void drawTab(Graphics g, int x, int y, int width, int height, int tabHeight, int tabWidth, int tabLeading, int thick, int side) {
switch (side) {
case SwingConstants.TOP:
drawTopTab(g, x, y, width, height, tabHeight, tabWidth, tabLeading, thick);
break;
case SwingConstants.BOTTOM:
drawBottomTab(g, x, y, width, height, tabHeight, tabWidth, tabLeading, thick);
break;
}
}
// TabDocking
// |------
// | |
// |--- |--------
// | |
// | |
// | |
// | |
// | |
// -----------------|
private static void drawTopTab(Graphics g, int x, int y, int width, int height, int tabHeight, int tabWidth, int tabLeading, int thick) {
drawLine(g, x + width - thick, y + tabHeight, x + width - thick, y + height - 1, thick); // right
drawLine(g, x + tabLeading + thick, y + tabHeight, x, y + tabHeight, thick); // top leading
drawLine(g, x + tabWidth + tabLeading, y + tabHeight, x + tabWidth + tabLeading, y, thick); // tab right
drawLine(g, x + tabWidth + tabLeading, y, x + tabLeading + 1, y, thick); // tab top
drawLine(g, x + tabLeading, y, x + tabLeading, y + tabHeight, thick); // tab left
drawLine(g, x + width, y + tabHeight, x + tabWidth + tabLeading, y + tabHeight, thick); // top rest
drawLine(g, x, y + tabHeight, x, y + height, thick); // left
drawLine(g, x, y + height - thick, x + width, y + height - thick, thick); // bottom
// commented because of using fillRect
// g.drawLine(x, y, x + width - 1, y); // top
// g.drawLine(x + width, y, x + width, y + height - tabHeight - 1); // right
//
// g.drawLine(x + width, y + height - tabHeight, x + tabWidth + tabLeading + 1, y + height - tabHeight); // bottom rest
// g.drawLine(x + tabWidth + tabLeading, y + height - tabHeight, x + tabWidth + tabLeading, y + height - 1); // tab right
// g.drawLine(x + tabWidth + tabLeading, y + height, x + tabLeading + 1, y + height); // tab bottom
// g.drawLine(x + tabLeading, y + height, x + tabLeading, y + height - tabHeight + 1); // tab left
// g.drawLine(x + tabLeading, y + height - tabHeight, x + 1, y + height - tabHeight); // bottom leading
//
// g.drawLine(x, y + height - tabHeight, x, y + 1); // left
}
// TabDocking
// -----------------|
// | |
// | |
// | |
// | |
// | |
// |--- |--------
// | |
// |------
private static void drawBottomTab(Graphics g, int x, int y, int width, int height, int tabHeight, int tabWidth, int tabLeading, int thick) {
drawLine(g, x, y, x + width - 1, y, thick); // top
drawLine(g, x + width - thick, y, x + width - thick, y + height - tabHeight - 1, thick); // right
drawLine(g, x + width, y + height - tabHeight - thick, x + tabWidth + tabLeading, y + height - tabHeight - thick, thick); // bottom rest
drawLine(g, x + tabWidth + tabLeading, y + height - tabHeight, x + tabWidth + tabLeading, y + height, thick); // tab right
drawLine(g, x + tabWidth + tabLeading, y + height - thick, x + tabLeading + 1, y + height - thick, thick); // tab bottom
drawLine(g, x + tabLeading, y + height, x + tabLeading, y + height - tabHeight, thick); // tab left
drawLine(g, x + tabLeading + thick, y + height - tabHeight - thick, x + 1, y + height - tabHeight - thick, thick); // bottom leading
drawLine(g, x, y + height - tabHeight, x, y + 1, thick); // left
// commented because of using fillRect
// g.drawLine(x, y, x + width - 1, y); // top
// g.drawLine(x + width, y, x + width, y + height - tabHeight - 1); // right
//
// g.drawLine(x + width, y + height - tabHeight, x + tabWidth + tabLeading + 1, y + height - tabHeight); // bottom rest
// g.drawLine(x + tabWidth + tabLeading, y + height - tabHeight, x + tabWidth + tabLeading, y + height - 1); // tab right
// g.drawLine(x + tabWidth + tabLeading, y + height, x + tabLeading + 1, y + height); // tab bottom
// g.drawLine(x + tabLeading, y + height, x + tabLeading, y + height - tabHeight + 1); // tab left
// g.drawLine(x + tabLeading, y + height - tabHeight, x + 1, y + height - tabHeight); // bottom leading
//
// g.drawLine(x, y + height - tabHeight, x, y + 1); // left
}
/**
* Overwrite setBounds so that width and height are always even.
*
* @param r the new bounding rectangle for this component
*/
@Override
public void setBounds(Rectangle r) {
setBounds(r.x, r.y, r.width, r.height);
}
/**
* Overwrite setBounds so that width and height are always even.
*
* It looks ugly for either dimension is odd when painting with DOTTED_STROKE
*
* @param x the new x-coordinate of this component
* @param y the new y-coordinate of this component
* @param width the new width
of this component
* @param height the new height
of this component
*/
@Override
public void setBounds(int x, int y, int width, int height) {
if (isLightweight()) {
if (getOutlineMode() == PARTIAL_OUTLINE_MODE) {
if (_ghost && (getRelativeContainer() != null || getParent() != null)) {
Graphics g = (getRelativeContainer() != null ? getRelativeContainer() : getParent()).getGraphics();
g.setXORMode(_lineColor);
paintOutline(g, true);
super.setBounds(x, y,
width % 2 == 0 ? width + 1 : width,
height % 2 == 0 ? height + 1 : height);
// paintOutline(g, true);
}
else {
super.setBounds(x, y,
width % 2 == 0 ? width + 1 : width,
height % 2 == 0 ? height + 1 : height);
}
}
else {
Rectangle rectangle = new Rectangle(x, y,
width % 2 == 0 ? width + 1 : width,
height % 2 == 0 ? height + 1 : height);
if (getParent() != null) {
Rectangle parentRectangle = getParent().getBounds();
super.setBounds(x, y,
width % 2 == 0 ? width + 1 : width,
height % 2 == 0 ? height + 1 : height);
if (getOutlineMode() == MIX_OUTLINE_MODE && parentRectangle.contains(rectangle)) {
_leftOutline.setVisible(false);
_rightOutline.setVisible(false);
_topOutline.setVisible(false);
_bottomOutline.setVisible(false);
}
else {
super.setVisible(false);
Point point = rectangle.getLocation();
SwingUtilities.convertPointToScreen(point, getParent());
arrangeOutline(rectangle, point);
}
}
}
}
else {
if (getRelativeContainer() != null) {
Point point = new Point(x, y);
SwingUtilities.convertPointToScreen(point, getRelativeContainer());
super.setBounds(point.x, point.y,
width % 2 == 0 ? width + 1 : width,
height % 2 == 0 ? height + 1 : height);
}
}
}
private void arrangeOutline(Rectangle rectangle, Point point) {
// boolean beyondLeft = rectangle.x < 0;
// boolean beyondTop = rectangle.y < 0;
// boolean beyondRight = rectangle.x + rectangle.width > parentRectangle.x + parentRectangle.width;
// boolean beyondBottom = rectangle.y + rectangle.height > parentRectangle.y + parentRectangle.height;
_leftOutline.setBounds(point.x, point.y, _thickness, rectangle.height - _thickness);
_topOutline.setBounds(point.x + _thickness, point.y, rectangle.width - _thickness - 1, _thickness);
_rightOutline.setBounds(point.x + rectangle.width - _thickness - 1, point.y + _thickness, _thickness, rectangle.height - _thickness);
_bottomOutline.setBounds(point.x, point.y + rectangle.height - _thickness, rectangle.width - 1, _thickness);
if (!_topOutline.isVisible()) {
_topOutline.setVisible(true);
}
if (!_leftOutline.isVisible()) {
_leftOutline.setVisible(true);
}
if (!_rightOutline.isVisible()) {
_rightOutline.setVisible(true);
}
if (!_bottomOutline.isVisible()) {
_bottomOutline.setVisible(true);
}
}
/**
* Gets tab height.
*
* @return tab height
*/
public int getTabHeight() {
return _tabHeight;
}
/**
* Sets the tab height.
*
* @param tabHeight
*/
public void setTabHeight(int tabHeight) {
_tabHeight = tabHeight;
}
/**
* Returns true if the contour is in tab-dock mode.
*
* @return true if tab-docking; false otherwise
*/
public boolean isTabDocking() {
return _tabDocking;
}
/**
* Sets the tab-docking mode.
*
* @param tabDocking new mode
*/
public void setTabDocking(boolean tabDocking) {
_tabDocking = tabDocking;
updateCursor();
}
/**
* Gets the side of the tab.
*
* @return the side of the tab
*/
public int getTabSide() {
return _tabSide;
}
/**
* Sets the side of the tab.
*
* @param tabSide
*/
public void setTabSide(int tabSide) {
_tabSide = tabSide;
}
/**
* Returns true if the contour is in floating mode.
*
* @return true if floating; false otherwise
*/
public boolean isFloating() {
return _floating;
}
/**
* Sets the floating mode.
*
* @param floating new mode
*/
public void setFloating(boolean floating) {
_floating = floating;
updateCursor();
}
/**
* Gets the attached component of this contour.
*
* @return the attached component
*/
public Component getAttachedComponent() {
return _attachedComponent;
}
/**
* Sets the attached components.
*
* @param attachedComponent attached component to be set
*/
public void setAttachedComponent(Component attachedComponent) {
_attachedComponent = attachedComponent;
}
/**
* Gets the side of the attached component which the contour is attached to.
*
* @return side the attached side
*/
public int getAttachedSide() {
return _attachedSide;
}
/**
* Sets the side of the attached component which the contour is attached to.
*
* @param attachedSide the new attached side to be set
*/
public void setAttachedSide(int attachedSide) {
_attachedSide = attachedSide;
updateCursor();
}
private void updateCursor() {
if (getGlassPane() == null) { // if glass pane is not set, no way to change cursor shape.
return;
}
// show a stop cursor if the floating is not allowed
if (!isVisible() && _floating) {
getGlassPane().setCursor(JideCursors.getPredefinedCursor(JideCursors.DRAG_STOP_CURSOR));
return;
}
else {
getGlassPane().setCursor(Cursor.getDefaultCursor());
}
if (!_changeCursor) {
return;
}
if (!isVisible()) {
getGlassPane().setCursor(Cursor.getDefaultCursor());
return;
}
if (isVisible() && (!_allowDocking || _floating)) {
getGlassPane().setCursor(JideCursors.getPredefinedCursor(JideCursors.FLOAT_CURSOR));
}
else if (isVisible() && _tabDocking) {
getGlassPane().setCursor(JideCursors.getPredefinedCursor(JideCursors.TAB_CURSOR));
}
else if (getAttachedComponent() instanceof JideSplitPaneDivider) {
if (((JideSplitPaneDivider) getAttachedComponent()).getJideSplitPane().getOrientation() == JideSplitPane.HORIZONTAL_SPLIT) {
getGlassPane().setCursor(JideCursors.getPredefinedCursor(JideCursors.HORIZONTAL_CURSOR));
}
else {
getGlassPane().setCursor(JideCursors.getPredefinedCursor(JideCursors.VERTICAL_CURSOR));
}
}
else {
switch (_attachedSide) {
case 1:
getGlassPane().setCursor(JideCursors.getPredefinedCursor(JideCursors.NORTH_CURSOR));
break;
case 2:
getGlassPane().setCursor(JideCursors.getPredefinedCursor(JideCursors.SOUTH_CURSOR));
break;
case 4:
getGlassPane().setCursor(JideCursors.getPredefinedCursor(JideCursors.EAST_CURSOR));
break;
case 8:
getGlassPane().setCursor(JideCursors.getPredefinedCursor(JideCursors.WEST_CURSOR));
break;
default:
getGlassPane().setCursor(Cursor.getDefaultCursor());
break;
}
}
}
/**
* When you dragged a component, several other components could be dragged. For example, if user drags on title bar
* of FrameContainer, all components in the FrameContainer are considered as dragged. If user drags on tab, only
* selected one is dragged.
*
* @return true
if all dragged components are affected; false
otherwise.
*/
public boolean isSingle() {
return _single;
}
/**
* Sets the value of single.
*
* @param single true
if all dragged components are affected; false
otherwise.
*/
public void setSingle(boolean single) {
_single = single;
}
/**
* Checks if docking is allowed.
*
* @return true
if docking is allowed; false
otherwise.
*/
public boolean isAllowDocking() {
return _allowDocking;
}
/**
* Sets the value of docking.
*
* @param allowDocking true
if docking is allowed; false
otherwise.
*/
public void setAllowDocking(boolean allowDocking) {
_allowDocking = allowDocking;
updateCursor();
}
public Container getRelativeContainer() {
return _relativeContainer;
}
public void setRelativeContainer(Container relativeContainer) {
_relativeContainer = relativeContainer;
}
/**
* Gets saved X position of contour before it's hidden.
*
* @return saved X position
*/
public int getSaveX() {
return _saveX;
}
/**
* Gets saved Y position of contour before it's hidden.
*
* @return saved Y position
*/
public int getSaveY() {
return _saveY;
}
/**
* Gets saved mouse modifier before the contour is hidden.
*
* @return saved mouse modifier
*/
public int getSaveMouseModifier() {
return _saveMouseModifier;
}
/**
* Gets saved dragged component before the contour is hidden.
*
* @return saved dragged component
*/
public JComponent getSaveDraggedComponent() {
return _saveDraggedComponent;
}
/**
* Stores information before the contour is hidden. Those information will be used to restore when the contour is
* set visible again.
*
* @param comp the dragged component
* @param saveX X position of the contour
* @param saveY Y position of the contour
* @param saveMouseModifier mouse modifier in the MouseEvent
*/
public void setDraggingInformation(JComponent comp, int saveX, int saveY, int saveMouseModifier) {
_saveDraggedComponent = comp;
_saveX = saveX;
_saveY = saveY;
_saveMouseModifier = saveMouseModifier;
}
public void cleanup() {
if (getOutlineMode() != PARTIAL_OUTLINE_MODE) {
// System.out.println("0");
_leftOutline.dispose();
_rightOutline.dispose();
_topOutline.dispose();
_bottomOutline.dispose();
_leftOutline = null;
_rightOutline = null;
_topOutline = null;
_bottomOutline = null;
}
if (getGlassPane() != null) {
getGlassPane().setCursor(Cursor.getDefaultCursor());
}
}
// private Screen _screen;
// private Container _savedContainer;
/**
* Makes the component visible or invisible. Overrides Component.setVisible
.
*
* @param aFlag true to make the component visible; false to make it invisible
*/
@Override
public void setVisible(boolean aFlag) {
super.setVisible(aFlag);
updateCursor();
if (!aFlag && getOutlineMode() != PARTIAL_OUTLINE_MODE) {
_leftOutline.setVisible(false);
_rightOutline.setVisible(false);
_topOutline.setVisible(false);
_bottomOutline.setVisible(false);
}
}
/**
* Determines whether this component should be visible when its parent is visible. Components are initially visible,
* with the exception of top level components such as Frame
objects.
*
* @return true
if the component is visible, false
otherwise
*
* @see #setVisible
* @since JDK1.0
*/
@Override
public boolean isVisible() {
if (super.isVisible()) {
return true;
}
else if (getOutlineMode() != PARTIAL_OUTLINE_MODE &&
(_topOutline.isVisible() || _bottomOutline.isVisible() || _leftOutline.isVisible() || _rightOutline.isVisible())) {
return true;
}
else
return false;
}
class Outline extends JWindow {
public Outline() {
this.setVisible(false);
setBackground(_lineColor);
}
@Override
public void paint(Graphics g) {
g.setColor(_lineColor);
g.fillRect(0, 0, getWidth(), getHeight());
}
// Code for double buffer but doesn't work very well.
// Image offscreen;
//
// public void invalidate() {
// super.invalidate();
// offscreen = null;
// }
//
// public void update(Graphics g) {
// paint(g);
// }
//
// public void paint(Graphics g) {
// if (offscreen == null) {
// offscreen = createImage(getSize().width, getSize().height);
// }
// Graphics og = offscreen.getGraphics();
// // Here do something to draw your rectangle
// // og.setClip(0,0,getSize().width, getSize().height);
// super.paint(g);
//
// g.drawImage(offscreen, 0, 0, null);
// og.dispose();
// }
}
public int getOutlineMode() {
return _outlineMode;
}
public void setOutlineMode(int outlineMode) {
if (outlineMode != PARTIAL_OUTLINE_MODE && _outlineMode == PARTIAL_OUTLINE_MODE) {
initOutline();
}
_outlineMode = outlineMode;
}
public Component getGlassPane() {
return _glassPane;
}
public void setGlassPane(Component glassPane) {
_glassPane = glassPane;
}
public boolean isChangeCursor() {
return _changeCursor;
}
public void setChangeCursor(boolean changeCursor) {
_changeCursor = changeCursor;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy