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

Last change on this file was 12445, checked in by Don-vip, 7 years ago

update to error-prone 2.0.21, groovy 2.4.12

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