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