Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores
* CA 94065 USA or visit www.oracle.com if you need additional information or
* have any questions.
*/
package com.codename1.ui.html;
import com.codename1.ui.Component;
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.Font;
import com.codename1.ui.Label;
import com.codename1.ui.TextArea;
import com.codename1.ui.geom.Dimension;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.FlowLayout;
import com.codename1.ui.plaf.Border;
import com.codename1.ui.plaf.Style;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
/**
* This class is responsible for applying CSS directives to an HTMLComponent
*
* @author Ofir Leitner
*/
class CSSEngine {
private static int DEFAULT_3D_BORDER_COLOR = 0x9a9a9a; // default color for outset/inset/ridge/groove
//int count; //for debugging
private static CSSEngine instance; // The instance of this singleton class
private static Hashtable specialKeys; // A hashtable containing all recognized special key strings and their keycodes
private Hashtable matchingFonts = new Hashtable(); // A hashtable used as a cache for quick find of matching fonts
/**
* A list of the attributes that can contain a URL, in order to scan them and update relative URLs to an absolute one
*/
private static final int[] URL_ATTRIBUTES = {CSSElement.CSS_BACKGROUND_IMAGE,CSSElement.CSS_LIST_STYLE_IMAGE};
/**
* Denotes that the selector should be applied to the unselected style of the component
*/
final static int STYLE_UNSELECTED=1;
/**
* Denotes that the selector should be applied to the selected style of the component
*/
final static int STYLE_SELECTED=2;
/**
* Denotes that the selector should be applied to the pressed style of the component
*/
final static int STYLE_PRESSED=4;
/**
* The indentation applied on a list when its 'list-style-position' is 'inside' vs. 'outside'
*/
private final static int INDENT_LIST_STYLE_POSITION = 15;
static final String CLIENT_PROPERTY_CSS_CONTENT = "cssContent";
// The possible values of the 'text-transform' attribute
private static final int TEXT_TRANSFORM_NONE = 0;
private static final int TEXT_TRANSFORM_UPPERCASE = 1;
private static final int TEXT_TRANSFORM_LOWERCASE = 2;
private static final int TEXT_TRANSFORM_CAPITALIZE = 3;
// The possible values of the 'text-decoration' attribute
private static final int TEXT_DECOR_UNDERLINE = 0;
private static final int TEXT_DECOR_LINETHROUGH = 1;
private static final int TEXT_DECOR_NONE = 2;
private static final int TEXT_DECOR_OVERLINE = 3;
// The possible values of the '-wap-input-required' attribute
private static final int INPUT_REQUIRED_TRUE = 0;
private static final int INPUT_REQUIRED_FALSE = 1;
// The possible values of the 'background-attachment' attribute
private static final int BG_ATTACHMENT_FIXED = 0;
private static final int BG_ATTACHMENT_SCROLL = 1;
// The possible values of the 'white-space' attribute
private static final int WHITE_SPACE_NORMAL = 0;
private static final int WHITE_SPACE_PRE = 1;
private static final int WHITE_SPACE_NOWRAP = 2;
// The possible values of the 'display' attribute
private static final int DISPLAY_INLINE=0;
private static final int DISPLAY_BLOCK=1;
private static final int DISPLAY_LIST_ITEM=2;
private static final int DISPLAY_NONE=3;
private static final int DISPLAY_MARQUEE=4;
// The possible values of the 'font-variant' attribute
private static final int FONT_VARIANT_NORMAL=0;
private static final int FONT_VARIANT_SMALLCAPS=1;
// The possible values of the 'list-style-position' attribute
private static final int LIST_STYLE_POSITION_INSIDE = 0;
private static final int LIST_STYLE_POSITION_OUTSIDE = 1;
// The possible values of the 'border-style' attribute
private static final int BORDER_STYLE_NONE = 0;
private static final int BORDER_STYLE_SOLID = 1;
private static final int BORDER_STYLE_DOTTED = 2;
private static final int BORDER_STYLE_DASHED = 3;
private static final int BORDER_STYLE_DOUBLE = 4;
private static final int BORDER_STYLE_GROOVE = 5;
private static final int BORDER_STYLE_RIDGE = 6;
private static final int BORDER_STYLE_INSET = 7;
private static final int BORDER_STYLE_OUTSET = 8;
private static final int[][] BORDER_OUTLINE_PROPERTIES = {
{CSSElement.CSS_BORDER_TOP_WIDTH,CSSElement.CSS_BORDER_TOP_STYLE,CSSElement.CSS_BORDER_TOP_COLOR},
{CSSElement.CSS_OUTLINE_WIDTH,CSSElement.CSS_OUTLINE_STYLE,CSSElement.CSS_OUTLINE_COLOR}
};
private static final int BORDER = 0;
private static final int OUTLINE = 1;
private static final int WIDTH = 0;
private static final int STYLE = 1;
private static final int COLOR = 2;
// The possible values of the 'visibility' attribute
private static final int VISIBILITY_HIDDEN=0;
private static final int VISIBILITY_VISIBLE=1;
private static final int VISIBILITY_COLLAPSE=2; // collapse behaves the same as hidden in most browsers.
// The possible values of the 'border-collapse' attribute
private static final int BORDER_COLLAPSE_COLLAPSE = 0;
private static final int BORDER_COLLAPSE_SEPARATE = 1;
// The possible values of the 'empty-cells' attribute
private static final int EMPTY_CELLS_HIDE = 0;
private static final int EMPTY_CELLS_SHOW = 1;
// The possible values of the 'caption-side' attribute
private static final int CAPTION_SIDE_BOTTOM = 0;
private static final int CAPTION_SIDE_TOP = 1;
// The possible values of the 'direction' attribute
private static final int DIRECTION_RTL = 0;
private static final int DIRECTION_LTR = 1;
/**
* Returns the singleton instance of CSSEngine and creates it if necessary
*
* @return The singleton instance of CSSEngine
*/
static CSSEngine getInstance() {
if (instance==null) {
instance=new CSSEngine();
}
return instance;
}
/**
* Adds support for a special key to be used as an accesskey.
* The CSS property -wap-accesskey supports special keys, for example "phone-send" that may have different key codes per device.
* This method allows pairing between such keys to their respective key codes.
* Note that these keys are valid only for -wap-aceesskey in CSS files, and not for the XHTML accesskey attribute.
*
* @param specialKeyName The name of the special key as denoted in CSS files
* @param specialKeyCode The special key code
*/
static void addSpecialKey(String specialKeyName,int specialKeyCode) {
if (specialKeys==null) {
specialKeys=new Hashtable();
}
specialKeys.put(specialKeyName,new Integer(specialKeyCode));
}
/**
* Sorts the CSS directives by their specificity level
*
* @param css A css vector holding CSSElements, where each element holds CSS selectors as its children
* @return a flat vector containing CSS selectors, sorted by specificity
*/
private CSSElement[] sortSelectorsBySpecificity(CSSElement[] css) {
Vector sortedSelectors=new Vector();
for(int s=0;s=((CSSElement)sortedSelectors.elementAt(i)).getSelectorSpecificity())) {
i++;
}
sortedSelectors.insertElementAt(currentSelector, i);
}
}
css = new CSSElement[sortedSelectors.size()];
for(int i=0;i0) && !(element.getUi().firstElement() instanceof HTMLLink)) ||
((element.getUi().size()>0) && (!((HTMLLink)element.getUi().firstElement()).linkVisited) && ((currentSelector.getSelectorPseudoClass() & CSSElement.PC_LINK)!=0)) ||
((element.getUi().size()>0) && ((HTMLLink)element.getUi().firstElement()).linkVisited) && ((currentSelector.getSelectorPseudoClass() & CSSElement.PC_VISITED)!=0)) {
applyStyle(element, currentSelector,htmlC);
}
} else {
CSSElement child=currentSelector.getCSSChildAt(0);
if (child.siblingSelector) {
if (!HTMLComponent.PROCESS_HTML_MP1_ONLY) { // sibling selectors are not supported in HTML-MP1
nextSiblingSelectors.addElement(child);
}
} else {
nextNestedSelectors.addElement(child);
// Check if this is a Descendant selector (i.e. div b - which means match any b that is the descendant of div
// If so then we pass not only the child selector (i.e. the b) but also the "* b" to allow matching later decendants
if (child.descendantSelector) {
CSSElement elem=new CSSElement("*");
elem.addChild(new CSSElement(child));
nextNestedSelectors.addElement(elem);
}
}
}
}
}
/**
* Checks if the specified class is contained in the specified text
* This is used for elements that have several classes i.e. class="class1 class2"
* Note: A simple indexOf could not be used since we need to find whole words and not frgaments of words
*
* @param selectorClass The text
* @param elementClass The word to find in the text
* @return true if the word is found, false otherwise
*/
private boolean containsClass(String elementClass,String selectorClass) {
if ((elementClass==null) || (selectorClass==null)) {
return false;
}
// The spaces addition is to make sure we get a whole word and not a fragment of a word
elementClass=" "+elementClass+" ";
// Selector can require multiple classes, i.e. class.1class2 (which needs to match to "class 1 class2" and "class2 class1" and also "class1 otherclasses class2"
int dotIndex=selectorClass.indexOf('.');
while (dotIndex!=-1) {
String curWord=selectorClass.substring(0, dotIndex);
if (elementClass.indexOf(" "+curWord+" ")==-1) {
return false;
}
selectorClass=selectorClass.substring(dotIndex+1);
dotIndex=selectorClass.indexOf('.');
}
return (elementClass.indexOf(" "+selectorClass+" ")!=-1);
}
/**
* Applies the given style attributes to the HTML DOM entry
*
* @param element The element to apply the style to
* @param selector The selector containing the style directives
* @param htmlC The HTMLComponent
*/
private void applyStyle(HTMLElement element, CSSElement selector, HTMLComponent htmlC) {
if ((element.getUi() != null) && (element.getUi().size()>0)) {
if (!HTMLComponent.PROCESS_HTML_MP1_ONLY) {
String reset=selector.getAttributeById(CSSElement.CSS_COUNTER_RESET);
if (reset!=null) {
htmlC.incCounter(reset, true);
}
String inc=selector.getAttributeById(CSSElement.CSS_COUNTER_INCREMENT);
if (inc!=null) {
htmlC.incCounter(inc, false);
}
if ((selector.getSelectorPseudoClass() & (CSSElement.PC_BEFORE|CSSElement.PC_AFTER))!=0) {
handleContentProperty(element,selector,htmlC);
return;
}
}
for(int iter = 0 ; iter < element.getUi().size() ; iter++) {
Object o = element.getUi().elementAt(iter);
if(o != null && o instanceof Component) {
final Component cmp = (Component)o;
applyStyleToUIElement(cmp, selector,element,htmlC);
}
}
}
}
/**
* Returns a mask of the STYLE_* constants of which CodenameOne styles this selector should be applied to
*
* @param cmp The component in question
* @param selector The selector
* @return a mask of the STYLE_* constants of which CodenameOne styles this selector should be applied to
*/
private int getApplicableStyles(Component cmp,CSSElement selector) {
int result=0;
if (cmp instanceof HTMLLink) {
int pseudoClass=selector.getSelectorPseudoClass();
boolean done=false;
if ((pseudoClass & CSSElement.PC_FOCUS)!=0) { // Focused (i.e. CSS focus/hover)
result|=STYLE_SELECTED;
done=true;
}
if ((pseudoClass & CSSElement.PC_ACTIVE)!=0) { // active in CSS means pressed in CodenameOne
result|=STYLE_PRESSED;
done=true;
}
if (!done) {
result|=STYLE_SELECTED|STYLE_UNSELECTED;
}
} else {
result|=STYLE_SELECTED|STYLE_UNSELECTED;
}
return result;
}
/**
* Sets the specified color as the foreground color of the component and all its children
*
* @param cmp The component to work on
* @param color The color to set
* @param selector The selector with the color directive
*/
private void setColorRecursive(Component cmp,int color,CSSElement selector) {
int styles=getApplicableStyles(cmp, selector);
if ((styles & STYLE_UNSELECTED)!=0) {
cmp.getUnselectedStyle().setFgColor(color);
}
if ((styles & STYLE_SELECTED)!=0) {
cmp.getSelectedStyle().setFgColor(color);
}
if ((styles & STYLE_PRESSED)!=0) {
((HTMLLink)cmp).getPressedStyle().setFgColor(color);
}
if (cmp instanceof Container) {
Container cont=(Container)cmp;
for(int i=0;i='a') && (c<='z')) {
c-=32; // 'A' is ASCII 65, and 'a' is ASCII 97, difference: 32
}
capNextLetter=false;
}
newText+=c;
}
label.setText(newText);
break;
}
}
}
/**
* Sets the alignment of the component and all its children according to the given alignment
*
* @param cmp The component to set the alignment on
* @param align The alignment - one of left,center,right
*/
private void setTextAlignmentRecursive(Component cmp,int align) {
if (cmp instanceof Container) {
Container cont=(Container)cmp;
if (cont.getLayout() instanceof FlowLayout) {
cont.setLayout(new FlowLayout(align));
}
for(int i=0;i0)) {
// Note that we don't need to consider the "applicable" styles, as this is a container and will always return selected+unselected
cont.getComponentAt(0).getUnselectedStyle().setMargin(Component.LEFT, indent);
cont.getComponentAt(0).getSelectedStyle().setMargin(Component.LEFT, indent);
}
for(int i=0;i1)) { //If it's just one word or already no-wrapped, no need to process
String word="";
String newText="";
for(int c=0;cui.getPreferredW())) {
width=minWidth;
}
if ((maxWidth!=-1) && (maxWidthui.getPreferredH())) {
height=minHeight;
}
if ((maxHeight!=-1) && (maxHeight=0 && marginComp != null) { // Only positive or 0
if ((styles & STYLE_SELECTED)!=0) {
marginComp.getSelectedStyle().setMargin(i-CSSElement.CSS_MARGIN_TOP, marginPixels);
// If this is a link and the selector applies only to Selected, it means this is an 'a:focus'
// Since the marginComp is not focusable (as it is the container holding the link), HTMLLink takes care of focusing the
// parent when the link focuses
if ((ui instanceof HTMLLink) && (styles==STYLE_SELECTED)) {
((HTMLLink)ui).setParentChangesOnFocus();
}
}
if ((styles & STYLE_UNSELECTED)!=0) {
marginComp.getUnselectedStyle().setMargin(i-CSSElement.CSS_MARGIN_TOP, marginPixels);
}
// Since we don't apply the margin/padding on the component but rather on its parent
// There is no point in setting the PRESSED style since we don't have a pressed event from Button, nor do we have a pressedStyle for containers
// That's why we can't do the same trick as in selected style, and the benefit of this rather "edge" case (That is anyway not implemented in all browsers) seems rather small
// if ((styles & STYLE_PRESSED)!=0) {
// ((HTMLLink)ui).getPressedStyle().setMargin(i-CSSElement.CSS_MARGIN_TOP, marginPixels);
// }
}
}
Component padComp=ui;
if (ui instanceof Label) {
padComp=ui.getParent();
} else if ((element.getTagId()==HTMLElement.TAG_LI) && (ui.getParent().getLayout() instanceof BorderLayout)) {
padComp=ui.getParent();
}
for(int i=CSSElement.CSS_PADDING_TOP;i<=CSSElement.CSS_PADDING_RIGHT;i++) {
int padPixels=-1;
if ((i==CSSElement.CSS_PADDING_TOP) || (i==CSSElement.CSS_PADDING_BOTTOM)) {
padPixels=selector.getAttrLengthVal(i, ui, htmlC.getHeight());
} else {
padPixels=selector.getAttrLengthVal(i, ui, htmlC.getWidth());
}
if (padPixels>=0) { // Only positive or 0
if ((styles & STYLE_SELECTED)!=0) {
if(padComp != null){
padComp.getSelectedStyle().setPadding(i-CSSElement.CSS_PADDING_TOP, padPixels);
}
if ((ui instanceof HTMLLink) && (styles==STYLE_SELECTED)) { // See comment on margins
((HTMLLink)ui).setParentChangesOnFocus();
}
}
if ((styles & STYLE_UNSELECTED)!=0) {
if(padComp != null){
padComp.getUnselectedStyle().setPadding(i-CSSElement.CSS_PADDING_TOP, padPixels);
}
}
// See comment in margin on why PRESSED was dropped
// if ((styles & STYLE_PRESSED)!=0) {
// ((HTMLLink)padComp).getPressedStyle().setPadding(i-CSSElement.CSS_PADDING_TOP, padPixels);
// }
}
}
//
// Text
//
// Text Alignment
int align=selector.getAttrVal(CSSElement.CSS_TEXT_ALIGN);
if (align!=-1) {
switch(element.getTagId()) {
case HTMLElement.TAG_TD:
case HTMLElement.TAG_TH:
setTableCellAlignment(element, ui, align, true);
break;
case HTMLElement.TAG_TR:
setTableCellAlignmentTR(element, ui, align, true);
break;
case HTMLElement.TAG_TABLE:
setTableAlignment(ui, align, true);
break;
default:
setTextAlignmentRecursive(ui, align); // TODO - this sometimes may collide with the HTML align attribute. If the style of the same tag has alignment it overrides the align attribute, but if it is inherited, the align tag prevails
}
}
// Vertical align
int valign=selector.getAttrVal(CSSElement.CSS_VERTICAL_ALIGN);
if (valign!=-1) {
switch(element.getTagId()) {
case HTMLElement.TAG_TD:
case HTMLElement.TAG_TH:
setTableCellAlignment(element, ui, valign, false);
break;
case HTMLElement.TAG_TR:
setTableCellAlignmentTR(element, ui, valign, false);
break;
// case Element.TAG_TABLE: // vertical alignment denoted in the table tag doesn't affect it in most browsers
// setTableAlignment(element, ui, valign, false);
// break;
default:
//TODO - implement vertical alignment for non-table elements
}
}
// Text Transform
int transform=selector.getAttrVal(CSSElement.CSS_TEXT_TRANSFORM);
if (transform!=-1) {
setTextTransformRecursive(ui, transform);
}
// Text indentation
int indent=selector.getAttrLengthVal(CSSElement.CSS_TEXT_INDENT, ui, htmlC.getWidth());
if (indent>=0) { // Only positive (0 also as it may cancel previous margins)
setTextIndentationRecursive(ui, indent);
}
//
// Font
//
// Font family
String fontFamily=selector.getAttributeById(CSSElement.CSS_FONT_FAMILY);
if (fontFamily!=null) {
int index=fontFamily.indexOf(',');
if (index!=-1) { // Currently we ignore font families fall back (i.e. Arial,Helvetica,Sans-serif) since even finding a match for one font is quite expensive performance-wise
fontFamily=fontFamily.substring(0, index);
}
}
// Font Style
int fontStyle=selector.getAttrVal(CSSElement.CSS_FONT_STYLE);
// Font Weight
int fontWeight=selector.getAttrVal(CSSElement.CSS_FONT_WEIGHT);
int fontSize=selector.getAttrLengthVal(CSSElement.CSS_FONT_SIZE,ui, ui.getStyle().getFont().getHeight());
if (fontSize<-1) {
int curSize=ui.getStyle().getFont().getHeight();
if (fontSize==CSSElement.FONT_SIZE_LARGER) {
fontSize=curSize+2;
} else if (fontSize==CSSElement.FONT_SIZE_SMALLER) {
fontSize=curSize-2;
}
}
// Since J2ME doesn't support small-caps fonts, when a small-caps font varinat is requested
// the font-family is changed to "smallcaps" which should be loaded to HTMLComponent and the theme as a bitmap font
// If no smallcaps font is found at all, then the family stays the same, but if even only one is found - the best match will be used.
int fontVariant=selector.getAttrVal(CSSElement.CSS_FONT_VARIANT);
if ((fontVariant==FONT_VARIANT_SMALLCAPS) && (htmlC.isSmallCapsFontAvailable())) {
fontFamily=CSSElement.SMALL_CAPS_STRING;
}
// Process font only if once of the font CSS properties was mentioned and valid
if ((fontFamily!=null) || (fontSize!=-1) || (fontStyle!=-1) || (fontWeight!=-1)) {
setFontRecursive(htmlC, ui, fontFamily, fontSize, fontStyle, fontWeight,selector);
}
// List style
int listType=-1;
String listImg=null;
Component borderUi=ui;
if ((element.getTagId()==HTMLElement.TAG_LI) || (element.getTagId()==HTMLElement.TAG_UL) || (element.getTagId()==HTMLElement.TAG_OL) || (element.getTagId()==HTMLElement.TAG_DIR) || (element.getTagId()==HTMLElement.TAG_MENU)) {
int listPos=selector.getAttrVal(CSSElement.CSS_LIST_STYLE_POSITION);
if (listPos==LIST_STYLE_POSITION_INSIDE) {
// Padding and not margin since background color should affect also the indented space
ui.getStyle().setPadding(Component.LEFT, ui.getStyle().getMargin(Component.LEFT)+INDENT_LIST_STYLE_POSITION);
Container parent=ui.getParent();
if (parent.getLayout() instanceof BorderLayout) {
borderUi=parent;
}
}
listType=selector.getAttrVal(CSSElement.CSS_LIST_STYLE_TYPE);
listImg=getCSSUrl(selector.getAttributeById(CSSElement.CSS_LIST_STYLE_IMAGE));
}
// Border
Border[] borders = new Border[4];
boolean leftBorder=false; // Used to prevent drawing a border in the middle of two words in the same segment
boolean rightBorder=false; // Used to prevent drawing a border in the middle of two words in the same segment
boolean hasBorder=false;
if ((borderUi==ui) && (element.getUi().size()>1)) {
if (element.getUi().firstElement()==borderUi) {
leftBorder=true;
} else if (element.getUi().lastElement()==borderUi) {
rightBorder=true;
}
} else {
leftBorder=true;
rightBorder=true;
}
for(int i=Component.TOP;i<=Component.RIGHT;i++) {
if ((i==Component.BOTTOM) || (i==Component.TOP) ||
((i==Component.LEFT) && (leftBorder)) ||
((i==Component.RIGHT) && (rightBorder))) {
borders[i]=createBorder(selector, borderUi, i,styles,BORDER);
if (borders[i]!=null) {
hasBorder=true;
}
}
}
if (hasBorder) {
Border curBorder=borderUi.getUnselectedStyle().getBorder();
if (((styles & STYLE_SELECTED)!=0) && ((styles & STYLE_UNSELECTED)==0)) {
curBorder=borderUi.getSelectedStyle().getBorder();
}
if ((styles & STYLE_PRESSED)!=0) {
curBorder=((HTMLLink)borderUi).getSelectedStyle().getBorder();
}
// In case this element was assigned a top border for instance, and then by belonging to another tag/class/id it has also a bottom border - this merges the two (and gives priority to the new one)
if ((curBorder!=null) && (curBorder.getCompoundBorders()!=null)) { // TODO - This doesn't cover the case of having another border (i.e. table/fieldset?) - Can also assign the non-CSS border to the other corners?
//curBorder.
Border[] oldBorders = curBorder.getCompoundBorders();
for(int i=Component.TOP;i<=Component.RIGHT;i++) {
if (borders[i]==null) {
borders[i]=oldBorders[i];
}
}
}
Border border=Border.createCompoundBorder(borders[Component.TOP], borders[Component.BOTTOM], borders[Component.LEFT], borders[Component.RIGHT]);
if (border!=null) {
if ((styles & STYLE_SELECTED)!=0) {
borderUi.getSelectedStyle().setBorder(border);
}
if ((styles & STYLE_UNSELECTED)!=0) {
borderUi.getUnselectedStyle().setBorder(border);
}
if ((styles & STYLE_PRESSED)!=0) {
((HTMLLink)borderUi).getPressedStyle().setBorder(border);
}
if (borderUi.getParent()!=null) {
borderUi.getParent().revalidate();
} else if (borderUi instanceof Container) {
((Container)borderUi).revalidate();
}
}
}
//
// Specific elements styling
//
// Access keys
v=selector.getAttributeById(CSSElement.CSS_WAP_ACCESSKEY);
if ((v!=null) && (v.length()>=1) &&
((element.getTagId()==HTMLElement.TAG_INPUT) || // These are the only tags that can accpet an access key
(element.getTagId()==HTMLElement.TAG_TEXTAREA) || (element.getTagId()==HTMLElement.TAG_LABEL) ||
((element.getTagId()==HTMLElement.TAG_A) && (ui instanceof HTMLLink) && ((HTMLLink)ui).parentLink==null)) // For A tags this is applied only to the first word, no need to apply it to each word of the link
) {
// The accesskey string may consist fallback assignments (comma seperated) and multiple assignments (space seperated) and any combination of those
// For example: "send *, #" (meaning: assign both the send and * keys, and if failed to assign one of those assign the # key instead)
int index=v.indexOf(',');
boolean assigned=false;
while (index!=-1) { // Handle fallback access keys
String key=v.substring(0,index).trim();
v=v.substring(index+1);
assigned=processAccessKeys(key, htmlC, ui);
if (assigned) {
break; // comma denotes fallback, and once we succeeded assigning the accesskey, the others are irrelevant
}
index=v.indexOf(',');
}
if (!assigned) {
processAccessKeys(v.trim(), htmlC, ui);
}
}
if (!HTMLComponent.PROCESS_HTML_MP1_ONLY) {
// Text decoration (In HTML-MP1 the only mandatory decoration is 'none')
int decoration=selector.getAttrVal(CSSElement.CSS_TEXT_DECORATION);
if (decoration==TEXT_DECOR_NONE) {
removeTextDecorationRecursive(ui,selector);
} else if (decoration==TEXT_DECOR_UNDERLINE) {
setTextDecorationRecursive(ui, Style.TEXT_DECORATION_UNDERLINE,selector);
} else if (decoration==TEXT_DECOR_LINETHROUGH) {
setTextDecorationRecursive(ui, Style.TEXT_DECORATION_STRIKETHRU,selector);
} else if (decoration==TEXT_DECOR_OVERLINE) {
setTextDecorationRecursive(ui, Style.TEXT_DECORATION_OVERLINE,selector);
}
// Word spacing
if (!HTMLComponent.FIXED_WIDTH) {
int wordSpace=selector.getAttrLengthVal(CSSElement.CSS_WORD_SPACING, ui, 0); // The relative dimension is 0, since percentage doesn't work with word-spacing in browsers
if (wordSpace!=-1) {
setWordSpacingRecursive(ui, wordSpace);
}
}
// Line height
// Technically the font height should be queried when actually resizing the line (since it may differ for a big block) - but since this would be ery time consuming and also major browsers don't take it into account - we'll do the same
//int lineHeight=selector.getAttrLengthVal(CSSElement.CSS_LINE_HEIGHT, ui, ui.getStyle().getFont().getHeight());
int lineHeight=selector.getAttrLengthVal(CSSElement.CSS_LINE_HEIGHT, ui, ui.getStyle().getFont().getHeight());
if (lineHeight!=-1) {
lineHeight=Math.max(0, lineHeight-ui.getStyle().getFont().getHeight()); // 100% means normal line height (don't add margin). Sizes below will not work, even they do in regular browsers
setLineHeightRecursive(ui, lineHeight/2);
}
// Quotes
String quotesStr=selector.getAttributeById(CSSElement.CSS_QUOTES);
if (quotesStr!=null) {
Vector quotes = htmlC.getWords(quotesStr, Component.LEFT, false);
int size=quotes.size();
if ((size==2) || (size==4)) {
String[] quotesArr = new String[4];
for(int i=0;i0) {
Component listItemCmp=bulletCont.getComponentAt(0);
if (listItemCmp instanceof Component) {
HTMLListItem listItem=((HTMLListItem)listItemCmp);
listItem.setStyleType(listType);
listItem.setImage(listImg);
}
}
}
}
} else if ((element.getTagId()==HTMLElement.TAG_UL) || (element.getTagId()==HTMLElement.TAG_OL) || (element.getTagId()==HTMLElement.TAG_DIR) || (element.getTagId()==HTMLElement.TAG_MENU)) {
Container ulCont = (Container)ui;
for(int i=0;i=1) {
cmp=liCont.getComponentAt(0);
if (cmp instanceof Container) {
Container liContFirstLine=(Container)cmp;
if (liContFirstLine.getComponentCount()>=1) {
cmp=liContFirstLine.getComponentAt(0);
if (cmp instanceof HTMLListItem) {
HTMLListItem listItem=(HTMLListItem)cmp;
listItem.setStyleType(listType);
listItem.setImage(listImg);
}
}
}
}
}
}
}
}
}
private void addOutlineToStyle(Style style,Border outline) {
Border curBorder=style.getBorder();
if (curBorder!=null) {
curBorder.addOuterBorder(outline);
} else {
style.setBorder(outline);
}
}
/**
* Sets the alignment of all cells in the table
*
* @param ui The component representing the table (a HTMLTable)
* @param align The alignment
* @param isHorizontal true for horizontal alignment, false for vertical alignment
*/
private void setTableAlignment(Component ui,int align,boolean isHorizontal) {
HTMLTable table=(HTMLTable)ui;
HTMLTableModel model= ((HTMLTableModel)table.getModel());
model.setAlignToAll(isHorizontal, align);
table.setModel(model);
}
/**
* Sets the table cell 'ui' to the requested alignment
*
* @param tdTag The element representing the table cell (TD/TH tag)
* @param ui The component representing the table (a HTMLTable)
* @param align The alignment
* @param isHorizontal true for horizontal alignment, false for vertical alignment
*/
private void setTableCellAlignment(HTMLElement tdTag,Component ui,int align,boolean isHorizontal) {
HTMLElement trTag=(HTMLElement)tdTag.getParent();
while ((trTag!=null) && (trTag.getTagId()!=HTMLElement.TAG_TR)) { // Though in strict XHTML TR can only contain TD/TH - in some HTMLs TR doesn't have to be the direct parent of the tdTag, i.e.: