Source code: com/trapezium/chisel/gui/ScrollablePane.java
1 /* ScrollablePane
2 *
3 */
4
5 package com.trapezium.chisel.gui;
6
7 import java.awt.*;
8 import java.awt.event.*;
9 import java.io.*;
10 import java.net.URL;
11 import java.util.*;
12
13 //import com.trapezium.chisel.*;
14
15 /** The base container for our own gui implementation. This class includes
16 commonly needed functionality such as double buffering, background texture
17 and border.
18 */
19 public class ScrollablePane extends ChiselAWTPane implements AdjustmentListener {
20
21 // if the mode is DYNAMIC, the scroll bars disappear when not needed.
22 public static final int STATIC = 0;
23 public static final int DYNAMIC = 1;
24
25 HolderPane holder; // owns the reference plane
26 Component hScrollComponent;
27 Component vScrollComponent;
28 int mode = STATIC;
29
30 private ComponentAdapter tracker; // tracks position of children
31 private Rectangle fullRect; // the bounds of all children
32
33 public ScrollablePane() {
34 this(NO_BORDER);
35 }
36
37 public ScrollablePane(int border) {
38 this(border, false);
39 }
40
41 public ScrollablePane(int border, boolean opaque) {
42 super(border);
43
44 tracker = new ComponentTracker(this);
45
46 createScrollers(opaque);
47
48 holder = new HolderPane();
49 holder.setLayout(null);
50 add(holder);
51
52 fullRect = new Rectangle(0, 0, 0, 0);
53 setScrollValues();
54 }
55
56 public void setScrollMode(int mode) {
57 this.mode = mode;
58 }
59
60 private boolean hEnabled = true;
61 private boolean vEnabled = true;
62
63 public void enableScroll(boolean horiz, boolean vert) {
64 hEnabled = horiz;
65 hScrollComponent.setVisible(horiz);
66 vEnabled = vert;
67 vScrollComponent.setVisible(vert);
68 }
69
70 public void setErrorMarks( BitSet errorMarks, BitSet warningMarks, BitSet nonconformanceMarks ) {
71 Slider s = (Slider)vScrollComponent;
72 s.setErrorMarks( errorMarks, warningMarks, nonconformanceMarks );
73 }
74
75 public void setScrollIncrements(int hUnit, int vUnit, int hBlock, int vBlock) {
76 // System.out.println(getClass().getName() + " scroll units " + hUnit + "," + vUnit + " blocks " + hBlock + "," + vBlock);
77
78 Adjustable hScroller = (Adjustable) hScrollComponent;
79 hScroller.setUnitIncrement(hUnit);
80 hScroller.setBlockIncrement(hBlock);
81
82 Adjustable vScroller = (Adjustable) vScrollComponent;
83 vScroller.setUnitIncrement(vUnit);
84 vScroller.setBlockIncrement(vBlock);
85 }
86
87 final public Component getHScrollComponent() {
88 return hScrollComponent;
89 }
90 final public Component getVScrollComponent() {
91 return vScrollComponent;
92 }
93
94 void createScrollers(boolean opaque) {
95 hScrollComponent = new Slider(Adjustable.HORIZONTAL, 0, 100);
96 ((Slider) hScrollComponent).setOpaque(opaque);
97 add(hScrollComponent);
98
99 vScrollComponent = new Slider(Adjustable.VERTICAL, 0, 100);
100 ((Slider) vScrollComponent).setOpaque(opaque);
101 add(vScrollComponent);
102 }
103
104 class ComponentTracker extends ComponentAdapter {
105
106 ScrollablePane pane;
107
108 public ComponentTracker(ScrollablePane pane) {
109 super();
110 this.pane = pane;
111 }
112
113 public void componentMoved(ComponentEvent evt) {
114 setScrollValues(evt);
115 }
116 public void componentResized(ComponentEvent evt) {
117 setScrollValues(evt);
118 }
119 public void componentHidden(ComponentEvent evt) {
120 setScrollValues(evt);
121 }
122 public void componentShown(ComponentEvent evt) {
123 setScrollValues(evt);
124 }
125 void setScrollValues(ComponentEvent evt) {
126 pane.setScrollValues();
127 /****
128 Container granny = evt.getComponent().getParent().getParent();
129 if (granny instanceof ScrollablePane) {
130 ScrollablePane pane = (ScrollablePane) granny;
131 pane.calibrateComponents();
132 //System.out.println("Setting scroll values in response to " + evt.getComponent());
133 pane.setScrollValues();
134 } *****/
135 }
136 }
137
138 public void adjustmentValueChanged(AdjustmentEvent evt) {
139
140 if (!isEnabled()) {
141 return;
142 }
143
144 Adjustable hscroller = (Adjustable) hScrollComponent;
145 int xnew = hscroller.getValue() - hscroller.getMinimum();
146 Adjustable vscroller = (Adjustable) vScrollComponent;
147 int ynew = vscroller.getValue() - vscroller.getMinimum();
148 Point pt = holder.getLocation();
149
150 //System.out.println("adjValChgd xnew,ynew " + xnew + "," + ynew + " holder.getLocation " + pt);
151
152 if (xnew != pt.x || ynew != pt.y) {
153 Dimension size = getSize();
154 Insets insets = getInsets();
155 size.width -= insets.left + insets.right;
156 size.height -= insets.top + insets.bottom;
157 if (xnew + fullRect.width < size.width) {
158 fullRect.width = size.width - xnew;
159 }
160 if (ynew + fullRect.height < size.height) {
161 fullRect.height = size.height - ynew;
162 }
163 holder.setBounds(-xnew, -ynew, fullRect.width - fullRect.x, fullRect.height - fullRect.y);
164 repaint();
165 }
166 }
167
168 /** move the holder without changing the apparent position of its children */
169 /***
170 void calibrateComponents() {
171 computeFullRect();
172
173 int x = fullRect.x;
174 int y = fullRect.y;
175
176 System.out.println( "*** calibrate: Set holder x,y to " + x + "," + y );
177
178 Point pt = holder.getLocation();
179 int dx = x - pt.x;
180 int dy = (y - pt.y); ///15;
181
182 holder.setBounds(x, y, fullRect.width, fullRect.height);
183
184 if (dx != 0 || dy != 0) {
185
186 int count = holder.getComponentCount();
187 for (int i = 0; i < count; i++) {
188 Component c = holder.getComponent(i);
189 Point cpt = c.getLocation();
190 c.setLocation(cpt.x - dx, cpt.y - dy);
191 }
192
193 int xval = ((Adjustable) hScrollComponent).getValue();
194 ((Adjustable) hScrollComponent).setValue(xval - dx);
195
196 int yval = ((Adjustable) vScrollComponent).getValue();
197 ((Adjustable) vScrollComponent).setValue(yval - dy);
198
199 }
200 repaint();
201 }
202 ****/
203
204
205 void computeFullRect() {
206 Rectangle rect = getBounds();
207 Insets insets = getInsets();
208 rect.width -= insets.left + insets.right;
209 rect.height -= insets.top + insets.bottom;
210 rect.x = 0;
211 rect.y = 0;
212
213 int count = holder.getComponentCount();
214 for (int i = 0; i < count; i++) {
215 Component c = holder.getComponent(i);
216 Rectangle bounds = c.getBounds();
217 //System.out.println("computeFullRect comp " + i + " has bounds " + bounds);
218 if (bounds.x < rect.x) {
219 rect.width += rect.x - bounds.x;
220 rect.x = bounds.x;
221 }
222 if (bounds.y < rect.y) {
223 rect.height += rect.y - bounds.y;
224 rect.y = bounds.y;
225 }
226 if (bounds.x + bounds.width > rect.x + rect.width) {
227 rect.width = bounds.x + bounds.width - rect.x;
228 }
229 if (bounds.y + bounds.height > rect.y + rect.height) {
230 rect.height = bounds.y + bounds.height - rect.y;
231 }
232 }
233 fullRect = rect;
234 }
235
236
237 /** set the horizontal and vertical scroll values. The default is to
238 set the min and max values equal to the furthest extents of all
239 child components and the visibleAmount values to the ScrollablePane's
240 bounds on the screen. */
241
242 public void setScrollValues() {
243
244 if (!isEnabled()) {
245 // System.out.println("ScrollablePane not enabled; setScrollValues bailing out");
246 return;
247 }
248 //System.out.print("setScrollValues: ");
249 //Throwable t = new Throwable();
250 //t.printStackTrace();
251
252 Dimension size = getSize();
253 Insets insets = getInsets();
254 size.width -= insets.left + insets.right;
255 size.height -= insets.top + insets.bottom;
256
257 computeFullRect();
258
259 Adjustable hScroller = (Adjustable) hScrollComponent;
260 int hval = hScroller.getValue();
261 hScroller.setVisibleAmount(size.width);
262 hScroller.setMinimum(fullRect.x);
263 int hmax = fullRect.x + fullRect.width;
264 hScroller.setMaximum(hmax);
265 if (hval < fullRect.x) {
266 hval = fullRect.x;
267 } else if (hval >= hmax) {
268 hval = hmax;
269 }
270 hScroller.setValue(hval);
271
272 Adjustable vScroller = (Adjustable) vScrollComponent;
273 int vval = vScroller.getValue();
274 vScroller.setVisibleAmount(size.height);
275 vScroller.setMinimum(fullRect.y);
276 int vmax = fullRect.y + fullRect.height;
277 vScroller.setMaximum(vmax);
278 if (vval < fullRect.y) {
279 vval = fullRect.y;
280 } else if (vval >= vmax) {
281 vval = vmax;
282 }
283 vScroller.setValue(vval);
284
285 int x = fullRect.x - hval;
286 int y = fullRect.y - vval;
287 int width = fullRect.width - fullRect.x;
288 int height = fullRect.height - fullRect.y;
289 holder.setBounds( -hval, -vval, width, height );
290 if (mode == DYNAMIC) {
291 if (hval <= 0 && width <= size.width) {
292 hScrollComponent.setVisible(false);
293 } else if (hEnabled && !hScrollComponent.isVisible()) {
294 hScrollComponent.setVisible(true);
295 }
296 if (vval <= 0 && height <= size.height) {
297 vScrollComponent.setVisible(false);
298 } else if (vEnabled && !vScrollComponent.isVisible()) {
299 vScrollComponent.setVisible(true);
300 }
301 }
302 }
303
304 /** this lets an outside component own the scrollers */
305 public void hookScrollersTo(Container c) {
306 remove(hScrollComponent);
307 c.add(hScrollComponent, 0);
308 remove(vScrollComponent);
309 c.add(vScrollComponent, 1);
310 }
311
312
313 /** the scrollers should always be in front */
314 public void moveToFront(Component c) {
315 if (c == hScrollComponent || c == vScrollComponent || c == holder) {
316 return;
317 } else if (c != holder.getComponent(0)) {
318 holder.remove(c);
319 holder.add(c, 0);
320 holder.repaint();
321 }
322 }
323
324 /** move to back */
325 public void moveToBack(Component c) {
326 if (c == hScrollComponent || c == vScrollComponent || c == holder) {
327 return;
328 } else {
329 holder.remove(c);
330 holder.add(c);
331 holder.repaint();
332 }
333 }
334
335 protected void addImpl(Component c, Object constraints, int index) {
336 if (c == hScrollComponent) {
337 ((Adjustable)c).addAdjustmentListener(this);
338 super.addImpl(c, constraints, index);
339 } else if (c == vScrollComponent) {
340 ((Adjustable)c).addAdjustmentListener(this);
341 super.addImpl(c, constraints, index);
342 } else if (c == holder) {
343 super.addImpl(c, constraints, index);
344 } else {
345 c.addComponentListener(tracker);
346 holder.add(c, constraints, index);
347 computeFullRect();
348 }
349 }
350
351 public void remove(Component comp) {
352 if (comp != hScrollComponent && comp != vScrollComponent && comp != holder) {
353 holder.remove(comp);
354 } //else {
355 super.remove(comp);
356 //}
357 }
358
359 public int getComponentCount() {
360 return holder.getComponentCount();
361 }
362
363 public Component getComponent(int n) {
364 return holder.getComponent(n);
365 }
366
367 public void addContainerListener(ContainerListener listener) {
368 holder.addContainerListener(listener);
369 }
370
371 /** determine the minimum size */
372 public Dimension getMinimumSize() {
373 Dimension min = hScrollComponent.getMinimumSize();
374 Dimension minv = vScrollComponent.getMinimumSize();
375 min.width += minv.width;
376 min.height += minv.height;
377 return min;
378 }
379
380
381 /** determine the preferred size */
382 public Dimension getPreferredSize() {
383 computeFullRect();
384 Insets insets = getInsets();
385 Dimension dim = new Dimension(fullRect.width + insets.left + insets.right, fullRect.height + insets.top + insets.bottom);
386 return dim;
387 }
388
389
390 /** lay out the pane. Only the scrollers are positioned; other
391 components remain where they are. */
392 public void doLayout() {
393
394 Dimension size = getSize();
395 Dimension minh = hScrollComponent.getMinimumSize();
396 Dimension minv = vScrollComponent.getMinimumSize();
397 Container owner = hScrollComponent.getParent();
398 if (owner == this) {
399 Insets insets = getInsets();
400 int top = insets.top;
401 int bottom = size.height - insets.bottom;
402 int left = insets.left;
403 int right = size.width - insets.right;
404
405 hScrollComponent.setBounds(left, bottom - minh.height, right - left - minv.width, minh.height);
406 vScrollComponent.setBounds(right - minv.width, top, minv.width, bottom - top - minh.height);
407 } else {
408 Point pt;
409 // getLocationOnScreen() bombs when the frame hasn't been shown yet
410 try {
411 pt = getLocationOnScreen();
412 Point ownerscreenpt = owner.getLocationOnScreen();
413 pt.x -= ownerscreenpt.x;
414 pt.y -= ownerscreenpt.y;
415 } catch (Exception e) {
416 pt = getLocation();
417 }
418
419 hScrollComponent.setBounds(pt.x, pt.y + size.height, size.width, minh.height);
420 vScrollComponent.setBounds(pt.x + size.width, pt.y, minv.width, size.height);
421 }
422
423 setScrollValues();
424
425 /****
426 computeFullRect();
427 Adjustable hscroller = (Adjustable) hScrollComponent;
428 Adjustable vscroller = (Adjustable) vScrollComponent;
429
430 int dx = 0;
431 int dy = 0;
432 if (hScrollComponent.isVisible() && ((Slider)hScrollComponent).isOpaque()) {
433 dy = minh.height;
434 }
435 if (vScrollComponent.isVisible() && ((Slider)vScrollComponent).isOpaque()) {
436 dx = minv.width;
437 }
438 holder.setBounds(-hscroller.getValue(), -vscroller.getValue(), fullRect.width, fullRect.height);
439 ****/
440 }
441
442 /** overrides paint in order to paint the holder separately, otherwise
443 it might be excessively clipped */
444 public void paint(Graphics g) {
445
446 Dimension size = getSize();
447 Insets insets = getInsets();
448
449 paintBackground(g);
450
451 // for the holder, translate the graphics and expand the clip rect to compensate
452 Rectangle hrect = holder.getBounds();
453 Rectangle clip = g.getClipBounds();
454 g.translate(hrect.x, hrect.y);
455 holder.paint(g);
456 g.translate(-hrect.x, -hrect.y);
457
458 Graphics gcomp;
459 Rectangle r;
460 if (hScrollComponent.isVisible()) {
461 r = hScrollComponent.getBounds();
462 gcomp = g.create(r.x, r.y, r.width, r.height);
463 try {
464 hScrollComponent.paint(gcomp);
465 } finally {
466 gcomp.dispose();
467 }
468 }
469 if (vScrollComponent.isVisible()) {
470 r = vScrollComponent.getBounds();
471 gcomp = g.create(r.x, r.y, r.width, r.height);
472 try {
473 vScrollComponent.paint(gcomp);
474 } finally {
475 gcomp.dispose();
476 }
477 }
478 }
479 }
480