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

Last change on this file since 6340 was 6340, checked in by Don-vip, 10 years ago

refactor of some GUI/widgets classes (impacts some plugins):

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