blob: 2b310efdebc5349e623e8f7a59336a66b59231db [file] [log] [blame]
/*
* Copyright 1996-2003 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.awt.motif;
import java.awt.*;
import java.awt.event.AdjustmentEvent;
import java.awt.peer.ScrollPanePeer;
import java.util.logging.*;
import sun.awt.PeerEvent;
class MScrollPanePeer extends MPanelPeer implements ScrollPanePeer {
private static final Logger log = Logger.getLogger("sun.awt.motif.MScrollPanePeer");
final static int UNIT_INCREMENT = 0;
final static int BLOCK_INCREMENT = 1;
boolean ignore;
native void create(MComponentPeer parent);
static {
initIDs();
}
/**
* Initialize JNI field and method IDs
*/
private static native void initIDs();
MScrollPanePeer(Component target) {
init(target);
scrollPaneInit();
}
MScrollPanePeer(Component target, Object arg) {
init(target, arg);
scrollPaneInit();
}
void scrollPaneInit() {
ignore = false;
ScrollPane sp = (ScrollPane)target;
Adjustable vadj, hadj;
if ((vadj = sp.getVAdjustable()) != null) {
pSetIncrement(Adjustable.VERTICAL, UNIT_INCREMENT, vadj.getUnitIncrement());
}
if ((hadj = sp.getHAdjustable()) != null) {
pSetIncrement(Adjustable.HORIZONTAL, UNIT_INCREMENT, hadj.getUnitIncrement());
}
super.pSetScrollbarBackground(sp.getBackground());
}
public void setScrollChild(MComponentPeer child) {
pSetScrollChild(child);
}
public void setBackground(Color c) {
super.setBackground(c);
pSetScrollbarBackground(c);
}
public void setForeground(Color c) {
super.setForeground(c);
pSetInnerForeground(c);
}
native void pSetScrollChild(MComponentPeer child);
native void pSetIncrement(int orient, int type, int incr);
native int pGetScrollbarSpace(int orient);
native int pGetBlockIncrement(int orient);
native Insets pInsets(int w, int h, int childw, int childh);
native int pGetShadow();
public int getHScrollbarHeight() {
ScrollPane sp = (ScrollPane)target;
if (sp.getScrollbarDisplayPolicy() == ScrollPane.SCROLLBARS_NEVER) {
return 0;
} else {
return pGetScrollbarSpace(Adjustable.HORIZONTAL);
}
}
public int getVScrollbarWidth() {
ScrollPane sp = (ScrollPane)target;
if (sp.getScrollbarDisplayPolicy() == ScrollPane.SCROLLBARS_NEVER) {
return 0;
} else {
return pGetScrollbarSpace(Adjustable.VERTICAL);
}
}
public Insets insets() {
ScrollPane sp = (ScrollPane)target;
Dimension d = sp.size();
Dimension cd;
Component c = getScrollChild();
if (c != null) {
cd = c.size();
} else {
cd = new Dimension(0, 0);
}
return pInsets(d.width, d.height, cd.width, cd.height);
}
public void setUnitIncrement(Adjustable adj, int u) {
ScrollPane sp = (ScrollPane)target;
if (sp.getScrollbarDisplayPolicy() != ScrollPane.SCROLLBARS_NEVER) {
pSetIncrement(adj.getOrientation(), UNIT_INCREMENT, u);
}
}
public void setValue(Adjustable adj, int v) {
if (! ignore) {
Point p;
Component c = getScrollChild();
if (c == null) {
return;
}
p = c.getLocation();
switch(adj.getOrientation()) {
case Adjustable.VERTICAL:
setScrollPosition(-(p.x), v);
break;
case Adjustable.HORIZONTAL:
setScrollPosition(v, -(p.y));
break;
}
}
}
public native void setScrollPosition(int x, int y);
public void childResized(int w, int h) {
// REMIND AIM: May need to revisit this...
if (((ScrollPane)target).getScrollbarDisplayPolicy() != ScrollPane.SCROLLBARS_NEVER) {
ScrollPane sp = (ScrollPane)target;
Adjustable vAdj = sp.getVAdjustable();
Adjustable hAdj = sp.getHAdjustable();
pSetIncrement(Scrollbar.VERTICAL, UNIT_INCREMENT, vAdj.getUnitIncrement());
pSetIncrement(Scrollbar.HORIZONTAL, UNIT_INCREMENT, hAdj.getUnitIncrement());
pSetIncrement(Scrollbar.VERTICAL, BLOCK_INCREMENT, vAdj.getBlockIncrement());
pSetIncrement(Scrollbar.HORIZONTAL, BLOCK_INCREMENT, hAdj.getBlockIncrement());
}
}
// NOTE: This method may be called by privileged threads.
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
private void postScrollEvent(int orient, int type,
int pos, boolean isAdjusting)
{
Runnable adjustor = new Adjustor(orient, type, pos, isAdjusting);
MToolkit.executeOnEventHandlerThread(new ScrollEvent(target, adjustor));
}
/**
* This is used to change the adjustable on dispatch thread to
* represent a change made in the native scrollbar. Since the
* change was reflected immediately at the native level,
* notification from the adjustable is temporarily ignored.
*/
class ScrollEvent extends PeerEvent {
ScrollEvent(Object source, Runnable runnable) {
super(source, runnable, 0L);
}
public PeerEvent coalesceEvents(PeerEvent newEvent) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "ScrollEvent coalesced " + newEvent);
}
if (newEvent instanceof ScrollEvent) {
return newEvent;
}
return null;
}
}
native void setTypedValue(ScrollPaneAdjustable adjustable, int value, int type);
/**
* Runnable for the ScrollEvent that performs the adjustment.
*/
class Adjustor implements Runnable {
int orient; // selects scrollbar
int type; // adjustment type
int pos; // new position (only used for absolute)
boolean isAdjusting; // isAdjusting status
Adjustor(int orient, int type, int pos, boolean isAdjusting) {
this.orient = orient;
this.type = type;
this.pos = pos;
this.isAdjusting = isAdjusting;
}
public void run() {
ScrollPane sp = (ScrollPane)MScrollPanePeer.this.target;
ScrollPaneAdjustable adj = null;
// ScrollPaneAdjustable made public in 1.4, but
// get[HV]Adjustable can't be declared to return
// ScrollPaneAdjustable because it would break backward
// compatibility -- hence the cast
if (orient == Adjustable.VERTICAL) {
adj = (ScrollPaneAdjustable)sp.getVAdjustable();
} else if (orient == Adjustable.HORIZONTAL) {
adj = (ScrollPaneAdjustable)sp.getHAdjustable();
} else {
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "Assertion failed: unknown orient");
}
}
if (adj == null) {
return;
}
int newpos = adj.getValue();
switch (type) {
case AdjustmentEvent.UNIT_DECREMENT:
newpos -= adj.getUnitIncrement();
break;
case AdjustmentEvent.UNIT_INCREMENT:
newpos += adj.getUnitIncrement();
break;
case AdjustmentEvent.BLOCK_DECREMENT:
newpos -= adj.getBlockIncrement();
break;
case AdjustmentEvent.BLOCK_INCREMENT:
newpos += adj.getBlockIncrement();
break;
case AdjustmentEvent.TRACK:
newpos = this.pos;
break;
default:
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "Assertion failed: unknown type");
}
return;
}
// keep scroll position in acceptable range
newpos = Math.max(adj.getMinimum(), newpos);
newpos = Math.min(adj.getMaximum(), newpos);
// set value; this will synchronously fire an AdjustmentEvent
try {
MScrollPanePeer.this.ignore = true;
adj.setValueIsAdjusting(isAdjusting);
// Fix for 4075484 - consider type information when creating AdjustmentEvent
// We can't just call adj.setValue() because it creates AdjustmentEvent with type=TRACK
// Instead, we call private method setTypedValue of ScrollPaneAdjustable.
// Because ScrollPaneAdjustable is in another package we should call it through native code.
setTypedValue(adj, newpos, type);
} finally {
MScrollPanePeer.this.ignore = false;
}
}
} // class Adjustor
private Component getScrollChild() {
ScrollPane sp = (ScrollPane)target;
Component child = null;
try {
child = sp.getComponent(0);
} catch (ArrayIndexOutOfBoundsException e) {
// do nothing. in this case we return null
}
return child;
}
final static int MARGIN = 1;
final static int SCROLLBAR = 16;
int hsbSpace;
int vsbSpace;
int vval;
int hval;
int vmax;
int hmax;
/*
* Print the native component by rendering the Motif look ourselves.
* ToDo(aim): needs to query native motif for more accurate size and
* color information.
*/
public void print(Graphics g) {
ScrollPane sp = (ScrollPane)target;
Dimension d = sp.size();
Color bg = sp.getBackground();
Color fg = sp.getForeground();
Point p = sp.getScrollPosition();
Component c = getScrollChild();
Dimension cd;
if (c != null) {
cd = c.size();
} else {
cd = new Dimension(0, 0);
}
int sbDisplay = sp.getScrollbarDisplayPolicy();
int vvis, hvis, vmin, hmin, vmax, hmax, vval, hval;
switch (sbDisplay) {
case ScrollPane.SCROLLBARS_NEVER:
hsbSpace = vsbSpace = 0;
break;
case ScrollPane.SCROLLBARS_ALWAYS:
hsbSpace = vsbSpace = SCROLLBAR;
break;
case ScrollPane.SCROLLBARS_AS_NEEDED:
hsbSpace = (cd.width <= (d.width - 2*MARGIN)? 0 : SCROLLBAR);
vsbSpace = (cd.height <= (d.height - 2*MARGIN)? 0 : SCROLLBAR);
if (hsbSpace == 0 && vsbSpace != 0) {
hsbSpace = (cd.width <= (d.width - SCROLLBAR - 2*MARGIN)? 0 : SCROLLBAR);
}
if (vsbSpace == 0 && hsbSpace != 0) {
vsbSpace = (cd.height <= (d.height - SCROLLBAR - 2*MARGIN)? 0 : SCROLLBAR);
}
}
vvis = hvis = vmin = hmin = vmax = hmax = vval = hval = 0;
if (vsbSpace > 0) {
vmin = 0;
vvis = d.height - (2*MARGIN) - hsbSpace;
vmax = Math.max(cd.height - vvis, 0);
vval = p.y;
}
if (hsbSpace > 0) {
hmin = 0;
hvis = d.width - (2*MARGIN) - vsbSpace;
hmax = Math.max(cd.width - hvis, 0);
hval = p.x;
}
// need to be careful to add the margins back in here because
// we're drawing the margin border, after all!
int w = d.width - vsbSpace;
int h = d.height - hsbSpace;
g.setColor(bg);
g.fillRect(0, 0, d.width, d.height);
if (hsbSpace > 0) {
int sbw = d.width - vsbSpace;
g.fillRect(1, d.height - SCROLLBAR - 3, sbw - 1, SCROLLBAR - 3);
Graphics ng = g.create();
try {
ng.translate(0, d.height - (SCROLLBAR - 2));
drawScrollbar(ng, bg, SCROLLBAR - 2, sbw,
hmin, hmax, hval, hvis, true);
} finally {
ng.dispose();
}
}
if (vsbSpace > 0) {
int sbh = d.height - hsbSpace;
g.fillRect(d.width - SCROLLBAR - 3, 1, SCROLLBAR - 3, sbh - 1);
Graphics ng = g.create();
try {
ng.translate(d.width - (SCROLLBAR - 2), 0);
drawScrollbar(ng, bg, SCROLLBAR - 2, sbh,
vmin, vmax, vval, vvis, false);
} finally {
ng.dispose();
}
}
draw3DRect(g, bg, 0, 0, w - 1, h - 1, false);
target.print(g);
sp.printComponents(g);
}
/**
* @see ContainerPeer#restack
*/
public void restack() {
// Since ScrollPane can only have one child its restacking does nothing.
// Also, it is dangerous, since SP child is actually not a child of SP widget
// but the child of SP content widget.
}
}