1 /* 2 * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.java.swing.plaf.gtk; 26 27 import sun.awt.ModalExclude; 28 import sun.awt.SunToolkit; 29 import sun.awt.UNIXToolkit; 30 31 import javax.swing.plaf.synth.*; 32 import java.awt.*; 33 import javax.swing.*; 34 import javax.swing.border.*; 35 import javax.swing.plaf.*; 36 import com.sun.java.swing.plaf.gtk.GTKConstants.ArrowType; 37 import com.sun.java.swing.plaf.gtk.GTKConstants.ExpanderStyle; 38 import com.sun.java.swing.plaf.gtk.GTKConstants.Orientation; 39 import com.sun.java.swing.plaf.gtk.GTKConstants.PositionType; 40 import com.sun.java.swing.plaf.gtk.GTKConstants.ShadowType; 41 import java.awt.image.BufferedImage; 42 import java.lang.reflect.InvocationTargetException; 43 import java.lang.reflect.Method; 44 45 /** 46 * @author Joshua Outwater 47 * @author Scott Violet 48 */ 49 // Need to support: 50 // default_outside_border: Insets when default. 51 // interior_focus: Indicates if focus should appear inside border, or 52 // outside border. 53 // focus-line-width: Integer giving size of focus border 54 // focus-padding: Integer giving padding between border and focus 55 // indicator. 56 // focus-line-pattern: 57 // 58 class GTKPainter extends SynthPainter { 59 private static final PositionType[] POSITIONS = { 60 PositionType.BOTTOM, PositionType.RIGHT, 61 PositionType.TOP, PositionType.LEFT 62 }; 63 64 private static final ShadowType[] SHADOWS = { 65 ShadowType.NONE, ShadowType.IN, ShadowType.OUT, 66 ShadowType.ETCHED_IN, ShadowType.OUT 67 }; 68 69 private static final GTKEngine ENGINE = GTKEngine.INSTANCE; 70 static final GTKPainter INSTANCE = new GTKPainter(); 71 GTKPainter()72 private GTKPainter() { 73 } 74 getName(SynthContext context)75 private String getName(SynthContext context) { 76 return (context.getRegion().isSubregion()) ? null : 77 context.getComponent().getName(); 78 } 79 paintCheckBoxBackground(SynthContext context, Graphics g, int x, int y, int w, int h)80 public void paintCheckBoxBackground(SynthContext context, 81 Graphics g, int x, int y, int w, int h) { 82 paintRadioButtonBackground(context, g, x, y, w, h); 83 } 84 paintCheckBoxMenuItemBackground(SynthContext context, Graphics g, int x, int y, int w, int h)85 public void paintCheckBoxMenuItemBackground(SynthContext context, 86 Graphics g, int x, int y, int w, int h) { 87 paintRadioButtonMenuItemBackground(context, g, x, y, w, h); 88 } 89 90 // FORMATTED_TEXT_FIELD paintFormattedTextFieldBackground(SynthContext context, Graphics g, int x, int y, int w, int h)91 public void paintFormattedTextFieldBackground(SynthContext context, 92 Graphics g, int x, int y, 93 int w, int h) { 94 paintTextBackground(context, g, x, y, w, h); 95 } 96 97 // 98 // TOOL_BAR_DRAG_WINDOW 99 // paintToolBarDragWindowBackground(SynthContext context, Graphics g, int x, int y, int w, int h)100 public void paintToolBarDragWindowBackground(SynthContext context, 101 Graphics g, int x, int y, 102 int w, int h) { 103 paintToolBarBackground(context, g, x, y, w, h); 104 } 105 106 107 // 108 // TOOL_BAR 109 // paintToolBarBackground(SynthContext context, Graphics g, int x, int y, int w, int h)110 public void paintToolBarBackground(SynthContext context, 111 Graphics g, int x, int y, 112 int w, int h) { 113 Region id = context.getRegion(); 114 int state = context.getComponentState(); 115 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 116 int orientation = ((JToolBar)context.getComponent()).getOrientation(); 117 synchronized (UNIXToolkit.GTK_LOCK) { 118 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, 119 state, orientation)) 120 { 121 ENGINE.startPainting(g, x, y, w, h, id, state, orientation); 122 ENGINE.paintBox(g, context, id, gtkState, ShadowType.OUT, 123 "handlebox_bin", x, y, w, h); 124 ENGINE.finishPainting(); 125 } 126 } 127 } 128 paintToolBarContentBackground(SynthContext context, Graphics g, int x, int y, int w, int h)129 public void paintToolBarContentBackground(SynthContext context, 130 Graphics g, 131 int x, int y, int w, int h) { 132 Region id = context.getRegion(); 133 int orientation = ((JToolBar)context.getComponent()).getOrientation(); 134 synchronized (UNIXToolkit.GTK_LOCK) { 135 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, orientation)) { 136 ENGINE.startPainting(g, x, y, w, h, id, orientation); 137 ENGINE.paintBox(g, context, id, SynthConstants.ENABLED, 138 ShadowType.OUT, "toolbar", x, y, w, h); 139 ENGINE.finishPainting(); 140 } 141 } 142 } 143 144 // 145 // PASSWORD_FIELD 146 // paintPasswordFieldBackground(SynthContext context, Graphics g, int x, int y, int w, int h)147 public void paintPasswordFieldBackground(SynthContext context, 148 Graphics g, int x, int y, 149 int w, int h) { 150 paintTextBackground(context, g, x, y, w, h); 151 } 152 153 // 154 // TEXT_FIELD 155 // paintTextFieldBackground(SynthContext context, Graphics g, int x, int y, int w, int h)156 public void paintTextFieldBackground(SynthContext context, Graphics g, 157 int x, int y, int w, int h) { 158 if (getName(context) == "Tree.cellEditor") { 159 paintTreeCellEditorBackground(context, g, x, y, w, h); 160 } else { 161 paintTextBackground(context, g, x, y, w, h); 162 } 163 } 164 165 // 166 // RADIO_BUTTON 167 // 168 // NOTE: this is called for JCheckBox too paintRadioButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h)169 public void paintRadioButtonBackground(SynthContext context, 170 Graphics g, int x, int y, 171 int w, int h) { 172 Region id = context.getRegion(); 173 int gtkState = GTKLookAndFeel.synthStateToGTKState( 174 id, context.getComponentState()); 175 if (gtkState == SynthConstants.MOUSE_OVER) { 176 synchronized (UNIXToolkit.GTK_LOCK) { 177 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 178 ENGINE.startPainting(g, x, y, w, h, id); 179 ENGINE.paintFlatBox(g, context, id, 180 SynthConstants.MOUSE_OVER, ShadowType.ETCHED_OUT, 181 "checkbutton", x, y, w, h, ColorType.BACKGROUND); 182 ENGINE.finishPainting(); 183 } 184 } 185 } 186 } 187 188 // 189 // RADIO_BUTTON_MENU_ITEM 190 // 191 // NOTE: this is called for JCheckBoxMenuItem too paintRadioButtonMenuItemBackground(SynthContext context, Graphics g, int x, int y, int w, int h)192 public void paintRadioButtonMenuItemBackground(SynthContext context, 193 Graphics g, int x, int y, 194 int w, int h) { 195 Region id = context.getRegion(); 196 int gtkState = GTKLookAndFeel.synthStateToGTKState( 197 id, context.getComponentState()); 198 if (gtkState == SynthConstants.MOUSE_OVER) { 199 synchronized (UNIXToolkit.GTK_LOCK) { 200 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 201 ShadowType shadow = (GTKLookAndFeel.is2_2() ? 202 ShadowType.NONE : ShadowType.OUT); 203 ENGINE.startPainting(g, x, y, w, h, id); 204 ENGINE.paintBox(g, context, id, gtkState, 205 shadow, "menuitem", x, y, w, h); 206 ENGINE.finishPainting(); 207 } 208 } 209 } 210 } 211 212 // 213 // LABEL 214 // paintLabelBackground(SynthContext context, Graphics g, int x, int y, int w, int h)215 public void paintLabelBackground(SynthContext context, 216 Graphics g, int x, int y, 217 int w, int h) { 218 String name = getName(context); 219 JComponent c = context.getComponent(); 220 Container container = c.getParent(); 221 222 if (name == "TableHeader.renderer" || 223 name == "GTKFileChooser.directoryListLabel" || 224 name == "GTKFileChooser.fileListLabel") { 225 226 paintButtonBackgroundImpl(context, g, Region.BUTTON, "button", 227 x, y, w, h, true, false, false, false); 228 } 229 /* 230 * If the label is a ListCellRenderer and it's in a container 231 * (CellRendererPane) which is in a JComboBox then we paint the label 232 * as a TextField like a gtk_entry for a combobox. 233 */ 234 else if (c instanceof ListCellRenderer && 235 container != null && 236 container.getParent() instanceof JComboBox ) { 237 paintTextBackground(context, g, x, y, w, h); 238 } 239 } 240 241 // 242 // INTERNAL_FRAME 243 // paintInternalFrameBorder(SynthContext context, Graphics g, int x, int y, int w, int h)244 public void paintInternalFrameBorder(SynthContext context, 245 Graphics g, int x, int y, 246 int w, int h) { 247 Metacity.INSTANCE.paintFrameBorder(context, g, x, y, w, h); 248 } 249 250 // 251 // DESKTOP_PANE 252 // paintDesktopPaneBackground(SynthContext context, Graphics g, int x, int y, int w, int h)253 public void paintDesktopPaneBackground(SynthContext context, 254 Graphics g, int x, int y, 255 int w, int h) { 256 // Does not call into ENGINE for better performance 257 fillArea(context, g, x, y, w, h, ColorType.BACKGROUND); 258 } 259 260 // 261 // DESKTOP_ICON 262 // paintDesktopIconBorder(SynthContext context, Graphics g, int x, int y, int w, int h)263 public void paintDesktopIconBorder(SynthContext context, 264 Graphics g, int x, int y, 265 int w, int h) { 266 Metacity.INSTANCE.paintFrameBorder(context, g, x, y, w, h); 267 } 268 paintButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h)269 public void paintButtonBackground(SynthContext context, Graphics g, 270 int x, int y, int w, int h) { 271 String name = getName(context); 272 if (name != null && name.startsWith("InternalFrameTitlePane.")) { 273 Metacity.INSTANCE.paintButtonBackground(context, g, x, y, w, h); 274 275 } else { 276 AbstractButton button = (AbstractButton)context.getComponent(); 277 boolean paintBG = button.isContentAreaFilled() && 278 button.isBorderPainted(); 279 boolean paintFocus = button.isFocusPainted(); 280 boolean defaultCapable = (button instanceof JButton) && 281 ((JButton)button).isDefaultCapable(); 282 boolean toolButton = (button.getParent() instanceof JToolBar); 283 paintButtonBackgroundImpl(context, g, Region.BUTTON, "button", 284 x, y, w, h, paintBG, paintFocus, defaultCapable, toolButton); 285 } 286 } 287 paintButtonBackgroundImpl(SynthContext context, Graphics g, Region id, String detail, int x, int y, int w, int h, boolean paintBackground, boolean paintFocus, boolean defaultCapable, boolean toolButton)288 private void paintButtonBackgroundImpl(SynthContext context, Graphics g, 289 Region id, String detail, int x, int y, int w, int h, 290 boolean paintBackground, boolean paintFocus, 291 boolean defaultCapable, boolean toolButton) { 292 int state = context.getComponentState(); 293 synchronized (UNIXToolkit.GTK_LOCK) { 294 if (ENGINE.paintCachedImage(g, x, y, w, h, id, state, detail, 295 paintBackground, paintFocus, defaultCapable, toolButton)) { 296 return; 297 } 298 ENGINE.startPainting(g, x, y, w, h, id, state, detail, 299 paintBackground, paintFocus, defaultCapable, toolButton); 300 301 // Paint the default indicator 302 GTKStyle style = (GTKStyle)context.getStyle(); 303 if (defaultCapable && !toolButton) { 304 Insets defaultInsets = style.getClassSpecificInsetsValue( 305 context, "default-border", 306 GTKStyle.BUTTON_DEFAULT_BORDER_INSETS); 307 308 if (paintBackground && (state & SynthConstants.DEFAULT) != 0) { 309 ENGINE.paintBox(g, context, id, SynthConstants.ENABLED, 310 ShadowType.IN, "buttondefault", x, y, w, h); 311 } 312 x += defaultInsets.left; 313 y += defaultInsets.top; 314 w -= (defaultInsets.left + defaultInsets.right); 315 h -= (defaultInsets.top + defaultInsets.bottom); 316 } 317 318 boolean interiorFocus = style.getClassSpecificBoolValue( 319 context, "interior-focus", true); 320 int focusSize = style.getClassSpecificIntValue( 321 context, "focus-line-width",1); 322 int focusPad = style.getClassSpecificIntValue( 323 context, "focus-padding", 1); 324 325 int totalFocusSize = focusSize + focusPad; 326 int xThickness = style.getXThickness(); 327 int yThickness = style.getYThickness(); 328 329 // Render the box. 330 if (!interiorFocus && 331 (state & SynthConstants.FOCUSED) == SynthConstants.FOCUSED) { 332 x += totalFocusSize; 333 y += totalFocusSize; 334 w -= 2 * totalFocusSize; 335 h -= 2 * totalFocusSize; 336 } 337 338 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 339 boolean paintBg; 340 if (toolButton) { 341 // Toolbar buttons should only have their background painted 342 // in the PRESSED, SELECTED, or MOUSE_OVER states. 343 paintBg = 344 (gtkState != SynthConstants.ENABLED) && 345 (gtkState != SynthConstants.DISABLED); 346 } else { 347 // Otherwise, always paint the button's background, unless 348 // the user has overridden it and we're in the ENABLED state. 349 paintBg = 350 paintBackground || 351 (gtkState != SynthConstants.ENABLED); 352 } 353 if (paintBg) { 354 ShadowType shadowType = ShadowType.OUT; 355 if ((state & (SynthConstants.PRESSED | 356 SynthConstants.SELECTED)) != 0) { 357 shadowType = ShadowType.IN; 358 } 359 ENGINE.paintBox(g, context, id, gtkState, 360 shadowType, detail, x, y, w, h); 361 } 362 363 // focus 364 if (paintFocus && (state & SynthConstants.FOCUSED) != 0) { 365 if (interiorFocus) { 366 x += xThickness + focusPad; 367 y += yThickness + focusPad; 368 w -= 2 * (xThickness + focusPad); 369 h -= 2 * (yThickness + focusPad); 370 } else { 371 x -= totalFocusSize; 372 y -= totalFocusSize; 373 w += 2 * totalFocusSize; 374 h += 2 * totalFocusSize; 375 } 376 ENGINE.paintFocus(g, context, id, gtkState, detail, x, y, w, h); 377 } 378 ENGINE.finishPainting(); 379 } 380 } 381 382 // 383 // ARROW_BUTTON 384 // paintArrowButtonForeground(SynthContext context, Graphics g, int x, int y, int w, int h, int direction)385 public void paintArrowButtonForeground(SynthContext context, Graphics g, 386 int x, int y, int w, int h, 387 int direction) { 388 Region id = context.getRegion(); 389 Component c = context.getComponent(); 390 String name = c.getName(); 391 392 ArrowType arrowType = null; 393 switch (direction) { 394 case SwingConstants.NORTH: 395 arrowType = ArrowType.UP; break; 396 case SwingConstants.SOUTH: 397 arrowType = ArrowType.DOWN; break; 398 case SwingConstants.EAST: 399 arrowType = ArrowType.RIGHT; break; 400 case SwingConstants.WEST: 401 arrowType = ArrowType.LEFT; break; 402 } 403 404 String detail = "arrow"; 405 if ((name == "ScrollBar.button") || (name == "TabbedPane.button")) { 406 if (arrowType == ArrowType.UP || arrowType == ArrowType.DOWN) { 407 detail = "vscrollbar"; 408 } else { 409 detail = "hscrollbar"; 410 } 411 } else if (name == "Spinner.nextButton" || 412 name == "Spinner.previousButton") { 413 detail = "spinbutton"; 414 } else if (name != "ComboBox.arrowButton") { 415 assert false : "unexpected name: " + name; 416 } 417 418 int gtkState = GTKLookAndFeel.synthStateToGTKState( 419 id, context.getComponentState()); 420 ShadowType shadowType = (gtkState == SynthConstants.PRESSED ? 421 ShadowType.IN : ShadowType.OUT); 422 synchronized (UNIXToolkit.GTK_LOCK) { 423 if (ENGINE.paintCachedImage(g, x, y, w, h, 424 gtkState, name, direction)) { 425 return; 426 } 427 ENGINE.startPainting(g, x, y, w, h, gtkState, name, direction); 428 ENGINE.paintArrow(g, context, id, gtkState, 429 shadowType, arrowType, detail, x, y, w, h); 430 ENGINE.finishPainting(); 431 } 432 } 433 paintArrowButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h)434 public void paintArrowButtonBackground(SynthContext context, 435 Graphics g, int x, int y, int w, int h) { 436 Region id = context.getRegion(); 437 AbstractButton button = (AbstractButton)context.getComponent(); 438 439 String name = button.getName(); 440 String detail = "button"; 441 int direction = SwingConstants.CENTER; 442 if ((name == "ScrollBar.button") || (name == "TabbedPane.button")) { 443 Integer prop = (Integer) 444 button.getClientProperty("__arrow_direction__"); 445 direction = (prop != null) ? 446 prop.intValue() : SwingConstants.WEST; 447 switch (direction) { 448 default: 449 case SwingConstants.EAST: 450 case SwingConstants.WEST: 451 detail = "hscrollbar"; 452 break; 453 case SwingConstants.NORTH: 454 case SwingConstants.SOUTH: 455 detail = "vscrollbar"; 456 break; 457 } 458 } else if (name == "Spinner.previousButton") { 459 detail = "spinbutton_down"; 460 } else if (name == "Spinner.nextButton") { 461 detail = "spinbutton_up"; 462 } else if (name != "ComboBox.arrowButton") { 463 assert false : "unexpected name: " + name; 464 } 465 466 int state = context.getComponentState(); 467 synchronized (UNIXToolkit.GTK_LOCK) { 468 if (ENGINE.paintCachedImage(g, x, y, w, h, id, 469 state, detail, direction)) 470 { 471 return; 472 } 473 ENGINE.startPainting(g, x, y, w, h, id, 474 state, detail, direction); 475 476 if (detail.startsWith("spin")) { 477 /* 478 * The ubuntulooks engine (and presumably others) expect us to 479 * first draw the full "spinbutton" background, and then draw 480 * the individual "spinbutton_up/down" buttons on top of that. 481 * Note that it is the state of the JSpinner (not its arrow 482 * button) that determines how we draw this background. 483 */ 484 int spinState = button.getParent().isEnabled() ? 485 SynthConstants.ENABLED : SynthConstants.DISABLED; 486 int mody = (detail == "spinbutton_up") ? y : y-h; 487 int modh = h*2; 488 ENGINE.paintBox(g, context, id, spinState, 489 ShadowType.IN, "spinbutton", 490 x, mody, w, modh); 491 } 492 493 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 494 ShadowType shadowType = ShadowType.OUT; 495 if ((gtkState & (SynthConstants.PRESSED | 496 SynthConstants.SELECTED)) != 0) 497 { 498 shadowType = ShadowType.IN; 499 } 500 ENGINE.paintBox(g, context, id, gtkState, 501 shadowType, detail, 502 x, y, w, h); 503 504 ENGINE.finishPainting(); 505 } 506 } 507 508 509 // 510 // LIST 511 // paintListBackground(SynthContext context, Graphics g, int x, int y, int w, int h)512 public void paintListBackground(SynthContext context, Graphics g, 513 int x, int y, int w, int h) { 514 // Does not call into ENGINE for better performance 515 fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND); 516 } 517 paintMenuBarBackground(SynthContext context, Graphics g, int x, int y, int w, int h)518 public void paintMenuBarBackground(SynthContext context, Graphics g, 519 int x, int y, int w, int h) { 520 Region id = context.getRegion(); 521 synchronized (UNIXToolkit.GTK_LOCK) { 522 if (ENGINE.paintCachedImage(g, x, y, w, h, id)) { 523 return; 524 } 525 GTKStyle style = (GTKStyle)context.getStyle(); 526 int shadow = style.getClassSpecificIntValue( 527 context, "shadow-type", 2); 528 ShadowType shadowType = SHADOWS[shadow]; 529 int gtkState = GTKLookAndFeel.synthStateToGTKState( 530 id, context.getComponentState()); 531 ENGINE.startPainting(g, x, y, w, h, id); 532 ENGINE.paintBox(g, context, id, gtkState, 533 shadowType, "menubar", x, y, w, h); 534 ENGINE.finishPainting(); 535 } 536 } 537 538 // 539 // MENU 540 // paintMenuBackground(SynthContext context, Graphics g, int x, int y, int w, int h)541 public void paintMenuBackground(SynthContext context, 542 Graphics g, 543 int x, int y, int w, int h) { 544 paintMenuItemBackground(context, g, x, y, w, h); 545 } 546 547 // This is called for both MENU and MENU_ITEM paintMenuItemBackground(SynthContext context, Graphics g, int x, int y, int w, int h)548 public void paintMenuItemBackground(SynthContext context, 549 Graphics g, 550 int x, int y, int w, int h) { 551 int gtkState = GTKLookAndFeel.synthStateToGTKState( 552 context.getRegion(), context.getComponentState()); 553 if (gtkState == SynthConstants.MOUSE_OVER) { 554 Region id = Region.MENU_ITEM; 555 synchronized (UNIXToolkit.GTK_LOCK) { 556 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 557 ShadowType shadow = (GTKLookAndFeel.is2_2() ? 558 ShadowType.NONE : ShadowType.OUT); 559 ENGINE.startPainting(g, x, y, w, h, id); 560 ENGINE.paintBox(g, context, id, gtkState, shadow, 561 "menuitem", x, y, w, h); 562 ENGINE.finishPainting(); 563 } 564 } 565 } 566 } 567 paintPopupMenuBackground(SynthContext context, Graphics g, int x, int y, int w, int h)568 public void paintPopupMenuBackground(SynthContext context, Graphics g, 569 int x, int y, int w, int h) { 570 Region id = context.getRegion(); 571 int gtkState = GTKLookAndFeel.synthStateToGTKState( 572 id, context.getComponentState()); 573 boolean isHW = SunToolkit.getHeavyweightComponent( 574 context.getComponent()) instanceof ModalExclude; 575 synchronized (UNIXToolkit.GTK_LOCK) { 576 if (ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, isHW)) { 577 return; 578 } 579 ENGINE.startPainting(g, x, y, w, h, id, gtkState); 580 ENGINE.paintBox(g, context, id, gtkState, 581 ShadowType.OUT, "menu", x, y, w, h); 582 583 GTKStyle style = (GTKStyle)context.getStyle(); 584 Insets insets = style.getInsets(context, null); 585 ENGINE.paintBackground(g, context, id, gtkState, 586 style.getGTKColor(context, gtkState, GTKColorType.BACKGROUND), 587 x + insets.left, y + insets.top, w - insets.left - insets.right, 588 h - insets.top - insets.bottom); 589 BufferedImage img = ENGINE.finishPainting(); 590 if(!isHW) { 591 int border = img.getRGB(0, h / 2); 592 if (img != null && border == img.getRGB(w / 2, h / 2)) { 593 // fix no menu borders in Adwaita theme 594 Graphics g2 = img.getGraphics(); 595 Color c = new Color(border); 596 g2.setColor(new Color(Math.max((int) (c.getRed() * 0.8), 0), 597 Math.max((int) (c.getGreen() * 0.8), 0), 598 Math.max((int) (c.getBlue() * 0.8), 0))); 599 g2.drawLine(0, 0, w - 1, 0); 600 g2.drawLine(w - 1, 0, w - 1, h - 1); 601 g2.drawLine(0, h - 1, 0, 1); 602 g2.setColor(c.darker()); 603 g2.drawLine(w - 1, h - 1, 0, h - 1); 604 g2.dispose(); 605 g.drawImage(img, x, y, null); 606 } 607 } 608 } 609 } 610 paintProgressBarBackground(SynthContext context, Graphics g, int x, int y, int w, int h)611 public void paintProgressBarBackground(SynthContext context, 612 Graphics g, 613 int x, int y, int w, int h) { 614 Region id = context.getRegion(); 615 synchronized (UNIXToolkit.GTK_LOCK) { 616 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 617 ENGINE.startPainting(g, x, y, w, h, id); 618 ENGINE.paintBox(g, context, id, SynthConstants.ENABLED, 619 ShadowType.IN, "trough", x, y, w, h); 620 ENGINE.finishPainting(); 621 } 622 } 623 } 624 paintProgressBarForeground(SynthContext context, Graphics g, int x, int y, int w, int h, int orientation)625 public void paintProgressBarForeground(SynthContext context, Graphics g, 626 int x, int y, int w, int h, 627 int orientation) { 628 Region id = context.getRegion(); 629 synchronized (UNIXToolkit.GTK_LOCK) { 630 // Note that we don't call paintCachedImage() here. Since the 631 // progress bar foreground is painted differently for each value 632 // it would be wasteful to try to cache an image for each state, 633 // so instead we simply avoid caching in this case. 634 if (w <= 0 || h <= 0) { 635 return; 636 } 637 ENGINE.startPainting(g, x, y, w, h, id, "fg"); 638 ENGINE.paintBox(g, context, id, SynthConstants.MOUSE_OVER, 639 ShadowType.OUT, "bar", x, y, w, h); 640 ENGINE.finishPainting(false); // don't bother caching the image 641 } 642 } 643 paintViewportBorder(SynthContext context, Graphics g, int x, int y, int w, int h)644 public void paintViewportBorder(SynthContext context, Graphics g, 645 int x, int y, int w, int h) { 646 Region id = context.getRegion(); 647 synchronized (UNIXToolkit.GTK_LOCK) { 648 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 649 ENGINE.startPainting(g, x, y, w, h, id); 650 ENGINE.paintShadow(g, context, id, SynthConstants.ENABLED, 651 ShadowType.IN, "scrolled_window", x, y, w, h); 652 ENGINE.finishPainting(); 653 } 654 } 655 } 656 paintSeparatorBackground(SynthContext context, Graphics g, int x, int y, int w, int h, int orientation)657 public void paintSeparatorBackground(SynthContext context, 658 Graphics g, 659 int x, int y, int w, int h, 660 int orientation) { 661 Region id = context.getRegion(); 662 int state = context.getComponentState(); 663 JComponent c = context.getComponent(); 664 665 GTKStyle style = (GTKStyle) context.getStyle(); 666 String detail; 667 // wide-separators are painted using box not line 668 if (style.getClassSpecificBoolValue(context, 669 "wide-separators", false)) { 670 Insets insets = c.getInsets(); 671 x += insets.left; 672 y += insets.top; 673 if (orientation == JSeparator.HORIZONTAL) { 674 w -= (insets.left + insets.right); 675 } else { 676 h -= (insets.top + insets.bottom); 677 } 678 if (GTKLookAndFeel.is3()) { 679 if (id == Region.POPUP_MENU_SEPARATOR) { 680 detail = "menuitem"; 681 h -= (insets.top + insets.bottom); 682 } else { 683 detail = "separator"; 684 } 685 } else { 686 detail = orientation == JSeparator.HORIZONTAL ? 687 "hseparator" : "vseparator"; 688 } 689 synchronized (UNIXToolkit.GTK_LOCK) { 690 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, state, 691 detail, orientation)) { 692 ENGINE.startPainting(g, x, y, w, h, id, state, 693 detail, orientation); 694 ENGINE.paintBox(g, context, id, state, 695 ShadowType.ETCHED_OUT, detail, x, y, w, h); 696 ENGINE.finishPainting(); 697 } 698 } 699 return; 700 } 701 702 /* 703 * Note: In theory, the style's x/y thickness values would determine 704 * the width of the separator content. In practice, however, some 705 * engines will render a line that is wider than the corresponding 706 * thickness value. For example, ubuntulooks reports x/y thickness 707 * values of 1 for separators, but always renders a 2-pixel wide line. 708 * As a result of all this, we need to be careful not to restrict 709 * the w/h values below too much, so that the full thickness of the 710 * rendered line will be captured by our image caching code. 711 */ 712 if (c instanceof JToolBar.Separator) { 713 /* 714 * GTK renders toolbar separators differently in that an 715 * artificial padding is added to each end of the separator. 716 * The value of 0.2f below is derived from the source code of 717 * gtktoolbar.c in the current version of GTK+ (2.8.20 at the 718 * time of this writing). Specifically, the relevant values are: 719 * SPACE_LINE_DIVISION 10.0 720 * SPACE_LINE_START 2.0 721 * SPACE_LINE_END 8.0 722 * These are used to determine the distance from the top (or left) 723 * edge of the toolbar to the other edge. So for example, the 724 * starting/top point of a vertical separator is 2/10 of the 725 * height of a horizontal toolbar away from the top edge, which 726 * is how we arrive at 0.2f below. Likewise, the ending/bottom 727 * point is 8/10 of the height away from the top edge, or in other 728 * words, it is 2/10 away from the bottom edge, which is again 729 * how we arrive at the 0.2f value below. 730 * 731 * The separator is also centered horizontally or vertically, 732 * depending on its orientation. This was determined empirically 733 * and by examining the code referenced above. 734 */ 735 detail = "toolbar"; 736 float pct = 0.2f; 737 JToolBar.Separator sep = (JToolBar.Separator)c; 738 Dimension size = sep.getSeparatorSize(); 739 if (orientation == JSeparator.HORIZONTAL) { 740 x += (int)(w * pct); 741 w -= (int)(w * pct * 2); 742 y += (size.height - style.getYThickness()) / 2; 743 } else { 744 y += (int)(h * pct); 745 h -= (int)(h * pct * 2); 746 x += (size.width - style.getXThickness()) / 2; 747 } 748 } else { 749 // For regular/menu separators, we simply subtract out the insets. 750 detail = "separator"; 751 Insets insets = c.getInsets(); 752 x += insets.left; 753 y += insets.top; 754 if (orientation == JSeparator.HORIZONTAL) { 755 w -= (insets.left + insets.right); 756 } else { 757 h -= (insets.top + insets.bottom); 758 } 759 } 760 761 synchronized (UNIXToolkit.GTK_LOCK) { 762 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, 763 state, detail, orientation)) { 764 ENGINE.startPainting(g, x, y, w, h, id, 765 state, detail, orientation); 766 if (orientation == JSeparator.HORIZONTAL) { 767 ENGINE.paintHline(g, context, id, state, 768 detail, x, y, w, h); 769 } else { 770 ENGINE.paintVline(g, context, id, state, 771 detail, x, y, w, h); 772 } 773 ENGINE.finishPainting(); 774 } 775 } 776 } 777 paintSliderTrackBackground(SynthContext context, Graphics g, int x, int y, int w,int h)778 public void paintSliderTrackBackground(SynthContext context, 779 Graphics g, 780 int x, int y, int w,int h) { 781 Region id = context.getRegion(); 782 int state = context.getComponentState(); 783 784 // For focused sliders, we paint focus rect outside the bounds passed. 785 // Need to adjust for that. 786 boolean focused = ((state & SynthConstants.FOCUSED) != 0); 787 int focusSize = 0; 788 if (focused) { 789 GTKStyle style = (GTKStyle)context.getStyle(); 790 focusSize = style.getClassSpecificIntValue( 791 context, "focus-line-width", 1) + 792 style.getClassSpecificIntValue( 793 context, "focus-padding", 1); 794 x -= focusSize; 795 y -= focusSize; 796 w += focusSize * 2; 797 h += focusSize * 2; 798 } 799 800 // The ubuntulooks engine paints slider troughs differently depending 801 // on the current slider value and its component orientation. 802 JSlider slider = (JSlider)context.getComponent(); 803 if (GTKLookAndFeel.is3()) { 804 if (slider.getOrientation() == JSlider.VERTICAL) { 805 y += 1; 806 h -= 2; 807 } else { 808 x += 1; 809 w -= 2; 810 } 811 } 812 double value = slider.getValue(); 813 double min = slider.getMinimum(); 814 double max = slider.getMaximum(); 815 double visible = 20; // not used for sliders; any value will work 816 817 synchronized (UNIXToolkit.GTK_LOCK) { 818 // Note that we don't call paintCachedImage() here. Since some 819 // engines (e.g. ubuntulooks) paint the slider background 820 // differently for any given slider value, it would be wasteful 821 // to try to cache an image for each state, so instead we simply 822 // avoid caching in this case. 823 if (w <= 0 || h <= 0) { 824 return; 825 } 826 ENGINE.startPainting(g, x, y, w, h, id, state, value); 827 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 828 ENGINE.setRangeValue(context, id, value, min, max, visible); 829 ENGINE.paintBox(g, context, id, gtkState, ShadowType.IN, 830 "trough", x + focusSize, y + focusSize, 831 w - 2 * focusSize, h - 2 * focusSize); 832 if (focused) { 833 ENGINE.paintFocus(g, context, id, SynthConstants.ENABLED, 834 "trough", x, y, w, h); 835 } 836 ENGINE.finishPainting(false); // don't bother caching the image 837 } 838 } 839 paintSliderThumbBackground(SynthContext context, Graphics g, int x, int y, int w, int h, int dir)840 public void paintSliderThumbBackground(SynthContext context, 841 Graphics g, int x, int y, int w, int h, int dir) { 842 Region id = context.getRegion(); 843 int gtkState = GTKLookAndFeel.synthStateToGTKState( 844 id, context.getComponentState()); 845 boolean hasFocus = GTKLookAndFeel.is3() && 846 ((context.getComponentState() & SynthConstants.FOCUSED) != 0); 847 synchronized (UNIXToolkit.GTK_LOCK) { 848 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, dir, 849 hasFocus)) { 850 Orientation orientation = (dir == JSlider.HORIZONTAL ? 851 Orientation.HORIZONTAL : Orientation.VERTICAL); 852 String detail = (dir == JSlider.HORIZONTAL ? 853 "hscale" : "vscale"); 854 ENGINE.startPainting(g, x, y, w, h, id, gtkState, dir); 855 ENGINE.paintSlider(g, context, id, gtkState, 856 ShadowType.OUT, detail, x, y, w, h, orientation, 857 hasFocus); 858 ENGINE.finishPainting(); 859 } 860 } 861 } 862 863 // 864 // SPINNER 865 // paintSpinnerBackground(SynthContext context, Graphics g, int x, int y, int w, int h)866 public void paintSpinnerBackground(SynthContext context, 867 Graphics g, 868 int x, int y, int w, int h) { 869 // This is handled in paintTextFieldBackground 870 } 871 872 // 873 // SPLIT_PANE_DIVIDER 874 // paintSplitPaneDividerBackground(SynthContext context, Graphics g, int x, int y, int w, int h)875 public void paintSplitPaneDividerBackground(SynthContext context, 876 Graphics g, 877 int x, int y, int w, int h) { 878 Region id = context.getRegion(); 879 int gtkState = GTKLookAndFeel.synthStateToGTKState( 880 id, context.getComponentState()); 881 JSplitPane splitPane = (JSplitPane)context.getComponent(); 882 Orientation orientation = 883 (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT ? 884 Orientation.VERTICAL : Orientation.HORIZONTAL); 885 synchronized (UNIXToolkit.GTK_LOCK) { 886 if (! ENGINE.paintCachedImage(g, x, y, w, h, 887 id, gtkState, orientation)) { 888 ENGINE.startPainting(g, x, y, w, h, id, gtkState, orientation); 889 ENGINE.paintHandle(g, context, id, gtkState, 890 ShadowType.OUT, "paned", x, y, w, h, orientation); 891 ENGINE.finishPainting(); 892 } 893 } 894 } 895 paintSplitPaneDragDivider(SynthContext context, Graphics g,int x, int y, int w, int h, int orientation)896 public void paintSplitPaneDragDivider(SynthContext context, 897 Graphics g,int x, int y, int w, int h, 898 int orientation) { 899 paintSplitPaneDividerForeground(context, g, x, y, w, h, orientation); 900 } 901 paintTabbedPaneContentBackground(SynthContext context, Graphics g, int x, int y, int w, int h)902 public void paintTabbedPaneContentBackground(SynthContext context, 903 Graphics g, int x, int y, int w, int h) { 904 JTabbedPane pane = (JTabbedPane)context.getComponent(); 905 int selectedIndex = pane.getSelectedIndex(); 906 PositionType placement = GTKLookAndFeel.SwingOrientationConstantToGTK( 907 pane.getTabPlacement()); 908 909 int gapStart = 0; 910 int gapSize = 0; 911 if (selectedIndex != -1) { 912 Rectangle tabBounds = pane.getBoundsAt(selectedIndex); 913 914 if (placement == PositionType.TOP || 915 placement == PositionType.BOTTOM) { 916 917 gapStart = tabBounds.x - x; 918 gapSize = tabBounds.width; 919 } 920 else { 921 gapStart = tabBounds.y - y; 922 gapSize = tabBounds.height; 923 } 924 } 925 926 Region id = context.getRegion(); 927 int gtkState = GTKLookAndFeel.synthStateToGTKState( 928 id, context.getComponentState()); 929 synchronized (UNIXToolkit.GTK_LOCK) { 930 if (! ENGINE.paintCachedImage(g, x, y, w, h, 931 id, gtkState, placement, gapStart, gapSize)) { 932 ENGINE.startPainting(g, x, y, w, h, 933 id, gtkState, placement, gapStart, gapSize); 934 ENGINE.paintBoxGap(g, context, id, gtkState, ShadowType.OUT, 935 "notebook", x, y, w, h, placement, gapStart, gapSize); 936 ENGINE.finishPainting(); 937 } 938 } 939 } 940 paintTabbedPaneTabBackground(SynthContext context, Graphics g, int x, int y, int w, int h, int tabIndex)941 public void paintTabbedPaneTabBackground(SynthContext context, 942 Graphics g, 943 int x, int y, int w, int h, 944 int tabIndex) { 945 Region id = context.getRegion(); 946 int state = context.getComponentState(); 947 int gtkState = ((state & SynthConstants.SELECTED) != 0 ? 948 SynthConstants.ENABLED : SynthConstants.PRESSED); 949 JTabbedPane pane = (JTabbedPane)context.getComponent(); 950 int placement = pane.getTabPlacement(); 951 952 synchronized (UNIXToolkit.GTK_LOCK) { 953 if (! ENGINE.paintCachedImage(g, x, y, w, h, 954 id, gtkState, placement, tabIndex)) { 955 PositionType side = POSITIONS[placement - 1]; 956 ENGINE.startPainting(g, x, y, w, h, 957 id, gtkState, placement, tabIndex); 958 ENGINE.paintExtension(g, context, id, gtkState, 959 ShadowType.OUT, "tab", x, y, w, h, side, tabIndex); 960 ENGINE.finishPainting(); 961 } 962 } 963 } 964 965 // 966 // TEXT_PANE 967 // paintTextPaneBackground(SynthContext context, Graphics g, int x, int y, int w, int h)968 public void paintTextPaneBackground(SynthContext context, Graphics g, 969 int x, int y, int w, int h) { 970 paintTextAreaBackground(context, g, x, y, w, h); 971 } 972 973 // 974 // EDITOR_PANE 975 // paintEditorPaneBackground(SynthContext context, Graphics g, int x, int y, int w, int h)976 public void paintEditorPaneBackground(SynthContext context, Graphics g, 977 int x, int y, int w, int h) { 978 paintTextAreaBackground(context, g, x, y, w, h); 979 } 980 981 // 982 // TEXT_AREA 983 // paintTextAreaBackground(SynthContext context, Graphics g, int x, int y, int w, int h)984 public void paintTextAreaBackground(SynthContext context, Graphics g, 985 int x, int y, int w, int h) { 986 // Does not call into ENGINE for better performance 987 fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND); 988 } 989 990 // 991 // TEXT_FIELD 992 // 993 // NOTE: Combobox and Label, Password and FormattedTextField calls this 994 // too. paintTextBackground(SynthContext context, Graphics g, int x, int y, int w, int h)995 private void paintTextBackground(SynthContext context, Graphics g, 996 int x, int y, int w, int h) { 997 // Text is odd in that it uses the TEXT_BACKGROUND vs BACKGROUND. 998 JComponent c = context.getComponent(); 999 Container container = c.getParent(); 1000 Container containerParent = null; 1001 GTKStyle style = (GTKStyle)context.getStyle(); 1002 Region id = context.getRegion(); 1003 int state = context.getComponentState(); 1004 1005 if (c instanceof ListCellRenderer && container != null) { 1006 containerParent = container.getParent(); 1007 if (containerParent instanceof JComboBox 1008 && containerParent.hasFocus()) { 1009 state |= SynthConstants.FOCUSED; 1010 } 1011 } 1012 1013 synchronized (UNIXToolkit.GTK_LOCK) { 1014 if (ENGINE.paintCachedImage(g, x, y, w, h, id, state)) { 1015 return; 1016 } 1017 1018 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 1019 int focusSize = 0; 1020 boolean interiorFocus = style.getClassSpecificBoolValue( 1021 context, "interior-focus", true); 1022 1023 focusSize = style.getClassSpecificIntValue(context, 1024 "focus-line-width",1); 1025 if (!interiorFocus && (state & SynthConstants.FOCUSED) != 0) { 1026 x += focusSize; 1027 y += focusSize; 1028 w -= 2 * focusSize; 1029 h -= 2 * focusSize; 1030 } 1031 1032 int xThickness = style.getXThickness(); 1033 int yThickness = style.getYThickness(); 1034 1035 ENGINE.startPainting(g, x, y, w, h, id, state); 1036 if (GTKLookAndFeel.is3()) { 1037 ENGINE.paintBackground(g, context, id, gtkState, null, 1038 x, y, w, h); 1039 } 1040 ENGINE.paintShadow(g, context, id, gtkState, 1041 ShadowType.IN, "entry", x, y, w, h); 1042 if (!GTKLookAndFeel.is3()) { 1043 ENGINE.paintFlatBox(g, context, id, 1044 gtkState, ShadowType.NONE, "entry_bg", 1045 x + xThickness, 1046 y + yThickness, 1047 w - (2 * xThickness), 1048 h - (2 * yThickness), 1049 ColorType.TEXT_BACKGROUND); 1050 } 1051 1052 if (focusSize > 0 && (state & SynthConstants.FOCUSED) != 0) { 1053 if (!interiorFocus) { 1054 x -= focusSize; 1055 y -= focusSize; 1056 w += 2 * focusSize; 1057 h += 2 * focusSize; 1058 } else { 1059 if (containerParent instanceof JComboBox) { 1060 x += (focusSize + 2); 1061 y += focusSize + (GTKLookAndFeel.is3() ? 3 : 1); 1062 w -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 1); 1063 h -= 2 * focusSize + (GTKLookAndFeel.is3() ? 6 : 2); 1064 } else { 1065 x += focusSize + (GTKLookAndFeel.is3() ? 2 : 0); 1066 y += focusSize + (GTKLookAndFeel.is3() ? 2 :0 ); 1067 w -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 0); 1068 h -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 0); 1069 } 1070 } 1071 ENGINE.paintFocus(g, context, id, gtkState, 1072 "entry", x, y, w, h); 1073 } 1074 ENGINE.finishPainting(); 1075 } 1076 } 1077 paintTreeCellEditorBackground(SynthContext context, Graphics g, int x, int y, int w, int h)1078 private void paintTreeCellEditorBackground(SynthContext context, Graphics g, 1079 int x, int y, int w, int h) { 1080 Region id = context.getRegion(); 1081 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1082 id, context.getComponentState()); 1083 synchronized (UNIXToolkit.GTK_LOCK) { 1084 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState)) { 1085 ENGINE.startPainting(g, x, y, w, h, id, gtkState); 1086 ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE, 1087 "entry_bg", x, y, w, h, ColorType.TEXT_BACKGROUND); 1088 ENGINE.finishPainting(); 1089 } 1090 } 1091 } 1092 1093 1094 // 1095 // ROOT_PANE 1096 // paintRootPaneBackground(SynthContext context, Graphics g, int x, int y, int w, int h)1097 public void paintRootPaneBackground(SynthContext context, Graphics g, 1098 int x, int y, int w, int h) { 1099 // Does not call into ENGINE for better performance 1100 fillArea(context, g, x, y, w, h, GTKColorType.BACKGROUND); 1101 } 1102 1103 // 1104 // TOGGLE_BUTTON 1105 // paintToggleButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h)1106 public void paintToggleButtonBackground(SynthContext context, 1107 Graphics g, 1108 int x, int y, int w, int h) { 1109 Region id = context.getRegion(); 1110 JToggleButton toggleButton = (JToggleButton)context.getComponent(); 1111 boolean paintBG = toggleButton.isContentAreaFilled() && 1112 toggleButton.isBorderPainted(); 1113 boolean paintFocus = toggleButton.isFocusPainted(); 1114 boolean toolButton = (toggleButton.getParent() instanceof JToolBar); 1115 paintButtonBackgroundImpl(context, g, id, "button", 1116 x, y, w, h, 1117 paintBG, paintFocus, false, toolButton); 1118 } 1119 1120 1121 // 1122 // SCROLL_BAR 1123 // paintScrollBarBackground(SynthContext context, Graphics g, int x, int y, int w,int h)1124 public void paintScrollBarBackground(SynthContext context, 1125 Graphics g, 1126 int x, int y, int w,int h) { 1127 Region id = context.getRegion(); 1128 boolean focused = 1129 (context.getComponentState() & SynthConstants.FOCUSED) != 0; 1130 synchronized (UNIXToolkit.GTK_LOCK) { 1131 if (ENGINE.paintCachedImage(g, x, y, w, h, id, focused)) { 1132 return; 1133 } 1134 ENGINE.startPainting(g, x, y, w, h, id, focused); 1135 1136 // Note: the scrollbar insets already include the "trough-border", 1137 // which is needed to position the scrollbar buttons properly. 1138 // But when we render, we need to take the trough border out 1139 // of the equation so that we paint the entire area covered by 1140 // the trough border and the scrollbar content itself. 1141 Insets insets = context.getComponent().getInsets(); 1142 GTKStyle style = (GTKStyle)context.getStyle(); 1143 int troughBorder = 1144 style.getClassSpecificIntValue(context, "trough-border", 1); 1145 insets.left -= troughBorder; 1146 insets.right -= troughBorder; 1147 insets.top -= troughBorder; 1148 insets.bottom -= troughBorder; 1149 1150 ENGINE.paintBox(g, context, id, SynthConstants.PRESSED, 1151 ShadowType.IN, "trough", 1152 x + insets.left, 1153 y + insets.top, 1154 w - insets.left - insets.right, 1155 h - insets.top - insets.bottom); 1156 1157 if (focused) { 1158 ENGINE.paintFocus(g, context, id, 1159 SynthConstants.ENABLED, "trough", x, y, w, h); 1160 } 1161 ENGINE.finishPainting(); 1162 } 1163 } 1164 1165 1166 // 1167 // SCROLL_BAR_THUMB 1168 // paintScrollBarThumbBackground(SynthContext context, Graphics g, int x, int y, int w, int h, int dir)1169 public void paintScrollBarThumbBackground(SynthContext context, 1170 Graphics g, int x, int y, int w, int h, int dir) { 1171 Region id = context.getRegion(); 1172 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1173 id, context.getComponentState()); 1174 1175 // The clearlooks engine paints scrollbar thumbs differently 1176 // depending on the current scroll value (specifically, it will avoid 1177 // rendering a certain line when the thumb is at the starting or 1178 // ending position). Therefore, we normalize the current value to 1179 // the range [0,100] here and then pass it down to setRangeValue() 1180 // so that the native widget is configured appropriately. Note that 1181 // there are really only four values that matter (min, middle, max, 1182 // or fill) so we restrict to one of those four values to avoid 1183 // blowing out the image cache. 1184 JScrollBar sb = (JScrollBar)context.getComponent(); 1185 boolean rtl = 1186 sb.getOrientation() == JScrollBar.HORIZONTAL && 1187 !sb.getComponentOrientation().isLeftToRight(); 1188 double min = 0; 1189 double max = 100; 1190 double visible = 20; 1191 double value; 1192 if (sb.getMaximum() - sb.getMinimum() == sb.getVisibleAmount()) { 1193 // In this case, the thumb fills the entire track, so it is 1194 // touching both ends at the same time 1195 value = 0; 1196 visible = 100; 1197 } else if (sb.getValue() == sb.getMinimum()) { 1198 // At minimum 1199 value = rtl ? 100 : 0; 1200 } else if (sb.getValue() >= sb.getMaximum() - sb.getVisibleAmount()) { 1201 // At maximum 1202 value = rtl ? 0 : 100; 1203 } else { 1204 // Somewhere in between 1205 value = 50; 1206 } 1207 1208 synchronized (UNIXToolkit.GTK_LOCK) { 1209 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, 1210 dir, value, visible, rtl)) 1211 { 1212 ENGINE.startPainting(g, x, y, w, h, id, gtkState, 1213 dir, value, visible, rtl); 1214 Orientation orientation = (dir == JScrollBar.HORIZONTAL ? 1215 Orientation.HORIZONTAL : Orientation.VERTICAL); 1216 ENGINE.setRangeValue(context, id, value, min, max, visible); 1217 ENGINE.paintSlider(g, context, id, gtkState, ShadowType.OUT, 1218 "slider", x, y, w, h, orientation, false); 1219 ENGINE.finishPainting(); 1220 } 1221 } 1222 } 1223 1224 // 1225 // TOOL_TIP 1226 // paintToolTipBackground(SynthContext context, Graphics g, int x, int y, int w,int h)1227 public void paintToolTipBackground(SynthContext context, Graphics g, 1228 int x, int y, int w,int h) { 1229 Region id = context.getRegion(); 1230 synchronized (UNIXToolkit.GTK_LOCK) { 1231 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 1232 ENGINE.startPainting(g, x, y, w, h, id); 1233 ENGINE.paintFlatBox(g, context, id, SynthConstants.ENABLED, 1234 ShadowType.OUT, "tooltip", x, y, w, h, 1235 ColorType.BACKGROUND); 1236 ENGINE.finishPainting(); 1237 } 1238 } 1239 } 1240 1241 1242 // 1243 // TREE_CELL 1244 // paintTreeCellBackground(SynthContext context, Graphics g, int x, int y, int w, int h)1245 public void paintTreeCellBackground(SynthContext context, Graphics g, 1246 int x, int y, int w, int h) { 1247 Region id = context.getRegion(); 1248 int state = context.getComponentState(); 1249 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 1250 synchronized (UNIXToolkit.GTK_LOCK) { 1251 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, state)) { 1252 ENGINE.startPainting(g, x, y, w, h, id, state); 1253 // the string arg should alternate based on row being painted, 1254 // but we currently don't pass that in. 1255 ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE, 1256 "cell_odd", x, y, w, h, ColorType.TEXT_BACKGROUND); 1257 ENGINE.finishPainting(); 1258 } 1259 } 1260 } 1261 paintTreeCellFocus(SynthContext context, Graphics g, int x, int y, int w, int h)1262 public void paintTreeCellFocus(SynthContext context, Graphics g, 1263 int x, int y, int w, int h) { 1264 Region id = Region.TREE_CELL; 1265 int state = context.getComponentState(); 1266 paintFocus(context, g, id, state, "treeview", x, y, w, h); 1267 } 1268 1269 1270 // 1271 // TREE 1272 // paintTreeBackground(SynthContext context, Graphics g, int x, int y, int w, int h)1273 public void paintTreeBackground(SynthContext context, Graphics g, 1274 int x, int y, int w, int h) { 1275 // As far as I can tell, these don't call into the ENGINE. 1276 fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND); 1277 } 1278 1279 1280 // 1281 // VIEWPORT 1282 // paintViewportBackground(SynthContext context, Graphics g, int x, int y, int w, int h)1283 public void paintViewportBackground(SynthContext context, Graphics g, 1284 int x, int y, int w, int h) { 1285 // As far as I can tell, these don't call into the ENGINE. 1286 // Also note that you don't want this to call into the ENGINE 1287 // as if it where to paint a background JViewport wouldn't scroll 1288 // correctly. 1289 fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND); 1290 } 1291 paintFocus(SynthContext context, Graphics g, Region id, int state, String detail, int x, int y, int w, int h)1292 void paintFocus(SynthContext context, Graphics g, Region id, 1293 int state, String detail, int x, int y, int w, int h) { 1294 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 1295 synchronized (UNIXToolkit.GTK_LOCK) { 1296 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, "focus")) { 1297 ENGINE.startPainting(g, x, y, w, h, id, gtkState, "focus"); 1298 ENGINE.paintFocus(g, context, id, gtkState, detail, x, y, w, h); 1299 ENGINE.finishPainting(); 1300 } 1301 } 1302 } 1303 paintMetacityElement(SynthContext context, Graphics g, int gtkState, String detail, int x, int y, int w, int h, ShadowType shadow, ArrowType direction)1304 void paintMetacityElement(SynthContext context, Graphics g, 1305 int gtkState, String detail, int x, int y, int w, int h, 1306 ShadowType shadow, ArrowType direction) { 1307 synchronized (UNIXToolkit.GTK_LOCK) { 1308 if (! ENGINE.paintCachedImage( 1309 g, x, y, w, h, gtkState, detail, shadow, direction)) { 1310 ENGINE.startPainting( 1311 g, x, y, w, h, gtkState, detail, shadow, direction); 1312 if (detail == "metacity-arrow") { 1313 ENGINE.paintArrow(g, context, Region.INTERNAL_FRAME_TITLE_PANE, 1314 gtkState, shadow, direction, "", x, y, w, h); 1315 1316 } else if (detail == "metacity-box") { 1317 ENGINE.paintBox(g, context, Region.INTERNAL_FRAME_TITLE_PANE, 1318 gtkState, shadow, "", x, y, w, h); 1319 1320 } else if (detail == "metacity-vline") { 1321 ENGINE.paintVline(g, context, Region.INTERNAL_FRAME_TITLE_PANE, 1322 gtkState, "", x, y, w, h); 1323 } 1324 ENGINE.finishPainting(); 1325 } 1326 } 1327 } 1328 paintIcon(SynthContext context, Graphics g, Method paintMethod, int x, int y, int w, int h)1329 void paintIcon(SynthContext context, Graphics g, 1330 Method paintMethod, int x, int y, int w, int h) { 1331 int state = context.getComponentState(); 1332 synchronized (UNIXToolkit.GTK_LOCK) { 1333 if (! ENGINE.paintCachedImage(g, x, y, w, h, state, paintMethod)) { 1334 ENGINE.startPainting(g, x, y, w, h, state, paintMethod); 1335 try { 1336 paintMethod.invoke(this, context, g, state, x, y, w, h); 1337 } catch (IllegalAccessException iae) { 1338 assert false; 1339 } catch (InvocationTargetException ite) { 1340 assert false; 1341 } 1342 ENGINE.finishPainting(); 1343 } 1344 } 1345 } 1346 paintIcon(SynthContext context, Graphics g, Method paintMethod, int x, int y, int w, int h, Object direction)1347 void paintIcon(SynthContext context, Graphics g, 1348 Method paintMethod, int x, int y, int w, int h, Object direction) { 1349 int state = context.getComponentState(); 1350 synchronized (UNIXToolkit.GTK_LOCK) { 1351 if (! ENGINE.paintCachedImage(g, 1352 x, y, w, h, state, paintMethod, direction)) { 1353 ENGINE.startPainting(g, 1354 x, y, w, h, state, paintMethod, direction); 1355 try { 1356 paintMethod.invoke(this, context, 1357 g, state, x, y, w, h, direction); 1358 } catch (IllegalAccessException iae) { 1359 assert false; 1360 } catch (InvocationTargetException ite) { 1361 assert false; 1362 } 1363 ENGINE.finishPainting(); 1364 } 1365 } 1366 } 1367 1368 // All icon painting methods are called from under GTK_LOCK 1369 paintTreeExpandedIcon(SynthContext context, Graphics g, int state, int x, int y, int w, int h)1370 public void paintTreeExpandedIcon(SynthContext context, 1371 Graphics g, int state, int x, int y, int w, int h) { 1372 ENGINE.paintExpander(g, context, Region.TREE, 1373 GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state), 1374 ExpanderStyle.EXPANDED, "expander", x, y, w, h); 1375 } 1376 paintTreeCollapsedIcon(SynthContext context, Graphics g, int state, int x, int y, int w, int h)1377 public void paintTreeCollapsedIcon(SynthContext context, 1378 Graphics g, int state, int x, int y, int w, int h) { 1379 ENGINE.paintExpander(g, context, Region.TREE, 1380 GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state), 1381 ExpanderStyle.COLLAPSED, "expander", x, y, w, h); 1382 } 1383 paintCheckBoxIcon(SynthContext context, Graphics g, int state, int x, int y, int w, int h)1384 public void paintCheckBoxIcon(SynthContext context, 1385 Graphics g, int state, int x, int y, int w, int h) { 1386 GTKStyle style = (GTKStyle)context.getStyle(); 1387 int size = style.getClassSpecificIntValue(context, 1388 "indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE); 1389 int offset = style.getClassSpecificIntValue(context, 1390 "indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING); 1391 1392 ENGINE.paintCheck(g, context, Region.CHECK_BOX, "checkbutton", 1393 x+offset, y+offset, size, size); 1394 } 1395 paintRadioButtonIcon(SynthContext context, Graphics g, int state, int x, int y, int w, int h)1396 public void paintRadioButtonIcon(SynthContext context, 1397 Graphics g, int state, int x, int y, int w, int h) { 1398 GTKStyle style = (GTKStyle)context.getStyle(); 1399 int size = style.getClassSpecificIntValue(context, 1400 "indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE); 1401 int offset = style.getClassSpecificIntValue(context, 1402 "indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING); 1403 1404 ENGINE.paintOption(g, context, Region.RADIO_BUTTON, "radiobutton", 1405 x+offset, y+offset, size, size); 1406 } 1407 paintMenuArrowIcon(SynthContext context, Graphics g, int state, int x, int y, int w, int h, ArrowType dir)1408 public void paintMenuArrowIcon(SynthContext context, Graphics g, 1409 int state, int x, int y, int w, int h, ArrowType dir) { 1410 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1411 context.getRegion(), state); 1412 ShadowType shadow = ShadowType.OUT; 1413 if (gtkState == SynthConstants.MOUSE_OVER) { 1414 shadow = ShadowType.IN; 1415 } 1416 if (!GTKLookAndFeel.is3()) { 1417 x += 3; 1418 y += 3; 1419 w = h = 7; 1420 } 1421 ENGINE.paintArrow(g, context, Region.MENU_ITEM, gtkState, shadow, 1422 dir, "menuitem", x, y, w, h); 1423 } 1424 paintCheckBoxMenuItemCheckIcon(SynthContext context, Graphics g, int state, int x, int y, int w, int h)1425 public void paintCheckBoxMenuItemCheckIcon(SynthContext context, 1426 Graphics g, int state, int x, int y, int w, int h) { 1427 1428 GTKStyle style = (GTKStyle)context.getStyle(); 1429 int size = style.getClassSpecificIntValue(context,"indicator-size", 1430 GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE); 1431 1432 ENGINE.paintCheck(g, context, Region.CHECK_BOX_MENU_ITEM, "check", 1433 x + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1434 y + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1435 size, size); 1436 } 1437 paintRadioButtonMenuItemCheckIcon(SynthContext context, Graphics g, int state, int x, int y, int w, int h)1438 public void paintRadioButtonMenuItemCheckIcon(SynthContext context, 1439 Graphics g, int state, int x, int y, int w, int h) { 1440 1441 GTKStyle style = (GTKStyle)context.getStyle(); 1442 int size = style.getClassSpecificIntValue(context,"indicator-size", 1443 GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE); 1444 1445 ENGINE.paintOption(g, context, Region.RADIO_BUTTON_MENU_ITEM, "option", 1446 x + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1447 y + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1448 size, size); 1449 } 1450 paintToolBarHandleIcon(SynthContext context, Graphics g, int state, int x, int y, int w, int h, Orientation orientation)1451 public void paintToolBarHandleIcon(SynthContext context, Graphics g, 1452 int state, int x, int y, int w, int h, Orientation orientation) { 1453 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1454 context.getRegion(), state); 1455 1456 // The orientation parameter passed down by Synth refers to the 1457 // orientation of the toolbar, but the one we pass to GTK refers 1458 // to the orientation of the handle. Therefore, we need to swap 1459 // the value here: horizontal toolbars have vertical handles, and 1460 // vice versa. 1461 orientation = (orientation == Orientation.HORIZONTAL) ? 1462 Orientation.VERTICAL : Orientation.HORIZONTAL; 1463 1464 ENGINE.paintHandle(g, context, Region.TOOL_BAR, gtkState, 1465 ShadowType.OUT, "handlebox", x, y, w, h, orientation); 1466 } 1467 paintAscendingSortIcon(SynthContext context, Graphics g, int state, int x, int y, int w, int h)1468 public void paintAscendingSortIcon(SynthContext context, 1469 Graphics g, int state, int x, int y, int w, int h) { 1470 ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED, 1471 ShadowType.IN, ArrowType.UP, "arrow", x, y, w, h); 1472 } 1473 paintDescendingSortIcon(SynthContext context, Graphics g, int state, int x, int y, int w, int h)1474 public void paintDescendingSortIcon(SynthContext context, 1475 Graphics g, int state, int x, int y, int w, int h) { 1476 ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED, 1477 ShadowType.IN, ArrowType.DOWN, "arrow", x, y, w, h); 1478 } 1479 1480 /* 1481 * Fill an area with color determined from this context's Style using the 1482 * specified GTKColorType 1483 */ fillArea(SynthContext context, Graphics g, int x, int y, int w, int h, ColorType colorType)1484 private void fillArea(SynthContext context, Graphics g, 1485 int x, int y, int w, int h, ColorType colorType) { 1486 if (context.getComponent().isOpaque()) { 1487 Region id = context.getRegion(); 1488 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, 1489 context.getComponentState()); 1490 GTKStyle style = (GTKStyle)context.getStyle(); 1491 1492 g.setColor(style.getGTKColor(context, gtkState, colorType)); 1493 g.fillRect(x, y, w, h); 1494 } 1495 } 1496 1497 // Refer to GTKLookAndFeel for details on this. 1498 @SuppressWarnings("serial") // Superclass is not serializable across versions 1499 static class ListTableFocusBorder extends AbstractBorder implements 1500 UIResource { 1501 1502 private boolean selectedCell; 1503 private boolean focusedCell; 1504 getSelectedCellBorder()1505 public static ListTableFocusBorder getSelectedCellBorder() { 1506 return new ListTableFocusBorder(true, true); 1507 } 1508 getUnselectedCellBorder()1509 public static ListTableFocusBorder getUnselectedCellBorder() { 1510 return new ListTableFocusBorder(false, true); 1511 } 1512 getNoFocusCellBorder()1513 public static ListTableFocusBorder getNoFocusCellBorder() { 1514 return new ListTableFocusBorder(false, false); 1515 } 1516 ListTableFocusBorder(boolean selectedCell, boolean focusedCell)1517 public ListTableFocusBorder(boolean selectedCell, boolean focusedCell) { 1518 this.selectedCell = selectedCell; 1519 this.focusedCell = focusedCell; 1520 } 1521 getContext(Component c)1522 private SynthContext getContext(Component c) { 1523 SynthContext context = null; 1524 1525 ComponentUI ui = null; 1526 if (c instanceof JLabel) { 1527 ui = ((JLabel)c).getUI(); 1528 } 1529 1530 if (ui instanceof SynthUI) { 1531 context = ((SynthUI)ui).getContext((JComponent)c); 1532 } 1533 1534 return context; 1535 } 1536 paintBorder(Component c, Graphics g, int x, int y, int w, int h)1537 public void paintBorder(Component c, Graphics g, int x, int y, 1538 int w, int h) { 1539 if (focusedCell) { 1540 SynthContext context = getContext(c); 1541 int state = (selectedCell? SynthConstants.SELECTED: 1542 SynthConstants.FOCUSED | SynthConstants.ENABLED); 1543 1544 if (context != null) { 1545 GTKPainter.INSTANCE.paintFocus(context, g, 1546 Region.TABLE, state, "", x, y, w, h); 1547 } 1548 } 1549 } 1550 getBorderInsets(Component c, Insets i)1551 public Insets getBorderInsets(Component c, Insets i) { 1552 SynthContext context = getContext(c); 1553 1554 if (context != null) { 1555 i = context.getStyle().getInsets(context, i); 1556 } 1557 1558 return i; 1559 } 1560 isBorderOpaque()1561 public boolean isBorderOpaque() { 1562 return true; 1563 } 1564 } 1565 1566 // TitledBorder implementation for GTK L&F 1567 @SuppressWarnings("serial") // Superclass is not serializable across versions 1568 static class TitledBorder extends AbstractBorder implements UIResource { 1569 paintBorder(Component c, Graphics g, int x, int y, int w, int h)1570 public void paintBorder(Component c, Graphics g, int x, int y, 1571 int w, int h) { 1572 SynthContext context = getContext((JComponent)c); 1573 Region id = context.getRegion(); 1574 int state = context.getComponentState(); 1575 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 1576 1577 synchronized (UNIXToolkit.GTK_LOCK) { 1578 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 1579 ENGINE.startPainting(g, x, y, w, h, id); 1580 ENGINE.paintShadow(g, context, id, gtkState, ShadowType.ETCHED_IN, 1581 "frame", x, y, w, h); 1582 ENGINE.finishPainting(); 1583 } 1584 } 1585 } 1586 getBorderInsets(Component c, Insets i)1587 public Insets getBorderInsets(Component c, Insets i) { 1588 SynthContext context = getContext((JComponent)c); 1589 return context.getStyle().getInsets(context, i); 1590 } 1591 isBorderOpaque()1592 public boolean isBorderOpaque() { 1593 return true; 1594 } 1595 getStyle(JComponent c)1596 private SynthStyle getStyle(JComponent c) { 1597 return SynthLookAndFeel.getStyle(c, GTKEngine.CustomRegion.TITLED_BORDER); 1598 } 1599 getContext(JComponent c)1600 private SynthContext getContext(JComponent c) { 1601 int state = SynthConstants.DEFAULT; 1602 return new SynthContext(c, GTKEngine.CustomRegion.TITLED_BORDER, 1603 getStyle(c), state); 1604 } 1605 } 1606 } 1607