Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/awt/X11/XCheckboxPeer.java
32288 views
/*1* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt.X11;2627import java.awt.*;28import java.awt.peer.*;29import java.awt.event.*;30import java.awt.image.BufferedImage;31import javax.swing.plaf.basic.BasicGraphicsUtils;32import java.awt.geom.AffineTransform;33import java.util.Objects;3435import sun.util.logging.PlatformLogger;3637class XCheckboxPeer extends XComponentPeer implements CheckboxPeer {3839private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XCheckboxPeer");4041private static final Insets focusInsets = new Insets(0,0,0,0);42private static final Insets borderInsets = new Insets(2,2,2,2);43private static final int checkBoxInsetFromText = 2;4445//The check mark is less common than a plain "depressed" button,46//so don't use the checkmark.47// The checkmark shape:48private static final double MASTER_SIZE = 128.0;49private static final Polygon MASTER_CHECKMARK = new Polygon(50new int[] {1, 25,56,124,124,85, 64}, // X-coords51new int[] {59,35,67, 0, 12,66,123}, // Y-coords527);5354private Shape myCheckMark;5556private Color focusColor = SystemColor.windowText;5758private boolean pressed;59private boolean armed;60private boolean selected;6162private Rectangle textRect;63private Rectangle focusRect;64private int checkBoxSize;65private int cbX;66private int cbY;6768String label;69CheckboxGroup checkBoxGroup;7071XCheckboxPeer(Checkbox target) {72super(target);73pressed = false;74armed = false;75selected = target.getState();76label = target.getLabel();77if ( label == null ) {78label = "";79}80checkBoxGroup = target.getCheckboxGroup();81updateMotifColors(getPeerBackground());82}8384public void preInit(XCreateWindowParams params) {85// Put this here so it is executed before layout() is called from86// setFont() in XComponent.postInit()87textRect = new Rectangle();88focusRect = new Rectangle();89super.preInit(params);90}9192public boolean isFocusable() { return true; }9394public void focusGained(FocusEvent e) {95// TODO: only need to paint the focus bit96super.focusGained(e);97repaint();98}99100public void focusLost(FocusEvent e) {101// TODO: only need to paint the focus bit?102super.focusLost(e);103repaint();104}105106107void handleJavaKeyEvent(KeyEvent e) {108int i = e.getID();109switch (i) {110case KeyEvent.KEY_PRESSED:111keyPressed(e);112break;113case KeyEvent.KEY_RELEASED:114keyReleased(e);115break;116case KeyEvent.KEY_TYPED:117keyTyped(e);118break;119}120}121122public void keyTyped(KeyEvent e) {}123124public void keyPressed(KeyEvent e) {125if (e.getKeyCode() == KeyEvent.VK_SPACE)126{127//pressed=true;128//armed=true;129//selected=!selected;130action(!selected);131//repaint(); // Gets the repaint from action()132}133134}135136public void keyReleased(KeyEvent e) {}137138@Override139public void setLabel(String label) {140if (label == null) {141label = "";142}143if (!label.equals(this.label)) {144this.label = label;145layout();146repaint();147}148}149150void handleJavaMouseEvent(MouseEvent e) {151super.handleJavaMouseEvent(e);152int i = e.getID();153switch (i) {154case MouseEvent.MOUSE_PRESSED:155mousePressed(e);156break;157case MouseEvent.MOUSE_RELEASED:158mouseReleased(e);159break;160case MouseEvent.MOUSE_ENTERED:161mouseEntered(e);162break;163case MouseEvent.MOUSE_EXITED:164mouseExited(e);165break;166case MouseEvent.MOUSE_CLICKED:167mouseClicked(e);168break;169}170}171172public void mousePressed(MouseEvent e) {173if (XToolkit.isLeftMouseButton(e)) {174Checkbox cb = (Checkbox) e.getSource();175176if (cb.contains(e.getX(), e.getY())) {177if (log.isLoggable(PlatformLogger.Level.FINER)) {178log.finer("mousePressed() on " + target.getName() + " : armed = " + armed + ", pressed = " + pressed179+ ", selected = " + selected + ", enabled = " + isEnabled());180}181if (!isEnabled()) {182// Disabled buttons ignore all input...183return;184}185if (!armed) {186armed = true;187}188pressed = true;189repaint();190}191}192}193194public void mouseReleased(MouseEvent e) {195if (log.isLoggable(PlatformLogger.Level.FINER)) {196log.finer("mouseReleased() on " + target.getName() + ": armed = " + armed + ", pressed = " + pressed197+ ", selected = " + selected + ", enabled = " + isEnabled());198}199boolean sendEvent = false;200if (XToolkit.isLeftMouseButton(e)) {201// TODO: Multiclick Threshold? - see BasicButtonListener.java202if (armed) {203//selected = !selected;204// send action event205//action(e.getWhen(),e.getModifiers());206sendEvent = true;207}208pressed = false;209armed = false;210if (sendEvent) {211action(!selected); // Also gets repaint in action()212}213else {214repaint();215}216}217}218219public void mouseEntered(MouseEvent e) {220if (log.isLoggable(PlatformLogger.Level.FINER)) {221log.finer("mouseEntered() on " + target.getName() + ": armed = " + armed + ", pressed = " + pressed222+ ", selected = " + selected + ", enabled = " + isEnabled());223}224if (pressed) {225armed = true;226repaint();227}228}229230public void mouseExited(MouseEvent e) {231if (log.isLoggable(PlatformLogger.Level.FINER)) {232log.finer("mouseExited() on " + target.getName() + ": armed = " + armed + ", pressed = " + pressed233+ ", selected = " + selected + ", enabled = " + isEnabled());234}235if (armed) {236armed = false;237repaint();238}239}240241public void mouseClicked(MouseEvent e) {}242243public Dimension getMinimumSize() {244/*245* Spacing (number of pixels between check mark and label text) is246* currently set to 0, but in case it ever changes we have to add247* it. 8 is a heuristic number. Indicator size depends on font248* height, so we don't need to include it in checkbox's height249* calculation.250*/251FontMetrics fm = getFontMetrics(getPeerFont());252253int wdth = fm.stringWidth(label) + getCheckboxSize(fm) + (2 * checkBoxInsetFromText) + 8;254int hght = Math.max(fm.getHeight() + 8, 15);255256return new Dimension(wdth, hght);257}258259private int getCheckboxSize(FontMetrics fm) {260// the motif way of sizing is a bit inscutible, but this261// is a fair approximation262return (fm.getHeight() * 76 / 100) - 1;263}264265public void setBackground(Color c) {266updateMotifColors(c);267super.setBackground(c);268}269270/*271* Layout the checkbox/radio button and text label272*/273public void layout() {274Dimension size = getPeerSize();275Font f = getPeerFont();276FontMetrics fm = getFontMetrics(f);277String text = label;278279checkBoxSize = getCheckboxSize(fm);280281// Note - Motif appears to use an left inset that is slightly282// scaled to the checkbox/font size.283cbX = borderInsets.left + checkBoxInsetFromText;284cbY = size.height / 2 - checkBoxSize / 2;285int minTextX = borderInsets.left + 2 * checkBoxInsetFromText + checkBoxSize;286// FIXME: will need to account for alignment?287// FIXME: call layout() on alignment changes288//textRect.width = fm.stringWidth(text);289textRect.width = fm.stringWidth(text == null ? "" : text);290textRect.height = fm.getHeight();291292textRect.x = Math.max(minTextX, size.width / 2 - textRect.width / 2);293textRect.y = (size.height - textRect.height) / 2;294295focusRect.x = focusInsets.left;296focusRect.y = focusInsets.top;297focusRect.width = size.width-(focusInsets.left+focusInsets.right)-1;298focusRect.height = size.height-(focusInsets.top+focusInsets.bottom)-1;299300double fsize = (double) checkBoxSize;301myCheckMark = AffineTransform.getScaleInstance(fsize / MASTER_SIZE, fsize / MASTER_SIZE).createTransformedShape(MASTER_CHECKMARK);302}303@Override304void paintPeer(final Graphics g) {305//layout();306Dimension size = getPeerSize();307Font f = getPeerFont();308flush();309g.setColor(getPeerBackground()); // erase the existing button310g.fillRect(0,0, size.width, size.height);311if (label != null) {312g.setFont(f);313paintText(g, textRect, label);314}315316if (hasFocus()) {317paintFocus(g,318focusRect.x,319focusRect.y,320focusRect.width,321focusRect.height);322}323// Paint the checkbox or radio button324if (checkBoxGroup == null) {325paintCheckbox(g, cbX, cbY, checkBoxSize, checkBoxSize);326}327else {328paintRadioButton(g, cbX, cbY, checkBoxSize, checkBoxSize);329}330flush();331}332333// You'll note this looks suspiciously like paintBorder334public void paintCheckbox(Graphics g,335int x, int y, int w, int h) {336boolean useBufferedImage = false;337BufferedImage buffer = null;338Graphics2D g2 = null;339int rx = x;340int ry = y;341if (!(g instanceof Graphics2D)) {342// Fix for 5045936. While printing, g is an instance of343// sun.print.ProxyPrintGraphics which extends Graphics. So344// we use a separate buffered image and its graphics is345// always Graphics2D instance346buffer = graphicsConfig.createCompatibleImage(w, h);347g2 = buffer.createGraphics();348useBufferedImage = true;349rx = 0;350ry = 0;351}352else {353g2 = (Graphics2D)g;354}355try {356drawMotif3DRect(g2, rx, ry, w-1, h-1, armed | selected);357358// then paint the check359g2.setColor((armed | selected) ? selectColor : getPeerBackground());360g2.fillRect(rx+1, ry+1, w-2, h-2);361362if (armed | selected) {363//Paint the check364365// FIXME: is this the right color?366g2.setColor(getPeerForeground());367368AffineTransform af = g2.getTransform();369g2.setTransform(AffineTransform.getTranslateInstance(rx,ry));370g2.fill(myCheckMark);371g2.setTransform(af);372}373} finally {374if (useBufferedImage) {375g2.dispose();376}377}378if (useBufferedImage) {379g.drawImage(buffer, x, y, null);380}381}382383public void paintRadioButton(Graphics g, int x, int y, int w, int h) {384385g.setColor((armed | selected) ? darkShadow : lightShadow);386g.drawArc(x-1, y-1, w+2, h+2, 45, 180);387388g.setColor((armed | selected) ? lightShadow : darkShadow);389g.drawArc(x-1, y-1, w+2, h+2, 45, -180);390391if (armed | selected) {392g.setColor(selectColor);393g.fillArc(x+1, y+1, w-1, h-1, 0, 360);394}395}396397protected void paintText(Graphics g, Rectangle textRect, String text) {398FontMetrics fm = g.getFontMetrics();399400int mnemonicIndex = -1;401402if(isEnabled()) {403/*** paint the text normally */404g.setColor(getPeerForeground());405BasicGraphicsUtils.drawStringUnderlineCharAt(g,text,mnemonicIndex , textRect.x , textRect.y + fm.getAscent() );406}407else {408/*** paint the text disabled ***/409g.setColor(getPeerBackground().brighter());410411BasicGraphicsUtils.drawStringUnderlineCharAt(g,text, mnemonicIndex,412textRect.x, textRect.y + fm.getAscent());413g.setColor(getPeerBackground().darker());414BasicGraphicsUtils.drawStringUnderlineCharAt(g,text, mnemonicIndex,415textRect.x - 1, textRect.y + fm.getAscent() - 1);416}417}418419// TODO: copied directly from XButtonPeer. Should probabaly be shared420protected void paintFocus(Graphics g, int x, int y, int w, int h) {421g.setColor(focusColor);422g.drawRect(x,y,w,h);423}424425@Override426public void setState(boolean state) {427if (selected != state) {428selected = state;429repaint();430}431}432433@Override434public void setCheckboxGroup(final CheckboxGroup g) {435if (!Objects.equals(g, checkBoxGroup)) {436// If changed from grouped/ungrouped, need to repaint()437checkBoxGroup = g;438repaint();439}440}441442// NOTE: This method is called by privileged threads.443// DO NOT INVOKE CLIENT CODE ON THIS THREAD!444// From MCheckboxPeer445void action(boolean state) {446final Checkbox cb = (Checkbox)target;447final boolean newState = state;448XToolkit.executeOnEventHandlerThread(cb, new Runnable() {449public void run() {450CheckboxGroup cbg = checkBoxGroup;451// Bugid 4039594. If this is the current Checkbox in452// a CheckboxGroup, then return to prevent deselection.453// Otherwise, it's logical state will be turned off,454// but it will appear on.455if ((cbg != null) && (cbg.getSelectedCheckbox() == cb) &&456cb.getState()) {457//inUpCall = false;458cb.setState(true);459return;460}461// All clear - set the new state462cb.setState(newState);463notifyStateChanged(newState);464}465});466}467468void notifyStateChanged(boolean state) {469Checkbox cb = (Checkbox) target;470ItemEvent e = new ItemEvent(cb,471ItemEvent.ITEM_STATE_CHANGED,472cb.getLabel(),473state ? ItemEvent.SELECTED : ItemEvent.DESELECTED);474postEvent(e);475}476}477478479