1 /* |
|
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 |
|
26 package sun.awt.motif; |
|
27 |
|
28 import java.awt.*; |
|
29 import java.awt.event.AdjustmentEvent; |
|
30 import java.awt.peer.ScrollPanePeer; |
|
31 |
|
32 import java.util.logging.*; |
|
33 |
|
34 import sun.awt.PeerEvent; |
|
35 |
|
36 class 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 } |
|