1. /*
  2. * @(#)BlueprintEngine.java 1.23 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.java.swing.plaf.gtk;
  8. import java.awt.*;
  9. import java.awt.image.*;
  10. import java.security.AccessController;
  11. import java.util.*;
  12. import javax.swing.*;
  13. import javax.swing.plaf.ComponentUI;
  14. import javax.swing.plaf.synth.*;
  15. import sun.security.action.GetPropertyAction;
  16. import sun.swing.plaf.synth.SynthUI;
  17. /**
  18. * GTKEngine implementation that renders using images. The images to render
  19. * are dictated by the <code>BlueprintStyle.Info</code>.
  20. *
  21. * @version 1.23 12/19/03
  22. * @author Joshua Outwater
  23. */
  24. class BlueprintEngine extends GTKEngine implements GTKConstants {
  25. /**
  26. * By default we don't use smooth scaling as it is currently not optimized.
  27. */
  28. private static final Object RENDERING_HINT;
  29. private int COMPONENT_NORTH_WEST = 1;
  30. private int COMPONENT_NORTH = 2;
  31. private int COMPONENT_NORTH_EAST = 4;
  32. private int COMPONENT_WEST = 8;
  33. private int COMPONENT_CENTER = 16;
  34. private int COMPONENT_EAST = 32;
  35. private int COMPONENT_SOUTH_EAST = 64;
  36. private int COMPONENT_SOUTH = 128;
  37. private int COMPONENT_SOUTH_WEST = 256;
  38. private int COMPONENT_ALL = 512;
  39. private int _clipX1;
  40. private int _clipX2;
  41. private int _clipY1;
  42. private int _clipY2;
  43. static {
  44. if ("true".equals((String)AccessController.doPrivileged(
  45. new GetPropertyAction("swing.pixmap.smoothScaling")))) {
  46. RENDERING_HINT = RenderingHints.VALUE_INTERPOLATION_BILINEAR;
  47. }
  48. else {
  49. RENDERING_HINT = null;
  50. }
  51. }
  52. public void paintSlider(SynthContext context, Graphics g, int state,
  53. int shadowType, String info,
  54. int x, int y, int w, int h, int orientation) {
  55. if (!paintSimpleImage(context, g, x, y, w, h, true,
  56. ((BlueprintStyle)context.getStyle()).
  57. getInfo("SLIDER", info, state, shadowType, orientation,
  58. UNDEFINED, UNDEFINED, null))) {
  59. super.paintSlider(context, g, state, shadowType, info,
  60. x, y, w, h, orientation);
  61. }
  62. }
  63. public void paintHline(SynthContext context, Graphics g, int state,
  64. String info, int x, int y, int w, int h) {
  65. SynthStyle style = context.getStyle();
  66. Component c = context.getComponent();
  67. // We have a different style to use if we are the child of
  68. // a popup menu.
  69. c = c.getParent();
  70. if (c instanceof JPopupMenu) {
  71. SynthStyle newStyle = getStyle((JPopupMenu)c,
  72. ((JPopupMenu)c).getUI());
  73. if (newStyle != null) {
  74. style = newStyle;
  75. }
  76. }
  77. BlueprintStyle.Info blueprintInfo =
  78. ((BlueprintStyle)style).getInfo("HLINE", info,
  79. state, UNDEFINED, GTKConstants.HORIZONTAL,
  80. UNDEFINED, UNDEFINED, null);
  81. if (blueprintInfo != null && blueprintInfo.getImage() != null) {
  82. themeBlueprintRender(context, g, x, y, w, h,
  83. blueprintInfo.getImage(), blueprintInfo.getImageInsets(),
  84. COMPONENT_ALL, blueprintInfo.getStretch(), false,
  85. blueprintInfo.isBkgMask(), blueprintInfo.isRecolorable(),
  86. blueprintInfo.getColorizeColor());
  87. } else {
  88. super.paintHline(context, g, state, info, x, y, w, h);
  89. }
  90. }
  91. public void paintVline(SynthContext context, Graphics g, int state,
  92. String info, int x, int y, int w, int h) {
  93. BlueprintStyle.Info blueprintInfo =
  94. ((BlueprintStyle)context.getStyle()).getInfo("VLINE", info,
  95. state, UNDEFINED, GTKConstants.VERTICAL,
  96. UNDEFINED, UNDEFINED, null);
  97. if (blueprintInfo != null && blueprintInfo.getImage() != null) {
  98. themeBlueprintRender(context, g, x, y, w, h, blueprintInfo.getImage(),
  99. blueprintInfo.getImageInsets(), COMPONENT_ALL,
  100. blueprintInfo.getStretch(), false,
  101. blueprintInfo.isBkgMask(), blueprintInfo.isRecolorable(),
  102. blueprintInfo.getColorizeColor());
  103. } else {
  104. super.paintVline(context, g, state, info, x, y, w, h);
  105. }
  106. }
  107. public void paintArrow(SynthContext context, Graphics g, int state,
  108. int shadowType, int direction, String info,
  109. int x, int y, int w, int h) {
  110. Component c = context.getComponent();
  111. // Don't paint the arrow if we're in a spinner or combo box.
  112. // We get that from the image.
  113. if (c.getName() == "Spinner.nextButton" ||
  114. c.getName() == "Spinner.previousButton" ||
  115. c.getName() == "ComboBox.arrowButton") {
  116. return;
  117. }
  118. String parentType = null;
  119. c = c.getParent();
  120. if (c != null && c instanceof JComponent) {
  121. c = c.getParent();
  122. if (c != null && c instanceof JComponent) {
  123. parentType = getComponentType((JComponent)c);
  124. }
  125. }
  126. if (!paintSimpleImage(context, g, x, y, w, h, true,
  127. ((BlueprintStyle)context.getStyle()).
  128. getInfo("ARROW", info, state, shadowType,
  129. UNDEFINED, UNDEFINED, direction, parentType))) {
  130. super.paintArrow(context, g, state, shadowType, direction,
  131. info, x, y, w, h);
  132. }
  133. }
  134. public void paintBox(SynthContext context, Graphics g, int state,
  135. int shadowType, String info, int x, int y,
  136. int w, int h) {
  137. int orientation;
  138. Region id = context.getRegion();
  139. Component c = context.getComponent();
  140. SynthStyle style = context.getStyle();
  141. // Blueprint checks to make sure that we aren't calling
  142. // paintBox on a slider/scrollbar with detail hscrollbar or
  143. // vscrollbar, because they do the work in paintArrow instead.
  144. // We do it here because we have the correct bounds for the whole
  145. // button.
  146. Integer arrowDirection =
  147. (Integer)((JComponent)c).getClientProperty("__arrow_direction__");
  148. if (info == "vscrollbar" || info == "hscrollbar" &&
  149. arrowDirection != null) {
  150. int direction = arrowDirection.intValue();
  151. switch (direction) {
  152. case SwingConstants.NORTH:
  153. direction = GTKConstants.ARROW_UP;
  154. break;
  155. case SwingConstants.SOUTH:
  156. direction = GTKConstants.ARROW_DOWN;
  157. break;
  158. case SwingConstants.EAST:
  159. direction = GTKConstants.ARROW_RIGHT;
  160. break;
  161. case SwingConstants.WEST:
  162. direction = GTKConstants.ARROW_LEFT;
  163. break;
  164. }
  165. c = (JComponent)c.getParent();
  166. if (c == null || !(c instanceof JComponent)) {
  167. return;
  168. }
  169. if (c instanceof JScrollBar) {
  170. SynthStyle newStyle = getStyle((JScrollBar)c,
  171. ((JScrollBar)c).getUI());
  172. if (newStyle != null) {
  173. style = newStyle;
  174. }
  175. if (paintSimpleImage(context, g, x, y, w, h, true,
  176. ((BlueprintStyle)style).getInfo("STEPPER", info,
  177. state, UNDEFINED, UNDEFINED, UNDEFINED,
  178. direction, null))) {
  179. return;
  180. }
  181. if (!paintSimpleImage(context, g, x, y, w, h, true,
  182. ((BlueprintStyle)style).getInfo("BOX", info, state,
  183. shadowType, UNDEFINED, UNDEFINED,
  184. UNDEFINED, null))) {
  185. super.paintBox(context, g, state, shadowType, info,
  186. x, y, w, h);
  187. }
  188. return;
  189. }
  190. }
  191. // If the button is in a spinner get the style of the JSpinner.
  192. if (c.getName() == "Spinner.nextButton" ||
  193. c.getName() == "Spinner.previousButton" &&
  194. arrowDirection != null) {
  195. if (arrowDirection.intValue() == SwingConstants.NORTH) {
  196. info = "spinbutton_up";
  197. } else {
  198. info = "spinbutton_down";
  199. }
  200. c = c.getParent();
  201. if (c instanceof JSpinner) {
  202. SynthStyle newStyle = getStyle((JSpinner)c,
  203. ((JSpinner)c).getUI());
  204. if (newStyle != null) {
  205. style = newStyle;
  206. }
  207. }
  208. }
  209. if (id == Region.SCROLL_BAR) {
  210. if (((JScrollBar)c).getOrientation() ==
  211. SwingConstants.HORIZONTAL) {
  212. orientation = GTKConstants.HORIZONTAL;
  213. }
  214. else {
  215. orientation = GTKConstants.VERTICAL;
  216. }
  217. }
  218. else if (id == Region.SLIDER_TRACK) {
  219. if (((JSlider)c).getOrientation() ==
  220. SwingConstants.HORIZONTAL) {
  221. orientation = GTKConstants.HORIZONTAL;
  222. }
  223. else {
  224. orientation = GTKConstants.VERTICAL;
  225. }
  226. }
  227. else {
  228. orientation = UNDEFINED;
  229. }
  230. String parentType = null;
  231. if (c != null) {
  232. c = c.getParent();
  233. if (c != null && c instanceof JComponent) {
  234. parentType = getComponentType((JComponent)c);
  235. }
  236. }
  237. if (!paintSimpleImage(context, g, x, y, w, h, true,
  238. ((BlueprintStyle)style).getInfo("BOX", info, state,
  239. shadowType, orientation, UNDEFINED, UNDEFINED,
  240. parentType))) {
  241. super.paintBox(context, g, state, shadowType, info, x, y, w, h);
  242. }
  243. }
  244. public void paintBoxGap(SynthContext context, Graphics g, int state,
  245. int shadow, String key, int x, int y,
  246. int w, int h, int gapSide, int gapStart,
  247. int gapSize) {
  248. BlueprintStyle.Info info = ((BlueprintStyle)context.getStyle()).getInfo(
  249. "BOX_GAP", key, state, shadow, UNDEFINED, gapSide, UNDEFINED,
  250. null);
  251. if (info != null) {
  252. paintGapImage(context, info, g, x, y, w, h, true, gapSide,
  253. gapStart, gapSize);
  254. } else {
  255. super.paintBoxGap(context, g, state, shadow, key, x, y, w, h,
  256. gapSide, gapStart, gapSize);
  257. }
  258. }
  259. public void paintHandle(SynthContext context, Graphics g, int paintState,
  260. int shadowType, String info, int x, int y,
  261. int w, int h, int orientation) {
  262. if (info == "handlebox" || info == "dockitem") {
  263. w -=2;
  264. h -=1;
  265. }
  266. if (!paintSimpleImage(context, g, x, y, w, h, true,
  267. ((BlueprintStyle)context.getStyle()).
  268. getInfo("HANDLE", info, paintState, shadowType,
  269. orientation, UNDEFINED, UNDEFINED, null))) {
  270. super.paintHandle(context, g, paintState, shadowType, info, x, y,
  271. w, h, orientation);
  272. }
  273. }
  274. public void paintOption(SynthContext context, Graphics g, int paintState,
  275. int shadowType, String info, int x, int y,
  276. int w, int h) {
  277. if (!paintSimpleImage(context, g, x, y, w, h, true,
  278. ((BlueprintStyle)context.getStyle()).
  279. getInfo("OPTION", info, paintState, shadowType,
  280. UNDEFINED, UNDEFINED, UNDEFINED, null))) {
  281. super.paintOption(context, g, paintState, shadowType, info, x, y,
  282. w, h);
  283. }
  284. }
  285. public void paintFocus(SynthContext context, Graphics g, int state,
  286. String key, int x, int y, int w, int h) {
  287. if (!paintSimpleImage(context, g, x, y, w, h, true,
  288. ((BlueprintStyle)context.getStyle()).
  289. getInfo("FOCUS", key, state, UNDEFINED, UNDEFINED,
  290. UNDEFINED, UNDEFINED, null))) {
  291. super.paintFocus(context, g, state, key, x, y, w, h);
  292. }
  293. }
  294. public void paintShadow(SynthContext context, Graphics g, int state,
  295. int shadowType, String info, int x, int y,
  296. int w, int h) {
  297. Component c = context.getComponent();
  298. String parentType = null;
  299. SynthStyle style = context.getStyle();
  300. if (c.getName() == "ComboBox.textField") {
  301. parentType = "GtkCombo";
  302. } else if (c.getName() == "ComboBox.renderer") {
  303. c = c.getParent();
  304. if (c != null) {
  305. c = c.getParent();
  306. parentType = "GtkCombo";
  307. }
  308. }
  309. if (c instanceof JComboBox) {
  310. // Use the Style from the editor
  311. JComboBox cb = (JComboBox)c;
  312. Component editor = cb.getEditor().getEditorComponent();
  313. if (editor instanceof JTextField) {
  314. if (!cb.isEditable() && editor.getParent() == null) {
  315. // GTKStyleFactory hands back a bogus Style when a
  316. // Component doesn't have a parent. As the editor
  317. // is only parented when the JComboBox is editable it
  318. // means we can get back a bogus style. To force the
  319. // real style to be assigned we parent the editor.
  320. // YES, this is ugly!
  321. cb.add(editor);
  322. cb.remove(editor);
  323. }
  324. SynthStyle newStyle = getStyle((JTextField)editor,
  325. ((JTextField)editor).getUI());
  326. if (newStyle != null) {
  327. style = newStyle;
  328. }
  329. }
  330. }
  331. if (info == "menu" && parentType == "GtkHBox") {
  332. return;
  333. }
  334. if (!paintSimpleImage(context, g, x, y, w, h, true,
  335. ((BlueprintStyle)style).getInfo("SHADOW", info, state,
  336. shadowType, UNDEFINED, UNDEFINED, UNDEFINED,
  337. parentType))) {
  338. super.paintShadow(context, g, state, shadowType, info, x, y, w, h);
  339. }
  340. }
  341. public void paintExpander(SynthContext context, Graphics g, int state,
  342. int expanderStyle, String info, int x,
  343. int y, int w, int h) {
  344. // It does not appear that there is a way to override this.
  345. super.paintExpander(context, g, state, expanderStyle, info, x, y, w,h);
  346. }
  347. public void paintCheck(SynthContext context, Graphics g, int state,
  348. int shadowType, String info, int x, int y,
  349. int w, int h) {
  350. if (!paintSimpleImage(context, g, x, y, w, h, true,
  351. ((BlueprintStyle)context.getStyle()).
  352. getInfo("CHECK", info, state, shadowType, UNDEFINED,
  353. UNDEFINED, UNDEFINED, null))) {
  354. super.paintCheck(context, g, state, shadowType, info, x, y, w, h);
  355. }
  356. }
  357. public void paintExtension(SynthContext context, Graphics g, int state,
  358. int shadowType, String info, int x, int y,
  359. int w, int h, int placement, int tabIndex) {
  360. if (!paintSimpleImage(context, g, x, y, w, h, true,
  361. ((BlueprintStyle)context.getStyle()).
  362. getInfo("EXTENSION", info, state, shadowType,
  363. UNDEFINED, placement, UNDEFINED, null))) {
  364. super.paintExtension(context, g, state, shadowType, info, x, y,
  365. w, h, placement, tabIndex);
  366. }
  367. }
  368. public void paintFlatBox(SynthContext context, Graphics g, int state,
  369. String key, int x, int y, int w, int h) {
  370. if (key == "checkbutton" && state == SynthConstants.MOUSE_OVER) {
  371. return;
  372. }
  373. Component c = context.getComponent();
  374. String parentType = null;
  375. c = c.getParent();
  376. if (c instanceof CellRendererPane) {
  377. // Skip the CellRendererPane
  378. c = c.getParent();
  379. }
  380. if (c != null && c instanceof JComponent) {
  381. parentType = getComponentType((JComponent)c);
  382. }
  383. if (!paintSimpleImage(context, g, x, y, w, h, true,
  384. ((BlueprintStyle)context.getStyle()).
  385. getInfo("FLAT_BOX", key, state, UNDEFINED, UNDEFINED,
  386. UNDEFINED, UNDEFINED, parentType))) {
  387. super.paintFlatBox(context, g, state, key, x, y, w, h);
  388. }
  389. }
  390. void paintBackground(SynthContext context, Graphics g, int state,
  391. Color color, int x, int y, int w, int h) {
  392. JComponent c = context.getComponent();
  393. if (c instanceof JPopupMenu) {
  394. if (paintSimpleImage(context, g, x, y, w, h, true,
  395. ((BlueprintStyle)context.getStyle()).
  396. getInfo("BACKGROUND", null, state, UNDEFINED,
  397. UNDEFINED, UNDEFINED, UNDEFINED, null))) {
  398. return;
  399. }
  400. }
  401. super.paintBackground(context, g, state, color, x, y, w, h);
  402. }
  403. /**
  404. * Paints a gap image. This renders the image into a portion of
  405. * the passed in region that is dictated
  406. * by the <code>gapSide</code> and <code>size</code> arguments. For
  407. * example, if <code>gapSide</code> is <code>GTKConstants.TOP</code>,
  408. * this will render the image into the space:
  409. * <table>
  410. * <tr><td>x origin<td> <code>x</code> + <code>gapStart</code>
  411. * <tr><td>y origin<td> <code>y</code>
  412. * <tr><td>width<td> <code>gapSize</code>
  413. * <tr><td>height<td> <code>size</code>
  414. * </table>
  415. *
  416. * @param context Context used to retrieve style information.
  417. * @param info Blueprint style info
  418. * @param g Graphics object to paint to
  419. * @param x X origin
  420. * @param y Y origin
  421. * @param w Width to draw to
  422. * @param h Height to draw to
  423. * @param drawCenter Whether or not the center is drawn.
  424. * @param gapSide Side the gap is on, one of GTKConstants.LEFT,
  425. * GTKConstants.RIGHT, GTKConstants.TOP or GTKConstants.BOTTOM
  426. * @param gapStart Starting location of the gap. The axis the gap is
  427. * on is dictated by the gapSide
  428. * @param gapSize size of the gap
  429. */
  430. private void paintGapImage(SynthContext context, BlueprintStyle.Info info,
  431. Graphics g, int x, int y, int w, int h,
  432. boolean drawCenter, int gapSide, int gapStart,
  433. int gapSize) {
  434. Rectangle r1 = new Rectangle();
  435. Rectangle r2 = new Rectangle();
  436. Rectangle r3 = new Rectangle();
  437. int size = 0;
  438. int componentMask = COMPONENT_ALL;
  439. Image startImage = info.getGapStartImage();
  440. Image image = info.getGapImage();
  441. Image endImage = info.getGapEndImage();
  442. if (!drawCenter) {
  443. componentMask |= COMPONENT_CENTER;
  444. }
  445. // Blueprint doesn't look at each individual image for size, just the
  446. // starting image.
  447. if (startImage != null) {
  448. if (gapSide == TOP || gapSize == BOTTOM) {
  449. size = startImage.getHeight(null);
  450. } else {
  451. size = startImage.getWidth(null);
  452. }
  453. } else {
  454. if (gapSide == TOP || gapSize == BOTTOM) {
  455. size = ((BlueprintStyle)context.getStyle()).getYThickness();
  456. } else {
  457. size = ((BlueprintStyle)context.getStyle()).getXThickness();
  458. }
  459. }
  460. if (gapSize > 0) {
  461. switch(gapSide) {
  462. case TOP:
  463. if (!drawCenter) {
  464. componentMask |= COMPONENT_NORTH_WEST | COMPONENT_NORTH |
  465. COMPONENT_NORTH_EAST;
  466. }
  467. // gap start
  468. r1.x = x;
  469. r1.y = y;
  470. r1.width = gapStart;
  471. r1.height = size;
  472. // gap
  473. r2.x = x + gapStart;
  474. r2.y = y;
  475. r2.width = gapSize;
  476. r2.height = size;
  477. // gap end
  478. r3.x = x + gapStart + gapSize;
  479. r3.y = y;
  480. r3.width = w - (gapStart + gapSize);
  481. r3.height = size;
  482. break;
  483. case BOTTOM:
  484. if (!drawCenter) {
  485. componentMask |= COMPONENT_SOUTH_WEST | COMPONENT_SOUTH |
  486. COMPONENT_SOUTH_EAST;
  487. }
  488. // gap start
  489. r1.x = x;
  490. r1.y = y + h - size;
  491. r1.width = gapStart;
  492. r1.height = size;
  493. // gap
  494. r2.x = x + gapStart;
  495. r2.y = y + h - size;
  496. r2.width = gapSize;
  497. r2.height = size;
  498. // gap end
  499. r3.x = x + gapStart + gapSize;
  500. r3.y = y + h - size;
  501. r3.width = w - (gapStart + gapSize);
  502. r3.height = size;
  503. break;
  504. case LEFT:
  505. if (!drawCenter) {
  506. componentMask |= COMPONENT_NORTH_WEST | COMPONENT_WEST |
  507. COMPONENT_SOUTH_WEST;
  508. }
  509. // gap start
  510. r1.x = x;
  511. r1.y = y;
  512. r1.width = size;
  513. r1.height = gapStart;
  514. // gap
  515. r2.x = x;
  516. r2.y = y + gapStart;
  517. r2.width = size;
  518. r2.height = gapSize;
  519. // gap end
  520. r3.x = x;
  521. r3.y = y + gapStart + gapSize;
  522. r3.width = size;
  523. r3.height = h - (gapStart + gapSize);
  524. break;
  525. case RIGHT:
  526. if (!drawCenter) {
  527. componentMask |= COMPONENT_NORTH_EAST | COMPONENT_EAST |
  528. COMPONENT_SOUTH_EAST;
  529. }
  530. // gap start
  531. r1.x = x + w - size;
  532. r1.y = y;
  533. r1.width = size;
  534. r1.height = gapStart;
  535. // gap
  536. r2.x = x + w - size;
  537. r2.y = y + gapStart;
  538. r2.width = size;
  539. r2.height = gapSize;
  540. // gap end
  541. r3.x = x + w - size;
  542. r3.y = y + gapStart + gapSize;
  543. r3.width = size;
  544. r3.height = h - (gapStart + gapSize);
  545. break;
  546. }
  547. themeBlueprintRender(context, g, x, y, w, h, info.getImage(),
  548. info.getImageInsets(), componentMask, true, false,
  549. info.isBkgMask(), info.isRecolorable(),
  550. info.getColorizeColor());
  551. // NOTE:
  552. // stretch should be queried from the info, but there is currently
  553. // no support for that field for gap images in BlueprintStyle.Info.
  554. if (startImage != null) {
  555. themeBlueprintRender(context, g, r1.x, r1.y, r1.width, r1.height,
  556. startImage, info.getGapStartInsets(), COMPONENT_ALL,
  557. true, false, false, false, null);
  558. }
  559. if (image != null) {
  560. themeBlueprintRender(context, g, r2.x, r2.y, r2.width, r2.height,
  561. image, info.getGapInsets(), COMPONENT_ALL,
  562. true, false, false, false, null);
  563. }
  564. if (endImage != null) {
  565. themeBlueprintRender(context, g, r3.x, r3.y, r3.width, r3.height,
  566. endImage, info.getGapEndInsets(), COMPONENT_ALL,
  567. true, false, false, false, null);
  568. }
  569. }
  570. }
  571. /**
  572. * Paints the image and overlay image from the passed in style.
  573. *
  574. * @param g Graphics object to paint to
  575. * @param x X origin
  576. * @param y Y origin
  577. * @param w Width to draw to
  578. * @param h Height to draw to
  579. * @param drawCenter Whether the center of the image should be drawn
  580. * @param info Used to fetch image, insets and overlay image from
  581. */
  582. private boolean paintSimpleImage(SynthContext context, Graphics g, int x, int y,
  583. int w, int h, boolean drawCenter, BlueprintStyle.Info info) {
  584. if (info != null) {
  585. Rectangle clip = g.getClipBounds();
  586. _clipX1 = clip.x;
  587. _clipY1 = clip.y;
  588. _clipX2 = _clipX1 + clip.width;
  589. _clipY2 = _clipY1 + clip.height;
  590. themeBlueprintRender(context, g, x, y, w, h, info.getImage(),
  591. info.getImageInsets(), drawCenter ? COMPONENT_ALL :
  592. COMPONENT_ALL | COMPONENT_CENTER, info.getStretch(),
  593. false, info.isBkgMask(), info.isRecolorable(),
  594. info.getColorizeColor());
  595. if (drawCenter) {
  596. themeBlueprintRender(context, g, x, y, w, h,
  597. info.getOverlayImage(), info.getOverlayInsets(),
  598. COMPONENT_ALL, info.getOverlayStretch(), true,
  599. false, false, null);
  600. }
  601. return true;
  602. }
  603. return false;
  604. }
  605. /**
  606. * Paints the image in the specified region.
  607. *
  608. * @param g Graphics object to paint to
  609. * @param x X origin
  610. * @param y Y origin
  611. * @param w Width to draw to
  612. * @param h Height to draw to
  613. * @param image Image to render
  614. * @param insets Insets used to determine portion of image that is fixed.
  615. * @param componentMask Mask defining the areas of the image to draw.
  616. * @param stretch Stretch the image to fit the drawing area.
  617. * @param center Centers the image to the middle of the drawing area.
  618. * @param isBkgMask Whether or not the image is a background mask.
  619. * @param isRecolorable If the image is recolorable.
  620. * @param colorizeColor Color to use if image is recolorable.
  621. */
  622. private void themeBlueprintRender(SynthContext context, Graphics g,
  623. int x, int y, int w, int h, Image image,
  624. Insets insets, int componentMask,
  625. boolean stretch, boolean center,
  626. boolean isBkgMask, boolean isRecolorable,
  627. Color colorizeColor) {
  628. if (image == null) {
  629. return;
  630. }
  631. if (insets == null) {
  632. insets = GTKPainter.EMPTY_INSETS;
  633. }
  634. int iw = image.getWidth(null);
  635. int ih = image.getHeight(null);
  636. if (isBkgMask) {
  637. // Colorize mask using the colorizeColor from info.
  638. BufferedImage i = new BufferedImage(iw, ih,
  639. BufferedImage.TYPE_INT_ARGB);
  640. Graphics2D g3 = i.createGraphics();
  641. boolean topParentReached = false;
  642. int steps = 0;
  643. Component compParent = context.getComponent();
  644. while (!topParentReached && steps <= 2) {
  645. compParent = compParent.getParent ();
  646. steps ++;
  647. if (compParent != null) {
  648. Color color = compParent.getBackground ();
  649. if (color != null) {
  650. if (!color.equals (colorizeColor) &&
  651. !color.equals (Color.black) &&
  652. !(compParent instanceof JFileChooser)) {
  653. colorizeColor = color;
  654. topParentReached = true;
  655. }
  656. }
  657. } else {
  658. topParentReached = true;
  659. }
  660. }
  661. if (colorizeColor == null) {
  662. colorizeColor = ((GTKStyle)context.getStyle()).
  663. getGTKColor(context.getComponent(), context.getRegion(),
  664. context.getComponentState(),
  665. ColorType.BACKGROUND);
  666. }
  667. g3.setColor(colorizeColor);
  668. g3.fillRect(0, 0, iw, ih);
  669. g3.setComposite(AlphaComposite.DstIn);
  670. g3.drawImage(image, 0, 0, null);
  671. g3.dispose();
  672. image = i;
  673. } else if (isRecolorable) {
  674. // Create a copy of the image to manipulate the pixels.
  675. BufferedImage i = new BufferedImage(iw, ih,
  676. BufferedImage.TYPE_INT_ARGB);
  677. Graphics2D g3 = i.createGraphics();
  678. g3.setComposite(AlphaComposite.Src);
  679. g3.drawImage(image, 0, 0, null);
  680. g3.dispose();
  681. int red = colorizeColor.getRed();
  682. int green = colorizeColor.getGreen();
  683. int blue = colorizeColor.getBlue();
  684. int alpha = colorizeColor.getAlpha();
  685. Color color = RGBtoHLS(red, green, blue);
  686. int hue = color.getRed();
  687. int lum = color.getGreen();
  688. int sat = color.getBlue();
  689. int[] pixels = null;
  690. // Get the pixel data from the image.
  691. pixels = i.getRaster().getPixels(0, 0, iw, ih, pixels);
  692. // Colorize the pixels.
  693. for (int index = 0; index < pixels.length; index+=4) {
  694. red = pixels[index];
  695. green = pixels[index + 1];
  696. blue = pixels[index + 2];
  697. color = RGBtoHLS(red, green, blue);
  698. red = hue;
  699. green = color.getGreen();
  700. blue = sat;
  701. color = HLStoRGB(red, green, blue);
  702. pixels[index] = color.getRed();
  703. pixels[index + 1] = color.getGreen();
  704. pixels[index + 2] = color.getBlue();
  705. pixels[index + 3] = Math.min(
  706. pixels[index + 3], alpha);
  707. }
  708. // Set the pixel data for the image.
  709. i.getRaster().setPixels(0, 0, iw, ih, pixels);
  710. image = i;
  711. }
  712. if (stretch) {
  713. // themeBlueprintComputeHints();
  714. }
  715. if (iw <= 0 || ih <= 0) {
  716. return;
  717. }
  718. Object lastHint;
  719. Object renderingHint = RENDERING_HINT;
  720. if (renderingHint != null && stretch) {
  721. Graphics2D g2 = (Graphics2D)g;
  722. lastHint = g2.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
  723. if (lastHint == null) {
  724. lastHint = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
  725. }
  726. g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
  727. renderingHint);
  728. }
  729. else {
  730. lastHint = null;
  731. }
  732. if (!stretch) {
  733. if (center) {
  734. /* Center the image. */
  735. blueprintRender(image, g, 0, 0, iw, ih, x + (w / 2) - (iw / 2),
  736. y + (h / 2) - (ih / 2), iw, ih);
  737. }
  738. else {
  739. /* Tile the image. */
  740. int lastIY = 0;
  741. for (int yCounter = y, maxY = y + h; yCounter < maxY;
  742. yCounter += (ih - lastIY), lastIY = 0) {
  743. int lastIX = 0;
  744. for (int xCounter = x, maxX = x + w; xCounter < maxX;
  745. xCounter += (iw - lastIX), lastIX = 0) {
  746. int dx2 = Math.min(maxX, xCounter + iw - lastIX);
  747. int dy2 = Math.min(maxY, yCounter + ih - lastIY);
  748. if (intersectsClip(xCounter, yCounter, dx2, dy2)) {
  749. g.drawImage(image, xCounter, yCounter, dx2, dy2,
  750. lastIX, lastIY, lastIX + dx2 -xCounter,
  751. lastIY + dy2 - yCounter, null);
  752. }
  753. }
  754. }
  755. }
  756. }
  757. else {
  758. int srcX[] = new int[4];
  759. int srcY[] = new int[4];
  760. int destX[] = new int[4];
  761. int destY[] = new int[4];
  762. srcX[0] = 0;
  763. srcX[1] = insets.left;
  764. srcX[2] = iw - insets.right;
  765. srcX[3] = iw;
  766. srcY[0] = 0;
  767. srcY[1] = insets.top;
  768. srcY[2] = ih - insets.bottom;
  769. srcY[3] = ih;
  770. destX[0] = x;
  771. destX[1] = x + insets.left;
  772. destX[2] = x + w - insets.right;
  773. destX[3] = x + w;
  774. destY[0] = y;
  775. destY[1] = y + insets.top;
  776. destY[2] = y + h - insets.bottom;
  777. destY[3] = y + h;
  778. /* Scale the image. */
  779. if ((componentMask & COMPONENT_ALL) != 0) {
  780. componentMask = (COMPONENT_ALL - 1) & ~componentMask;
  781. }
  782. // top left
  783. if ((componentMask & COMPONENT_NORTH_WEST) != 0) {
  784. blueprintRender(image, g,
  785. srcX[0], srcY[0],
  786. srcX[1] - srcX[0], srcY[1] - srcY[0],
  787. destX[0], destY[0],
  788. destX[1] - destX[0], destY[1] - destY[0]);
  789. }
  790. // top
  791. if ((componentMask & COMPONENT_NORTH) != 0) {
  792. blueprintRender(image, g,
  793. srcX[1], srcY[0],
  794. srcX[2] - srcX[1], srcY[1] - srcY[0],
  795. destX[1], destY[0],
  796. destX[2] - destX[1], destY[1] - destY[0]);
  797. }
  798. // top right
  799. if ((componentMask & COMPONENT_NORTH_EAST) != 0) {
  800. blueprintRender(image, g,
  801. srcX[2], srcY[0],
  802. srcX[3] - srcX[2], srcY[1] - srcY[0],
  803. destX[2], destY[0],
  804. destX[3] - destX[2], destY[1] - destY[0]);
  805. }
  806. // left
  807. if ((componentMask & COMPONENT_WEST) != 0) {
  808. blueprintRender(image, g,
  809. srcX[0], srcY[1],
  810. srcX[1] - srcX[0], srcY[2] - srcY[1],
  811. destX[0], destY[1],
  812. destX[1] - destX[0], destY[2] - destY[1]);
  813. }
  814. // center
  815. if ((componentMask & COMPONENT_CENTER) != 0) {
  816. blueprintRender(image, g,
  817. srcX[1], srcY[1],
  818. srcX[2] - srcX[1], srcY[2] - srcY[1],
  819. destX[1], destY[1],
  820. destX[2] - destX[1], destY[2] - destY[1]);
  821. }
  822. // right
  823. if ((componentMask & COMPONENT_EAST) != 0) {
  824. blueprintRender(image, g,
  825. srcX[2], srcY[1],
  826. srcX[3] - srcX[2], srcY[2] - srcY[1],
  827. destX[2], destY[1],
  828. destX[3] - destX[2], destY[2] - destY[1]);
  829. }
  830. // bottom left
  831. if ((componentMask & COMPONENT_SOUTH_WEST) != 0) {
  832. blueprintRender(image, g,
  833. srcX[0], srcY[2],
  834. srcX[1] - srcX[0], srcY[3] - srcY[2],
  835. destX[0], destY[2],
  836. destX[1] - destX[0], destY[3] - destY[2]);
  837. }
  838. // bottom
  839. if ((componentMask & COMPONENT_SOUTH) != 0) {
  840. blueprintRender(image, g,
  841. srcX[1], srcY[2],
  842. srcX[2] - srcX[1], srcY[3] - srcY[2],
  843. destX[1], destY[2],
  844. destX[2] - destX[1], destY[3] - destY[2]);
  845. }
  846. // bottom right
  847. if ((componentMask & COMPONENT_SOUTH_EAST) != 0) {
  848. blueprintRender(image, g,
  849. srcX[2], srcY[2],
  850. srcX[3] - srcX[2], srcY[3] - srcY[2],
  851. destX[2], destY[2],
  852. destX[3] - destX[2], destY[3] - destY[2]);
  853. }
  854. }
  855. if (lastHint != null) {
  856. ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
  857. lastHint);
  858. }
  859. }
  860. /**
  861. * Draws a portion of an image stretched.
  862. *
  863. * @param image Image to render.
  864. * @param g Graphics to render to
  865. * @param srcX X origin to draw from
  866. * @param srxY Y origin to draw from
  867. * @param srcWidth Width of source
  868. * @param srcHeight Height of source
  869. * @param destX X origin to draw to
  870. * @param destY Y origin to draw to
  871. * @param destWidth Width of destination
  872. * @param destHeight Height of destination
  873. */
  874. private void blueprintRender(Image image, Graphics g,
  875. int srcX, int srcY, int srcWidth, int srcHeight,
  876. int destX, int destY, int destWidth, int destHeight) {
  877. if (destWidth <= 0 || destHeight <= 0 ||
  878. !intersectsClip(destX, destY,
  879. destX + destWidth, destY + destHeight)) {
  880. // Bogus location, nothing to paint
  881. return;
  882. }
  883. if (srcWidth == 0 && srcHeight == 0) {
  884. // Paint bilinear gradient.
  885. } else if (srcHeight == 0 && destHeight == srcHeight) {
  886. // Paint horizontal gradient.
  887. } else if (srcHeight == 0 && destWidth == srcWidth) {
  888. // Paint vertical gradient.
  889. }
  890. g.drawImage(image, destX, destY, destX + destWidth, destY + destHeight,
  891. srcX, srcY, srcX + srcWidth, srcY + srcHeight, null);
  892. }
  893. private boolean hasAncestorOfTypeFromList(JComponent c, ArrayList list) {
  894. if (list == null) {
  895. return false;
  896. }
  897. Iterator itr = list.iterator();
  898. while (itr.hasNext()) {
  899. if (hasAncestorOfType(c, (String)itr.next())) {
  900. return true;
  901. }
  902. }
  903. return false;
  904. }
  905. private boolean hasAncestorOfType(JComponent c, String parentType) {
  906. String type = null;
  907. while (c != null) {
  908. type = getComponentType(c);
  909. if (type == parentType) {
  910. return true;
  911. }
  912. if (c.getParent() instanceof JComponent) {
  913. c = (JComponent)c.getParent();
  914. } else {
  915. c = null;
  916. }
  917. }
  918. return false;
  919. }
  920. private String getComponentType(JComponent c) {
  921. return GTKStyleFactory.gtkClassFor(SynthLookAndFeel.getRegion(c));
  922. }
  923. private SynthStyle getStyle(JComponent c, ComponentUI ui) {
  924. if (ui instanceof SynthUI) {
  925. SynthContext parentContext = ((SynthUI)ui).getContext(c);
  926. // Note that we don't dispose of the context here, while this
  927. // isn't good, it just means we won't be recycling as often as
  928. // we can.
  929. return parentContext.getStyle();
  930. }
  931. return null;
  932. }
  933. /**
  934. * Returns true if the passed in region intersects the clip.
  935. */
  936. private boolean intersectsClip(int x1, int y1, int x2, int y2) {
  937. return ((x2 < x1 || x2 > _clipX1) &&
  938. (y2 < y1 || y2 > _clipY1) &&
  939. (_clipX2 < _clipX1 || _clipX2 > x1) &&
  940. (_clipY2 < _clipY1 || _clipY2 > y1));
  941. }
  942. /**
  943. * Convert RGB to HLS.
  944. *
  945. * @param r Red
  946. * @param g Green
  947. * @param b Blue
  948. * @return Color Where red = hue, green = lightness and blue = saturation.
  949. */
  950. private Color RGBtoHLS(int r, int g, int b) {
  951. int h, l, s;
  952. int min, max;
  953. int delta;
  954. if (r > g) {
  955. max = Math.max(r, b);
  956. min = Math.min(g, b);
  957. } else {
  958. max = Math.max(g, b);
  959. min = Math.min(r, b);
  960. }
  961. l = (max + min) / 2;
  962. if (max == min) {
  963. s = 0;
  964. h = 0;
  965. } else {
  966. delta = (max - min);
  967. if (l < 128) {
  968. s = 255 * delta / (max + min);
  969. } else {
  970. s = 255 * delta / (511 - max - min);
  971. }
  972. if (r == max) {
  973. h = (g - b) / delta;
  974. } else if (g == max) {
  975. h = 2 + (b - r) / delta;
  976. } else {
  977. h = 4 + (r - g) / delta;
  978. }
  979. h = (int)(h * 42.5);
  980. if (h < 0) {
  981. h+= 255;
  982. } else if (h > 255) {
  983. h -= 255;
  984. }
  985. }
  986. return new Color(h, l, s);
  987. }
  988. /**
  989. * Convert HLS to RGB.
  990. *
  991. * @param hue Hue
  992. * @param lightness Lightness
  993. * @param saturation Saturation
  994. * @return Color Resulting RGB color.
  995. */
  996. private Color HLStoRGB(int hue, int lightness, int saturation) {
  997. double h = hue;
  998. double l = lightness;
  999. double s = saturation;
  1000. double m1, m2;
  1001. if (s == 0) {
  1002. hue = lightness;
  1003. saturation = lightness;
  1004. } else {
  1005. if (l < 128) {
  1006. m2 = (l * (255 + s)) / 65025.0;
  1007. } else {
  1008. m2 = (l + s - (l * s) / 255.0) / 255.0;
  1009. }
  1010. m1 = (l / 127.5) - m2;
  1011. hue = HLSvalue(m1, m2, h + 85);
  1012. lightness = HLSvalue(m1, m2, h);
  1013. saturation = HLSvalue(m1, m2, h - 85);
  1014. }
  1015. return new Color(hue, lightness, saturation);
  1016. }
  1017. private int HLSvalue(double n1, double n2, double hue) {
  1018. double value;
  1019. if (hue > 255) {
  1020. hue -= 255;
  1021. } else if (hue < 0) {
  1022. hue += 255;
  1023. }
  1024. if (hue < 42.5) {
  1025. value = n1 + (n2 - n1) * (hue / 42.5);
  1026. } else if (hue < 127.5) {
  1027. value = n2;
  1028. } else if (hue < 170) {
  1029. value = n1 + (n2 - n1) * ((170 - hue) / 42.5);
  1030. } else {
  1031. value = n1;
  1032. }
  1033. return (int)(value * 255);
  1034. }
  1035. }