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