source: josm/trunk/src/org/openstreetmap/josm/gui/MultiSplitPane.java@ 6253

Last change on this file since 6253 was 6084, checked in by bastiK, 11 years ago

see #8902 - add missing @Override annotations (patch by shinigami)

  • Property svn:eol-style set to native
File size: 13.2 KB
Line 
1/*
2 * $Id: MultiSplitPane.java,v 1.15 2005/10/26 14:29:54 hansmuller Exp $
3 *
4 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
5 * Santa Clara, California 95054, U.S.A. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22//package org.jdesktop.swingx;
23package org.openstreetmap.josm.gui;
24
25import java.awt.Color;
26import java.awt.Cursor;
27import java.awt.Graphics;
28import java.awt.Graphics2D;
29import java.awt.Rectangle;
30import java.awt.event.KeyEvent;
31import java.awt.event.KeyListener;
32import java.awt.event.MouseEvent;
33import javax.accessibility.AccessibleContext;
34import javax.accessibility.AccessibleRole;
35import javax.swing.JPanel;
36import javax.swing.event.MouseInputAdapter;
37
38import org.openstreetmap.josm.gui.MultiSplitLayout.Divider;
39import org.openstreetmap.josm.gui.MultiSplitLayout.Node;
40
41/**
42 *
43 * <p>
44 * All properties in this class are bound: when a properties value
45 * is changed, all PropertyChangeListeners are fired.
46 *
47 * @author Hans Muller
48 */
49public class MultiSplitPane extends JPanel {
50 private AccessibleContext accessibleContext = null;
51 private boolean continuousLayout = true;
52 private DividerPainter dividerPainter = new DefaultDividerPainter();
53
54 /**
55 * Creates a MultiSplitPane with it's LayoutManager set to
56 * to an empty MultiSplitLayout.
57 */
58 public MultiSplitPane() {
59 super(new MultiSplitLayout());
60 InputHandler inputHandler = new InputHandler();
61 addMouseListener(inputHandler);
62 addMouseMotionListener(inputHandler);
63 addKeyListener(inputHandler);
64 setFocusable(true);
65 }
66
67 /**
68 * A convenience method that returns the layout manager cast
69 * to MutliSplitLayout.
70 *
71 * @return this MultiSplitPane's layout manager
72 * @see java.awt.Container#getLayout
73 * @see #setModel
74 */
75 public final MultiSplitLayout getMultiSplitLayout() {
76 return (MultiSplitLayout)getLayout();
77 }
78
79 /**
80 * A convenience method that sets the MultiSplitLayout model.
81 * Equivalent to <code>getMultiSplitLayout.setModel(model)</code>
82 *
83 * @param model the root of the MultiSplitLayout model
84 * @see #getMultiSplitLayout
85 * @see MultiSplitLayout#setModel
86 */
87 public final void setModel(Node model) {
88 getMultiSplitLayout().setModel(model);
89 }
90
91 /**
92 * A convenience method that sets the MultiSplitLayout dividerSize
93 * property. Equivalent to
94 * <code>getMultiSplitLayout().setDividerSize(newDividerSize)</code>.
95 *
96 * @param dividerSize the value of the dividerSize property
97 * @see #getMultiSplitLayout
98 * @see MultiSplitLayout#setDividerSize
99 */
100 public final void setDividerSize(int dividerSize) {
101 getMultiSplitLayout().setDividerSize(dividerSize);
102 }
103
104 /**
105 * Sets the value of the <code>continuousLayout</code> property.
106 * If true, then the layout is revalidated continuously while
107 * a divider is being moved. The default value of this property
108 * is true.
109 *
110 * @param continuousLayout value of the continuousLayout property
111 * @see #isContinuousLayout
112 */
113 public void setContinuousLayout(boolean continuousLayout) {
114 boolean oldContinuousLayout = continuousLayout;
115 this.continuousLayout = continuousLayout;
116 firePropertyChange("continuousLayout", oldContinuousLayout, continuousLayout);
117 }
118
119 /**
120 * Returns true if dragging a divider only updates
121 * the layout when the drag gesture ends (typically, when the
122 * mouse button is released).
123 *
124 * @return the value of the <code>continuousLayout</code> property
125 * @see #setContinuousLayout
126 */
127 public boolean isContinuousLayout() {
128 return continuousLayout;
129 }
130
131 /**
132 * Returns the Divider that's currently being moved, typically
133 * because the user is dragging it, or null.
134 *
135 * @return the Divider that's being moved or null.
136 */
137 public Divider activeDivider() {
138 return dragDivider;
139 }
140
141 /**
142 * Draws a single Divider. Typically used to specialize the
143 * way the active Divider is painted.
144 *
145 * @see #getDividerPainter
146 * @see #setDividerPainter
147 */
148 public static abstract class DividerPainter {
149 /**
150 * Paint a single Divider.
151 *
152 * @param g the Graphics object to paint with
153 * @param divider the Divider to paint
154 */
155 public abstract void paint(Graphics g, Divider divider);
156 }
157
158 private class DefaultDividerPainter extends DividerPainter {
159 @Override
160 public void paint(Graphics g, Divider divider) {
161 if ((divider == activeDivider()) && !isContinuousLayout()) {
162 Graphics2D g2d = (Graphics2D)g;
163 g2d.setColor(Color.black);
164 g2d.fill(divider.getBounds());
165 }
166 }
167 }
168
169 /**
170 * The DividerPainter that's used to paint Dividers on this MultiSplitPane.
171 * This property may be null.
172 *
173 * @return the value of the dividerPainter Property
174 * @see #setDividerPainter
175 */
176 public DividerPainter getDividerPainter() {
177 return dividerPainter;
178 }
179
180 /**
181 * Sets the DividerPainter that's used to paint Dividers on this
182 * MultiSplitPane. The default DividerPainter only draws
183 * the activeDivider (if there is one) and then, only if
184 * continuousLayout is false. The value of this property is
185 * used by the paintChildren method: Dividers are painted after
186 * the MultiSplitPane's children have been rendered so that
187 * the activeDivider can appear "on top of" the children.
188 *
189 * @param dividerPainter the value of the dividerPainter property, can be null
190 * @see #paintChildren
191 * @see #activeDivider
192 */
193 public void setDividerPainter(DividerPainter dividerPainter) {
194 this.dividerPainter = dividerPainter;
195 }
196
197 /**
198 * Uses the DividerPainter (if any) to paint each Divider that
199 * overlaps the clip Rectangle. This is done after the call to
200 * <code>super.paintChildren()</code> so that Dividers can be
201 * rendered "on top of" the children.
202 * <p>
203 * {@inheritDoc}
204 */
205 @Override
206 protected void paintChildren(Graphics g) {
207 super.paintChildren(g);
208 DividerPainter dp = getDividerPainter();
209 Rectangle clipR = g.getClipBounds();
210 if ((dp != null) && (clipR != null)) {
211 Graphics dpg = g.create();
212 try {
213 MultiSplitLayout msl = getMultiSplitLayout();
214 for(Divider divider : msl.dividersThatOverlap(clipR)) {
215 dp.paint(dpg, divider);
216 }
217 }
218 finally {
219 dpg.dispose();
220 }
221 }
222 }
223
224 private boolean dragUnderway = false;
225 private MultiSplitLayout.Divider dragDivider = null;
226 private Rectangle initialDividerBounds = null;
227 private boolean oldFloatingDividers = true;
228 private int dragOffsetX = 0;
229 private int dragOffsetY = 0;
230 private int dragMin = -1;
231 private int dragMax = -1;
232
233 private void startDrag(int mx, int my) {
234 requestFocusInWindow();
235 MultiSplitLayout msl = getMultiSplitLayout();
236 MultiSplitLayout.Divider divider = msl.dividerAt(mx, my);
237 if (divider != null) {
238 MultiSplitLayout.Node prevNode = divider.previousSibling();
239 MultiSplitLayout.Node nextNode = divider.nextSibling();
240 if ((prevNode == null) || (nextNode == null)) {
241 dragUnderway = false;
242 }
243 else {
244 initialDividerBounds = divider.getBounds();
245 dragOffsetX = mx - initialDividerBounds.x;
246 dragOffsetY = my - initialDividerBounds.y;
247 dragDivider = divider;
248 Rectangle prevNodeBounds = prevNode.getBounds();
249 Rectangle nextNodeBounds = nextNode.getBounds();
250 if (dragDivider.isVertical()) {
251 dragMin = prevNodeBounds.x;
252 dragMax = nextNodeBounds.x + nextNodeBounds.width;
253 dragMax -= dragDivider.getBounds().width;
254 }
255 else {
256 dragMin = prevNodeBounds.y;
257 dragMax = nextNodeBounds.y + nextNodeBounds.height;
258 dragMax -= dragDivider.getBounds().height;
259 }
260 oldFloatingDividers = getMultiSplitLayout().getFloatingDividers();
261 getMultiSplitLayout().setFloatingDividers(false);
262 dragUnderway = true;
263 }
264 }
265 else {
266 dragUnderway = false;
267 }
268 }
269
270 private void repaintDragLimits() {
271 Rectangle damageR = dragDivider.getBounds();
272 if (dragDivider.isVertical()) {
273 damageR.x = dragMin;
274 damageR.width = dragMax - dragMin;
275 }
276 else {
277 damageR.y = dragMin;
278 damageR.height = dragMax - dragMin;
279 }
280 repaint(damageR);
281 }
282
283 private void updateDrag(int mx, int my) {
284 if (!dragUnderway) {
285 return;
286 }
287 Rectangle oldBounds = dragDivider.getBounds();
288 Rectangle bounds = new Rectangle(oldBounds);
289 if (dragDivider.isVertical()) {
290 bounds.x = mx - dragOffsetX;
291 bounds.x = Math.max(bounds.x, dragMin);
292 bounds.x = Math.min(bounds.x, dragMax);
293 }
294 else {
295 bounds.y = my - dragOffsetY;
296 bounds.y = Math.max(bounds.y, dragMin);
297 bounds.y = Math.min(bounds.y, dragMax);
298 }
299 dragDivider.setBounds(bounds);
300 if (isContinuousLayout()) {
301 revalidate();
302 repaintDragLimits();
303 }
304 else {
305 repaint(oldBounds.union(bounds));
306 }
307 }
308
309 private void clearDragState() {
310 dragDivider = null;
311 initialDividerBounds = null;
312 oldFloatingDividers = true;
313 dragOffsetX = dragOffsetY = 0;
314 dragMin = dragMax = -1;
315 dragUnderway = false;
316 }
317
318 private void finishDrag(int x, int y) {
319 if (dragUnderway) {
320 clearDragState();
321 if (!isContinuousLayout()) {
322 revalidate();
323 repaint();
324 }
325 }
326 }
327
328 private void cancelDrag() {
329 if (dragUnderway) {
330 dragDivider.setBounds(initialDividerBounds);
331 getMultiSplitLayout().setFloatingDividers(oldFloatingDividers);
332 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
333 repaint();
334 revalidate();
335 clearDragState();
336 }
337 }
338
339 private void updateCursor(int x, int y, boolean show) {
340 if (dragUnderway) {
341 return;
342 }
343 int cursorID = Cursor.DEFAULT_CURSOR;
344 if (show) {
345 MultiSplitLayout.Divider divider = getMultiSplitLayout().dividerAt(x, y);
346 if (divider != null) {
347 cursorID = (divider.isVertical()) ?
348 Cursor.E_RESIZE_CURSOR :
349 Cursor.N_RESIZE_CURSOR;
350 }
351 }
352 setCursor(Cursor.getPredefinedCursor(cursorID));
353 }
354
355 private class InputHandler extends MouseInputAdapter implements KeyListener {
356
357 @Override
358 public void mouseEntered(MouseEvent e) {
359 updateCursor(e.getX(), e.getY(), true);
360 }
361
362 @Override
363 public void mouseMoved(MouseEvent e) {
364 updateCursor(e.getX(), e.getY(), true);
365 }
366
367 @Override
368 public void mouseExited(MouseEvent e) {
369 updateCursor(e.getX(), e.getY(), false);
370 }
371
372 @Override
373 public void mousePressed(MouseEvent e) {
374 startDrag(e.getX(), e.getY());
375 }
376 @Override
377 public void mouseReleased(MouseEvent e) {
378 finishDrag(e.getX(), e.getY());
379 }
380 @Override
381 public void mouseDragged(MouseEvent e) {
382 updateDrag(e.getX(), e.getY());
383 }
384 @Override
385 public void keyPressed(KeyEvent e) {
386 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
387 cancelDrag();
388 }
389 }
390 @Override
391 public void keyReleased(KeyEvent e) { }
392 @Override
393 public void keyTyped(KeyEvent e) { }
394 }
395
396 @Override
397 public AccessibleContext getAccessibleContext() {
398 if( accessibleContext == null ) {
399 accessibleContext = new AccessibleMultiSplitPane();
400 }
401 return accessibleContext;
402 }
403
404 protected class AccessibleMultiSplitPane extends AccessibleJPanel {
405 @Override
406 public AccessibleRole getAccessibleRole() {
407 return AccessibleRole.SPLIT_PANE;
408 }
409 }
410}
Note: See TracBrowser for help on using the repository browser.