/src/org/mt4j/components/css/util/CSSHelper.java

http://mt4j.googlecode.com/ · Java · 415 lines · 256 code · 67 blank · 92 comment · 42 complexity · 79b568c8dadfc876b380ba21d4a1a111 MD5 · raw file

  1. package org.mt4j.components.css.util;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.List;
  5. import org.mt4j.MTApplication;
  6. import org.mt4j.components.MTComponent;
  7. import org.mt4j.components.StateChange;
  8. import org.mt4j.components.StateChangeEvent;
  9. import org.mt4j.components.StateChangeListener;
  10. import org.mt4j.components.TransformSpace;
  11. import org.mt4j.components.clipping.Clip;
  12. import org.mt4j.components.css.style.CSSStyle;
  13. import org.mt4j.components.css.style.CSSStyle.BackgroundRepeat;
  14. import org.mt4j.components.css.style.CSSStyleHierarchy;
  15. import org.mt4j.components.css.util.CSSKeywords.Position;
  16. import org.mt4j.components.visibleComponents.shapes.MTCSSStylableShape;
  17. import org.mt4j.components.visibleComponents.shapes.MTPolygon;
  18. import org.mt4j.components.visibleComponents.widgets.MTImage;
  19. import org.mt4j.util.MT4jSettings;
  20. import org.mt4j.util.MTColor;
  21. import org.mt4j.util.math.Tools3D;
  22. import org.mt4j.util.math.Vector3D;
  23. import org.mt4j.util.math.Vertex;
  24. import org.mt4j.util.opengl.GLTexture;
  25. import org.mt4j.util.opengl.GLTexture.EXPANSION_FILTER;
  26. import org.mt4j.util.opengl.GLTexture.SHRINKAGE_FILTER;
  27. import org.mt4j.util.opengl.GLTexture.TEXTURE_TARGET;
  28. import org.mt4j.util.opengl.GLTexture.WRAP_MODE;
  29. import org.mt4j.util.opengl.GLTextureSettings;
  30. import processing.core.PImage;
  31. /**
  32. * The Class CSSHelper.
  33. */
  34. public class CSSHelper {
  35. /** The private style sheets (unique to an object) */
  36. private List<CSSStyle> privateStyleSheets = new ArrayList<CSSStyle>();
  37. /** The currently relevant global style sheet */
  38. private List<CSSStyleHierarchy> sheets = new ArrayList<CSSStyleHierarchy>();
  39. /** The virtual style sheet (generated from the global and private style sheets) */
  40. private CSSStyle virtualStyleSheet = null;
  41. /** The CSS style manager. */
  42. private CSSStyleManager cssStyleManager;
  43. /** The MTApplication. */
  44. private MTApplication app;
  45. /** The MTComponent. */
  46. private MTComponent c;
  47. /**
  48. * Instantiates a new CSS helper.
  49. *
  50. * @param c the MTComponent
  51. * @param a the MTApplication
  52. */
  53. public CSSHelper(MTComponent c, MTApplication a) {
  54. this.c = c;
  55. this.app = a;
  56. this.cssStyleManager = a.getCssStyleManager();
  57. addListeners();
  58. }
  59. /**
  60. * Instantiates a new CSS helper.
  61. *
  62. * @param c the MTComponent
  63. * @param a the MTApplication
  64. * @param s the new private CSSStyle
  65. */
  66. public CSSHelper(MTComponent c, MTApplication a, CSSStyle s) {
  67. this(c,a);
  68. this.getPrivateStyleSheets().add(s);
  69. }
  70. /**
  71. * Instantiates a new CSS helper.
  72. *
  73. * @param c the MTComponent
  74. * @param a the MTApplication
  75. * @param s the list of private style sheets
  76. */
  77. public CSSHelper(MTComponent c, MTApplication a, List<CSSStyle> s) {
  78. this(c,a);
  79. this.getPrivateStyleSheets().addAll(s);
  80. }
  81. /**
  82. * Adds the listeners to the MTComponent, so applyStyleSheet() is called every time the component is added as child
  83. */
  84. private void addListeners() {
  85. if (c instanceof MTCSSStylableShape) {
  86. final MTCSSStylableShape cssShape = (MTCSSStylableShape) c;
  87. cssShape.addStateChangeListener(StateChange.ADDED_TO_PARENT,
  88. new StateChangeListener() {
  89. public void stateChanged(StateChangeEvent evt) {
  90. cssShape.applyStyleSheet();//rather leave the applyStyleSheet() method and implementation to the component itself
  91. // applyStyleSheet(CSSHelper.this.c);
  92. }
  93. });
  94. }
  95. }
  96. /**
  97. * Apply the style sheet. Disambiguate between different subclasses of MTComponent
  98. */
  99. public void applyStyleSheet(MTComponent c) {
  100. //This method can be used by a component which only implements the cssstylable interface and
  101. //doesent extend MTCssStylableShape to get some standard behaviour
  102. //like the calling of applyStyleSheet on all their children
  103. if (c instanceof CSSStylableComponent) {
  104. CSSStylableComponent sc = (CSSStylableComponent)c;
  105. if (!sc.isCssForceDisabled() && ((sc.isCSSStyled() && !app.getCssStyleManager().isGloballyDisabled()) || app.getCssStyleManager().isGloballyEnabled())) {
  106. evaluateStyleSheets();
  107. for (MTComponent d : c.getChildren()) {
  108. if (d instanceof CSSStylableComponent) {
  109. CSSStylableComponent s = (CSSStylableComponent) d;
  110. s.applyStyleSheet();
  111. }
  112. }
  113. }
  114. }
  115. }
  116. /**
  117. * Evaluate the style sheets (in order of relevance).
  118. */
  119. private void evaluateStyleSheets() {
  120. sheets = cssStyleManager.getRelevantStyles(c);
  121. Collections.sort(sheets);
  122. virtualStyleSheet = new CSSStyle(app);
  123. for (CSSStyleHierarchy h : sheets) {
  124. virtualStyleSheet.addStyleSheet(h.getStyle());
  125. }
  126. for (CSSStyle s : privateStyleSheets) {
  127. virtualStyleSheet.addStyleSheet(s);
  128. }
  129. }
  130. /**
  131. * Gets the private style sheets.
  132. *
  133. * @return the private style sheets
  134. */
  135. public List<CSSStyle> getPrivateStyleSheets() {
  136. return privateStyleSheets;
  137. }
  138. /**
  139. * Gets the currently relevant style sheets.
  140. *
  141. * @return the sheets
  142. */
  143. public List<CSSStyleHierarchy> getSheets() {
  144. return sheets;
  145. }
  146. /**
  147. * Gets the x distance (between a float and a Vertex)
  148. *
  149. * @param x the x-position
  150. * @param v2 the vertex to compare to
  151. * @return the x-distance
  152. */
  153. private static float getXDistance(float x, Vertex v2) {
  154. float distance = v2.x - x;
  155. if (distance >= 0)
  156. return distance;
  157. else
  158. return -distance;
  159. }
  160. /**
  161. * Gets the y distance (between a float and a vertex)
  162. *
  163. * @param y the y-position
  164. * @param v2 the vertex to compare to
  165. * @return the y-distance
  166. */
  167. private static float getYDistance(float y, Vertex v2) {
  168. float distance = v2.y - y;
  169. if (distance >= 0)
  170. return distance;
  171. else
  172. return -distance;
  173. }
  174. /**
  175. * Sets the private style sheets.
  176. *
  177. * @param privateStyleSheets the new private style sheets
  178. */
  179. public void setPrivateStyleSheets(List<CSSStyle> privateStyleSheets) {
  180. this.privateStyleSheets = privateStyleSheets;
  181. }
  182. /**
  183. * Sets the relevant style sheets.
  184. *
  185. * @param sheets the new sheets
  186. */
  187. public void setSheets(List<CSSStyleHierarchy> sheets) {
  188. this.sheets = sheets;
  189. }
  190. /**
  191. * Adds a style sheet.
  192. *
  193. * @param sheet the new style sheet
  194. */
  195. public void setStyleSheet(CSSStyle sheet) {
  196. this.privateStyleSheets.add(sheet);
  197. }
  198. /**
  199. * Sets the texture for a tiled background.
  200. *
  201. * @param p the MTPolygon to apply it to
  202. * @param bgImage the background-image
  203. */
  204. public void setBackground(MTPolygon p) {
  205. PImage bgImage = virtualStyleSheet.getBackgroundImage();
  206. if (bgImage != null) {
  207. if (virtualStyleSheet.getBackgroundRepeat() != BackgroundRepeat.NONE) {
  208. boolean pot = Tools3D.isPowerOfTwoDimension(bgImage);
  209. boolean tiled = true;
  210. p.setFillColor(MTColor.WHITE);
  211. if (tiled) {
  212. // Generate texture coordinates to repeat the texture over the whole
  213. // background (works only with OpenGL)
  214. Vertex[] backgroundVertices = p.getVerticesLocal();
  215. float minx, miny;
  216. if (backgroundVertices.length > 0) {
  217. minx = backgroundVertices[0].x;
  218. miny = backgroundVertices[0].y;
  219. for (Vertex vtx : backgroundVertices) {
  220. if (vtx.x < minx)
  221. minx = vtx.x;
  222. if (vtx.y < miny)
  223. miny = vtx.y;
  224. }
  225. for (Vertex vtx : backgroundVertices) {
  226. vtx.setTexCoordU(getXDistance(minx, vtx)
  227. / bgImage.width);
  228. vtx.setTexCoordV(getYDistance(miny, vtx)
  229. / bgImage.height);
  230. }
  231. }
  232. // Update changed texture coordinates for opengl buffer drawing
  233. if (MT4jSettings.getInstance().isOpenGlMode())
  234. p.getGeometryInfo().updateTextureBuffer(p.isUseVBOs());
  235. }
  236. WRAP_MODE horizontal = WRAP_MODE.CLAMP, vertical = WRAP_MODE.CLAMP;
  237. switch (virtualStyleSheet.getBackgroundRepeat()) {
  238. case REPEAT:
  239. horizontal = WRAP_MODE.REPEAT;
  240. vertical = WRAP_MODE.REPEAT;
  241. case XREPEAT:
  242. horizontal = WRAP_MODE.REPEAT;
  243. case YREPEAT:
  244. vertical = WRAP_MODE.REPEAT;
  245. }
  246. if (MT4jSettings.getInstance().isOpenGlMode()) {
  247. GLTextureSettings g = new GLTextureSettings(
  248. TEXTURE_TARGET.TEXTURE_2D,
  249. SHRINKAGE_FILTER.BilinearNoMipMaps,
  250. EXPANSION_FILTER.Bilinear, horizontal,
  251. vertical);
  252. GLTexture tex;
  253. if (pot) {
  254. tex = new GLTexture(app, bgImage, g);
  255. } else {
  256. if (tiled) {
  257. g.target = TEXTURE_TARGET.RECTANGULAR;
  258. g.shrinkFilter = SHRINKAGE_FILTER.Trilinear; // Because NPOT texture with GL_REPEAT isnt supported
  259. // -> gluBuild2Dmipmapds strechtes the texture to POT size
  260. tex = new GLTexture(app, bgImage, g);
  261. } else {
  262. g.target = TEXTURE_TARGET.RECTANGULAR;
  263. tex = new GLTexture(app, bgImage, g);
  264. }
  265. }
  266. p.setTexture(tex);
  267. } else {
  268. p.setTexture(bgImage);
  269. }
  270. } else {
  271. if (virtualStyleSheet.getBackgroundPosition() != null) {
  272. MTImage img = new MTImage(app,bgImage);
  273. p.addChild(img);
  274. img.setPickable(false);
  275. float xPos = 0;
  276. float yPos = 0;
  277. switch (virtualStyleSheet.getBackgroundPosition().getxType()) {
  278. case ABSOLUTE:
  279. xPos = virtualStyleSheet.getBackgroundPosition().getxPos();
  280. break;
  281. case RELATIVE:
  282. xPos = determineAbsolutePosition(p, virtualStyleSheet.getBackgroundPosition().getxPos(), true);
  283. break;
  284. case KEYWORD:
  285. xPos = determineAbsolutePosition(p, virtualStyleSheet.getBackgroundPosition().getxKeywordPosition(), true);
  286. break;
  287. }
  288. switch (virtualStyleSheet.getBackgroundPosition().getyType()) {
  289. case ABSOLUTE:
  290. xPos = virtualStyleSheet.getBackgroundPosition().getyPos();
  291. break;
  292. case RELATIVE:
  293. xPos = determineAbsolutePosition(p, virtualStyleSheet.getBackgroundPosition().getyPos(), false);
  294. break;
  295. case KEYWORD:
  296. xPos = determineAbsolutePosition(p, virtualStyleSheet.getBackgroundPosition().getyKeywordPosition(), false);
  297. break;
  298. }
  299. img.setPositionRelativeToParent(
  300. p.getVerticesLocal()[0].addLocal(calcPos(p, virtualStyleSheet.getBackgroundImage(), xPos, yPos)));
  301. Clip c = new Clip(app, p.getBounds().getVectorsLocal()[0].x,p.getBounds().getVectorsLocal()[0].y,p.getBounds().getWidthXY(TransformSpace.LOCAL),p.getBounds().getHeightXY(TransformSpace.LOCAL));
  302. img.setClip(c);
  303. //p.setChildClip(new Clip(p));
  304. } else {
  305. p.setTexture(bgImage);
  306. }
  307. }
  308. }
  309. }
  310. private float determineAbsolutePosition(MTPolygon p, Position po, boolean isHorizontal) {
  311. float returnValue = 0;
  312. if (isHorizontal) {
  313. switch (po) {
  314. case LEFT:
  315. return calcPos(p,virtualStyleSheet.getBackgroundImage(), 0,0).x;
  316. case RIGHT:
  317. return calcPos(p,virtualStyleSheet.getBackgroundImage(), p.getWidthXY(TransformSpace.LOCAL) - (float)virtualStyleSheet.getBackgroundImage().width,0).x;
  318. case CENTER:
  319. return calcPos(p,virtualStyleSheet.getBackgroundImage(), (p.getWidthXY(TransformSpace.LOCAL) / 2f) - ((float)virtualStyleSheet.getBackgroundImage().width/2f),0).x;
  320. }
  321. } else {
  322. switch (po) {
  323. case TOP:
  324. return calcPos(p,virtualStyleSheet.getBackgroundImage(), 0,0).y;
  325. case BOTTOM:
  326. return calcPos(p, virtualStyleSheet.getBackgroundImage(), 0, p.getHeightXY(TransformSpace.LOCAL) - (float)virtualStyleSheet.getBackgroundImage().height).y;
  327. case CENTER:
  328. return calcPos(p, virtualStyleSheet.getBackgroundImage(), 0, (p.getHeightXY(TransformSpace.LOCAL)/2f) - ((float)virtualStyleSheet.getBackgroundImage().height / 2f)).y;
  329. }
  330. }
  331. return returnValue;
  332. }
  333. private float determineAbsolutePosition(MTPolygon p, float po, boolean isHorizontal) {
  334. if (isHorizontal) {
  335. return calcPos(p, virtualStyleSheet.getBackgroundImage(), p.getWidthXY(TransformSpace.LOCAL) * po ,0).x;
  336. } else {
  337. return calcPos(p, virtualStyleSheet.getBackgroundImage(), 0 ,p.getHeightXY(TransformSpace.LOCAL) * po).x;
  338. }
  339. }
  340. private Vector3D calcPos(MTPolygon box, PImage ta, float xo, float yo) {
  341. return new Vector3D((ta.width / 2) + xo,
  342. (ta.height / 2) + yo);
  343. }
  344. public CSSStyle getVirtualStyleSheet() {
  345. evaluateStyleSheets();
  346. return virtualStyleSheet;
  347. }
  348. }