1 /* 2 * Copyright (c) 2002, 2012, 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 final static GTKEngine ENGINE = GTKEngine.INSTANCE; 67 final static 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 double value = slider.getValue(); 747 double min = slider.getMinimum(); 748 double max = slider.getMaximum(); 749 double visible = 20; // not used for sliders; any value will work 750 751 synchronized (UNIXToolkit.GTK_LOCK) { 752 // Note that we don't call paintCachedImage() here. Since some 753 // engines (e.g. ubuntulooks) paint the slider background 754 // differently for any given slider value, it would be wasteful 755 // to try to cache an image for each state, so instead we simply 756 // avoid caching in this case. 757 if (w <= 0 || h <= 0) { 758 return; 759 } 760 ENGINE.startPainting(g, x, y, w, h, id, state, value); 761 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 762 ENGINE.setRangeValue(context, id, value, min, max, visible); 763 ENGINE.paintBox(g, context, id, gtkState, ShadowType.IN, 764 "trough", x + focusSize, y + focusSize, 765 w - 2 * focusSize, h - 2 * focusSize); 766 if (focused) { 767 ENGINE.paintFocus(g, context, id, SynthConstants.ENABLED, 768 "trough", x, y, w, h); 769 } 770 ENGINE.finishPainting(false); // don't bother caching the image 771 } 772 } 773 774 public void paintSliderThumbBackground(SynthContext context, 775 Graphics g, int x, int y, int w, int h, int dir) { 776 Region id = context.getRegion(); 777 int gtkState = GTKLookAndFeel.synthStateToGTKState( 778 id, context.getComponentState()); 779 synchronized (UNIXToolkit.GTK_LOCK) { 780 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, dir)) { 781 Orientation orientation = (dir == JSlider.HORIZONTAL ? 782 Orientation.HORIZONTAL : Orientation.VERTICAL); 783 String detail = (dir == JSlider.HORIZONTAL ? 784 "hscale" : "vscale"); 785 ENGINE.startPainting(g, x, y, w, h, id, gtkState, dir); 786 ENGINE.paintSlider(g, context, id, gtkState, 787 ShadowType.OUT, detail, x, y, w, h, orientation); 788 ENGINE.finishPainting(); 789 } 790 } 791 } 792 793 // 794 // SPINNER 795 // 796 public void paintSpinnerBackground(SynthContext context, 797 Graphics g, 798 int x, int y, int w, int h) { 799 // This is handled in paintTextFieldBackground 800 } 801 802 // 803 // SPLIT_PANE_DIVIDER 804 // 805 public void paintSplitPaneDividerBackground(SynthContext context, 806 Graphics g, 807 int x, int y, int w, int h) { 808 Region id = context.getRegion(); 809 int gtkState = GTKLookAndFeel.synthStateToGTKState( 810 id, context.getComponentState()); 811 JSplitPane splitPane = (JSplitPane)context.getComponent(); 812 Orientation orientation = 813 (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT ? 814 Orientation.VERTICAL : Orientation.HORIZONTAL); 815 synchronized (UNIXToolkit.GTK_LOCK) { 816 if (! ENGINE.paintCachedImage(g, x, y, w, h, 817 id, gtkState, orientation)) { 818 ENGINE.startPainting(g, x, y, w, h, id, gtkState, orientation); 819 ENGINE.paintHandle(g, context, id, gtkState, 820 ShadowType.OUT, "paned", x, y, w, h, orientation); 821 ENGINE.finishPainting(); 822 } 823 } 824 } 825 826 public void paintSplitPaneDragDivider(SynthContext context, 827 Graphics g,int x, int y, int w, int h, 828 int orientation) { 829 paintSplitPaneDividerForeground(context, g, x, y, w, h, orientation); 830 } 831 832 public void paintTabbedPaneContentBackground(SynthContext context, 833 Graphics g, int x, int y, int w, int h) { 834 JTabbedPane pane = (JTabbedPane)context.getComponent(); 835 int selectedIndex = pane.getSelectedIndex(); 836 PositionType placement = GTKLookAndFeel.SwingOrientationConstantToGTK( 837 pane.getTabPlacement()); 838 839 int gapStart = 0; 840 int gapSize = 0; 841 if (selectedIndex != -1) { 842 Rectangle tabBounds = pane.getBoundsAt(selectedIndex); 843 844 if (placement == PositionType.TOP || 845 placement == PositionType.BOTTOM) { 846 847 gapStart = tabBounds.x - x; 848 gapSize = tabBounds.width; 849 } 850 else { 851 gapStart = tabBounds.y - y; 852 gapSize = tabBounds.height; 853 } 854 } 855 856 Region id = context.getRegion(); 857 int gtkState = GTKLookAndFeel.synthStateToGTKState( 858 id, context.getComponentState()); 859 synchronized (UNIXToolkit.GTK_LOCK) { 860 if (! ENGINE.paintCachedImage(g, x, y, w, h, 861 id, gtkState, placement, gapStart, gapSize)) { 862 ENGINE.startPainting(g, x, y, w, h, 863 id, gtkState, placement, gapStart, gapSize); 864 ENGINE.paintBoxGap(g, context, id, gtkState, ShadowType.OUT, 865 "notebook", x, y, w, h, placement, gapStart, gapSize); 866 ENGINE.finishPainting(); 867 } 868 } 869 } 870 871 public void paintTabbedPaneTabBackground(SynthContext context, 872 Graphics g, 873 int x, int y, int w, int h, 874 int tabIndex) { 875 Region id = context.getRegion(); 876 int state = context.getComponentState(); 877 int gtkState = ((state & SynthConstants.SELECTED) != 0 ? 878 SynthConstants.ENABLED : SynthConstants.PRESSED); 879 JTabbedPane pane = (JTabbedPane)context.getComponent(); 880 int placement = pane.getTabPlacement(); 881 882 synchronized (UNIXToolkit.GTK_LOCK) { 883 if (! ENGINE.paintCachedImage(g, x, y, w, h, 884 id, gtkState, placement, tabIndex)) { 885 PositionType side = POSITIONS[placement - 1]; 886 ENGINE.startPainting(g, x, y, w, h, 887 id, gtkState, placement, tabIndex); 888 ENGINE.paintExtension(g, context, id, gtkState, 889 ShadowType.OUT, "tab", x, y, w, h, side, tabIndex); 890 ENGINE.finishPainting(); 891 } 892 } 893 } 894 895 // 896 // TEXT_PANE 897 // 898 public void paintTextPaneBackground(SynthContext context, Graphics g, 899 int x, int y, int w, int h) { 900 paintTextAreaBackground(context, g, x, y, w, h); 901 } 902 903 // 904 // EDITOR_PANE 905 // 906 public void paintEditorPaneBackground(SynthContext context, Graphics g, 907 int x, int y, int w, int h) { 908 paintTextAreaBackground(context, g, x, y, w, h); 909 } 910 911 // 912 // TEXT_AREA 913 // 914 public void paintTextAreaBackground(SynthContext context, Graphics g, 915 int x, int y, int w, int h) { 916 // Does not call into ENGINE for better performance 917 fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND); 918 } 919 920 // 921 // TEXT_FIELD 922 // 923 // NOTE: Combobox and Label, Password and FormattedTextField calls this 924 // too. 925 private void paintTextBackground(SynthContext context, Graphics g, 926 int x, int y, int w, int h) { 927 // Text is odd in that it uses the TEXT_BACKGROUND vs BACKGROUND. 928 JComponent c = context.getComponent(); 929 Container container = c.getParent(); 930 Container containerParent = null; 931 GTKStyle style = (GTKStyle)context.getStyle(); 932 Region id = context.getRegion(); 933 int state = context.getComponentState(); 934 935 if (c instanceof ListCellRenderer && container != null) { 936 containerParent = container.getParent(); 937 if (containerParent instanceof JComboBox 938 && containerParent.hasFocus()) { 939 state |= SynthConstants.FOCUSED; 940 } 941 } 942 943 synchronized (UNIXToolkit.GTK_LOCK) { 944 if (ENGINE.paintCachedImage(g, x, y, w, h, id, state)) { 945 return; 946 } 947 948 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 949 int focusSize = 0; 950 boolean interiorFocus = style.getClassSpecificBoolValue( 951 context, "interior-focus", true); 952 953 focusSize = style.getClassSpecificIntValue(context, 954 "focus-line-width",1); 955 if (!interiorFocus && (state & SynthConstants.FOCUSED) != 0) { 956 x += focusSize; 957 y += focusSize; 958 w -= 2 * focusSize; 959 h -= 2 * focusSize; 960 } 961 962 int xThickness = style.getXThickness(); 963 int yThickness = style.getYThickness(); 964 965 ENGINE.startPainting(g, x, y, w, h, id, state); 966 ENGINE.paintShadow(g, context, id, gtkState, 967 ShadowType.IN, "entry", x, y, w, h); 968 ENGINE.paintFlatBox(g, context, id, 969 gtkState, ShadowType.NONE, "entry_bg", 970 x + xThickness, 971 y + yThickness, 972 w - (2 * xThickness), 973 h - (2 * yThickness), 974 ColorType.TEXT_BACKGROUND); 975 976 if (focusSize > 0 && (state & SynthConstants.FOCUSED) != 0) { 977 if (!interiorFocus) { 978 x -= focusSize; 979 y -= focusSize; 980 w += 2 * focusSize; 981 h += 2 * focusSize; 982 } else { 983 if (containerParent instanceof JComboBox) { 984 x += (focusSize + 2); 985 y += (focusSize + 1); 986 w -= (2 * focusSize + 1); 987 h -= (2 * focusSize + 2); 988 } else { 989 x += focusSize; 990 y += focusSize; 991 w -= 2 * focusSize; 992 h -= 2 * focusSize; 993 } 994 } 995 ENGINE.paintFocus(g, context, id, gtkState, 996 "entry", x, y, w, h); 997 } 998 ENGINE.finishPainting(); 999 } 1000 } 1001 1002 private void paintTreeCellEditorBackground(SynthContext context, Graphics g, 1003 int x, int y, int w, int h) { 1004 Region id = context.getRegion(); 1005 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1006 id, context.getComponentState()); 1007 synchronized (UNIXToolkit.GTK_LOCK) { 1008 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState)) { 1009 ENGINE.startPainting(g, x, y, w, h, id, gtkState); 1010 ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE, 1011 "entry_bg", x, y, w, h, ColorType.TEXT_BACKGROUND); 1012 ENGINE.finishPainting(); 1013 } 1014 } 1015 } 1016 1017 1018 // 1019 // ROOT_PANE 1020 // 1021 public void paintRootPaneBackground(SynthContext context, Graphics g, 1022 int x, int y, int w, int h) { 1023 // Does not call into ENGINE for better performance 1024 fillArea(context, g, x, y, w, h, GTKColorType.BACKGROUND); 1025 } 1026 1027 // 1028 // TOGGLE_BUTTON 1029 // 1030 public void paintToggleButtonBackground(SynthContext context, 1031 Graphics g, 1032 int x, int y, int w, int h) { 1033 Region id = context.getRegion(); 1034 JToggleButton toggleButton = (JToggleButton)context.getComponent(); 1035 boolean paintBG = toggleButton.isContentAreaFilled() && 1036 toggleButton.isBorderPainted(); 1037 boolean paintFocus = toggleButton.isFocusPainted(); 1038 boolean toolButton = (toggleButton.getParent() instanceof JToolBar); 1039 paintButtonBackgroundImpl(context, g, id, "button", 1040 x, y, w, h, 1041 paintBG, paintFocus, false, toolButton); 1042 } 1043 1044 1045 // 1046 // SCROLL_BAR 1047 // 1048 public void paintScrollBarBackground(SynthContext context, 1049 Graphics g, 1050 int x, int y, int w,int h) { 1051 Region id = context.getRegion(); 1052 boolean focused = 1053 (context.getComponentState() & SynthConstants.FOCUSED) != 0; 1054 synchronized (UNIXToolkit.GTK_LOCK) { 1055 if (ENGINE.paintCachedImage(g, x, y, w, h, id, focused)) { 1056 return; 1057 } 1058 ENGINE.startPainting(g, x, y, w, h, id, focused); 1059 1060 // Note: the scrollbar insets already include the "trough-border", 1061 // which is needed to position the scrollbar buttons properly. 1062 // But when we render, we need to take the trough border out 1063 // of the equation so that we paint the entire area covered by 1064 // the trough border and the scrollbar content itself. 1065 Insets insets = context.getComponent().getInsets(); 1066 GTKStyle style = (GTKStyle)context.getStyle(); 1067 int troughBorder = 1068 style.getClassSpecificIntValue(context, "trough-border", 1); 1069 insets.left -= troughBorder; 1070 insets.right -= troughBorder; 1071 insets.top -= troughBorder; 1072 insets.bottom -= troughBorder; 1073 1074 ENGINE.paintBox(g, context, id, SynthConstants.PRESSED, 1075 ShadowType.IN, "trough", 1076 x + insets.left, 1077 y + insets.top, 1078 w - insets.left - insets.right, 1079 h - insets.top - insets.bottom); 1080 1081 if (focused) { 1082 ENGINE.paintFocus(g, context, id, 1083 SynthConstants.ENABLED, "trough", x, y, w, h); 1084 } 1085 ENGINE.finishPainting(); 1086 } 1087 } 1088 1089 1090 // 1091 // SCROLL_BAR_THUMB 1092 // 1093 public void paintScrollBarThumbBackground(SynthContext context, 1094 Graphics g, int x, int y, int w, int h, int dir) { 1095 Region id = context.getRegion(); 1096 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1097 id, context.getComponentState()); 1098 1099 // The clearlooks engine paints scrollbar thumbs differently 1100 // depending on the current scroll value (specifically, it will avoid 1101 // rendering a certain line when the thumb is at the starting or 1102 // ending position). Therefore, we normalize the current value to 1103 // the range [0,100] here and then pass it down to setRangeValue() 1104 // so that the native widget is configured appropriately. Note that 1105 // there are really only four values that matter (min, middle, max, 1106 // or fill) so we restrict to one of those four values to avoid 1107 // blowing out the image cache. 1108 JScrollBar sb = (JScrollBar)context.getComponent(); 1109 boolean rtl = 1110 sb.getOrientation() == JScrollBar.HORIZONTAL && 1111 !sb.getComponentOrientation().isLeftToRight(); 1112 double min = 0; 1113 double max = 100; 1114 double visible = 20; 1115 double value; 1116 if (sb.getMaximum() - sb.getMinimum() == sb.getVisibleAmount()) { 1117 // In this case, the thumb fills the entire track, so it is 1118 // touching both ends at the same time 1119 value = 0; 1120 visible = 100; 1121 } else if (sb.getValue() == sb.getMinimum()) { 1122 // At minimum 1123 value = rtl ? 100 : 0; 1124 } else if (sb.getValue() >= sb.getMaximum() - sb.getVisibleAmount()) { 1125 // At maximum 1126 value = rtl ? 0 : 100; 1127 } else { 1128 // Somewhere in between 1129 value = 50; 1130 } 1131 1132 synchronized (UNIXToolkit.GTK_LOCK) { 1133 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, 1134 dir, value, visible, rtl)) 1135 { 1136 ENGINE.startPainting(g, x, y, w, h, id, gtkState, 1137 dir, value, visible, rtl); 1138 Orientation orientation = (dir == JScrollBar.HORIZONTAL ? 1139 Orientation.HORIZONTAL : Orientation.VERTICAL); 1140 ENGINE.setRangeValue(context, id, value, min, max, visible); 1141 ENGINE.paintSlider(g, context, id, gtkState, 1142 ShadowType.OUT, "slider", x, y, w, h, orientation); 1143 ENGINE.finishPainting(); 1144 } 1145 } 1146 } 1147 1148 // 1149 // TOOL_TIP 1150 // 1151 public void paintToolTipBackground(SynthContext context, Graphics g, 1152 int x, int y, int w,int h) { 1153 Region id = context.getRegion(); 1154 synchronized (UNIXToolkit.GTK_LOCK) { 1155 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 1156 ENGINE.startPainting(g, x, y, w, h, id); 1157 ENGINE.paintFlatBox(g, context, id, SynthConstants.ENABLED, 1158 ShadowType.OUT, "tooltip", x, y, w, h, 1159 ColorType.BACKGROUND); 1160 ENGINE.finishPainting(); 1161 } 1162 } 1163 } 1164 1165 1166 // 1167 // TREE_CELL 1168 // 1169 public void paintTreeCellBackground(SynthContext context, Graphics g, 1170 int x, int y, int w, int h) { 1171 Region id = context.getRegion(); 1172 int state = context.getComponentState(); 1173 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 1174 synchronized (UNIXToolkit.GTK_LOCK) { 1175 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, state)) { 1176 ENGINE.startPainting(g, x, y, w, h, id, state); 1177 // the string arg should alternate based on row being painted, 1178 // but we currently don't pass that in. 1179 ENGINE.paintFlatBox(g, context, id, gtkState, ShadowType.NONE, 1180 "cell_odd", x, y, w, h, ColorType.TEXT_BACKGROUND); 1181 ENGINE.finishPainting(); 1182 } 1183 } 1184 } 1185 1186 public void paintTreeCellFocus(SynthContext context, Graphics g, 1187 int x, int y, int w, int h) { 1188 Region id = Region.TREE_CELL; 1189 int state = context.getComponentState(); 1190 paintFocus(context, g, id, state, "treeview", x, y, w, h); 1191 } 1192 1193 1194 // 1195 // TREE 1196 // 1197 public void paintTreeBackground(SynthContext context, Graphics g, 1198 int x, int y, int w, int h) { 1199 // As far as I can tell, these don't call into the ENGINE. 1200 fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND); 1201 } 1202 1203 1204 // 1205 // VIEWPORT 1206 // 1207 public void paintViewportBackground(SynthContext context, Graphics g, 1208 int x, int y, int w, int h) { 1209 // As far as I can tell, these don't call into the ENGINE. 1210 // Also note that you don't want this to call into the ENGINE 1211 // as if it where to paint a background JViewport wouldn't scroll 1212 // correctly. 1213 fillArea(context, g, x, y, w, h, GTKColorType.TEXT_BACKGROUND); 1214 } 1215 1216 void paintFocus(SynthContext context, Graphics g, Region id, 1217 int state, String detail, int x, int y, int w, int h) { 1218 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 1219 synchronized (UNIXToolkit.GTK_LOCK) { 1220 if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, "focus")) { 1221 ENGINE.startPainting(g, x, y, w, h, id, gtkState, "focus"); 1222 ENGINE.paintFocus(g, context, id, gtkState, detail, x, y, w, h); 1223 ENGINE.finishPainting(); 1224 } 1225 } 1226 } 1227 1228 void paintMetacityElement(SynthContext context, Graphics g, 1229 int gtkState, String detail, int x, int y, int w, int h, 1230 ShadowType shadow, ArrowType direction) { 1231 synchronized (UNIXToolkit.GTK_LOCK) { 1232 if (! ENGINE.paintCachedImage( 1233 g, x, y, w, h, gtkState, detail, shadow, direction)) { 1234 ENGINE.startPainting( 1235 g, x, y, w, h, gtkState, detail, shadow, direction); 1236 if (detail == "metacity-arrow") { 1237 ENGINE.paintArrow(g, context, Region.INTERNAL_FRAME_TITLE_PANE, 1238 gtkState, shadow, direction, "", x, y, w, h); 1239 1240 } else if (detail == "metacity-box") { 1241 ENGINE.paintBox(g, context, Region.INTERNAL_FRAME_TITLE_PANE, 1242 gtkState, shadow, "", x, y, w, h); 1243 1244 } else if (detail == "metacity-vline") { 1245 ENGINE.paintVline(g, context, Region.INTERNAL_FRAME_TITLE_PANE, 1246 gtkState, "", x, y, w, h); 1247 } 1248 ENGINE.finishPainting(); 1249 } 1250 } 1251 } 1252 1253 void paintIcon(SynthContext context, Graphics g, 1254 Method paintMethod, int x, int y, int w, int h) { 1255 int state = context.getComponentState(); 1256 synchronized (UNIXToolkit.GTK_LOCK) { 1257 if (! ENGINE.paintCachedImage(g, x, y, w, h, state, paintMethod)) { 1258 ENGINE.startPainting(g, x, y, w, h, state, paintMethod); 1259 try { 1260 paintMethod.invoke(this, context, g, state, x, y, w, h); 1261 } catch (IllegalAccessException iae) { 1262 assert false; 1263 } catch (InvocationTargetException ite) { 1264 assert false; 1265 } 1266 ENGINE.finishPainting(); 1267 } 1268 } 1269 } 1270 1271 void paintIcon(SynthContext context, Graphics g, 1272 Method paintMethod, int x, int y, int w, int h, Object direction) { 1273 int state = context.getComponentState(); 1274 synchronized (UNIXToolkit.GTK_LOCK) { 1275 if (! ENGINE.paintCachedImage(g, 1276 x, y, w, h, state, paintMethod, direction)) { 1277 ENGINE.startPainting(g, 1278 x, y, w, h, state, paintMethod, direction); 1279 try { 1280 paintMethod.invoke(this, context, 1281 g, state, x, y, w, h, direction); 1282 } catch (IllegalAccessException iae) { 1283 assert false; 1284 } catch (InvocationTargetException ite) { 1285 assert false; 1286 } 1287 ENGINE.finishPainting(); 1288 } 1289 } 1290 } 1291 1292 // All icon painting methods are called from under GTK_LOCK 1293 1294 public void paintTreeExpandedIcon(SynthContext context, 1295 Graphics g, int state, int x, int y, int w, int h) { 1296 ENGINE.paintExpander(g, context, Region.TREE, 1297 GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state), 1298 ExpanderStyle.EXPANDED, "treeview", x, y, w, h); 1299 } 1300 1301 public void paintTreeCollapsedIcon(SynthContext context, 1302 Graphics g, int state, int x, int y, int w, int h) { 1303 ENGINE.paintExpander(g, context, Region.TREE, 1304 GTKLookAndFeel.synthStateToGTKState(context.getRegion(), state), 1305 ExpanderStyle.COLLAPSED, "treeview", x, y, w, h); 1306 } 1307 1308 public void paintCheckBoxIcon(SynthContext context, 1309 Graphics g, int state, int x, int y, int w, int h) { 1310 GTKStyle style = (GTKStyle)context.getStyle(); 1311 int size = style.getClassSpecificIntValue(context, 1312 "indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE); 1313 int offset = style.getClassSpecificIntValue(context, 1314 "indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING); 1315 1316 ENGINE.paintCheck(g, context, Region.CHECK_BOX, "checkbutton", 1317 x+offset, y+offset, size, size); 1318 } 1319 1320 public void paintRadioButtonIcon(SynthContext context, 1321 Graphics g, int state, int x, int y, int w, int h) { 1322 GTKStyle style = (GTKStyle)context.getStyle(); 1323 int size = style.getClassSpecificIntValue(context, 1324 "indicator-size", GTKIconFactory.DEFAULT_ICON_SIZE); 1325 int offset = style.getClassSpecificIntValue(context, 1326 "indicator-spacing", GTKIconFactory.DEFAULT_ICON_SPACING); 1327 1328 ENGINE.paintOption(g, context, Region.RADIO_BUTTON, "radiobutton", 1329 x+offset, y+offset, size, size); 1330 } 1331 1332 public void paintMenuArrowIcon(SynthContext context, Graphics g, 1333 int state, int x, int y, int w, int h, ArrowType dir) { 1334 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1335 context.getRegion(), state); 1336 ShadowType shadow = ShadowType.OUT; 1337 if (gtkState == SynthConstants.MOUSE_OVER) { 1338 shadow = ShadowType.IN; 1339 } 1340 ENGINE.paintArrow(g, context, Region.MENU_ITEM, gtkState, shadow, 1341 dir, "menuitem", x + 3, y + 3, 7, 7); 1342 } 1343 1344 public void paintCheckBoxMenuItemCheckIcon(SynthContext context, 1345 Graphics g, int state, int x, int y, int w, int h) { 1346 1347 GTKStyle style = (GTKStyle)context.getStyle(); 1348 int size = style.getClassSpecificIntValue(context,"indicator-size", 1349 GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE); 1350 1351 ENGINE.paintCheck(g, context, Region.CHECK_BOX_MENU_ITEM, "check", 1352 x + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1353 y + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1354 size, size); 1355 } 1356 1357 public void paintRadioButtonMenuItemCheckIcon(SynthContext context, 1358 Graphics g, int state, int x, int y, int w, int h) { 1359 1360 GTKStyle style = (GTKStyle)context.getStyle(); 1361 int size = style.getClassSpecificIntValue(context,"indicator-size", 1362 GTKIconFactory.DEFAULT_TOGGLE_MENU_ITEM_SIZE); 1363 1364 ENGINE.paintOption(g, context, Region.RADIO_BUTTON_MENU_ITEM, "option", 1365 x + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1366 y + GTKIconFactory.CHECK_ICON_EXTRA_INSET, 1367 size, size); 1368 } 1369 1370 public void paintToolBarHandleIcon(SynthContext context, Graphics g, 1371 int state, int x, int y, int w, int h, Orientation orientation) { 1372 int gtkState = GTKLookAndFeel.synthStateToGTKState( 1373 context.getRegion(), state); 1374 1375 // The orientation parameter passed down by Synth refers to the 1376 // orientation of the toolbar, but the one we pass to GTK refers 1377 // to the orientation of the handle. Therefore, we need to swap 1378 // the value here: horizontal toolbars have vertical handles, and 1379 // vice versa. 1380 orientation = (orientation == Orientation.HORIZONTAL) ? 1381 Orientation.VERTICAL : Orientation.HORIZONTAL; 1382 1383 ENGINE.paintHandle(g, context, Region.TOOL_BAR, gtkState, 1384 ShadowType.OUT, "handlebox", x, y, w, h, orientation); 1385 } 1386 1387 public void paintAscendingSortIcon(SynthContext context, 1388 Graphics g, int state, int x, int y, int w, int h) { 1389 ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED, 1390 ShadowType.IN, ArrowType.UP, "arrow", x, y, w, h); 1391 } 1392 1393 public void paintDescendingSortIcon(SynthContext context, 1394 Graphics g, int state, int x, int y, int w, int h) { 1395 ENGINE.paintArrow(g, context, Region.TABLE, SynthConstants.ENABLED, 1396 ShadowType.IN, ArrowType.DOWN, "arrow", x, y, w, h); 1397 } 1398 1399 /* 1400 * Fill an area with color determined from this context's Style using the 1401 * specified GTKColorType 1402 */ 1403 private void fillArea(SynthContext context, Graphics g, 1404 int x, int y, int w, int h, ColorType colorType) { 1405 if (context.getComponent().isOpaque()) { 1406 Region id = context.getRegion(); 1407 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, 1408 context.getComponentState()); 1409 GTKStyle style = (GTKStyle)context.getStyle(); 1410 1411 g.setColor(style.getGTKColor(context, gtkState, colorType)); 1412 g.fillRect(x, y, w, h); 1413 } 1414 } 1415 1416 // Refer to GTKLookAndFeel for details on this. 1417 static class ListTableFocusBorder extends AbstractBorder implements 1418 UIResource { 1419 1420 private boolean selectedCell; 1421 private boolean focusedCell; 1422 1423 public static ListTableFocusBorder getSelectedCellBorder() { 1424 return new ListTableFocusBorder(true, true); 1425 } 1426 1427 public static ListTableFocusBorder getUnselectedCellBorder() { 1428 return new ListTableFocusBorder(false, true); 1429 } 1430 1431 public static ListTableFocusBorder getNoFocusCellBorder() { 1432 return new ListTableFocusBorder(false, false); 1433 } 1434 1435 public ListTableFocusBorder(boolean selectedCell, boolean focusedCell) { 1436 this.selectedCell = selectedCell; 1437 this.focusedCell = focusedCell; 1438 } 1439 1440 private SynthContext getContext(Component c) { 1441 SynthContext context = null; 1442 1443 ComponentUI ui = null; 1444 if (c instanceof JLabel) { 1445 ui = ((JLabel)c).getUI(); 1446 } 1447 1448 if (ui instanceof SynthUI) { 1449 context = ((SynthUI)ui).getContext((JComponent)c); 1450 } 1451 1452 return context; 1453 } 1454 1455 public void paintBorder(Component c, Graphics g, int x, int y, 1456 int w, int h) { 1457 if (focusedCell) { 1458 SynthContext context = getContext(c); 1459 int state = (selectedCell? SynthConstants.SELECTED: 1460 SynthConstants.FOCUSED | SynthConstants.ENABLED); 1461 1462 if (context != null) { 1463 GTKPainter.INSTANCE.paintFocus(context, g, 1464 Region.TABLE, state, "", x, y, w, h); 1465 } 1466 } 1467 } 1468 1469 public Insets getBorderInsets(Component c, Insets i) { 1470 SynthContext context = getContext(c); 1471 1472 if (context != null) { 1473 i = context.getStyle().getInsets(context, i); 1474 } 1475 1476 return i; 1477 } 1478 1479 public boolean isBorderOpaque() { 1480 return true; 1481 } 1482 } 1483 1484 // TitledBorder implementation for GTK L&F 1485 static class TitledBorder extends AbstractBorder implements UIResource { 1486 1487 public void paintBorder(Component c, Graphics g, int x, int y, 1488 int w, int h) { 1489 SynthContext context = getContext((JComponent)c); 1490 Region id = context.getRegion(); 1491 int state = context.getComponentState(); 1492 int gtkState = GTKLookAndFeel.synthStateToGTKState(id, state); 1493 1494 synchronized (UNIXToolkit.GTK_LOCK) { 1495 if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { 1496 ENGINE.startPainting(g, x, y, w, h, id); 1497 ENGINE.paintShadow(g, context, id, gtkState, ShadowType.ETCHED_IN, 1498 "frame", x, y, w, h); 1499 ENGINE.finishPainting(); 1500 } 1501 } 1502 } 1503 1504 public Insets getBorderInsets(Component c, Insets i) { 1505 SynthContext context = getContext((JComponent)c); 1506 return context.getStyle().getInsets(context, i); 1507 } 1508 1509 public boolean isBorderOpaque() { 1510 return true; 1511 } 1512 1513 private SynthStyle getStyle(JComponent c) { 1514 return SynthLookAndFeel.getStyle(c, GTKEngine.CustomRegion.TITLED_BORDER); 1515 } 1516 1517 private SynthContext getContext(JComponent c) { 1518 int state = SynthConstants.DEFAULT; 1519 return new SynthContext(c, GTKEngine.CustomRegion.TITLED_BORDER, 1520 getStyle(c), state); 1521 } 1522 } 1523 }