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.UNIXToolkit; 28 29 import sun.awt.ModalExclude; 30 import sun.awt.SunToolkit; 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 final static GTKEngine ENGINE = GTKEngine.INSTANCE; 70 final static GTKPainter INSTANCE = new GTKPainter(); 71 72 private GTKPainter() { 73 } 74 75 private String getName(SynthContext context) { 76 return (context.getRegion().isSubregion()) ? null : 77 context.getComponent().getName(); 78 } 79 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 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 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 // 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 // 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 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 // 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 // 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 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 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 // 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 // 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 // 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 // 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 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 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 // 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 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 // 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 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 // 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 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 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 int xThickness = style.getXThickness(); 585 int yThickness = style.getYThickness(); 586 ENGINE.paintBackground(g, context, id, gtkState, 587 style.getGTKColor(context, gtkState, GTKColorType.BACKGROUND), 588 x + xThickness, y + yThickness, 589 w - xThickness - xThickness, h - yThickness - yThickness); 590 BufferedImage img = ENGINE.finishPainting(); 591 if(!isHW) { 592 int border = img.getRGB(0, h / 2); 593 if (img != null && border == img.getRGB(w / 2, h / 2)) { 594 // fix no menu borders in Adwaita theme 595 Graphics g2 = img.getGraphics(); 596 Color c = new Color(border); 597 g2.setColor(new Color(Math.max((int) (c.getRed() * 0.8), 0), 598 Math.max((int) (c.getGreen() * 0.8), 0), 599 Math.max((int) (c.getBlue() * 0.8), 0))); 600 g2.drawLine(0, 0, w - 1, 0); 601 g2.drawLine(w - 1, 0, w - 1, h - 1); 602 g2.drawLine(0, h - 1, 0, 1); 603 g2.setColor(c.darker()); 604 g2.drawLine(w - 1, h - 1, 0, h - 1); 605 g2.dispose(); 606 g.drawImage(img, x, y, null); 607 } 608 } 609 } 610 } 611 612 public void paintProgressBarBackground(SynthContext context, 613 Graphics g, 614 int x, int y, int w, int h) { 615 Region id = context.getRegion(); 616 synchronized (UNIXToolkit.GTK_LOCK) { 617 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 618 ENGINE.startPainting(g, x, y, w, h, id); 619 ENGINE.paintBox(g, context, id, SynthConstants.ENABLED, 620 ShadowType.IN, "trough", x, y, w, h); 621 ENGINE.finishPainting(); 622 } 623 } 624 } 625 626 public void paintProgressBarForeground(SynthContext context, Graphics g, 627 int x, int y, int w, int h, 628 int orientation) { 629 Region id = context.getRegion(); 630 synchronized (UNIXToolkit.GTK_LOCK) { 631 // Note that we don't call paintCachedImage() here. Since the 632 // progress bar foreground is painted differently for each value 633 // it would be wasteful to try to cache an image for each state, 634 // so instead we simply avoid caching in this case. 635 if (w <= 0 || h <= 0) { 636 return; 637 } 638 ENGINE.startPainting(g, x, y, w, h, id, "fg"); 639 ENGINE.paintBox(g, context, id, SynthConstants.MOUSE_OVER, 640 ShadowType.OUT, "bar", x, y, w, h); 641 ENGINE.finishPainting(false); // don't bother caching the image 642 } 643 } 644 645 public void paintViewportBorder(SynthContext context, Graphics g, 646 int x, int y, int w, int h) { 647 Region id = context.getRegion(); 648 synchronized (UNIXToolkit.GTK_LOCK) { 649 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 650 ENGINE.startPainting(g, x, y, w, h, id); 651 ENGINE.paintShadow(g, context, id, SynthConstants.ENABLED, 652 ShadowType.IN, "scrolled_window", x, y, w, h); 653 ENGINE.finishPainting(); 654 } 655 } 656 } 657 658 public void paintSeparatorBackground(SynthContext context, 659 Graphics g, 660 int x, int y, int w, int h, 661 int orientation) { 662 Region id = context.getRegion(); 663 int state = context.getComponentState(); 664 JComponent c = context.getComponent(); 665 666 /* 667 * Note: In theory, the style's x/y thickness values would determine 668 * the width of the separator content. In practice, however, some 669 * engines will render a line that is wider than the corresponding 670 * thickness value. For example, ubuntulooks reports x/y thickness 671 * values of 1 for separators, but always renders a 2-pixel wide line. 672 * As a result of all this, we need to be careful not to restrict 673 * the w/h values below too much, so that the full thickness of the 674 * rendered line will be captured by our image caching code. 675 */ 676 String detail; 677 if (c instanceof JToolBar.Separator) { 678 /* 679 * GTK renders toolbar separators differently in that an 680 * artificial padding is added to each end of the separator. 681 * The value of 0.2f below is derived from the source code of 682 * gtktoolbar.c in the current version of GTK+ (2.8.20 at the 683 * time of this writing). Specifically, the relevant values are: 684 * SPACE_LINE_DIVISION 10.0 685 * SPACE_LINE_START 2.0 686 * SPACE_LINE_END 8.0 687 * These are used to determine the distance from the top (or left) 688 * edge of the toolbar to the other edge. So for example, the 689 * starting/top point of a vertical separator is 2/10 of the 690 * height of a horizontal toolbar away from the top edge, which 691 * is how we arrive at 0.2f below. Likewise, the ending/bottom 692 * point is 8/10 of the height away from the top edge, or in other 693 * words, it is 2/10 away from the bottom edge, which is again 694 * how we arrive at the 0.2f value below. 695 * 696 * The separator is also centered horizontally or vertically, 697 * depending on its orientation. This was determined empirically 698 * and by examining the code referenced above. 699 */ 700 detail = "toolbar"; 701 float pct = 0.2f; 702 JToolBar.Separator sep = (JToolBar.Separator)c; 703 Dimension size = sep.getSeparatorSize(); 704 GTKStyle style = (GTKStyle)context.getStyle(); 705 if (orientation == JSeparator.HORIZONTAL) { 706 x += (int)(w * pct); 707 w -= (int)(w * pct * 2); 708 y += (size.height - style.getYThickness()) / 2; 709 } else { 710 y += (int)(h * pct); 711 h -= (int)(h * pct * 2); 712 x += (size.width - style.getXThickness()) / 2; 713 } 714 } else { 715 // For regular/menu separators, we simply subtract out the insets. 716 detail = "separator"; 717 Insets insets = c.getInsets(); 718 x += insets.left; 719 y += insets.top; 720 if (orientation == JSeparator.HORIZONTAL) { 721 w -= (insets.left + insets.right); 722 } else { 723 h -= (insets.top + insets.bottom); 724 } 725 if (GTKLookAndFeel.is3()) { 726 if (id == Region.POPUP_MENU_SEPARATOR) { 727 detail = "menuitem"; 728 h -= (insets.top + insets.bottom); 729 } else { 730 detail = "separator"; 731 } 732 } else { 733 detail = orientation == JSeparator.HORIZONTAL ? 734 "hseparator" : "vseparator"; 735 } 736 } 737 738 synchronized (UNIXToolkit.GTK_LOCK) { 739 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, 740 state, detail, orientation)) { 741 ENGINE.startPainting(g, x, y, w, h, id, 742 state, detail, orientation); 743 if (orientation == JSeparator.HORIZONTAL) { 744 ENGINE.paintHline(g, context, id, state, 745 detail, x, y, w, h); 746 } else { 747 ENGINE.paintVline(g, context, id, state, 748 detail, x, y, w, h); 749 } 750 ENGINE.finishPainting(); 751 } 752 } 753 } 754 755 public void paintSliderTrackBackground(SynthContext context, 756 Graphics g, 757 int x, int y, int w,int h) { 758 Region id = context.getRegion(); 759 int state = context.getComponentState(); 760 761 // For focused sliders, we paint focus rect outside the bounds passed. 762 // Need to adjust for that. 763 boolean focused = ((state & SynthConstants.FOCUSED) != 0); 764 int focusSize = 0; 765 if (focused) { 766 GTKStyle style = (GTKStyle)context.getStyle(); 767 focusSize = style.getClassSpecificIntValue( 768 context, "focus-line-width", 1) + 769 style.getClassSpecificIntValue( 770 context, "focus-padding", 1); 771 x -= focusSize; 772 y -= focusSize; 773 w += focusSize * 2; 774 h += focusSize * 2; 775 } 776 777 // The ubuntulooks engine paints slider troughs differently depending 778 // on the current slider value and its component orientation. 779 JSlider slider = (JSlider)context.getComponent(); 780 if (GTKLookAndFeel.is3()) { 781 if (slider.getOrientation() == JSlider.VERTICAL) { 782 y += 1; 783 h -= 2; 784 } else { 785 x += 1; 786 w -= 2; 787 } 788 } 789 double value = slider.getValue(); 790 double min = slider.getMinimum(); 791 double max = slider.getMaximum(); 792 double visible = 20; // not used for sliders; any value will work 793 794 synchronized (UNIXToolkit.GTK_LOCK) { 795 // Note that we don't call paintCachedImage() here. Since some 796 // engines (e.g. ubuntulooks) paint the slider background 797 // differently for any given slider value, it would be wasteful 798 // to try to cache an image for each state, so instead we simply 799 // avoid caching in this case. 800 if (w <= 0 || h <= 0) { 801 return; 802 } 803 ENGINE.startPainting(g, x, y, w, h, id, state, value); 804 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 805 ENGINE.setRangeValue(context, id, value, min, max, visible); 806 ENGINE.paintBox(g, context, id, gtkState, ShadowType.IN, 807 "trough", x + focusSize, y + focusSize, 808 w - 2 * focusSize, h - 2 * focusSize); 809 if (focused) { 810 ENGINE.paintFocus(g, context, id, SynthConstants.ENABLED, 811 "trough", x, y, w, h); 812 } 813 ENGINE.finishPainting(false); // don't bother caching the image 814 } 815 } 816 817 public void paintSliderThumbBackground(SynthContext context, 818 Graphics g, int x, int y, int w, int h, int dir) { 819 Region id = context.getRegion(); 820 int gtkState = GTKLookAndFeel.synthStateToGTKState( 821 id, context.getComponentState()); 822 boolean hasFocus = GTKLookAndFeel.is3() && 823 ((context.getComponentState() & SynthConstants.FOCUSED) != 0); 824 synchronized (UNIXToolkit.GTK_LOCK) { 825 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, dir, hasFocus)) { 826 Orientation orientation = (dir == JSlider.HORIZONTAL ? 827 Orientation.HORIZONTAL : Orientation.VERTICAL); 828 String detail = (dir == JSlider.HORIZONTAL ? 829 "hscale" : "vscale"); 830 ENGINE.startPainting(g, x, y, w, h, id, gtkState, dir); 831 ENGINE.paintSlider(g, context, id, gtkState, 832 ShadowType.OUT, detail, x, y, w, h, orientation, hasFocus); 833 ENGINE.finishPainting(); 834 } 835 } 836 } 837 838 // 839 // SPINNER 840 // 841 public void paintSpinnerBackground(SynthContext context, 842 Graphics g, 843 int x, int y, int w, int h) { 844 // This is handled in paintTextFieldBackground 845 } 846 847 // 848 // SPLIT_PANE_DIVIDER 849 // 850 public void paintSplitPaneDividerBackground(SynthContext context, 851 Graphics g, 852 int x, int y, int w, int h) { 853 Region id = context.getRegion(); 854 int gtkState = GTKLookAndFeel.synthStateToGTKState( 855 id, context.getComponentState()); 856 JSplitPane splitPane = (JSplitPane)context.getComponent(); 857 Orientation orientation = 858 (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT ? 859 Orientation.VERTICAL : Orientation.HORIZONTAL); 860 synchronized (UNIXToolkit.GTK_LOCK) { 861 if (! ENGINE.paintCachedImage(g, x, y, w, h, 862 id, gtkState, orientation)) { 863 ENGINE.startPainting(g, x, y, w, h, id, gtkState, orientation); 864 ENGINE.paintHandle(g, context, id, gtkState, 865 ShadowType.OUT, "paned", x, y, w, h, orientation); 866 ENGINE.finishPainting(); 867 } 868 } 869 } 870 871 public void paintSplitPaneDragDivider(SynthContext context, 872 Graphics g,int x, int y, int w, int h, 873 int orientation) { 874 paintSplitPaneDividerForeground(context, g, x, y, w, h, orientation); 875 } 876 877 public void paintTabbedPaneContentBackground(SynthContext context, 878 Graphics g, int x, int y, int w, int h) { 879 JTabbedPane pane = (JTabbedPane)context.getComponent(); 880 int selectedIndex = pane.getSelectedIndex(); 881 PositionType placement = GTKLookAndFeel.SwingOrientationConstantToGTK( 882 pane.getTabPlacement()); 883 884 int gapStart = 0; 885 int gapSize = 0; 886 if (selectedIndex != -1) { 887 Rectangle tabBounds = pane.getBoundsAt(selectedIndex); 888 889 if (placement == PositionType.TOP || 890 placement == PositionType.BOTTOM) { 891 892 gapStart = tabBounds.x - x; 893 gapSize = tabBounds.width; 894 } 895 else { 896 gapStart = tabBounds.y - y; 897 gapSize = tabBounds.height; 898 } 899 } 900 901 Region id = context.getRegion(); 902 int gtkState = GTKLookAndFeel.synthStateToGTKState( 903 id, context.getComponentState()); 904 synchronized (UNIXToolkit.GTK_LOCK) { 905 if (! ENGINE.paintCachedImage(g, x, y, w, h, 906 id, gtkState, placement, gapStart, gapSize)) { 907 ENGINE.startPainting(g, x, y, w, h, 908 id, gtkState, placement, gapStart, gapSize); 909 ENGINE.paintBoxGap(g, context, id, gtkState, ShadowType.OUT, 910 "notebook", x, y, w, h, placement, gapStart, gapSize); 911 ENGINE.finishPainting(); 912 } 913 } 914 } 915 916 public void paintTabbedPaneTabBackground(SynthContext context, 917 Graphics g, 918 int x, int y, int w, int h, 919 int tabIndex) { 920 Region id = context.getRegion(); 921 int state = context.getComponentState(); 922 int gtkState = ((state & SynthConstants.SELECTED) != 0 ? 923 SynthConstants.ENABLED : SynthConstants.PRESSED); 924 JTabbedPane pane = (JTabbedPane)context.getComponent(); 925 int placement = pane.getTabPlacement(); 926 927 synchronized (UNIXToolkit.GTK_LOCK) { 928 if (! ENGINE.paintCachedImage(g, x, y, w, h, 929 id, gtkState, placement, tabIndex)) { 930 PositionType side = POSITIONS[placement - 1]; 931 ENGINE.startPainting(g, x, y, w, h, 932 id, gtkState, placement, tabIndex); 933 ENGINE.paintExtension(g, context, id, gtkState, 934 ShadowType.OUT, "tab", x, y, w, h, side, tabIndex); 935 ENGINE.finishPainting(); 936 } 937 } 938 } 939 940 // 941 // TEXT_PANE 942 // 943 public void paintTextPaneBackground(SynthContext context, Graphics g, 944 int x, int y, int w, int h) { 945 paintTextAreaBackground(context, g, x, y, w, h); 946 } 947 948 // 949 // EDITOR_PANE 950 // 951 public void paintEditorPaneBackground(SynthContext context, Graphics g, 952 int x, int y, int w, int h) { 953 paintTextAreaBackground(context, g, x, y, w, h); 954 } 955 956 // 957 // TEXT_AREA 958 // 959 public void paintTextAreaBackground(SynthContext context, Graphics g, 960 int x, int y, int w, int h) { 961 // Does not call into ENGINE for better performance 962 fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND); 963 } 964 965 // 966 // TEXT_FIELD 967 // 968 // NOTE: Combobox and Label, Password and FormattedTextField calls this 969 // too. 970 private void paintTextBackground(SynthContext context, Graphics g, 971 int x, int y, int w, int h) { 972 // Text is odd in that it uses the TEXT_BACKGROUND vs BACKGROUND. 973 JComponent c = context.getComponent(); 974 Container container = c.getParent(); 975 Container containerParent = null; 976 GTKStyle style = (GTKStyle)context.getStyle(); 977 Region id = context.getRegion(); 978 int state = context.getComponentState(); 979 980 if (c instanceof ListCellRenderer && container != null) { 981 containerParent = container.getParent(); 982 if (containerParent instanceof JComboBox 983 && containerParent.hasFocus()) { 984 state |= SynthConstants.FOCUSED; 985 } 986 } 987 988 synchronized (UNIXToolkit.GTK_LOCK) { 989 if (ENGINE.paintCachedImage(g, x, y, w, h, id, state)) { 990 return; 991 } 992 993 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 994 int focusSize = 0; 995 boolean interiorFocus = style.getClassSpecificBoolValue( 996 context, "interior-focus", true); 997 998 focusSize = style.getClassSpecificIntValue(context, 999 "focus-line-width",1); 1000 if (!interiorFocus && (state & SynthConstants.FOCUSED) != 0) { 1001 x += focusSize; 1002 y += focusSize; 1003 w -= 2 * focusSize; 1004 h -= 2 * focusSize; 1005 } 1006 1007 int xThickness = style.getXThickness(); 1008 int yThickness = style.getYThickness(); 1009 1010 ENGINE.startPainting(g, x, y, w, h, id, state); 1011 if (GTKLookAndFeel.is3()) { 1012 ENGINE.paintBackground(g, context, id, gtkState, null, 1013 x, y, w, h); 1014 } 1015 ENGINE.paintShadow(g, context, id, gtkState, 1016 ShadowType.IN, "entry", x, y, w, h); 1017 if (!GTKLookAndFeel.is3()) { 1018 ENGINE.paintFlatBox(g, context, id, 1019 gtkState, ShadowType.NONE, "entry_bg", 1020 x + xThickness, 1021 y + yThickness, 1022 w - (2 * xThickness), 1023 h - (2 * yThickness), 1024 ColorType.TEXT_BACKGROUND); 1025 } 1026 1027 if (focusSize > 0 && (state & SynthConstants.FOCUSED) != 0) { 1028 if (!interiorFocus) { 1029 x -= focusSize; 1030 y -= focusSize; 1031 w += 2 * focusSize; 1032 h += 2 * focusSize; 1033 } else { 1034 if (containerParent instanceof JComboBox) { 1035 x += (focusSize + 2); 1036 y += focusSize + (GTKLookAndFeel.is3() ? 3 : 1); 1037 w -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 1); 1038 h -= 2 * focusSize + (GTKLookAndFeel.is3() ? 6 : 2); 1039 } else { 1040 x += focusSize + (GTKLookAndFeel.is3() ? 2 : 0); 1041 y += focusSize + (GTKLookAndFeel.is3() ? 2 :0 ); 1042 w -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 0); 1043 h -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 0); 1044 } 1045 } 1046 ENGINE.paintFocus(g, context, id, gtkState, 1047 "entry", x, y, w, h); 1048 } 1049 ENGINE.finishPainting(); 1050 } 1051 } 1052 1053 private void paintTreeCellEditorBackground(SynthContext context, Graphics g, 1054 int x, int y, int w, int h) { 1055 Region id = context.getRegion(); 1056 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1057 id, context.getComponentState()); 1058 synchronized (UNIXToolkit.GTK_LOCK) { 1059 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState)) { 1060 ENGINE.startPainting(g, x, y, w, h, id, gtkState); 1061 ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE, 1062 "entry_bg", x, y, w, h, ColorType.TEXT_BACKGROUND); 1063 ENGINE.finishPainting(); 1064 } 1065 } 1066 } 1067 1068 1069 // 1070 // ROOT_PANE 1071 // 1072 public void paintRootPaneBackground(SynthContext context, Graphics g, 1073 int x, int y, int w, int h) { 1074 // Does not call into ENGINE for better performance 1075 fillArea(context, g, x, y, w, h, GTKColorType.BACKGROUND); 1076 } 1077 1078 // 1079 // TOGGLE_BUTTON 1080 // 1081 public void paintToggleButtonBackground(SynthContext context, 1082 Graphics g, 1083 int x, int y, int w, int h) { 1084 Region id = context.getRegion(); 1085 JToggleButton toggleButton = (JToggleButton)context.getComponent(); 1086 boolean paintBG = toggleButton.isContentAreaFilled() && 1087 toggleButton.isBorderPainted(); 1088 boolean paintFocus = toggleButton.isFocusPainted(); 1089 boolean toolButton = (toggleButton.getParent() instanceof JToolBar); 1090 paintButtonBackgroundImpl(context, g, id, "button", 1091 x, y, w, h, 1092 paintBG, paintFocus, false, toolButton); 1093 } 1094 1095 1096 // 1097 // SCROLL_BAR 1098 // 1099 public void paintScrollBarBackground(SynthContext context, 1100 Graphics g, 1101 int x, int y, int w,int h) { 1102 Region id = context.getRegion(); 1103 boolean focused = 1104 (context.getComponentState() & SynthConstants.FOCUSED) != 0; 1105 synchronized (UNIXToolkit.GTK_LOCK) { 1106 if (ENGINE.paintCachedImage(g, x, y, w, h, id, focused)) { 1107 return; 1108 } 1109 ENGINE.startPainting(g, x, y, w, h, id, focused); 1110 1111 // Note: the scrollbar insets already include the "trough-border", 1112 // which is needed to position the scrollbar buttons properly. 1113 // But when we render, we need to take the trough border out 1114 // of the equation so that we paint the entire area covered by 1115 // the trough border and the scrollbar content itself. 1116 Insets insets = context.getComponent().getInsets(); 1117 GTKStyle style = (GTKStyle)context.getStyle(); 1118 int troughBorder = 1119 style.getClassSpecificIntValue(context, "trough-border", 1); 1120 insets.left -= troughBorder; 1121 insets.right -= troughBorder; 1122 insets.top -= troughBorder; 1123 insets.bottom -= troughBorder; 1124 1125 ENGINE.paintBox(g, context, id, SynthConstants.PRESSED, 1126 ShadowType.IN, "trough", 1127 x + insets.left, 1128 y + insets.top, 1129 w - insets.left - insets.right, 1130 h - insets.top - insets.bottom); 1131 1132 if (focused) { 1133 ENGINE.paintFocus(g, context, id, 1134 SynthConstants.ENABLED, "trough", x, y, w, h); 1135 } 1136 ENGINE.finishPainting(); 1137 } 1138 } 1139 1140 1141 // 1142 // SCROLL_BAR_THUMB 1143 // 1144 public void paintScrollBarThumbBackground(SynthContext context, 1145 Graphics g, int x, int y, int w, int h, int dir) { 1146 Region id = context.getRegion(); 1147 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1148 id, context.getComponentState()); 1149 1150 // The clearlooks engine paints scrollbar thumbs differently 1151 // depending on the current scroll value (specifically, it will avoid 1152 // rendering a certain line when the thumb is at the starting or 1153 // ending position). Therefore, we normalize the current value to 1154 // the range [0,100] here and then pass it down to setRangeValue() 1155 // so that the native widget is configured appropriately. Note that 1156 // there are really only four values that matter (min, middle, max, 1157 // or fill) so we restrict to one of those four values to avoid 1158 // blowing out the image cache. 1159 JScrollBar sb = (JScrollBar)context.getComponent(); 1160 boolean rtl = 1161 sb.getOrientation() == JScrollBar.HORIZONTAL && 1162 !sb.getComponentOrientation().isLeftToRight(); 1163 double min = 0; 1164 double max = 100; 1165 double visible = 20; 1166 double value; 1167 if (sb.getMaximum() - sb.getMinimum() == sb.getVisibleAmount()) { 1168 // In this case, the thumb fills the entire track, so it is 1169 // touching both ends at the same time 1170 value = 0; 1171 visible = 100; 1172 } else if (sb.getValue() == sb.getMinimum()) { 1173 // At minimum 1174 value = rtl ? 100 : 0; 1175 } else if (sb.getValue() >= sb.getMaximum() - sb.getVisibleAmount()) { 1176 // At maximum 1177 value = rtl ? 0 : 100; 1178 } else { 1179 // Somewhere in between 1180 value = 50; 1181 } 1182 1183 synchronized (UNIXToolkit.GTK_LOCK) { 1184 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, 1185 dir, value, visible, rtl)) 1186 { 1187 ENGINE.startPainting(g, x, y, w, h, id, gtkState, 1188 dir, value, visible, rtl); 1189 Orientation orientation = (dir == JScrollBar.HORIZONTAL ? 1190 Orientation.HORIZONTAL : Orientation.VERTICAL); 1191 ENGINE.setRangeValue(context, id, value, min, max, visible); 1192 ENGINE.paintSlider(g, context, id, gtkState, 1193 ShadowType.OUT, "slider", x, y, w, h, orientation, false); 1194 ENGINE.finishPainting(); 1195 } 1196 } 1197 } 1198 1199 // 1200 // TOOL_TIP 1201 // 1202 public void paintToolTipBackground(SynthContext context, Graphics g, 1203 int x, int y, int w,int h) { 1204 Region id = context.getRegion(); 1205 synchronized (UNIXToolkit.GTK_LOCK) { 1206 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 1207 ENGINE.startPainting(g, x, y, w, h, id); 1208 ENGINE.paintFlatBox(g, context, id, SynthConstants.ENABLED, 1209 ShadowType.OUT, "tooltip", x, y, w, h, 1210 ColorType.BACKGROUND); 1211 ENGINE.finishPainting(); 1212 } 1213 } 1214 } 1215 1216 1217 // 1218 // TREE_CELL 1219 // 1220 public void paintTreeCellBackground(SynthContext context, Graphics g, 1221 int x, int y, int w, int h) { 1222 Region id = context.getRegion(); 1223 int state = context.getComponentState(); 1224 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 1225 synchronized (UNIXToolkit.GTK_LOCK) { 1226 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, state)) { 1227 ENGINE.startPainting(g, x, y, w, h, id, state); 1228 // the string arg should alternate based on row being painted, 1229 // but we currently don't pass that in. 1230 ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE, 1231 "cell_odd", x, y, w, h, ColorType.TEXT_BACKGROUND); 1232 ENGINE.finishPainting(); 1233 } 1234 } 1235 } 1236 1237 public void paintTreeCellFocus(SynthContext context, Graphics g, 1238 int x, int y, int w, int h) { 1239 Region id = Region.TREE_CELL; 1240 int state = context.getComponentState(); 1241 paintFocus(context, g, id, state, "treeview", x, y, w, h); 1242 } 1243 1244 1245 // 1246 // TREE 1247 // 1248 public void paintTreeBackground(SynthContext context, Graphics g, 1249 int x, int y, int w, int h) { 1250 // As far as I can tell, these don't call into the ENGINE. 1251 fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND); 1252 } 1253 1254 1255 // 1256 // VIEWPORT 1257 // 1258 public void paintViewportBackground(SynthContext context, Graphics g, 1259 int x, int y, int w, int h) { 1260 // As far as I can tell, these don't call into the ENGINE. 1261 // Also note that you don't want this to call into the ENGINE 1262 // as if it where to paint a background JViewport wouldn't scroll 1263 // correctly. 1264 fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND); 1265 } 1266 1267 void paintFocus(SynthContext context, Graphics g, Region id, 1268 int state, String detail, int x, int y, int w, int h) { 1269 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 1270 synchronized (UNIXToolkit.GTK_LOCK) { 1271 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, "focus")) { 1272 ENGINE.startPainting(g, x, y, w, h, id, gtkState, "focus"); 1273 ENGINE.paintFocus(g, context, id, gtkState, detail, x, y, w, h); 1274 ENGINE.finishPainting(); 1275 } 1276 } 1277 } 1278 1279 void paintMetacityElement(SynthContext context, Graphics g, 1280 int gtkState, String detail, int x, int y, int w, int h, 1281 ShadowType shadow, ArrowType direction) { 1282 synchronized (UNIXToolkit.GTK_LOCK) { 1283 if (! ENGINE.paintCachedImage( 1284 g, x, y, w, h, gtkState, detail, shadow, direction)) { 1285 ENGINE.startPainting( 1286 g, x, y, w, h, gtkState, detail, shadow, direction); 1287 if (detail == "metacity-arrow") { 1288 ENGINE.paintArrow(g, context, Region.INTERNAL_FRAME_TITLE_PANE, 1289 gtkState, shadow, direction, "", x, y, w, h); 1290 1291 } else if (detail == "metacity-box") { 1292 ENGINE.paintBox(g, context, Region.INTERNAL_FRAME_TITLE_PANE, 1293 gtkState, shadow, "", x, y, w, h); 1294 1295 } else if (detail == "metacity-vline") { 1296 ENGINE.paintVline(g, context, Region.INTERNAL_FRAME_TITLE_PANE, 1297 gtkState, "", x, y, w, h); 1298 } 1299 ENGINE.finishPainting(); 1300 } 1301 } 1302 } 1303 1304 void paintIcon(SynthContext context, Graphics g, 1305 Method paintMethod, int x, int y, int w, int h) { 1306 int state = context.getComponentState(); 1307 synchronized (UNIXToolkit.GTK_LOCK) { 1308 if (! ENGINE.paintCachedImage(g, x, y, w, h, state, paintMethod)) { 1309 ENGINE.startPainting(g, x, y, w, h, state, paintMethod); 1310 try { 1311 paintMethod.invoke(this, context, g, state, x, y, w, h); 1312 } catch (IllegalAccessException iae) { 1313 assert false; 1314 } catch (InvocationTargetException ite) { 1315 assert false; 1316 } 1317 ENGINE.finishPainting(); 1318 } 1319 } 1320 } 1321 1322 void paintIcon(SynthContext context, Graphics g, 1323 Method paintMethod, int x, int y, int w, int h, Object direction) { 1324 int state = context.getComponentState(); 1325 synchronized (UNIXToolkit.GTK_LOCK) { 1326 if (! ENGINE.paintCachedImage(g, 1327 x, y, w, h, state, paintMethod, direction)) { 1328 ENGINE.startPainting(g, 1329 x, y, w, h, state, paintMethod, direction); 1330 try { 1331 paintMethod.invoke(this, context, 1332 g, state, x, y, w, h, direction); 1333 } catch (IllegalAccessException iae) { 1334 assert false; 1335 } catch (InvocationTargetException ite) { 1336 assert false; 1337 } 1338 ENGINE.finishPainting(); 1339 } 1340 } 1341 } 1342 1343 // All icon painting methods are called from under GTK_LOCK 1344 1345 public void paintTreeExpandedIcon(SynthContext context, 1346 Graphics g, int state, int x, int y, int w, int h) { 1347 ENGINE.paintExpander(g, context, Region.TREE, 1348 GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state), 1349 ExpanderStyle.EXPANDED, "expander", x, y, w, h); 1350 } 1351 1352 public void paintTreeCollapsedIcon(SynthContext context, 1353 Graphics g, int state, int x, int y, int w, int h) { 1354 ENGINE.paintExpander(g, context, Region.TREE, 1355 GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state), 1356 ExpanderStyle.COLLAPSED, "expander", x, y, w, h); 1357 } 1358 1359 public void paintCheckBoxIcon(SynthContext context, 1360 Graphics g, int state, int x, int y, int w, int h) { 1361 GTKStyle style = (GTKStyle)context.getStyle(); 1362 int size = style.getClassSpecificIntValue(context, 1363 "indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE); 1364 int offset = style.getClassSpecificIntValue(context, 1365 "indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING); 1366 1367 ENGINE.paintCheck(g, context, Region.CHECK_BOX, "checkbutton", 1368 x+offset, y+offset, size, size); 1369 } 1370 1371 public void paintRadioButtonIcon(SynthContext context, 1372 Graphics g, int state, int x, int y, int w, int h) { 1373 GTKStyle style = (GTKStyle)context.getStyle(); 1374 int size = style.getClassSpecificIntValue(context, 1375 "indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE); 1376 int offset = style.getClassSpecificIntValue(context, 1377 "indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING); 1378 1379 ENGINE.paintOption(g, context, Region.RADIO_BUTTON, "radiobutton", 1380 x+offset, y+offset, size, size); 1381 } 1382 1383 public void paintMenuArrowIcon(SynthContext context, Graphics g, 1384 int state, int x, int y, int w, int h, ArrowType dir) { 1385 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1386 context.getRegion(), state); 1387 ShadowType shadow = ShadowType.OUT; 1388 if (gtkState == SynthConstants.MOUSE_OVER) { 1389 shadow = ShadowType.IN; 1390 } 1391 if (!GTKLookAndFeel.is3()) { 1392 x += 3; 1393 y += 3; 1394 w = h = 7; 1395 } 1396 ENGINE.paintArrow(g, context, Region.MENU_ITEM, gtkState, shadow, 1397 dir, "menuitem", x, y, w, h); 1398 } 1399 1400 public void paintCheckBoxMenuItemCheckIcon(SynthContext context, 1401 Graphics g, int state, int x, int y, int w, int h) { 1402 1403 GTKStyle style = (GTKStyle)context.getStyle(); 1404 int size = style.getClassSpecificIntValue(context,"indicator-size", 1405 GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE); 1406 1407 ENGINE.paintCheck(g, context, Region.CHECK_BOX_MENU_ITEM, "check", 1408 x + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1409 y + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1410 size, size); 1411 } 1412 1413 public void paintRadioButtonMenuItemCheckIcon(SynthContext context, 1414 Graphics g, int state, int x, int y, int w, int h) { 1415 1416 GTKStyle style = (GTKStyle)context.getStyle(); 1417 int size = style.getClassSpecificIntValue(context,"indicator-size", 1418 GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE); 1419 1420 ENGINE.paintOption(g, context, Region.RADIO_BUTTON_MENU_ITEM, "option", 1421 x + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1422 y + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1423 size, size); 1424 } 1425 1426 public void paintToolBarHandleIcon(SynthContext context, Graphics g, 1427 int state, int x, int y, int w, int h, Orientation orientation) { 1428 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1429 context.getRegion(), state); 1430 1431 // The orientation parameter passed down by Synth refers to the 1432 // orientation of the toolbar, but the one we pass to GTK refers 1433 // to the orientation of the handle. Therefore, we need to swap 1434 // the value here: horizontal toolbars have vertical handles, and 1435 // vice versa. 1436 orientation = (orientation == Orientation.HORIZONTAL) ? 1437 Orientation.VERTICAL : Orientation.HORIZONTAL; 1438 1439 ENGINE.paintHandle(g, context, Region.TOOL_BAR, gtkState, 1440 ShadowType.OUT, "handlebox", x, y, w, h, orientation); 1441 } 1442 1443 public void paintAscendingSortIcon(SynthContext context, 1444 Graphics g, int state, int x, int y, int w, int h) { 1445 ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED, 1446 ShadowType.IN, ArrowType.UP, "arrow", x, y, w, h); 1447 } 1448 1449 public void paintDescendingSortIcon(SynthContext context, 1450 Graphics g, int state, int x, int y, int w, int h) { 1451 ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED, 1452 ShadowType.IN, ArrowType.DOWN, "arrow", x, y, w, h); 1453 } 1454 1455 /* 1456 * Fill an area with color determined from this context's Style using the 1457 * specified GTKColorType 1458 */ 1459 private void fillArea(SynthContext context, Graphics g, 1460 int x, int y, int w, int h, ColorType colorType) { 1461 if (context.getComponent().isOpaque()) { 1462 Region id = context.getRegion(); 1463 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, 1464 context.getComponentState()); 1465 GTKStyle style = (GTKStyle)context.getStyle(); 1466 1467 g.setColor(style.getGTKColor(context, gtkState, colorType)); 1468 g.fillRect(x, y, w, h); 1469 } 1470 } 1471 1472 // Refer to GTKLookAndFeel for details on this. 1473 static class ListTableFocusBorder extends AbstractBorder implements 1474 UIResource { 1475 1476 private boolean selectedCell; 1477 private boolean focusedCell; 1478 1479 public static ListTableFocusBorder getSelectedCellBorder() { 1480 return new ListTableFocusBorder(true, true); 1481 } 1482 1483 public static ListTableFocusBorder getUnselectedCellBorder() { 1484 return new ListTableFocusBorder(false, true); 1485 } 1486 1487 public static ListTableFocusBorder getNoFocusCellBorder() { 1488 return new ListTableFocusBorder(false, false); 1489 } 1490 1491 public ListTableFocusBorder(boolean selectedCell, boolean focusedCell) { 1492 this.selectedCell = selectedCell; 1493 this.focusedCell = focusedCell; 1494 } 1495 1496 private SynthContext getContext(Component c) { 1497 SynthContext context = null; 1498 1499 ComponentUI ui = null; 1500 if (c instanceof JLabel) { 1501 ui = ((JLabel)c).getUI(); 1502 } 1503 1504 if (ui instanceof SynthUI) { 1505 context = ((SynthUI)ui).getContext((JComponent)c); 1506 } 1507 1508 return context; 1509 } 1510 1511 public void paintBorder(Component c, Graphics g, int x, int y, 1512 int w, int h) { 1513 if (focusedCell) { 1514 SynthContext context = getContext(c); 1515 int state = (selectedCell? SynthConstants.SELECTED: 1516 SynthConstants.FOCUSED | SynthConstants.ENABLED); 1517 1518 if (context != null) { 1519 GTKPainter.INSTANCE.paintFocus(context, g, 1520 Region.TABLE, state, "", x, y, w, h); 1521 } 1522 } 1523 } 1524 1525 public Insets getBorderInsets(Component c, Insets i) { 1526 SynthContext context = getContext(c); 1527 1528 if (context != null) { 1529 i = context.getStyle().getInsets(context, i); 1530 } 1531 1532 return i; 1533 } 1534 1535 public boolean isBorderOpaque() { 1536 return true; 1537 } 1538 } 1539 1540 // TitledBorder implementation for GTK L&F 1541 static class TitledBorder extends AbstractBorder implements UIResource { 1542 1543 public void paintBorder(Component c, Graphics g, int x, int y, 1544 int w, int h) { 1545 SynthContext context = getContext((JComponent)c); 1546 Region id = context.getRegion(); 1547 int state = context.getComponentState(); 1548 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 1549 1550 synchronized (UNIXToolkit.GTK_LOCK) { 1551 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 1552 ENGINE.startPainting(g, x, y, w, h, id); 1553 ENGINE.paintShadow(g, context, id, gtkState, ShadowType.ETCHED_IN, 1554 "frame", x, y, w, h); 1555 ENGINE.finishPainting(); 1556 } 1557 } 1558 } 1559 1560 public Insets getBorderInsets(Component c, Insets i) { 1561 SynthContext context = getContext((JComponent)c); 1562 return context.getStyle().getInsets(context, i); 1563 } 1564 1565 public boolean isBorderOpaque() { 1566 return true; 1567 } 1568 1569 private SynthStyle getStyle(JComponent c) { 1570 return SynthLookAndFeel.getStyle(c, GTKEngine.CustomRegion.TITLED_BORDER); 1571 } 1572 1573 private SynthContext getContext(JComponent c) { 1574 int state = SynthConstants.DEFAULT; 1575 return new SynthContext(c, GTKEngine.CustomRegion.TITLED_BORDER, 1576 getStyle(c), state); 1577 } 1578 } 1579 }