blob: 2b310efdebc5349e623e8f7a59336a66b59231db [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2003 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.awt.motif;
27
28import java.awt.*;
29import java.awt.event.AdjustmentEvent;
30import java.awt.peer.ScrollPanePeer;
31
32import java.util.logging.*;
33
34import sun.awt.PeerEvent;
35
36class MScrollPanePeer extends MPanelPeer implements ScrollPanePeer {
37
38 private static final Logger log = Logger.getLogger("sun.awt.motif.MScrollPanePeer");
39
40 final static int UNIT_INCREMENT = 0;
41 final static int BLOCK_INCREMENT = 1;
42
43 boolean ignore;
44
45 native void create(MComponentPeer parent);
46
47 static {
48 initIDs();
49 }
50
51 /**
52 * Initialize JNI field and method IDs
53 */
54 private static native void initIDs();
55
56 MScrollPanePeer(Component target) {
57 init(target);
58 scrollPaneInit();
59 }
60
61 MScrollPanePeer(Component target, Object arg) {
62 init(target, arg);
63 scrollPaneInit();
64 }
65
66 void scrollPaneInit() {
67 ignore = false;
68 ScrollPane sp = (ScrollPane)target;
69 Adjustable vadj, hadj;
70 if ((vadj = sp.getVAdjustable()) != null) {
71 pSetIncrement(Adjustable.VERTICAL, UNIT_INCREMENT, vadj.getUnitIncrement());
72 }
73 if ((hadj = sp.getHAdjustable()) != null) {
74 pSetIncrement(Adjustable.HORIZONTAL, UNIT_INCREMENT, hadj.getUnitIncrement());
75 }
76 super.pSetScrollbarBackground(sp.getBackground());
77 }
78
79 public void setScrollChild(MComponentPeer child) {
80 pSetScrollChild(child);
81 }
82
83 public void setBackground(Color c) {
84 super.setBackground(c);
85 pSetScrollbarBackground(c);
86 }
87
88 public void setForeground(Color c) {
89 super.setForeground(c);
90 pSetInnerForeground(c);
91 }
92
93 native void pSetScrollChild(MComponentPeer child);
94 native void pSetIncrement(int orient, int type, int incr);
95 native int pGetScrollbarSpace(int orient);
96 native int pGetBlockIncrement(int orient);
97 native Insets pInsets(int w, int h, int childw, int childh);
98 native int pGetShadow();
99
100 public int getHScrollbarHeight() {
101 ScrollPane sp = (ScrollPane)target;
102 if (sp.getScrollbarDisplayPolicy() == ScrollPane.SCROLLBARS_NEVER) {
103 return 0;
104 } else {
105 return pGetScrollbarSpace(Adjustable.HORIZONTAL);
106 }
107 }
108
109 public int getVScrollbarWidth() {
110 ScrollPane sp = (ScrollPane)target;
111 if (sp.getScrollbarDisplayPolicy() == ScrollPane.SCROLLBARS_NEVER) {
112 return 0;
113 } else {
114 return pGetScrollbarSpace(Adjustable.VERTICAL);
115 }
116 }
117
118 public Insets insets() {
119 ScrollPane sp = (ScrollPane)target;
120 Dimension d = sp.size();
121 Dimension cd;
122 Component c = getScrollChild();
123 if (c != null) {
124 cd = c.size();
125 } else {
126 cd = new Dimension(0, 0);
127 }
128 return pInsets(d.width, d.height, cd.width, cd.height);
129 }
130
131 public void setUnitIncrement(Adjustable adj, int u) {
132 ScrollPane sp = (ScrollPane)target;
133 if (sp.getScrollbarDisplayPolicy() != ScrollPane.SCROLLBARS_NEVER) {
134 pSetIncrement(adj.getOrientation(), UNIT_INCREMENT, u);
135 }
136 }
137
138 public void setValue(Adjustable adj, int v) {
139 if (! ignore) {
140 Point p;
141 Component c = getScrollChild();
142 if (c == null) {
143 return;
144 }
145 p = c.getLocation();
146 switch(adj.getOrientation()) {
147 case Adjustable.VERTICAL:
148 setScrollPosition(-(p.x), v);
149 break;
150 case Adjustable.HORIZONTAL:
151 setScrollPosition(v, -(p.y));
152 break;
153 }
154 }
155 }
156
157 public native void setScrollPosition(int x, int y);
158
159 public void childResized(int w, int h) {
160 // REMIND AIM: May need to revisit this...
161 if (((ScrollPane)target).getScrollbarDisplayPolicy() != ScrollPane.SCROLLBARS_NEVER) {
162 ScrollPane sp = (ScrollPane)target;
163 Adjustable vAdj = sp.getVAdjustable();
164 Adjustable hAdj = sp.getHAdjustable();
165 pSetIncrement(Scrollbar.VERTICAL, UNIT_INCREMENT, vAdj.getUnitIncrement());
166 pSetIncrement(Scrollbar.HORIZONTAL, UNIT_INCREMENT, hAdj.getUnitIncrement());
167 pSetIncrement(Scrollbar.VERTICAL, BLOCK_INCREMENT, vAdj.getBlockIncrement());
168 pSetIncrement(Scrollbar.HORIZONTAL, BLOCK_INCREMENT, hAdj.getBlockIncrement());
169 }
170
171 }
172
173 // NOTE: This method may be called by privileged threads.
174 // DO NOT INVOKE CLIENT CODE ON THIS THREAD!
175 private void postScrollEvent(int orient, int type,
176 int pos, boolean isAdjusting)
177 {
178 Runnable adjustor = new Adjustor(orient, type, pos, isAdjusting);
179 MToolkit.executeOnEventHandlerThread(new ScrollEvent(target, adjustor));
180 }
181
182 /**
183 * This is used to change the adjustable on dispatch thread to
184 * represent a change made in the native scrollbar. Since the
185 * change was reflected immediately at the native level,
186 * notification from the adjustable is temporarily ignored.
187 */
188 class ScrollEvent extends PeerEvent {
189 ScrollEvent(Object source, Runnable runnable) {
190 super(source, runnable, 0L);
191 }
192
193 public PeerEvent coalesceEvents(PeerEvent newEvent) {
194 if (log.isLoggable(Level.FINEST)) {
195 log.log(Level.FINEST, "ScrollEvent coalesced " + newEvent);
196 }
197 if (newEvent instanceof ScrollEvent) {
198 return newEvent;
199 }
200 return null;
201 }
202 }
203
204 native void setTypedValue(ScrollPaneAdjustable adjustable, int value, int type);
205
206 /**
207 * Runnable for the ScrollEvent that performs the adjustment.
208 */
209 class Adjustor implements Runnable {
210 int orient; // selects scrollbar
211 int type; // adjustment type
212 int pos; // new position (only used for absolute)
213 boolean isAdjusting; // isAdjusting status
214
215 Adjustor(int orient, int type, int pos, boolean isAdjusting) {
216 this.orient = orient;
217 this.type = type;
218 this.pos = pos;
219 this.isAdjusting = isAdjusting;
220 }
221
222 public void run() {
223 ScrollPane sp = (ScrollPane)MScrollPanePeer.this.target;
224 ScrollPaneAdjustable adj = null;
225
226 // ScrollPaneAdjustable made public in 1.4, but
227 // get[HV]Adjustable can't be declared to return
228 // ScrollPaneAdjustable because it would break backward
229 // compatibility -- hence the cast
230
231 if (orient == Adjustable.VERTICAL) {
232 adj = (ScrollPaneAdjustable)sp.getVAdjustable();
233 } else if (orient == Adjustable.HORIZONTAL) {
234 adj = (ScrollPaneAdjustable)sp.getHAdjustable();
235 } else {
236 if (log.isLoggable(Level.FINE)) {
237 log.log(Level.FINE, "Assertion failed: unknown orient");
238 }
239 }
240
241 if (adj == null) {
242 return;
243 }
244
245 int newpos = adj.getValue();
246 switch (type) {
247 case AdjustmentEvent.UNIT_DECREMENT:
248 newpos -= adj.getUnitIncrement();
249 break;
250 case AdjustmentEvent.UNIT_INCREMENT:
251 newpos += adj.getUnitIncrement();
252 break;
253 case AdjustmentEvent.BLOCK_DECREMENT:
254 newpos -= adj.getBlockIncrement();
255 break;
256 case AdjustmentEvent.BLOCK_INCREMENT:
257 newpos += adj.getBlockIncrement();
258 break;
259 case AdjustmentEvent.TRACK:
260 newpos = this.pos;
261 break;
262 default:
263 if (log.isLoggable(Level.FINE)) {
264 log.log(Level.FINE, "Assertion failed: unknown type");
265 }
266 return;
267 }
268
269 // keep scroll position in acceptable range
270 newpos = Math.max(adj.getMinimum(), newpos);
271 newpos = Math.min(adj.getMaximum(), newpos);
272
273 // set value; this will synchronously fire an AdjustmentEvent
274 try {
275 MScrollPanePeer.this.ignore = true;
276 adj.setValueIsAdjusting(isAdjusting);
277
278 // Fix for 4075484 - consider type information when creating AdjustmentEvent
279 // We can't just call adj.setValue() because it creates AdjustmentEvent with type=TRACK
280 // Instead, we call private method setTypedValue of ScrollPaneAdjustable.
281 // Because ScrollPaneAdjustable is in another package we should call it through native code.
282 setTypedValue(adj, newpos, type);
283 } finally {
284 MScrollPanePeer.this.ignore = false;
285 }
286 }
287 } // class Adjustor
288
289
290 private Component getScrollChild() {
291 ScrollPane sp = (ScrollPane)target;
292 Component child = null;
293 try {
294 child = sp.getComponent(0);
295 } catch (ArrayIndexOutOfBoundsException e) {
296 // do nothing. in this case we return null
297 }
298 return child;
299 }
300
301 final static int MARGIN = 1;
302 final static int SCROLLBAR = 16;
303 int hsbSpace;
304 int vsbSpace;
305 int vval;
306 int hval;
307 int vmax;
308 int hmax;
309 /*
310 * Print the native component by rendering the Motif look ourselves.
311 * ToDo(aim): needs to query native motif for more accurate size and
312 * color information.
313 */
314 public void print(Graphics g) {
315 ScrollPane sp = (ScrollPane)target;
316 Dimension d = sp.size();
317 Color bg = sp.getBackground();
318 Color fg = sp.getForeground();
319 Point p = sp.getScrollPosition();
320 Component c = getScrollChild();
321 Dimension cd;
322 if (c != null) {
323 cd = c.size();
324 } else {
325 cd = new Dimension(0, 0);
326 }
327 int sbDisplay = sp.getScrollbarDisplayPolicy();
328 int vvis, hvis, vmin, hmin, vmax, hmax, vval, hval;
329
330 switch (sbDisplay) {
331 case ScrollPane.SCROLLBARS_NEVER:
332 hsbSpace = vsbSpace = 0;
333 break;
334 case ScrollPane.SCROLLBARS_ALWAYS:
335 hsbSpace = vsbSpace = SCROLLBAR;
336 break;
337 case ScrollPane.SCROLLBARS_AS_NEEDED:
338 hsbSpace = (cd.width <= (d.width - 2*MARGIN)? 0 : SCROLLBAR);
339 vsbSpace = (cd.height <= (d.height - 2*MARGIN)? 0 : SCROLLBAR);
340
341 if (hsbSpace == 0 && vsbSpace != 0) {
342 hsbSpace = (cd.width <= (d.width - SCROLLBAR - 2*MARGIN)? 0 : SCROLLBAR);
343 }
344 if (vsbSpace == 0 && hsbSpace != 0) {
345 vsbSpace = (cd.height <= (d.height - SCROLLBAR - 2*MARGIN)? 0 : SCROLLBAR);
346 }
347 }
348
349 vvis = hvis = vmin = hmin = vmax = hmax = vval = hval = 0;
350
351 if (vsbSpace > 0) {
352 vmin = 0;
353 vvis = d.height - (2*MARGIN) - hsbSpace;
354 vmax = Math.max(cd.height - vvis, 0);
355 vval = p.y;
356 }
357 if (hsbSpace > 0) {
358 hmin = 0;
359 hvis = d.width - (2*MARGIN) - vsbSpace;
360 hmax = Math.max(cd.width - hvis, 0);
361 hval = p.x;
362 }
363
364 // need to be careful to add the margins back in here because
365 // we're drawing the margin border, after all!
366 int w = d.width - vsbSpace;
367 int h = d.height - hsbSpace;
368
369 g.setColor(bg);
370 g.fillRect(0, 0, d.width, d.height);
371
372 if (hsbSpace > 0) {
373 int sbw = d.width - vsbSpace;
374 g.fillRect(1, d.height - SCROLLBAR - 3, sbw - 1, SCROLLBAR - 3);
375 Graphics ng = g.create();
376 try {
377 ng.translate(0, d.height - (SCROLLBAR - 2));
378 drawScrollbar(ng, bg, SCROLLBAR - 2, sbw,
379 hmin, hmax, hval, hvis, true);
380 } finally {
381 ng.dispose();
382 }
383 }
384 if (vsbSpace > 0) {
385 int sbh = d.height - hsbSpace;
386 g.fillRect(d.width - SCROLLBAR - 3, 1, SCROLLBAR - 3, sbh - 1);
387 Graphics ng = g.create();
388 try {
389 ng.translate(d.width - (SCROLLBAR - 2), 0);
390 drawScrollbar(ng, bg, SCROLLBAR - 2, sbh,
391 vmin, vmax, vval, vvis, false);
392 } finally {
393 ng.dispose();
394 }
395 }
396
397 draw3DRect(g, bg, 0, 0, w - 1, h - 1, false);
398
399 target.print(g);
400 sp.printComponents(g);
401 }
402
403 /**
404 * @see ContainerPeer#restack
405 */
406 public void restack() {
407 // Since ScrollPane can only have one child its restacking does nothing.
408 // Also, it is dangerous, since SP child is actually not a child of SP widget
409 // but the child of SP content widget.
410 }
411}