/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 1998, 1999 Wabasoft <www.wabasoft.com> * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine 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. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.ui; import totalcross.ui.event.*; import totalcross.ui.gfx.*; import totalcross.ui.image.*; import totalcross.util.*; import totalcross.res.*; import totalcross.sys.*; /** * Radio is a radio control. * Radios can be grouped together using a RadioGroupController. * <p> * Here is an example showing a radio being used: * * <pre> * public class MyProgram extends MainWindow * { * RadioGroupController rgGender; * * public void initUI() * { * rgGender = new RadioGroupController(); * add(new Radio("Male", rgGender), LEFT, AFTER); * add(new Radio("Female", rgGender), AFTER+2, SAME); * rgGender.setSelectedIndex(radioMale); // activate the specified one. * } * * public void onEvent(Event event) * { * if (event.type == ControlEvent.PRESSED && (event.target instanceof Radio) && ((Radio)event.target).getRadioGroup() == rgGender) * { * boolean male = rgGender.getSelectedIndex() == 0; * ... handle radio Male being pressed * </pre> * @see totalcross.ui.RadioGroupController */ public class Radio extends Control { private String text; private boolean checked; RadioGroupController radioGroup; private int colors[] = new int[4]; private int cColor,bColor; private int textW; /** Sets the text color of the check. Defaults to the foreground color. * @since TotalCross 2.0. */ public int textColor = -1; /** Set to the color of the check, if you want to make it different of the foreground color. * @since TotalCross 1.3 */ public int checkColor = -1; /** Set to true to left justify this control if the width is above the preferred one. */ public boolean leftJustify; private Image imgSel, imgUnsel; private static Hashtable imgs; // cache the images private static Image vistaSelected,vistaUnselected; /** Creates a radio control displaying the given text. */ public Radio(String text) { this.text = text; textW = fm.stringWidth(text); } /** Creates a radio control with the given text attached to the given RadioGroupController */ public Radio(String text, RadioGroupController radioGroup) { this(text); this.radioGroup = radioGroup; radioGroup.add(this); } /** Returns the RadioGroupController that this radio belongs to, or null if none. */ public RadioGroupController getRadioGroup() { return radioGroup; } /** "Merge" the colors between the original grayscale image and the current foreground. */ private Image getImage(boolean isSelected) throws Exception { if (vistaSelected == null) { vistaSelected = new Image("totalcross/res/radioon_vista.png"); vistaUnselected = new Image("totalcross/res/radiooff_vista.png"); } String key = (isSelected?"*":"") + foreColor + "|" + backColor + "|" + fmH + (isEnabled()?"*":""); // guich@tc110a_110: added backColor. Image img; if (imgs == null) imgs = new Hashtable(4); else if ((img=(Image)imgs.get(key)) != null) return img; img = isSelected ? vistaSelected.getFrameInstance(0) : vistaUnselected.getFrameInstance(0); int h = height == 0 ? getPreferredHeight() : height; img = img.getSmoothScaledInstance(h,h); img.applyColor(foreColor); if (!isEnabled()) img = img.getFadedInstance(); imgs.put(key, img); return img; } /** Sets the text. */ public void setText(String text) { this.text = text; Window.needsPaint = true; onFontChanged(); } /** Gets the text displayed in the radio. */ public String getText() { return text; } /** Returns the checked state of the control. */ public boolean isChecked() { return checked; } /** Sets the checked state of the control. */ public void setChecked(boolean checked) { setChecked(checked,Settings.sendPressEventOnChange); } /** Sets the checked state of the control, and send the press event if desired. */ public void setChecked(boolean checked, boolean sendPress) { if (this.checked == checked) return; this.checked = checked; if (radioGroup != null) // guich@402_21: now the radiogroup has a property that indicates the index of the selected Radio. radioGroup.setSelectedItem(this,checked); Window.needsPaint = true; if (sendPress) postPressedEvent(); } /** returns the preffered width of this control. */ public int getPreferredWidth() { return textW+2+getPreferredHeight(); // use getPreferredHeight since it may be overloaded } /** returns the preffered height of this control. */ public int getPreferredHeight() { return fmH+Edit.prefH; } /** Called by the system to pass events to the radio control. */ public void onEvent(Event event) { if (event.target != this || !isEnabled()) return; switch (event.type) { case KeyEvent.ACTION_KEY_PRESS: // guich@550_15 checked = !checked; repaintNow(); if (radioGroup != null) radioGroup.setSelectedItem(this); postPressedEvent(); break; default: if (!isActionEvent(event)) break; PenEvent pe = (PenEvent)event; if (isInsideOrNear(pe.x,pe.y)) { Window.needsPaint = true; checked = !checked; if (radioGroup != null) radioGroup.setSelectedItem(this); postPressedEvent(); } break; } } private final static int coords1[] = { // dark grey top 4, 0, 7, 0, 2, 1, 3, 1, 8, 1, 9, 1, // dark grey left 0, 4, 0, 7, 1, 2, 1, 3, 1, 8, 1, 9, // black top 4, 1, 7, 1, 2, 2, 3, 2, 8, 2, 9, 2, // black left 1, 4, 1, 7, 2, 3, 2, 3, 2, 8, 2, 8, // light grey bottom 2, 9, 3, 9, 8, 9, 9, 9, 4, 10, 7, 10, // light grey right 9, 3, 9, 3, 9, 8, 9, 8, 10, 4, 10, 7, // bottom white 2, 10, 3, 10, 8, 10, 9, 10, 4, 11, 7, 11, // right white 10, 2, 10, 3, 10, 8, 10, 9, 11, 4, 11, 7 }; private final static int coords2[] = { // dark grey top 5, 0, 9, 0, 3, 1, 4, 1, 10, 1, 11, 1, 12, 2, 12, 2, // dark grey left 0, 5, 0, 9, 1, 3, 1, 4, 1, 10, 1, 11, 2, 2, 2, 2, // black top 5, 1, 9, 1, 3, 2, 4, 2, 10, 2, 11, 2, 12, 3, 12, 3, // black left 1, 5, 1, 9, 2, 3, 2, 4, 2, 10, 2, 11, 2, 10, 2, 11, // light grey bottom 3, 12, 4, 12, 5, 13, 9, 13, 10, 12, 11, 12, 10, 12, 11, 12, // light grey right 12, 10, 12, 11, 13, 5, 13, 9, 12, 4, 12, 4, 12, 4, 12, 4, // bottom white 2, 12, 2, 12, 3, 13, 4, 13, 5, 14, 9, 14, 10, 13, 11, 13, // right white 12, 12, 12, 12, 13, 10, 13, 11, 14, 5, 14, 9, 13, 3, 13, 4 }; protected void onColorsChanged(boolean colorsChanged) { cColor = getForeColor(); bColor = UIColors.sameColors ? backColor : Color.brighter(getBackColor()); // guich@572_15 colors[0] = colors[2] = Color.brighter(cColor); colors[3] = bColor; colors[1] = cColor; if (uiVista) try { imgSel = getImage(true); imgUnsel = getImage(false); } catch (Exception e) { imgSel = imgUnsel = null; } } protected void onFontChanged() { textW = fm.stringWidth(this.text); onColorsChanged(false); } /** Called by the system to draw the radio control. */ public void onPaint(Graphics g) { int xx,yy; // guich@200b4_126: erase the back always if (!transparentBackground) { g.backColor = backColor; g.fillRect(0,0,width,height); } boolean big = fmH >= 20; if (uiAndroid) try { int hh = Math.min(width - (textW+2), height); Image ret = isEnabled() ? Resources.radioBkg.getNormalInstance(hh,hh,foreColor) : Resources.radioBkg.getDisabledInstance(hh, hh, foreColor); yy = (height - hh) / 2; g.drawImage(ret,0,yy); if (checked) g.drawImage(Resources.radioSel.getPressedInstance(hh,hh,backColor,checkColor != -1 ? checkColor : foreColor,isEnabled()),0,yy); } catch (ImageException ie) {} else if (uiVista && imgSel != null) g.drawImage(checked ? imgSel : imgUnsel, 0, (height-imgSel.getHeight())/2); // guich@tc122_50: /2 else { int i=0,k,j=0; int kk = big?8:6; // number of elements per arc xx = 0; // guich@tc100: can't be -1, now we have real clipping that will cut out if draw out of bounds yy = (this.height - (big?15:12)) >> 1; // guich@tc114_69: always 14 g.translate(xx,yy); int []coords = big?coords2:coords1; // white center g.backColor = bColor; if (big) g.fillCircle(7,7,7); else g.fillCircle(5,6,4); if (uiVista && isEnabled()) // guich@573_6: shade diagonally { g.foreColor = Color.darker(bColor,UIColors.vistaFadeStep*2); for (k=9,j=6; j >= 0; j--) // bigger k -> darker { g.foreColor = Color.darker(g.foreColor,UIColors.vistaFadeStep); g.drawLine(2,4+j,4+j,2); } } // 3d borders if (uiFlat) { g.foreColor = colors[j]; if (big) g.drawCircle(7,7,7); else g.drawCircle(5,6,4); } else for (j = 0; j < 4; j++) if (colors[j] != -1) { g.foreColor = colors[j]; for (k = kk; k > 0; k--) g.drawLine(coords[i++], coords[i++], coords[i++], coords[i++]); } else i += kk << 2; // checked g.foreColor = cColor; if (checked) { g.backColor = cColor; if (uiVista) // guich@573_6 { if (big) { g.backColor = Color.darker(cColor,UIColors.vistaFadeStep*9); g.fillCircle(7,7,4); g.backColor = cColor; g.fillCircle(7,7,2); } else { g.backColor = cColor; g.fillRect(5, 4, 2, 4); g.foreColor = Color.darker(cColor,UIColors.vistaFadeStep*9); g.drawLine(4, 5, 4, 6); g.drawLine(7, 5, 7, 6); } } else if (big) g.fillCircle(7,7,3); else if (uiFlat) g.fillCircle(5,6,2); else { g.fillRect(5, 4, 2, 4); g.drawLine(4, 5, 4, 6); g.drawLine(7, 5, 7, 6); } } g.translate(-xx,-yy); } // draw label yy = (this.height - fmH) >> 1; xx = leftJustify ? (uiFlat ? fmH/2+4 : getPreferredHeight()+1) : (this.width - textW); // guich@300_69 - guich@tc122_42: use preferred height g.foreColor = textColor != -1 ? (isEnabled() ? textColor : Color.interpolate(textColor,backColor)) : cColor; g.drawText(text, xx, yy, textShadowColor != -1, textShadowColor); } /** Clears this control, checking it if clearValueInt is 1. */ public void clear() // guich@572_19 { setChecked(clearValueInt == 1); } }