1/** <title>GSThemeDrawing</title>
2
3   <abstract>The theme methods for drawing controls</abstract>
4
5   Copyright (C) 2004-2010 Free Software Foundation, Inc.
6
7   Author: Adam Fedor <fedor@gnu.org>
8   Date: Jan 2004
9
10   This file is part of the GNU Objective C User interface library.
11
12   This library is free software; you can redistribute it and/or
13   modify it under the terms of the GNU Lesser General Public
14   License as published by the Free Software Foundation; either
15   version 2 of the License, or (at your option) any later version.
16
17   This library is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
20   Lesser General Public License for more details.
21
22   You should have received a copy of the GNU Lesser General Public
23   License along with this library; see the file COPYING.LIB.
24   If not, see <http://www.gnu.org/licenses/> or write to the
25   Free Software Foundation, 51 Franklin Street, Fifth Floor,
26   Boston, MA 02110-1301, USA.
27*/
28
29#import "GSThemePrivate.h"
30
31#import "Foundation/NSUserDefaults.h"
32#import "Foundation/NSIndexSet.h"
33
34#import "AppKit/NSAttributedString.h"
35#import "AppKit/NSBezierPath.h"
36#import "AppKit/NSButtonCell.h"
37#import "AppKit/NSBrowser.h"
38#import "AppKit/NSBrowserCell.h"
39#import "AppKit/NSCell.h"
40#import "AppKit/NSColor.h"
41#import "AppKit/NSColorList.h"
42#import "AppKit/NSColorWell.h"
43#import "AppKit/NSGraphics.h"
44#import "AppKit/NSImage.h"
45#import "AppKit/NSMenuView.h"
46#import "AppKit/NSMenuItemCell.h"
47#import "AppKit/NSParagraphStyle.h"
48#import "AppKit/NSPopUpButtonCell.h"
49#import "AppKit/NSProgressIndicator.h"
50#import "AppKit/NSScroller.h"
51#import "AppKit/NSScrollView.h"
52#import "AppKit/NSStringDrawing.h"
53#import "AppKit/NSTableView.h"
54#import "AppKit/NSTableColumn.h"
55#import "AppKit/NSTableHeaderCell.h"
56#import "AppKit/NSTableHeaderView.h"
57#import "AppKit/NSView.h"
58#import "AppKit/NSTabView.h"
59#import "AppKit/NSTabViewItem.h"
60#import "AppKit/PSOperators.h"
61#import "AppKit/NSSliderCell.h"
62
63#import "GNUstepGUI/GSToolbarView.h"
64#import "GNUstepGUI/GSTitleView.h"
65
66/* a border width of 5 gives a reasonable compromise between Cocoa metrics and looking good */
67/* 7.0 gives us the NeXT Look (which is 8 pix wide including the shadow) */
68#define COLOR_WELL_BORDER_WIDTH 7.0
69
70@interface NSTableView (Private)
71- (CGFloat *)_columnOrigins;
72- (void) _willDisplayCell: (NSCell*)cell
73	   forTableColumn: (NSTableColumn *)tb
74		      row: (NSInteger)index;
75- (id)_objectValueForTableColumn: (NSTableColumn *)tb
76			     row: (NSInteger)index;
77@end
78
79@interface NSCell (Private)
80- (void) _setInEditing: (BOOL)flag;
81- (BOOL) _inEditing;
82- (void) _drawEditorWithFrame: (NSRect)cellFrame
83                       inView: (NSView *)controlView;
84- (void) _drawAttributedText: (NSAttributedString*)aString
85                     inFrame: (NSRect)aRect;
86@end
87
88@implementation	GSTheme (Drawing)
89- (void) setKeyEquivalent: (NSString *)key
90            forButtonCell: (NSButtonCell *)cell
91{
92  if([cell image] == nil && ([key isEqualToString:@"\r"] ||
93			     [key isEqualToString:@"\n"]))
94    {
95      [cell setImagePosition: NSImageRight];
96      [cell setImage: [NSImage imageNamed:@"common_ret"]];
97      [cell setAlternateImage: [NSImage imageNamed:@"common_retH"]];
98    }
99  else if([key isEqualToString:@"\r"] == NO &&
100	  [key isEqualToString:@"\n"] == NO)
101    {
102      NSImage *cellImage = [cell image];
103      if(cellImage == [NSImage imageNamed:@"common_ret"])
104	{
105	  [cell setImage: nil];
106	  [cell setAlternateImage: nil];
107	}
108    }
109}
110
111- (void) drawButton: (NSRect)frame
112                 in: (NSCell*)cell
113               view: (NSView*)view
114              style: (int)style
115              state: (GSThemeControlState)state
116{
117  GSDrawTiles	*tiles = nil;
118  NSColor	*color = nil;
119  NSString	*name = [self nameForElement: cell];
120
121  if (name == nil)
122    {
123      name = GSStringFromBezelStyle(style);
124    }
125
126  color = [self colorNamed: name state: state];
127  if (color == nil)
128    {
129      if (state == GSThemeNormalState)
130	{
131          color = [NSColor controlBackgroundColor];
132	}
133      else if (state == GSThemeHighlightedState
134	       || state == GSThemeHighlightedFirstResponderState)
135	{
136          color = [NSColor selectedControlColor];
137	}
138      else if (state == GSThemeSelectedState
139	       || state == GSThemeSelectedFirstResponderState)
140	{
141          color = [NSColor selectedControlColor];
142	}
143      else
144    	{
145          color = [NSColor controlBackgroundColor];
146	}
147    }
148
149  tiles = [self tilesNamed: name state: state];
150  if (tiles == nil)
151    {
152      tiles = [self tilesNamed: @"NSButton" state: state];
153    }
154
155  if (tiles == nil)
156    {
157      switch (style)
158        {
159	  case NSRoundRectBezelStyle:
160	  case NSTexturedRoundedBezelStyle:
161	  case NSRoundedBezelStyle:
162	    [self drawRoundBezel: frame withColor: color];
163	    break;
164	  case NSTexturedSquareBezelStyle:
165	    frame = NSInsetRect(frame, 0, 1);
166	  case NSSmallSquareBezelStyle:
167	  case NSRegularSquareBezelStyle:
168	  case NSShadowlessSquareBezelStyle:
169	    [color set];
170	    NSRectFill(frame);
171	    [[NSColor controlShadowColor] set];
172	    NSFrameRectWithWidth(frame, 1);
173	    break;
174	  case NSThickSquareBezelStyle:
175	    [color set];
176	    NSRectFill(frame);
177	    [[NSColor controlShadowColor] set];
178	    NSFrameRectWithWidth(frame, 1.5);
179	    break;
180	  case NSThickerSquareBezelStyle:
181	    [color set];
182	    NSRectFill(frame);
183	    [[NSColor controlShadowColor] set];
184	    NSFrameRectWithWidth(frame, 2);
185	    break;
186	  case NSCircularBezelStyle:
187	    frame = NSInsetRect(frame, 3, 3);
188	    [self drawCircularBezel: frame withColor: color];
189	    break;
190	  case NSHelpButtonBezelStyle:
191	    [self drawCircularBezel: frame withColor: color];
192	    {
193	      NSDictionary *attributes = [NSDictionary dictionaryWithObject: [NSFont controlContentFontOfSize: 0]
194								     forKey: NSFontAttributeName];
195	      NSAttributedString *questionMark = [[[NSAttributedString alloc]
196						    initWithString: _(@"?")
197							attributes: attributes] autorelease];
198
199	      NSRect textRect;
200	      textRect.size = [questionMark size];
201	      textRect.origin.x = NSMidX(frame) - (textRect.size.width / 2);
202	      textRect.origin.y = NSMidY(frame) - (textRect.size.height / 2);
203
204	      [questionMark drawInRect: textRect];
205	    }
206	    break;
207	  case NSDisclosureBezelStyle:
208	  case NSRoundedDisclosureBezelStyle:
209	  case NSRecessedBezelStyle:
210	    // FIXME
211	    break;
212	  default:
213	    [color set];
214	    NSRectFill(frame);
215
216	    if (state == GSThemeNormalState || state == GSThemeHighlightedState)
217	      {
218		[self drawButton: frame withClip: NSZeroRect];
219	      }
220	    else if (state == GSThemeSelectedState || state == GSThemeSelectedFirstResponderState)
221	      {
222		[self drawGrayBezel: frame withClip: NSZeroRect];
223	      }
224	    else
225	      {
226		[self drawButton: frame withClip: NSZeroRect];
227	      }
228	}
229    }
230  else
231    {
232      /* Use tiles to draw button border with central part filled with color
233       */
234      [self fillRect: frame
235	   withTiles: tiles
236	  background: color];
237    }
238}
239
240- (GSThemeMargins) buttonMarginsForCell: (NSCell*)cell
241				  style: (int)style
242				  state: (GSThemeControlState)state
243{
244  GSDrawTiles	*tiles = nil;
245  NSString	*name = [self nameForElement: cell];
246  GSThemeMargins margins;
247
248  if (name == nil)
249    {
250      name = GSStringFromBezelStyle(style);
251    }
252
253  tiles = [self tilesNamed: name state: state];
254  if (tiles == nil)
255    {
256      tiles = [self tilesNamed: @"NSButton" state: state];
257    }
258
259  if (tiles == nil)
260    {
261      switch (style)
262        {
263	  case NSRoundRectBezelStyle:
264	  case NSTexturedRoundedBezelStyle:
265	  case NSRoundedBezelStyle:
266	    margins.left = 5; margins.top = 5; margins.right = 5; margins.bottom = 5;
267	    return margins;
268	  case NSTexturedSquareBezelStyle:
269	    margins.left = 3; margins.top = 3; margins.right = 3; margins.bottom = 3;
270	    return margins;
271	  case NSSmallSquareBezelStyle:
272	  case NSRegularSquareBezelStyle:
273	  case NSShadowlessSquareBezelStyle:
274	    margins.left = 2; margins.top = 2; margins.right = 2; margins.bottom = 2;
275	    return margins;
276	  case NSThickSquareBezelStyle:
277	    margins.left = 3; margins.top = 3; margins.right = 3; margins.bottom = 3;
278	    return margins;
279	  case NSThickerSquareBezelStyle:
280	    margins.left = 4; margins.top = 4; margins.right = 4; margins.bottom = 4;
281	    return margins;
282	  case NSCircularBezelStyle:
283	    margins.left = 5; margins.top = 5; margins.right = 5; margins.bottom = 5;
284	    return margins;
285	  case NSHelpButtonBezelStyle:
286	    margins.left = 2; margins.top = 2; margins.right = 2; margins.bottom = 2;
287	    return margins;
288	  case NSDisclosureBezelStyle:
289	  case NSRoundedDisclosureBezelStyle:
290	  case NSRecessedBezelStyle:
291	    // FIXME
292	    margins.left = 0; margins.top = 0; margins.right = 0; margins.bottom = 0;
293	    return margins;
294	  default:
295	    margins.left = 2; margins.top = 2; margins.right = 3; margins.bottom = 3;
296	    return margins;
297	}
298    }
299  else
300    {
301      margins = [tiles themeMargins];
302      return margins;
303    }
304}
305
306- (void) drawFocusFrame: (NSRect) frame view: (NSView*) view
307{
308  GSDrawTiles *tiles = [self tilesNamed: @"NSFocusRing" state: GSThemeNormalState];
309
310  if (tiles == nil)
311    {
312      NSDottedFrameRect(frame);
313    }
314  else
315    {
316      [self fillRect: frame
317           withTiles: tiles];
318    }
319}
320
321- (void) drawWindowBackground: (NSRect) frame view: (NSView*) view
322{
323  NSColor *c;
324
325  c = [[view window] backgroundColor];
326  [c set];
327  NSRectFill (frame);
328}
329
330- (void) drawBorderType: (NSBorderType)aType
331                  frame: (NSRect)frame
332                   view: (NSView*)view
333{
334  NSString      *name = GSStringFromBorderType(aType);
335  GSDrawTiles   *tiles = [self tilesNamed: name state: GSThemeNormalState];
336
337  if (tiles == nil)
338    {
339      switch (aType)
340	{
341	  case NSLineBorder:
342	    [[NSColor controlDarkShadowColor] set];
343	    NSFrameRect(frame);
344	    break;
345	  case NSGrooveBorder:
346	    [self drawGroove: frame withClip: NSZeroRect];
347	    break;
348	  case NSBezelBorder:
349	    [self drawWhiteBezel: frame withClip: NSZeroRect];
350	    break;
351	  case NSNoBorder:
352	  default:
353	    break;
354	}
355    }
356  else
357    {
358      [self fillRect: frame
359           withTiles: tiles];
360    }
361}
362
363- (NSSize) sizeForBorderType: (NSBorderType)aType
364{
365  NSString      *name = GSStringFromBorderType(aType);
366  GSDrawTiles   *tiles = [self tilesNamed: name state: GSThemeNormalState];
367
368  if (tiles == nil)
369    {
370      // Returns the size of a border
371      switch (aType)
372	{
373	  case NSLineBorder:
374	    return NSMakeSize(1, 1);
375	  case NSGrooveBorder:
376	  case NSBezelBorder:
377	    return NSMakeSize(2, 2);
378	  case NSNoBorder:
379	  default:
380	    return NSZeroSize;
381	}
382    }
383  else
384    {
385      // FIXME: We assume the button's top and right padding are the same as
386      // its bottom and left.
387
388      GSThemeMargins margins = [tiles themeMargins];
389      return NSMakeSize(margins.left, margins.bottom);
390    }
391}
392
393- (void) drawBorderForImageFrameStyle: (NSImageFrameStyle)frameStyle
394                                frame: (NSRect)frame
395                                 view: (NSView*)view
396{
397  NSString      *name = GSStringFromImageFrameStyle(frameStyle);
398  GSDrawTiles   *tiles = [self tilesNamed: name state: GSThemeNormalState];
399
400  if (tiles == nil)
401    {
402      switch (frameStyle)
403	{
404	case NSImageFrameNone:
405	  // do nothing
406	  break;
407	case NSImageFramePhoto:
408	  [self drawFramePhoto: frame withClip: NSZeroRect];
409	  break;
410	case NSImageFrameGrayBezel:
411	  [self drawGrayBezel: frame withClip: NSZeroRect];
412	  break;
413	case NSImageFrameGroove:
414	  [self drawGroove: frame withClip: NSZeroRect];
415	  break;
416	case NSImageFrameButton:
417	  [self drawButton: frame withClip: NSZeroRect];
418	  break;
419	}
420    }
421  else
422    {
423      [self fillRect: frame
424           withTiles: tiles];
425    }
426}
427
428- (NSSize) sizeForImageFrameStyle: (NSImageFrameStyle)frameStyle
429{
430  // Get border size
431  switch (frameStyle)
432    {
433      case NSImageFrameNone:
434      default:
435        return NSZeroSize;
436      case NSImageFramePhoto:
437        // FIXME
438        return NSMakeSize(2, 2);
439      case NSImageFrameGrayBezel:
440      case NSImageFrameGroove:
441      case NSImageFrameButton:
442        return NSMakeSize(2, 2);
443    }
444}
445
446
447/* NSScroller themeing.
448 */
449- (BOOL) scrollerArrowsSameEndForScroller: (NSScroller *)aScroller
450{
451  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
452
453  if ([defs objectForKey: @"GSScrollerArrowsSameEnd"] != nil)
454    {
455      return [defs boolForKey: @"GSScrollerArrowsSameEnd"];
456    }
457  else
458    {
459      NSInterfaceStyle interfaceStyle =
460	NSInterfaceStyleForKey(@"NSScrollerInterfaceStyle", aScroller);
461
462      if ((interfaceStyle == NSNextStepInterfaceStyle
463	   || interfaceStyle == NSMacintoshInterfaceStyle
464	   || interfaceStyle == GSWindowMakerInterfaceStyle))
465	{
466	  return YES;
467	}
468      else
469	{
470	  return NO;
471	}
472    }
473}
474
475- (BOOL) scrollerScrollsByPageForScroller: (NSScroller *)aScroller
476{
477  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
478
479  if ([defs objectForKey: @"GSScrollerScrollsByPage"] != nil)
480    {
481      return [defs boolForKey: @"GSScrollerScrollsByPage"];
482    }
483  else
484    {
485      NSInterfaceStyle interfaceStyle =
486	NSInterfaceStyleForKey(@"NSScrollerInterfaceStyle", aScroller);
487
488      if (interfaceStyle == NSNextStepInterfaceStyle
489	  || interfaceStyle == NSMacintoshInterfaceStyle
490	  || interfaceStyle == GSWindowMakerInterfaceStyle)
491	{
492	  /* NeXTstep style is to scroll to point.
493	   */
494	  return NO;
495	}
496      else
497	{
498	  /* Windows style is to scroll by a page.
499	   */
500	  return YES;
501	}
502    }
503}
504
505- (NSButtonCell*) cellForScrollerArrow: (NSScrollerArrow)arrow
506			    horizontal: (BOOL)horizontal
507{
508  NSButtonCell	*cell;
509  NSString	*name;
510
511  cell = [NSButtonCell new];
512  if (horizontal)
513    {
514      if (arrow == NSScrollerDecrementArrow)
515	{
516	  [cell setHighlightsBy:
517	    NSChangeBackgroundCellMask | NSContentsCellMask];
518	  [cell setImage: [NSImage imageNamed: @"common_ArrowLeft"]];
519	  [cell setAlternateImage: [NSImage imageNamed: @"common_ArrowLeftH"]];
520	  [cell setImagePosition: NSImageOnly];
521          name = GSScrollerLeftArrow;
522	}
523      else
524	{
525	  [cell setHighlightsBy:
526	    NSChangeBackgroundCellMask | NSContentsCellMask];
527	  [cell setImage: [NSImage imageNamed: @"common_ArrowRight"]];
528	  [cell setAlternateImage: [NSImage imageNamed: @"common_ArrowRightH"]];
529	  [cell setImagePosition: NSImageOnly];
530          name = GSScrollerRightArrow;
531	}
532    }
533  else
534    {
535      if (arrow == NSScrollerDecrementArrow)
536	{
537	  [cell setHighlightsBy:
538	    NSChangeBackgroundCellMask | NSContentsCellMask];
539	  [cell setImage: [NSImage imageNamed: @"common_ArrowUp"]];
540	  [cell setAlternateImage: [NSImage imageNamed: @"common_ArrowUpH"]];
541	  [cell setImagePosition: NSImageOnly];
542          name = GSScrollerUpArrow;
543	}
544      else
545	{
546	  [cell setHighlightsBy:
547	    NSChangeBackgroundCellMask | NSContentsCellMask];
548	  [cell setImage: [NSImage imageNamed: @"common_ArrowDown"]];
549	  [cell setAlternateImage: [NSImage imageNamed: @"common_ArrowDownH"]];
550	  [cell setImagePosition: NSImageOnly];
551          name = GSScrollerDownArrow;
552	}
553    }
554  [self setName: name forElement: cell temporary: YES];
555  RELEASE(cell);
556  return cell;
557}
558
559- (NSCell*) cellForScrollerKnob: (BOOL)horizontal
560{
561  NSButtonCell	*cell;
562
563  cell = [NSButtonCell new];
564  [cell setButtonType: NSMomentaryChangeButton];
565  [cell setImagePosition: NSImageOnly];
566  if (horizontal)
567    {
568      [self setName: GSScrollerHorizontalKnob forElement: cell temporary: YES];
569      [cell setImage: [NSImage imageNamed: @"common_DimpleHoriz"]];
570    }
571  else
572    {
573      [self setName: GSScrollerVerticalKnob forElement: cell temporary: YES];
574      [cell setImage: [NSImage imageNamed: @"common_Dimple"]];
575
576    }
577  RELEASE(cell);
578  return cell;
579}
580
581- (NSCell*) cellForScrollerKnobSlot: (BOOL)horizontal
582{
583  GSDrawTiles   *tiles;
584  NSButtonCell	*cell;
585  NSColor	*color;
586  NSString      *name;
587
588  if (horizontal)
589    {
590      name = GSScrollerHorizontalSlot;
591    }
592  else
593    {
594      name = GSScrollerVerticalSlot;
595    }
596
597  tiles = [self tilesNamed: name state: GSThemeNormalState];
598  color = [self colorNamed: name state: GSThemeNormalState];
599
600  cell = [NSButtonCell new];
601  [cell setBordered: (tiles != nil)];
602  [cell setTitle: nil];
603
604  [self setName: name forElement: cell temporary: YES];
605
606  if (color == nil)
607    {
608      color = [NSColor scrollBarColor];
609    }
610  [cell setBackgroundColor: color];
611  RELEASE(cell);
612  return cell;
613}
614
615- (float) defaultScrollerWidth
616{
617  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
618  float defaultScrollerWidth;
619
620  if ([defs objectForKey: @"GSScrollerDefaultWidth"] != nil)
621    {
622      defaultScrollerWidth = [defs floatForKey: @"GSScrollerDefaultWidth"];
623    }
624  else
625    {
626      defaultScrollerWidth = 18.0;
627    }
628  return defaultScrollerWidth;
629}
630
631- (BOOL) scrollViewUseBottomCorner
632{
633  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
634  if ([defs objectForKey: @"GSScrollViewUseBottomCorner"] != nil)
635    {
636      return [defs boolForKey: @"GSScrollViewUseBottomCorner"];
637    }
638  return YES;
639}
640
641- (BOOL) scrollViewScrollersOverlapBorders
642{
643  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
644  if ([defs objectForKey: @"GSScrollViewScrollersOverlapBorders"] != nil)
645    {
646      return [defs boolForKey: @"GSScrollViewScrollersOverlapBorders"];
647    }
648  return NO;
649}
650
651- (NSColor *) toolbarBackgroundColor
652{
653  NSColor *color;
654
655  color = [self colorNamed: @"toolbarBackgroundColor"
656                state: GSThemeNormalState];
657  if (color == nil)
658    {
659      color = [NSColor clearColor];
660    }
661  return color;
662}
663
664- (NSColor *) toolbarBorderColor
665{
666  NSColor *color;
667
668  color = [self colorNamed: @"toolbarBorderColor"
669                state: GSThemeNormalState];
670  if (color == nil)
671    {
672      color = [NSColor darkGrayColor];
673    }
674  return color;
675}
676
677- (void) drawToolbarRect: (NSRect)aRect
678                   frame: (NSRect)viewFrame
679              borderMask: (unsigned int)borderMask
680{
681  // We draw the background
682  [[self toolbarBackgroundColor] set];
683  [NSBezierPath fillRect: aRect];
684
685  // We draw the border
686  [[self toolbarBorderColor] set];
687  if (borderMask & GSToolbarViewBottomBorder)
688    {
689      [NSBezierPath strokeLineFromPoint: NSMakePoint(0, 0.5)
690                    toPoint: NSMakePoint(viewFrame.size.width, 0.5)];
691    }
692  if (borderMask & GSToolbarViewTopBorder)
693    {
694      [NSBezierPath strokeLineFromPoint: NSMakePoint(0,
695                                                     viewFrame.size.height - 0.5)
696                    toPoint: NSMakePoint(viewFrame.size.width,
697                                         viewFrame.size.height -  0.5)];
698    }
699  if (borderMask & GSToolbarViewLeftBorder)
700    {
701      [NSBezierPath strokeLineFromPoint: NSMakePoint(0.5, 0)
702                    toPoint: NSMakePoint(0.5, viewFrame.size.height)];
703    }
704  if (borderMask & GSToolbarViewRightBorder)
705    {
706      [NSBezierPath strokeLineFromPoint: NSMakePoint(viewFrame.size.width - 0.5,0)
707                    toPoint: NSMakePoint(viewFrame.size.width - 0.5,
708                                         viewFrame.size.height)];
709    }
710}
711
712- (BOOL) toolbarIsOpaque
713{
714  if ([[self toolbarBackgroundColor] alphaComponent] < 1.0)
715    {
716      return NO;
717    }
718  else
719    {
720      return YES;
721    }
722}
723
724- (NSRect) stepperUpButtonRectWithFrame: (NSRect)frame
725{
726  NSSize size = [[NSImage imageNamed: @"common_StepperUp"] size];
727  NSRect upRect = {{NSMinX(frame), NSMinY(frame)}, {size.width, size.height}};
728
729  upRect.origin.x += ((int)frame.size.width / 2) - ((int)size.width / 2);
730  upRect.origin.y += ((int)frame.size.height / 2);
731  return upRect;
732}
733
734- (NSRect) stepperDownButtonRectWithFrame: (NSRect)frame
735{
736  NSSize size = [[NSImage imageNamed: @"common_StepperDown"] size];
737  NSRect downRect = {{NSMinX(frame), NSMinY(frame)}, {size.width, size.height}};
738
739  downRect.origin.x += ((int)frame.size.width / 2) - ((int)size.width / 2);
740  downRect.origin.y += ((int)frame.size.height / 2) - size.height;
741  return downRect;
742}
743
744- (void) drawStepperBorder: (NSRect)frame
745{
746}
747
748- (NSRect) drawStepperLightButton: (NSRect)border : (NSRect)clip
749{
750  return NSZeroRect;
751}
752
753- (void) drawStepperUpButton: (NSRect)aRect
754{
755  NSImage *image = [NSImage imageNamed: @"common_StepperUp"];
756  [image drawInRect: aRect
757	   fromRect: NSZeroRect
758	  operation: NSCompositeSourceOver
759	   fraction: 1
760     respectFlipped: YES
761	      hints: nil];
762}
763
764- (void) drawStepperHighlightUpButton: (NSRect)aRect
765{
766  NSImage *image = [NSImage imageNamed: @"common_StepperUpHighlighted"];
767  [image drawInRect: aRect
768	   fromRect: NSZeroRect
769	  operation: NSCompositeSourceOver
770	   fraction: 1
771     respectFlipped: YES
772	      hints: nil];
773}
774
775- (void) drawStepperDownButton: (NSRect)aRect
776{
777  NSImage *image = [NSImage imageNamed: @"common_StepperDown"];
778  [image drawInRect: aRect
779	   fromRect: NSZeroRect
780	  operation: NSCompositeSourceOver
781	   fraction: 1
782     respectFlipped: YES
783	      hints: nil];
784}
785
786- (void) drawStepperHighlightDownButton: (NSRect)aRect
787{
788  NSImage *image = [NSImage imageNamed: @"common_StepperDownHighlighted"];
789  [image drawInRect: aRect
790	   fromRect: NSZeroRect
791	  operation: NSCompositeSourceOver
792	   fraction: 1
793     respectFlipped: YES
794	      hints: nil];
795}
796
797- (void) drawStepperCell: (NSCell*)cell
798               withFrame: (NSRect)cellFrame
799                  inView: (NSView*)controlView
800             highlightUp: (BOOL)highlightUp
801           highlightDown: (BOOL)highlightDown
802{
803  const NSRect upRect = [self stepperUpButtonRectWithFrame: cellFrame];
804  const NSRect downRect = [self stepperDownButtonRectWithFrame: cellFrame];
805
806  [self drawStepperBorder: cellFrame];
807
808  if (highlightUp)
809    [self drawStepperHighlightUpButton: upRect];
810  else
811    [self drawStepperUpButton: upRect];
812
813  if (highlightDown)
814    [self drawStepperHighlightDownButton: downRect];
815  else
816    [self drawStepperDownButton: downRect];
817}
818
819// NSSegmentedControl drawing methods
820
821- (void) drawSegmentedControlSegment: (NSCell *)cell
822                           withFrame: (NSRect)cellFrame
823                              inView: (NSView *)controlView
824                               style: (NSSegmentStyle)style
825                               state: (GSThemeControlState)state
826                         roundedLeft: (BOOL)roundedLeft
827                        roundedRight: (BOOL)roundedRight
828{
829  GSDrawTiles *tiles;
830  NSString  *name = GSStringFromSegmentStyle(style);
831  if (roundedLeft)
832    {
833      name = [name stringByAppendingString: @"RoundedLeft"];
834    }
835  if (roundedRight)
836    {
837      name = [name stringByAppendingString: @"RoundedRight"];
838    }
839
840  tiles = [self tilesNamed: name state: state];
841
842  if (tiles == nil)
843    {
844      [self drawButton: cellFrame
845                    in: cell
846                  view: controlView
847                 style: NSRegularSquareBezelStyle
848                 state: state];
849    }
850  else
851    {
852      [self fillRect: cellFrame
853           withTiles: tiles];
854    }
855}
856
857- (NSColor *) menuBackgroundColor
858{
859  NSColor *color = [self colorNamed: @"menuBackgroundColor"
860                              state: GSThemeNormalState];
861  if (color == nil)
862    {
863      color = [NSColor windowBackgroundColor];
864    }
865  return color;
866}
867
868- (NSColor *) menuItemBackgroundColor
869{
870  NSColor *color = [self colorNamed: @"menuItemBackgroundColor"
871                              state: GSThemeNormalState];
872  if (color == nil)
873    {
874      color = [NSColor controlBackgroundColor];
875    }
876  return color;
877}
878
879- (NSColor *) menuBorderColor
880{
881  NSColor *color = [self colorNamed: @"menuBorderColor"
882                              state: GSThemeNormalState];
883  if (color == nil)
884    {
885      color = [NSColor darkGrayColor];
886    }
887  return color;
888}
889
890- (NSColor *) menuBarBackgroundColor
891{
892  NSColor *color = [self colorNamed: @"menuBarBackgroundColor"
893                              state: GSThemeNormalState];
894  if (color == nil)
895    {
896      color = [self menuBackgroundColor];
897    }
898  return color;
899}
900
901- (NSColor *) menuBarBorderColor
902{
903  NSColor *color = [self colorNamed: @"menuBarBorderColor"
904                              state: GSThemeNormalState];
905  if (color == nil)
906    {
907      color = [self menuBorderColor];
908    }
909  return color;
910}
911
912- (NSColor *) menuBorderColorForEdge: (NSRectEdge)edge isHorizontal: (BOOL)horizontal
913{
914  if (horizontal && edge == NSMinYEdge)
915    {
916      return [self menuBorderColor];
917    }
918  else if (edge == NSMinXEdge || edge == NSMaxYEdge)
919    {
920      // Draw the dark gray upper left lines.
921      return [self menuBorderColor];
922    }
923  return nil;
924}
925
926- (void) drawBackgroundForMenuView: (NSMenuView*)menuView
927                         withFrame: (NSRect)bounds
928                         dirtyRect: (NSRect)dirtyRect
929                        horizontal: (BOOL)horizontal
930{
931  NSString  *name = horizontal ? GSMenuHorizontalBackground :
932    GSMenuVerticalBackground;
933  GSDrawTiles *tiles = [self tilesNamed: name state: GSThemeNormalState];
934
935  if (tiles == nil)
936    {
937      NSRectEdge sides[4] = { NSMinXEdge, NSMaxYEdge, NSMaxXEdge, NSMinYEdge };
938      NSColor *colors[] = {[self menuBorderColorForEdge: NSMinXEdge isHorizontal: horizontal],
939                           [self menuBorderColorForEdge: NSMaxYEdge isHorizontal: horizontal],
940                           [self menuBorderColorForEdge: NSMaxXEdge isHorizontal: horizontal],
941                           [self menuBorderColorForEdge: NSMinYEdge isHorizontal: horizontal]};
942
943      [[self menuBackgroundColor] set];
944      NSRectFill(NSIntersectionRect(bounds, dirtyRect));
945      NSDrawColorTiledRects(bounds, dirtyRect, sides, colors, 4);
946    }
947  else
948    {
949      [self fillRect: bounds
950           withTiles: tiles];
951    }
952}
953
954- (BOOL) drawsBorderForMenuItemCell: (NSMenuItemCell *)cell
955                              state: (GSThemeControlState)state
956                       isHorizontal: (BOOL)horizontal
957{
958  return [cell isBordered];
959}
960
961- (void) drawBorderAndBackgroundForMenuItemCell: (NSMenuItemCell *)cell
962                                      withFrame: (NSRect)cellFrame
963                                         inView: (NSView *)controlView
964                                          state: (GSThemeControlState)state
965                                   isHorizontal: (BOOL)isHorizontal
966{
967  NSString  *name = isHorizontal ? GSMenuHorizontalItem :
968    GSMenuVerticalItem;
969  GSDrawTiles *tiles = [self tilesNamed: name state: state];
970
971  if (tiles == nil)
972    {
973      NSColor	*backgroundColor = [cell backgroundColor];
974
975      if (isHorizontal)
976	{
977	  cellFrame = [cell drawingRectForBounds: cellFrame];
978	  [backgroundColor set];
979	  NSRectFill(cellFrame);
980	  return;
981	}
982
983      // Set cell's background color
984      [backgroundColor set];
985      NSRectFill(cellFrame);
986
987      if (![self drawsBorderForMenuItemCell: cell
988                                      state: state
989                               isHorizontal: isHorizontal])
990        {
991          return;
992        }
993
994      if (state == GSThemeSelectedState)
995	{
996          [self drawGrayBezel: cellFrame withClip: NSZeroRect];
997        }
998      else
999        {
1000          [self drawButton: cellFrame withClip: NSZeroRect];
1001        }
1002    }
1003  else
1004    {
1005      [self fillRect: cellFrame
1006           withTiles: tiles];
1007    }
1008}
1009
1010- (NSColor *) menuSeparatorColor
1011{
1012  NSColor *color = [self colorNamed: @"menuSeparatorColor"
1013                              state: GSThemeNormalState];
1014  NSInterfaceStyle style = NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil);
1015
1016  // TODO: Remove the style check... Windows theming should be in a subclass
1017  // probably
1018  if (color == nil && style == NSWindows95InterfaceStyle)
1019    {
1020      color = [NSColor blackColor];
1021    }
1022  return color;
1023}
1024
1025- (CGFloat) menuSeparatorInset
1026{
1027  return 3.0;
1028}
1029
1030- (CGFloat) menuSubmenuHorizontalOverlap
1031{
1032  return [[NSUserDefaults standardUserDefaults]
1033    floatForKey: @"GSMenuSubmenuHorizontalOverlap"];
1034}
1035
1036- (CGFloat) menuSubmenuVerticalOverlap
1037{
1038  return [[NSUserDefaults standardUserDefaults]
1039    floatForKey: @"GSMenuSubmenuVerticalOverlap"];
1040}
1041
1042- (void) drawSeparatorItemForMenuItemCell: (NSMenuItemCell *)cell
1043                                withFrame: (NSRect)cellFrame
1044                                   inView: (NSView *)controlView
1045                             isHorizontal: (BOOL)isHorizontal
1046{
1047  GSDrawTiles *tiles;
1048
1049  tiles = [self tilesNamed: GSMenuSeparatorItem state: GSThemeNormalState];
1050  if (tiles == nil)
1051    {
1052      NSBezierPath *path = [NSBezierPath bezierPath];
1053      CGFloat inset = [self menuSeparatorInset];
1054      NSPoint start = NSMakePoint(inset, cellFrame.size.height / 2 +
1055				         cellFrame.origin.y + 0.5);
1056      NSPoint end = NSMakePoint(cellFrame.size.width - inset,
1057        cellFrame.size.height / 2 + cellFrame.origin.y + 0.5);
1058
1059      [[self menuSeparatorColor] set];
1060
1061      [path setLineWidth: 0.0];
1062      [path moveToPoint: start];
1063      [path lineToPoint: end];
1064
1065      [path stroke];
1066    }
1067  else
1068    {
1069      [self fillRect: cellFrame
1070           withTiles: tiles];
1071    }
1072}
1073
1074- (void) drawTitleForMenuItemCell: (NSMenuItemCell *)cell
1075                        withFrame: (NSRect)cellFrame
1076                           inView: (NSView *)controlView
1077                            state: (GSThemeControlState)state
1078                     isHorizontal: (BOOL)isHorizontal
1079{
1080  [cell _drawText: [[cell menuItem] title]
1081          inFrame: [cell titleRectForBounds: cellFrame]];
1082}
1083
1084- (Class) titleViewClassForMenuView: (NSMenuView *)aMenuView
1085{
1086  return [GSTitleView class];
1087}
1088
1089- (NSRect) drawMenuTitleBackground: (GSTitleView *)aTitleView
1090			withBounds: (NSRect)bounds
1091			  withClip: (NSRect)clipRect
1092{
1093  GSDrawTiles *tiles = [self tilesNamed: GSMenuTitleBackground state: GSThemeNormalState];
1094
1095  if (tiles == nil)
1096    {
1097      NSRect     workRect = bounds;
1098      NSRectEdge top_left[] = {NSMinXEdge, NSMaxYEdge};
1099      CGFloat      darkGrays[] = {NSDarkGray, NSDarkGray};
1100      NSColor *titleColor;
1101
1102      titleColor = [self colorNamed: @"GSMenuBar" state: GSThemeNormalState];
1103      if (titleColor == nil)
1104	{
1105	  titleColor = [NSColor blackColor];
1106	}
1107
1108      // Draw the dark gray upper left lines for menu and black for others.
1109      // Rectangle 1
1110      workRect = NSDrawTiledRects(workRect, workRect, top_left, darkGrays, 2);
1111
1112      // Rectangle 2
1113      // Draw the title box's button.
1114      [self drawButton: workRect withClip: workRect];
1115
1116      // Overdraw white top and left lines with light gray lines for window title
1117      workRect.origin.y += 1;
1118      workRect.size.height -= 1;
1119      workRect.size.width -= 1;
1120
1121      // Rectangle 3
1122      // Paint background
1123      workRect.origin.x += 1;
1124      workRect.origin.y += 1;
1125      workRect.size.height -= 2;
1126      workRect.size.width -= 2;
1127
1128      [titleColor set];
1129      NSRectFill(workRect);
1130
1131      return workRect;
1132    }
1133  else
1134    {
1135      return [self fillRect: bounds
1136		  withTiles: tiles];
1137    }
1138}
1139
1140- (CGFloat) menuBarHeight
1141{
1142  CGFloat height = [[NSUserDefaults standardUserDefaults]
1143		     floatForKey: @"GSMenuBarHeight"];
1144  if (height <= 0)
1145    {
1146      return 22;
1147    }
1148  return height;
1149}
1150
1151- (CGFloat) menuItemHeight
1152{
1153  CGFloat height = [[NSUserDefaults standardUserDefaults]
1154		     floatForKey: @"GSMenuItemHeight"];
1155  if (height <= 0)
1156    {
1157      return 20;
1158    }
1159  return height;
1160}
1161
1162- (CGFloat) menuSeparatorHeight
1163{
1164  CGFloat height = [[NSUserDefaults standardUserDefaults]
1165		     floatForKey: @"GSMenuSeparatorHeight"];
1166  if (height <= 0)
1167    {
1168      return 20;
1169    }
1170  return height;
1171}
1172
1173// NSColorWell drawing method
1174- (NSRect) drawColorWellBorder: (NSColorWell*)well
1175                    withBounds: (NSRect)bounds
1176                      withClip: (NSRect)clipRect
1177{
1178  NSRect aRect = bounds;
1179
1180  if ([well isBordered])
1181    {
1182      GSThemeControlState state;
1183      GSDrawTiles *tiles;
1184
1185      if ([[well cell] isHighlighted] || [well isActive])
1186	{
1187          state = GSThemeHighlightedState;
1188	}
1189      else
1190	{
1191          state = GSThemeNormalState;
1192	}
1193
1194      tiles = [self tilesNamed: GSColorWell state: state];
1195      if (tiles == nil)
1196        {
1197	  /*
1198	   * Draw border.
1199	   */
1200	  [self drawButton: aRect withClip: clipRect];
1201
1202	  /*
1203	   * Fill in control color.
1204	   */
1205	  if (state == GSThemeHighlightedState)
1206	    {
1207	      [[NSColor selectedControlColor] set];
1208	    }
1209	  else
1210	    {
1211	      [[NSColor controlColor] set];
1212	    }
1213	  aRect = NSInsetRect(aRect, 2.0, 2.0);
1214	  NSRectFill(NSIntersectionRect(aRect, clipRect));
1215        }
1216      else
1217        {
1218          aRect = [self fillRect: aRect
1219                       withTiles: tiles];
1220        }
1221
1222      /*
1223       * Set an inset rect for the color area
1224       */
1225      aRect = NSInsetRect(bounds, COLOR_WELL_BORDER_WIDTH, COLOR_WELL_BORDER_WIDTH);
1226    }
1227
1228  /*
1229   * OpenStep 4.2 behavior is to omit the inner border for
1230   * non-enabled NSColorWell objects.
1231   */
1232  if ([well isEnabled])
1233    {
1234      GSDrawTiles *tiles = [self tilesNamed: GSColorWellInnerBorder state: GSThemeNormalState];
1235      if (tiles == nil)
1236        {
1237	  /*
1238	   * Draw inner frame.
1239	   */
1240	  [self drawGrayBezel: aRect withClip: clipRect];
1241	  aRect = NSInsetRect(aRect, 2.0, 2.0);
1242	}
1243      else
1244	{
1245	  [self fillRect: aRect
1246	       withTiles: tiles];
1247
1248	  aRect = [tiles contentRectForRect: aRect isFlipped: [well isFlipped]];
1249	}
1250    }
1251
1252  return aRect;
1253}
1254
1255// progress indicator drawing methods
1256static NSColor *fillColour = nil;
1257#define MaxCount 10
1258static int indeterminateMaxCount = MaxCount;
1259static int spinningMaxCount = MaxCount;
1260static NSColor *indeterminateColors[MaxCount];
1261static NSImage *spinningImages[MaxCount];
1262
1263- (void) initProgressIndicatorDrawing
1264{
1265  int i;
1266
1267  // FIXME: Should come from defaults and should be reset when defaults change
1268  // FIXME: Should probably get the color from the color extension list (see NSToolbar)
1269  fillColour = RETAIN([NSColor controlShadowColor]);
1270
1271  // Load images for indeterminate style
1272  for (i = 0; i < MaxCount; i++)
1273    {
1274      NSString *imgName = [NSString stringWithFormat: @"common_ProgressIndeterminate_%d", i + 1];
1275      NSImage *image = [NSImage imageNamed: imgName];
1276
1277      if (image == nil)
1278        {
1279          indeterminateMaxCount = i;
1280          break;
1281        }
1282          indeterminateColors[i] = RETAIN([NSColor colorWithPatternImage: image]);
1283    }
1284
1285  // Load images for spinning style
1286  for (i = 0; i < MaxCount; i++)
1287    {
1288      NSString *imgName = [NSString stringWithFormat: @"common_ProgressSpinning_%d", i + 1];
1289      NSImage *image = [NSImage imageNamed: imgName];
1290
1291      if (image == nil)
1292        {
1293          spinningMaxCount = i;
1294          break;
1295        }
1296      spinningImages[i] = RETAIN(image);
1297    }
1298}
1299
1300- (void) drawProgressIndicator: (NSProgressIndicator*)progress
1301                    withBounds: (NSRect)bounds
1302                      withClip: (NSRect)rect
1303                       atCount: (int)count
1304                      forValue: (double)val
1305{
1306   NSRect r;
1307
1308   if (fillColour == nil)
1309     {
1310       [self initProgressIndicatorDrawing];
1311     }
1312
1313   // Draw the Bezel
1314   if ([progress isBezeled])
1315     {
1316       // Calc the inside rect to be drawn
1317       r = [self drawProgressIndicatorBezel: bounds withClip: rect];
1318     }
1319   else
1320     {
1321       r = bounds;
1322     }
1323
1324   if ([progress style] == NSProgressIndicatorSpinningStyle)
1325     {
1326       NSRect imgBox = {{0,0}, {0,0}};
1327
1328       if (spinningMaxCount != 0)
1329	 {
1330	   count = count % spinningMaxCount;
1331	   imgBox.size = [spinningImages[count] size];
1332	   [spinningImages[count] drawInRect: r
1333				    fromRect: imgBox
1334				   operation: NSCompositeSourceOver
1335				    fraction: 1.0];
1336	 }
1337     }
1338   else
1339     {
1340       if ([progress isIndeterminate])
1341         {
1342	   if (indeterminateMaxCount != 0)
1343	     {
1344	       count = count % indeterminateMaxCount;
1345	       [indeterminateColors[count] set];
1346	       NSRectFill(r);
1347	     }
1348         }
1349       else
1350         {
1351           // Draw determinate
1352           if ([progress isVertical])
1353             {
1354               float height = NSHeight(r) * val;
1355
1356               if ([progress isFlipped])
1357                 {
1358                   // Compensate for the flip
1359                   r.origin.y += NSHeight(r) - height;
1360                 }
1361               r.size.height = height;
1362             }
1363           else
1364             {
1365               r.size.width = NSWidth(r) * val;
1366             }
1367           r = NSIntersectionRect(r, rect);
1368           if (!NSIsEmptyRect(r))
1369             {
1370               [self drawProgressIndicatorBarDeterminate: (NSRect)r];
1371             }
1372         }
1373     }
1374}
1375
1376- (NSRect) drawProgressIndicatorBezel: (NSRect)bounds withClip: (NSRect) rect
1377{
1378  GSDrawTiles *tiles = [self tilesNamed: GSProgressIndicatorBezel
1379                                  state: GSThemeNormalState];
1380
1381  if (tiles == nil)
1382    {
1383      return [self drawGrayBezel: bounds withClip: rect];
1384    }
1385  else
1386    {
1387      [self fillRect: bounds
1388           withTiles: tiles];
1389
1390      return [tiles contentRectForRect: bounds
1391			     isFlipped: [[NSView focusView] isFlipped]];
1392    }
1393}
1394
1395- (void) drawProgressIndicatorBarDeterminate: (NSRect)bounds
1396{
1397  GSDrawTiles *tiles = [self tilesNamed: GSProgressIndicatorBarDeterminate
1398                                  state: GSThemeNormalState];
1399
1400  if (tiles == nil)
1401    {
1402      [fillColour set];
1403      NSRectFill(bounds);
1404    }
1405  else
1406    {
1407      [self fillRect: bounds
1408           withTiles: tiles
1409          background: fillColour];
1410    }
1411}
1412
1413// Table drawing methods
1414
1415- (NSColor *) tableHeaderTextColorForState: (GSThemeControlState)state
1416{
1417  NSColor *color;
1418
1419  color = [self colorNamed: @"tableHeaderTextColor"
1420		     state: state];
1421  if (color == nil)
1422    {
1423      if (state == GSThemeHighlightedState)
1424	color = [NSColor controlTextColor];
1425      else
1426	color = [NSColor windowFrameTextColor];
1427    }
1428  return color;
1429}
1430
1431- (void) drawTableCornerView: (NSView*)cornerView
1432                   withClip: (NSRect)aRect
1433{
1434  NSRect divide;
1435  NSRect rect;
1436  GSDrawTiles *tiles = [self tilesNamed: GSTableCorner state: GSThemeNormalState];
1437
1438  if ([cornerView isFlipped])
1439    {
1440      NSDivideRect(aRect, &divide, &rect, 1.0, NSMaxYEdge);
1441    }
1442  else
1443    {
1444      NSDivideRect(aRect, &divide, &rect, 1.0, NSMinYEdge);
1445    }
1446
1447  if (tiles == nil)
1448    {
1449      rect = [self drawDarkButton: rect withClip: aRect];
1450      [[NSColor controlShadowColor] set];
1451      NSRectFill(rect);
1452    }
1453  else
1454    {
1455       [self fillRect: aRect
1456            withTiles: tiles];
1457    }
1458}
1459
1460- (void) drawTableHeaderCell: (NSTableHeaderCell *)cell
1461                   withFrame: (NSRect)cellFrame
1462                      inView: (NSView *)controlView
1463                       state: (GSThemeControlState)state
1464{
1465  GSDrawTiles *tiles = [self tilesNamed: GSTableHeader state: state];
1466
1467  if (tiles == nil)
1468    {
1469      NSRect rect;
1470
1471      // Leave a 1pt thick horizontal line underneath the header
1472      if (![controlView isFlipped])
1473        {
1474          cellFrame.origin.y++;
1475        }
1476      cellFrame.size.height--;
1477
1478      if (state == GSThemeHighlightedState)
1479        {
1480          rect = [self drawButton: cellFrame withClip: cellFrame];
1481          [[NSColor controlColor] set];
1482          NSRectFill(rect);
1483        }
1484      else
1485        {
1486          rect = [self drawDarkButton: cellFrame withClip: cellFrame];
1487          [[NSColor controlShadowColor] set];
1488          NSRectFill(rect);
1489        }
1490    }
1491  else
1492    {
1493      [self fillRect: cellFrame
1494           withTiles: tiles];
1495    }
1496}
1497
1498
1499// Window decoration drawing methods
1500/* These include the black border. */
1501#define TITLE_HEIGHT 23.0
1502#define RESIZE_HEIGHT 9.0
1503#define TITLEBAR_BUTTON_SIZE 15.0
1504#define TITLEBAR_PADDING_TOP 4.0
1505#define TITLEBAR_PADDING_RIGHT 4.0
1506#define TITLEBAR_PADDING_LEFT 4.0
1507
1508- (float) titlebarHeight
1509{
1510  return TITLE_HEIGHT;
1511}
1512
1513- (float) resizebarHeight
1514{
1515  return RESIZE_HEIGHT;
1516}
1517
1518- (float) titlebarButtonSize
1519{
1520  return TITLEBAR_BUTTON_SIZE;
1521}
1522
1523- (float) titlebarPaddingRight
1524{
1525  return TITLEBAR_PADDING_RIGHT;
1526}
1527
1528- (float) titlebarPaddingTop
1529{
1530  return TITLEBAR_PADDING_TOP;
1531}
1532
1533- (float) titlebarPaddingLeft
1534{
1535  return TITLEBAR_PADDING_LEFT;
1536}
1537
1538static NSDictionary *titleTextAttributes[3] = {nil, nil, nil};
1539
1540- (void) drawTitleBarRect: (NSRect)titleBarRect
1541             forStyleMask: (unsigned int)styleMask
1542                    state: (int)inputState
1543                 andTitle: (NSString*)title
1544{
1545  static const NSRectEdge edges[4] = {NSMinXEdge, NSMaxYEdge,
1546				    NSMaxXEdge, NSMinYEdge};
1547  CGFloat grays[3][4] =
1548    {{NSLightGray, NSLightGray, NSDarkGray, NSDarkGray},
1549    {NSWhite, NSWhite, NSDarkGray, NSDarkGray},
1550    {NSLightGray, NSLightGray, NSBlack, NSBlack}};
1551  NSRect workRect;
1552  GSDrawTiles *tiles = nil;
1553
1554  if (!titleTextAttributes[0])
1555    {
1556      NSMutableParagraphStyle *p;
1557      NSColor *keyColor, *normalColor, *mainColor;
1558
1559      p = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
1560      [p setLineBreakMode: NSLineBreakByClipping];
1561
1562      // FIXME: refine color names based on style mask
1563      // (HUD or textured or regular window)
1564
1565      keyColor = [self colorNamed: @"keyWindowFrameTextColor"
1566                            state: GSThemeNormalState];
1567      if (nil == keyColor)
1568        {
1569          keyColor = [NSColor windowFrameTextColor];
1570        }
1571
1572      normalColor = [self colorNamed: @"normalWindowFrameTextColor"
1573                               state: GSThemeNormalState];
1574      if (nil == normalColor)
1575        {
1576          normalColor = [NSColor blackColor];
1577        }
1578
1579      mainColor = [self colorNamed: @"mainWindowFrameTextColor"
1580                             state: GSThemeNormalState];
1581      if (nil == mainColor)
1582        {
1583          mainColor = [NSColor windowFrameTextColor];
1584        }
1585
1586      titleTextAttributes[0] = [[NSMutableDictionary alloc]
1587	initWithObjectsAndKeys:
1588	  [NSFont titleBarFontOfSize: 0], NSFontAttributeName,
1589	  keyColor, NSForegroundColorAttributeName,
1590	  p, NSParagraphStyleAttributeName,
1591	  nil];
1592
1593      titleTextAttributes[1] = [[NSMutableDictionary alloc]
1594	initWithObjectsAndKeys:
1595	  [NSFont titleBarFontOfSize: 0], NSFontAttributeName,
1596	  normalColor, NSForegroundColorAttributeName,
1597	  p, NSParagraphStyleAttributeName,
1598	  nil];
1599
1600      titleTextAttributes[2] = [[NSMutableDictionary alloc]
1601	initWithObjectsAndKeys:
1602	  [NSFont titleBarFontOfSize: 0], NSFontAttributeName,
1603	  mainColor, NSForegroundColorAttributeName,
1604	  p, NSParagraphStyleAttributeName,
1605	  nil];
1606
1607      RELEASE(p);
1608    }
1609
1610  tiles = [self tilesNamed: @"GSWindowTitleBar" state: GSThemeNormalState];
1611  if (tiles == nil)
1612    {
1613      /*
1614      Draw the black border towards the rest of the window. (The outer black
1615      border is drawn in -drawRect: since it might be drawn even if we don't have
1616      a title bar.
1617      */
1618      NSColor *borderColor = [self colorNamed: @"windowBorderColor"
1619                                        state: GSThemeNormalState];
1620      if (nil == borderColor)
1621        {
1622          borderColor = [NSColor blackColor];
1623        }
1624      [borderColor set];
1625
1626      PSmoveto(0, NSMinY(titleBarRect) + 0.5);
1627      PSrlineto(titleBarRect.size.width, 0);
1628      PSstroke();
1629
1630      /*
1631      Draw the button-like border.
1632      */
1633      workRect = titleBarRect;
1634      workRect.origin.x += 1;
1635      workRect.origin.y += 1;
1636      workRect.size.width -= 2;
1637      workRect.size.height -= 2;
1638
1639      workRect = NSDrawTiledRects(workRect, workRect, edges, grays[inputState], 4);
1640
1641      /*
1642      Draw the background.
1643      */
1644      switch (inputState)
1645	{
1646	default:
1647	case 0:
1648	  [[NSColor windowFrameColor] set];
1649	  break;
1650	case 1:
1651	  [[NSColor lightGrayColor] set];
1652	  break;
1653	case 2:
1654	  [[NSColor darkGrayColor] set];
1655	  break;
1656	}
1657      NSRectFill(workRect);
1658    }
1659  else
1660    {
1661      [self fillRect: titleBarRect
1662          withTiles: tiles
1663         background: [NSColor windowFrameColor]];
1664      workRect = titleBarRect;
1665    }
1666  /* Draw the title. */
1667  if (styleMask & NSTitledWindowMask)
1668    {
1669      NSSize titleSize;
1670
1671      if (styleMask & NSMiniaturizableWindowMask)
1672	{
1673	  workRect.origin.x += 17;
1674	  workRect.size.width -= 17;
1675	}
1676      if (styleMask & NSClosableWindowMask)
1677	{
1678	  workRect.size.width -= 17;
1679	}
1680
1681      titleSize = [title sizeWithAttributes: titleTextAttributes[inputState]];
1682      if (titleSize.width <= workRect.size.width)
1683	workRect.origin.x = NSMidX(workRect) - titleSize.width / 2;
1684      workRect.origin.y = NSMidY(workRect) - titleSize.height / 2;
1685      workRect.size.height = titleSize.height;
1686      [title drawInRect: workRect
1687	 withAttributes: titleTextAttributes[inputState]];
1688    }
1689}
1690
1691// FIXME: Would be good if this took the window as a param
1692- (void) drawResizeBarRect: (NSRect)resizeBarRect
1693{
1694  GSDrawTiles *tiles;
1695  tiles = [self tilesNamed: @"GSWindowResizeBar" state: GSThemeNormalState];
1696  if (tiles == nil)
1697    {
1698      [[NSColor lightGrayColor] set];
1699      PSrectfill(1.0, 1.0, resizeBarRect.size.width - 2.0, RESIZE_HEIGHT - 3.0);
1700
1701      PSsetlinewidth(1.0);
1702
1703      [[NSColor blackColor] set];
1704      PSmoveto(0.0, 0.5);
1705      PSlineto(resizeBarRect.size.width, 0.5);
1706      PSstroke();
1707
1708      [[NSColor darkGrayColor] set];
1709      PSmoveto(1.0, RESIZE_HEIGHT - 0.5);
1710      PSlineto(resizeBarRect.size.width - 1.0, RESIZE_HEIGHT - 0.5);
1711      PSstroke();
1712
1713      [[NSColor whiteColor] set];
1714      PSmoveto(1.0, RESIZE_HEIGHT - 1.5);
1715      PSlineto(resizeBarRect.size.width - 1.0, RESIZE_HEIGHT - 1.5);
1716      PSstroke();
1717
1718
1719      /* Only draw the notches if there's enough space. */
1720      if (resizeBarRect.size.width < 30 * 2)
1721	return;
1722
1723      [[NSColor darkGrayColor] set];
1724      PSmoveto(27.5, 1.0);
1725      PSlineto(27.5, RESIZE_HEIGHT - 2.0);
1726      PSmoveto(resizeBarRect.size.width - 28.5, 1.0);
1727      PSlineto(resizeBarRect.size.width - 28.5, RESIZE_HEIGHT - 2.0);
1728      PSstroke();
1729
1730      [[NSColor whiteColor] set];
1731      PSmoveto(28.5, 1.0);
1732      PSlineto(28.5, RESIZE_HEIGHT - 2.0);
1733      PSmoveto(resizeBarRect.size.width - 27.5, 1.0);
1734      PSlineto(resizeBarRect.size.width - 27.5, RESIZE_HEIGHT - 2.0);
1735      PSstroke();
1736    }
1737  else
1738    {
1739      [self fillRect: resizeBarRect
1740           withTiles: tiles];
1741    }
1742}
1743
1744- (void) drawWindowBorder: (NSRect)rect
1745                withFrame: (NSRect)frame
1746             forStyleMask: (unsigned int)styleMask
1747                    state: (int)inputState
1748                 andTitle: (NSString*)title
1749{
1750  if (styleMask & (NSTitledWindowMask | NSClosableWindowMask
1751                   | NSMiniaturizableWindowMask))
1752    {
1753      NSRect titleBarRect;
1754
1755      titleBarRect = NSMakeRect(0.0, frame.size.height - TITLE_HEIGHT,
1756                                frame.size.width, TITLE_HEIGHT);
1757      if (NSIntersectsRect(rect, titleBarRect))
1758        [self drawTitleBarRect: titleBarRect
1759              forStyleMask: styleMask
1760              state: inputState
1761              andTitle: title];
1762    }
1763
1764  if (styleMask & NSResizableWindowMask)
1765    {
1766      NSRect resizeBarRect;
1767
1768      resizeBarRect = NSMakeRect(0.0, 0.0, frame.size.width, RESIZE_HEIGHT);
1769      if (NSIntersectsRect(rect, resizeBarRect))
1770        [self drawResizeBarRect: resizeBarRect];
1771    }
1772
1773  if (styleMask & (NSTitledWindowMask | NSClosableWindowMask
1774                   | NSMiniaturizableWindowMask | NSResizableWindowMask))
1775    {
1776      NSColor *borderColor = [self colorNamed: @"windowBorderColor"
1777                                        state: GSThemeNormalState];
1778      if (nil == borderColor)
1779        {
1780          borderColor = [NSColor blackColor];
1781        }
1782      [borderColor set];
1783      PSsetlinewidth(1.0);
1784      if (NSMinX(rect) < 1.0)
1785	{
1786	  PSmoveto(0.5, 0.0);
1787	  PSlineto(0.5, frame.size.height);
1788	  PSstroke();
1789	}
1790      if (NSMaxX(rect) > frame.size.width - 1.0)
1791	{
1792	  PSmoveto(frame.size.width - 0.5, 0.0);
1793	  PSlineto(frame.size.width - 0.5, frame.size.height);
1794	  PSstroke();
1795	}
1796      if (NSMaxY(rect) > frame.size.height - 1.0)
1797	{
1798	  PSmoveto(0.0, frame.size.height - 0.5);
1799	  PSlineto(frame.size.width, frame.size.height - 0.5);
1800	  PSstroke();
1801	}
1802      if (NSMinY(rect) < 1.0)
1803	{
1804	  PSmoveto(0.0, 0.5);
1805	  PSlineto(frame.size.width, 0.5);
1806	  PSstroke();
1807	}
1808    }
1809}
1810
1811- (NSColor *) browserHeaderTextColor
1812{
1813  NSColor *color;
1814
1815  color = [self colorNamed: @"browserHeaderTextColor"
1816		     state: GSThemeNormalState];
1817  if (color == nil)
1818    {
1819      color = [NSColor windowFrameTextColor];
1820    }
1821  return color;
1822}
1823
1824- (void) drawBrowserHeaderCell: (NSTableHeaderCell*)cell
1825	 	     withFrame: (NSRect)rect
1826			inView: (NSView*)view;
1827{
1828  GSDrawTiles *tiles;
1829  tiles = [self tilesNamed: GSBrowserHeader state: GSThemeNormalState];
1830  if (tiles == nil)
1831   {
1832     [self drawGrayBezel: rect withClip: NSZeroRect];
1833     [cell _drawBackgroundWithFrame: rect inView: view];
1834   }
1835  else
1836    {
1837      [self fillRect: rect
1838           withTiles: tiles];
1839    }
1840}
1841
1842- (NSRect) browserHeaderDrawingRectForCell: (NSTableHeaderCell*)cell
1843				 withFrame: (NSRect)rect
1844{
1845  GSDrawTiles *tiles;
1846  tiles = [self tilesNamed: GSBrowserHeader state: GSThemeNormalState];
1847  if (tiles == nil)
1848    {
1849      return NSInsetRect(rect, 2, 2);
1850    }
1851  else
1852    {
1853      const BOOL flipped = [[cell controlView] isFlipped];
1854      NSRect result = [tiles contentRectForRect: rect
1855				      isFlipped: flipped];
1856      return result;
1857    }
1858}
1859
1860typedef enum {
1861  GSTabSelectedLeft,
1862  GSTabSelectedRight,
1863  GSTabSelectedToUnSelectedJunction,
1864  GSTabSelectedFill,
1865  GSTabUnSelectedLeft,
1866  GSTabUnSelectedRight,
1867  GSTabUnSelectedToSelectedJunction,
1868  GSTabUnSelectedJunction,
1869  GSTabUnSelectedFill,
1870  GSTabBackgroundFill
1871} GSTabPart;
1872
1873- (NSImage *)imageForTabPart: (GSTabPart)part type: (NSTabViewType)type
1874{
1875  NSMutableString *imageName = [NSMutableString stringWithCapacity: 32];
1876  NSString *typeString = nil;
1877  NSString *partString = nil;
1878
1879  switch (type)
1880    {
1881    case NSTopTabsBezelBorder:
1882      typeString = @"";
1883      break;
1884    case NSBottomTabsBezelBorder:
1885      typeString = @"Down";
1886      break;
1887    case NSLeftTabsBezelBorder:
1888      typeString = @"Left";
1889      break;
1890    case NSRightTabsBezelBorder:
1891      typeString =  @"Right";
1892      break;
1893    default:
1894      return nil;
1895    }
1896
1897  switch (part)
1898    {
1899    case GSTabSelectedLeft:
1900      partString = @"SelectedLeft";
1901      break;
1902    case GSTabSelectedRight:
1903      partString = @"SelectedRight";
1904      break;
1905    case GSTabSelectedToUnSelectedJunction:
1906      partString = @"SelectedToUnSelectedJunction";
1907      break;
1908    case GSTabSelectedFill:
1909      return nil;
1910    case GSTabUnSelectedLeft:
1911      partString = @"UnSelectedLeft";
1912      break;
1913    case GSTabUnSelectedRight:
1914      partString = @"UnSelectedRight";
1915      break;
1916    case GSTabUnSelectedToSelectedJunction:
1917      partString = @"UnSelectedToSelectedJunction";
1918      break;
1919    case GSTabUnSelectedJunction:
1920      partString = @"UnSelectedJunction";
1921      break;
1922    case GSTabUnSelectedFill:
1923    case GSTabBackgroundFill:
1924      return nil;
1925    }
1926
1927  [imageName appendString: @"common_Tab"];
1928  [imageName appendString: typeString];
1929  [imageName appendString: partString];
1930
1931  return [NSImage imageNamed: imageName];
1932}
1933
1934- (GSDrawTiles *)tilesForTabPart: (GSTabPart)part type: (NSTabViewType)type
1935{
1936  NSString *name = nil;
1937
1938  if (type == NSTopTabsBezelBorder)
1939    {
1940      if (part == GSTabSelectedFill)
1941	name = GSTabViewSelectedTabFill;
1942      else if (part == GSTabUnSelectedFill)
1943	name = GSTabViewUnSelectedTabFill;
1944      else if (part == GSTabBackgroundFill)
1945	name = GSTabViewBackgroundTabFill;
1946    }
1947  else if (type == NSBottomTabsBezelBorder)
1948    {
1949      if (part == GSTabSelectedFill)
1950	name = GSTabViewBottomSelectedTabFill;
1951      else if (part == GSTabUnSelectedFill)
1952	name = GSTabViewBottomUnSelectedTabFill;
1953      else if (part == GSTabBackgroundFill)
1954	name = GSTabViewBottomBackgroundTabFill;
1955    }
1956  else if (type == NSLeftTabsBezelBorder)
1957    {
1958      if (part == GSTabSelectedFill)
1959	name = GSTabViewLeftSelectedTabFill;
1960      else if (part == GSTabUnSelectedFill)
1961	name = GSTabViewLeftUnSelectedTabFill;
1962      else if (part == GSTabBackgroundFill)
1963	name = GSTabViewLeftBackgroundTabFill;
1964    }
1965  else if (type == NSRightTabsBezelBorder)
1966    {
1967      if (part == GSTabSelectedFill)
1968	name = GSTabViewRightSelectedTabFill;
1969      else if (part == GSTabUnSelectedFill)
1970	name = GSTabViewRightUnSelectedTabFill;
1971      else if (part == GSTabBackgroundFill)
1972	name = GSTabViewRightBackgroundTabFill;
1973    }
1974
1975  return [self tilesNamed: name state: GSThemeNormalState];
1976}
1977
1978- (void) frameTabRectTopAndBottom: (NSRect)aRect
1979			 topColor: (NSColor *)topColor
1980		      bottomColor: (NSColor *)bottomColor
1981{
1982  NSRect bottom = aRect;
1983  NSRect top = aRect;
1984
1985  top.size.height = 1;
1986  bottom.origin.y = NSMaxY(aRect) - 1;
1987  bottom.size.height = 1;
1988
1989  [topColor set];
1990  NSRectFill(top);
1991
1992  [bottomColor set];
1993  NSRectFill(bottom);
1994}
1995
1996- (void) drawTabFillInRect: (NSRect)aRect forPart: (GSTabPart)part type: (NSTabViewType)type
1997{
1998  GSDrawTiles *tiles = [self tilesForTabPart: part type: type];
1999
2000  if (tiles == nil)
2001    {
2002      if (type == NSBottomTabsBezelBorder)
2003	{
2004	  switch (part)
2005	    {
2006	    case GSTabSelectedFill:
2007	      [self frameTabRectTopAndBottom: aRect
2008				    topColor: [NSColor clearColor]
2009				 bottomColor: [NSColor whiteColor]];
2010	      break;
2011	    case GSTabUnSelectedFill:
2012	      [self frameTabRectTopAndBottom: aRect
2013				    topColor: [NSColor darkGrayColor]
2014				 bottomColor: [NSColor whiteColor]];
2015	      break;
2016	    case GSTabBackgroundFill:
2017	      {
2018		const NSRect clip = aRect;
2019		aRect.origin.x -= 2;
2020		aRect.origin.y = NSMinY(aRect) - 2;
2021		aRect.size.width += 2;
2022		aRect.size.height = 4;
2023		[self drawButton: aRect withClip: clip];
2024		break;
2025	      }
2026	    default:
2027	      break;
2028	    }
2029	}
2030      else if (type == NSTopTabsBezelBorder)
2031	{
2032	  switch (part)
2033	    {
2034	    case GSTabSelectedFill:
2035	      [self frameTabRectTopAndBottom: aRect
2036				    topColor: [NSColor whiteColor]
2037				 bottomColor: [NSColor clearColor]];
2038	      break;
2039	    case GSTabUnSelectedFill:
2040	      [self frameTabRectTopAndBottom: aRect
2041				    topColor: [NSColor whiteColor]
2042				 bottomColor: [NSColor whiteColor]];
2043	      break;
2044	    case GSTabBackgroundFill:
2045	      {
2046		const NSRect clip = aRect;
2047		aRect.origin.x -= 2;
2048		aRect.origin.y = NSMaxY(aRect) - 1;
2049		aRect.size.width += 2;
2050		aRect.size.height = 4;
2051		[self drawButton: aRect withClip: clip];
2052		break;
2053	      }
2054	    default:
2055	      break;
2056	    }
2057	}
2058    }
2059  else
2060    {
2061      [self fillRect: aRect
2062           withTiles: tiles];
2063    }
2064}
2065
2066- (CGFloat) tabHeightForType: (NSTabViewType)type
2067{
2068  NSImage *img = [self imageForTabPart: GSTabUnSelectedLeft type: type];
2069  if (img == nil)
2070    {
2071      return 0;
2072    }
2073  return [img size].height;
2074}
2075
2076- (NSRect) tabViewBackgroundRectForBounds: (NSRect)aRect
2077			      tabViewType: (NSTabViewType)type
2078{
2079  const CGFloat tabHeight = [self tabHeightForType: type];
2080
2081  switch (type)
2082    {
2083      default:
2084      case NSTopTabsBezelBorder:
2085        aRect.size.height -= tabHeight;
2086        aRect.origin.y += tabHeight;
2087        break;
2088
2089      case NSBottomTabsBezelBorder:
2090        aRect.size.height -= tabHeight;
2091        break;
2092
2093      case NSLeftTabsBezelBorder:
2094        aRect.size.width -= tabHeight;
2095        aRect.origin.x += tabHeight;
2096        break;
2097
2098      case NSRightTabsBezelBorder:
2099        aRect.size.width -= tabHeight;
2100        break;
2101
2102      case NSNoTabsBezelBorder:
2103      case NSNoTabsLineBorder:
2104      case NSNoTabsNoBorder:
2105        break;
2106    }
2107
2108  return aRect;
2109}
2110
2111
2112- (NSRect) tabViewContentRectForBounds: (NSRect)aRect
2113			   tabViewType: (NSTabViewType)type
2114			       tabView: (NSTabView *)view
2115{
2116  NSRect cRect = [self tabViewBackgroundRectForBounds: aRect
2117						   tabViewType: type];
2118  NSString *name = GSStringFromTabViewType(type);
2119  GSDrawTiles *tiles = [self tilesNamed: name state: GSThemeNormalState];
2120
2121  if (tiles == nil)
2122    {
2123      switch (type)
2124	{
2125	case NSBottomTabsBezelBorder:
2126	  cRect.origin.x += 1;
2127	  cRect.origin.y += 1;
2128	  cRect.size.width -= 3;
2129	  cRect.size.height -= 2;
2130	  break;
2131	case NSNoTabsBezelBorder:
2132	  cRect.origin.x += 1;
2133	  cRect.origin.y += 1;
2134	  cRect.size.width -= 3;
2135	  cRect.size.height -= 2;
2136	  break;
2137	case NSNoTabsLineBorder:
2138	  cRect.origin.y += 1;
2139	  cRect.origin.x += 1;
2140	  cRect.size.width -= 2;
2141	  cRect.size.height -= 2;
2142	  break;
2143	case NSTopTabsBezelBorder:
2144	  cRect.origin.x += 1;
2145	  cRect.origin.y += 1;
2146	  cRect.size.width -= 3;
2147	  cRect.size.height -= 2;
2148	  break;
2149	case NSLeftTabsBezelBorder:
2150	  cRect.origin.x += 1;
2151	  cRect.origin.y += 1;
2152	  cRect.size.width -= 3;
2153	  cRect.size.height -= 2;
2154	  break;
2155	case NSRightTabsBezelBorder:
2156	  cRect.origin.x += 1;
2157	  cRect.origin.y += 1;
2158	  cRect.size.width -= 3;
2159	  cRect.size.height -= 2;
2160	  break;
2161	case NSNoTabsNoBorder:
2162	default:
2163	  break;
2164	}
2165    }
2166  else
2167    {
2168      cRect = [tiles contentRectForRect: cRect
2169			      isFlipped: [view isFlipped]];
2170    }
2171  return cRect;
2172}
2173
2174
2175- (void) drawTabViewBezelRect: (NSRect)aRect
2176                  tabViewType: (NSTabViewType)type
2177                       inView: (NSView *)view
2178{
2179  NSString *name = GSStringFromTabViewType(type);
2180  GSDrawTiles *tiles = [self tilesNamed: name state: GSThemeNormalState];
2181
2182  if (tiles == nil)
2183    {
2184      switch (type)
2185	{
2186	default:
2187	case NSTopTabsBezelBorder:
2188	  {
2189	    const NSRect clip = aRect;
2190	    aRect.size.height += 1;
2191	    aRect.origin.y -= 1;
2192	    [self drawButton: aRect withClip: clip];
2193	    break;
2194	  }
2195	case NSBottomTabsBezelBorder:
2196	  {
2197	    const NSRect clip = aRect;
2198	    aRect.size.height += 2;
2199	    [self drawButton: aRect withClip: clip];
2200	    break;
2201	  }
2202	case NSLeftTabsBezelBorder:
2203	case NSRightTabsBezelBorder:
2204	  [self drawButton: aRect withClip: NSZeroRect];
2205	  break;
2206	case NSNoTabsBezelBorder:
2207       	  break;
2208	case NSNoTabsLineBorder:
2209	  [[NSColor controlDarkShadowColor] set];
2210	  NSFrameRect(aRect);
2211	  break;
2212	case NSNoTabsNoBorder:
2213	  break;
2214	}
2215    }
2216  else
2217    {
2218      [self fillRect: aRect
2219           withTiles: tiles];
2220    }
2221}
2222
2223- (void) drawTabViewRect: (NSRect)rect
2224		  inView: (NSView *)view
2225	       withItems: (NSArray *)items
2226	    selectedItem: (NSTabViewItem *)selected
2227{
2228  NSGraphicsContext *ctxt = GSCurrentContext();
2229  const NSUInteger howMany = [items count];
2230  int i;
2231  int previousState = 0;
2232  const NSTabViewType type = [(NSTabView *)view tabViewType];
2233  const NSRect bounds = [view bounds];
2234  NSRect aRect = [self tabViewBackgroundRectForBounds: bounds tabViewType: type];
2235
2236  const BOOL truncate = [(NSTabView *)view allowsTruncatedLabels];
2237  const CGFloat tabHeight = [self tabHeightForType: type];
2238
2239  DPSgsave(ctxt);
2240
2241  [self drawTabViewBezelRect: aRect
2242 		 tabViewType: type
2243 		      inView: view];
2244
2245  if (type == NSBottomTabsBezelBorder
2246      || type == NSTopTabsBezelBorder)
2247    {
2248      NSPoint iP;
2249      if (type == NSTopTabsBezelBorder)
2250	iP = bounds.origin;
2251      else
2252	iP = NSMakePoint(aRect.origin.x, NSMaxY(aRect));
2253
2254      for (i = 0; i < howMany; i++)
2255        {
2256          NSRect r;
2257          NSTabViewItem *anItem = [items objectAtIndex: i];
2258          const NSTabState itemState = [anItem tabState];
2259          const NSSize s = [anItem sizeOfLabel: truncate];
2260
2261	  // Draw the left image
2262
2263          if (i == 0)
2264            {
2265	      NSImage *part = nil;
2266	      if (itemState == NSSelectedTab)
2267                {
2268		  part = [self imageForTabPart: GSTabSelectedLeft type: type];
2269                }
2270              else if (itemState == NSBackgroundTab)
2271                {
2272		  part = [self imageForTabPart: GSTabUnSelectedLeft type: type];
2273                }
2274              else
2275                NSLog(@"Not finished yet. Luff ya.\n");
2276
2277	      [part drawInRect: NSMakeRect(iP.x, iP.y, [part size].width, [part size].height)
2278		      fromRect: NSZeroRect
2279		     operation: NSCompositeSourceOver
2280		      fraction: 1.0
2281		respectFlipped: YES
2282			 hints: nil];
2283
2284	      iP.x += [part size].width;
2285            }
2286          else
2287            {
2288	      NSImage *part = nil;
2289              if (itemState == NSSelectedTab)
2290                {
2291		  part = [self imageForTabPart: GSTabUnSelectedToSelectedJunction type: type];
2292                }
2293              else if (itemState == NSBackgroundTab)
2294                {
2295                  if (previousState == NSSelectedTab)
2296                    {
2297		      part = [self imageForTabPart: GSTabSelectedToUnSelectedJunction type: type];
2298                    }
2299                  else
2300                    {
2301		      part = [self imageForTabPart: GSTabUnSelectedJunction type: type];
2302                    }
2303                }
2304              else
2305                NSLog(@"Not finished yet. Luff ya.\n");
2306
2307	      [part drawInRect: NSMakeRect(iP.x, iP.y, [part size].width, [part size].height)
2308		      fromRect: NSZeroRect
2309		     operation: NSCompositeSourceOver
2310		      fraction: 1.0
2311		respectFlipped: YES
2312			 hints: nil];
2313
2314	      iP.x += [part size].width;
2315            }
2316
2317	  // Draw the middle fill part of the tab
2318
2319          r.origin = iP;
2320          r.size.width = s.width;
2321          r.size.height = tabHeight;
2322
2323	  if (itemState == NSSelectedTab)
2324	    {
2325	      [self drawTabFillInRect: r forPart: GSTabSelectedFill type: type];
2326	    }
2327	  else if (itemState == NSBackgroundTab)
2328	    {
2329	      [self drawTabFillInRect: r forPart: GSTabUnSelectedFill type: type];
2330	    }
2331	  else
2332	    NSLog(@"Not finished yet. Luff ya.\n");
2333
2334          // Label
2335          [anItem drawLabel: truncate inRect: r];
2336
2337          iP.x += s.width;
2338          previousState = itemState;
2339
2340	  // For the rightmost tab, draw the right side
2341
2342          if (i == howMany - 1)
2343            {
2344	      NSImage *part = nil;
2345              if ([anItem tabState] == NSSelectedTab)
2346                {
2347		  part = [self imageForTabPart: GSTabSelectedRight type: type];
2348                }
2349              else if ([anItem tabState] == NSBackgroundTab)
2350                {
2351		  part = [self imageForTabPart: GSTabUnSelectedRight type: type];
2352                }
2353              else
2354                NSLog(@"Not finished yet. Luff ya.\n");
2355
2356	      [part drawInRect: NSMakeRect(iP.x, iP.y, [part size].width, [part size].height)
2357		      fromRect: NSZeroRect
2358		     operation: NSCompositeSourceOver
2359		      fraction: 1.0
2360		respectFlipped: YES
2361			 hints: nil];
2362
2363	      iP.x += [part size].width;
2364
2365	      // Draw the background fill
2366	      if (iP.x < NSMaxX(bounds))
2367		{
2368		  r.origin = iP;
2369		  r.size.width = NSMaxX(bounds) - iP.x;
2370		  r.size.height = tabHeight;
2371
2372		  [self drawTabFillInRect: r forPart: GSTabBackgroundFill type: type];
2373		}
2374            }
2375        }
2376    }
2377  // FIXME: Missing drawing code for other cases
2378
2379  DPSgrestore(ctxt);
2380}
2381
2382- (void) drawScrollerRect: (NSRect)rect
2383		   inView: (NSView *)view
2384		  hitPart: (NSScrollerPart)hitPart
2385	     isHorizontal: (BOOL)isHorizontal
2386{
2387  NSRect rectForPartIncrementLine;
2388  NSRect rectForPartDecrementLine;
2389  NSRect rectForPartKnobSlot;
2390  NSScroller *scroller = (NSScroller *)view;
2391
2392  rectForPartIncrementLine = [scroller rectForPart: NSScrollerIncrementLine];
2393  rectForPartDecrementLine = [scroller rectForPart: NSScrollerDecrementLine];
2394  rectForPartKnobSlot = [scroller rectForPart: NSScrollerKnobSlot];
2395
2396  /*
2397  [[[view window] backgroundColor] set];
2398  NSRectFill (rect);
2399  */
2400
2401  if (NSIntersectsRect (rect, rectForPartKnobSlot) == YES)
2402    {
2403      [scroller drawKnobSlot];
2404      [scroller drawKnob];
2405    }
2406
2407  if (NSIntersectsRect (rect, rectForPartDecrementLine) == YES)
2408    {
2409      [scroller drawArrow: NSScrollerDecrementArrow
2410		highlight: hitPart == NSScrollerDecrementLine];
2411    }
2412  if (NSIntersectsRect (rect, rectForPartIncrementLine) == YES)
2413    {
2414      [scroller drawArrow: NSScrollerIncrementArrow
2415		highlight: hitPart == NSScrollerIncrementLine];
2416    }
2417}
2418
2419- (void) drawBrowserRect: (NSRect)rect
2420		  inView: (NSView *)view
2421	withScrollerRect: (NSRect)scrollerRect
2422	      columnSize: (NSSize)columnSize
2423{
2424  NSBrowser *browser = (NSBrowser *)view;
2425  NSRect bounds = [view bounds];
2426
2427  // Load the first column if not already done
2428  if (![browser isLoaded])
2429    {
2430      [browser loadColumnZero];
2431    }
2432
2433  // Draws titles
2434  if ([browser isTitled])
2435    {
2436      int i;
2437
2438      for (i = [browser firstVisibleColumn];
2439	   i <= [browser lastVisibleColumn];
2440	   ++i)
2441        {
2442          NSRect titleRect = [browser titleFrameOfColumn: i];
2443          if (NSIntersectsRect (titleRect, rect) == YES)
2444            {
2445              [browser drawTitleOfColumn: i
2446                    inRect: titleRect];
2447            }
2448        }
2449    }
2450
2451  // Draws scroller border
2452
2453  if ([self browserUseBezels])
2454    {
2455      if ([browser hasHorizontalScroller] &&
2456	  [browser separatesColumns])
2457	{
2458	  NSRect scrollerBorderRect = scrollerRect;
2459	  NSSize bs = [self sizeForBorderType: NSBezelBorder];
2460
2461	  scrollerBorderRect.origin.x = 0;
2462	  scrollerBorderRect.origin.y = 0;
2463	  scrollerBorderRect.size.width += 2 * bs.width;
2464	  scrollerBorderRect.size.height += (2 * bs.height) - 1;
2465
2466	  if ((NSIntersectsRect (scrollerBorderRect, rect) == YES) && [view window])
2467	    {
2468	      [self drawGrayBezel: scrollerBorderRect withClip: rect];
2469	    }
2470	}
2471
2472      if (![browser separatesColumns])
2473	{
2474	  NSPoint p1,p2;
2475	  int     i, visibleColumns;
2476	  float   hScrollerWidth = [browser hasHorizontalScroller] ?
2477	    [NSScroller scrollerWidth] : 0;
2478
2479	  // Columns borders
2480	  [self drawGrayBezel: bounds withClip: rect];
2481
2482	  [[NSColor blackColor] set];
2483	  visibleColumns = [browser numberOfVisibleColumns];
2484	  for (i = 1; i < visibleColumns; i++)
2485	    {
2486	      p1 = NSMakePoint((columnSize.width * i) + 2 + (i-1),
2487                           columnSize.height + hScrollerWidth + 2);
2488	      p2 = NSMakePoint((columnSize.width * i) + 2 + (i-1),
2489			       hScrollerWidth + 2);
2490	      [NSBezierPath strokeLineFromPoint: p1 toPoint: p2];
2491	    }
2492
2493	  // Horizontal scroller border
2494	  if ([browser hasHorizontalScroller])
2495	    {
2496	      p1 = NSMakePoint(2, hScrollerWidth + 2);
2497	      p2 = NSMakePoint(rect.size.width - 2, hScrollerWidth + 2);
2498	      [NSBezierPath strokeLineFromPoint: p1 toPoint: p2];
2499	    }
2500	}
2501    }
2502
2503   if (![self browserUseBezels]
2504       && [self scrollViewScrollersOverlapBorders])
2505    {
2506      NSRect baseRect = NSMakeRect(0, 0, bounds.size.width, 1);
2507      NSRect colFrame = [browser frameOfColumn: [browser firstVisibleColumn]];
2508      NSRect scrollViewRect = NSUnionRect(baseRect, colFrame);
2509
2510      GSDrawTiles *tiles = [self tilesNamed: @"NSScrollView"
2511				      state: GSThemeNormalState];
2512
2513      [self fillRect: scrollViewRect
2514           withTiles: tiles];
2515    }
2516}
2517
2518- (CGFloat) browserColumnSeparation
2519{
2520  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
2521
2522  if ([defs objectForKey: @"GSBrowserColumnSeparation"] != nil)
2523    {
2524      return [defs floatForKey: @"GSBrowserColumnSeparation"];
2525    }
2526  else
2527    {
2528      return 4;
2529    }
2530}
2531
2532- (CGFloat) browserVerticalPadding
2533{
2534  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
2535
2536  if ([defs objectForKey: @"GSBrowserVerticalPadding"] != nil)
2537    {
2538      return [defs floatForKey: @"GSBrowserVerticalPadding"];
2539    }
2540  else
2541    {
2542      return 2;
2543    }
2544}
2545
2546- (BOOL) browserUseBezels
2547{
2548  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
2549
2550  if ([defs objectForKey: @"GSBrowserUseBezels"] != nil)
2551    {
2552      return [defs boolForKey: @"GSBrowserUseBezels"];
2553    }
2554  else
2555    {
2556      return YES;
2557    }
2558}
2559
2560- (void) drawMenuRect: (NSRect)rect
2561	       inView: (NSView *)view
2562	 isHorizontal: (BOOL)horizontal
2563	    itemCells: (NSArray *)itemCells
2564{
2565  int         i = 0;
2566  int         howMany = [itemCells count];
2567  NSMenuView *menuView = (NSMenuView *)view;
2568  NSRect      bounds = [view bounds];
2569
2570  [self drawBackgroundForMenuView: menuView
2571	withFrame: bounds
2572	dirtyRect: rect
2573	horizontal: horizontal];
2574
2575  // Draw the menu cells.
2576  for (i = 0; i < howMany; i++)
2577    {
2578      NSRect aRect;
2579      NSMenuItemCell *aCell;
2580
2581      aRect = [menuView rectOfItemAtIndex: i];
2582      if (NSIntersectsRect(rect, aRect) == YES)
2583        {
2584          aCell = [menuView menuItemCellForItemAtIndex: i];
2585          [aCell drawWithFrame: aRect inView: menuView];
2586        }
2587    }
2588}
2589
2590- (void) drawScrollViewRect: (NSRect)rect
2591		     inView: (NSView *)view
2592{
2593  NSScrollView  *scrollView = (NSScrollView *)view;
2594  GSTheme	*theme = [GSTheme theme];
2595  NSColor	*color;
2596  NSString	*name;
2597  NSBorderType   borderType = [scrollView borderType];
2598  NSRect         bounds = [view bounds];
2599  BOOL hasInnerBorder = ![[NSUserDefaults standardUserDefaults]
2600			   boolForKey: @"GSScrollViewNoInnerBorder"];
2601  GSDrawTiles *tiles = nil;
2602
2603  name = [theme nameForElement: self];
2604  if (name == nil)
2605    {
2606      name = @"NSScrollView";
2607    }
2608  color = [theme colorNamed: name state: GSThemeNormalState];
2609  tiles = [theme tilesNamed: name state: GSThemeNormalState];
2610  if (color == nil)
2611    {
2612      color = [NSColor controlDarkShadowColor];
2613    }
2614
2615  if (tiles == nil)
2616    {
2617      switch (borderType)
2618	{
2619	case NSNoBorder:
2620	  break;
2621
2622	case NSLineBorder:
2623	  [color set];
2624	  NSFrameRect(bounds);
2625	  break;
2626
2627	case NSBezelBorder:
2628	  [theme drawGrayBezel: bounds withClip: rect];
2629	  break;
2630
2631	case NSGrooveBorder:
2632	  [theme drawGroove: bounds withClip: rect];
2633	  break;
2634	}
2635    }
2636  else
2637    {
2638      [self fillRect: bounds
2639	   withTiles: tiles];
2640    }
2641
2642  if (hasInnerBorder)
2643    {
2644      NSScroller *vertScroller = [scrollView verticalScroller];
2645      NSScroller *horizScroller = [scrollView horizontalScroller];
2646      CGFloat scrollerWidth = [NSScroller scrollerWidth];
2647      NSRect scrollerFrame;
2648
2649      [color set];
2650
2651      if ([scrollView hasVerticalScroller])
2652	{
2653	  NSInterfaceStyle style;
2654	  CGFloat xpos;
2655
2656          scrollerFrame = [vertScroller frame];
2657
2658	  style = NSInterfaceStyleForKey(@"NSScrollViewInterfaceStyle", nil);
2659	  if (style == NSMacintoshInterfaceStyle
2660	      || style == NSWindows95InterfaceStyle)
2661	    {
2662              xpos = scrollerFrame.origin.x - 1.0;
2663	    }
2664	  else
2665	    {
2666              xpos = scrollerFrame.origin.x + scrollerWidth;
2667	    }
2668          NSRectFill(NSMakeRect(xpos, scrollerFrame.origin.y,
2669                                1.0, scrollerFrame.size.height));
2670	}
2671
2672      if ([scrollView hasHorizontalScroller])
2673	{
2674	  CGFloat ypos;
2675
2676          scrollerFrame = [horizScroller frame];
2677
2678	  if ([scrollView isFlipped])
2679	    {
2680	      ypos = scrollerFrame.origin.y - 1.0;
2681	    }
2682	  else
2683	    {
2684	      ypos = scrollerFrame.origin.y + scrollerWidth + 1.0;
2685	    }
2686          NSRectFill(NSMakeRect([horizScroller frame].origin.x, ypos,
2687                                scrollerFrame.size.width, 1.0));
2688	}
2689    }
2690}
2691
2692- (void) drawSliderBorderAndBackground: (NSBorderType)aType
2693				 frame: (NSRect)cellFrame
2694				inCell: (NSCell *)cell
2695			  isHorizontal: (BOOL)horizontal
2696{
2697  NSSliderType type = [(NSSliderCell *)cell sliderType];
2698  if (type == NSLinearSlider)
2699    {
2700      NSString *partName = (horizontal ? GSSliderHorizontalTrack : GSSliderVerticalTrack);
2701      GSDrawTiles *tiles = [self tilesNamed: partName
2702				      state: GSThemeNormalState];
2703
2704      if (tiles == nil)
2705	{
2706	  [[GSTheme theme] drawBorderType: aType
2707				    frame: cellFrame
2708				     view: [cell controlView]];
2709	}
2710      else
2711	{
2712	  // FIXME: This code could be factored out
2713	  NSSize tilesNaturalSize = [tiles size];
2714	  NSRect tilesRect;
2715
2716	  // Only stretch the tiles in one direction
2717	  if (horizontal)
2718	    {
2719	      tilesRect.size = NSMakeSize(cellFrame.size.width, tilesNaturalSize.height);
2720	    }
2721	  else
2722	    {
2723	      tilesRect.size = NSMakeSize(tilesNaturalSize.width, cellFrame.size.height);
2724	    }
2725	  tilesRect.origin = NSMakePoint((cellFrame.size.width - tilesRect.size.width) / 2.0,
2726					 (cellFrame.size.height - tilesRect.size.height) / 2.0);
2727
2728	  if ([cell controlView] != nil)
2729	    {
2730	      tilesRect = [[cell controlView] centerScanRect: tilesRect];
2731	    }
2732
2733	  [self fillRect: tilesRect
2734	       withTiles: tiles];
2735	}
2736    }
2737}
2738
2739- (void) drawBarInside: (NSRect)rect
2740		inCell: (NSCell *)cell
2741	       flipped: (BOOL)flipped
2742{
2743  NSSliderType type = [(NSSliderCell *)cell sliderType];
2744  if (type == NSLinearSlider)
2745    {
2746      BOOL horizontal = (rect.size.width > rect.size.height);
2747      NSString *partName = (horizontal ? GSSliderHorizontalTrack : GSSliderVerticalTrack);
2748      GSDrawTiles *tiles = [self tilesNamed: partName
2749				      state: GSThemeNormalState];
2750
2751      if (tiles == nil)
2752	{
2753	  [[NSColor scrollBarColor] set];
2754	  NSRectFill(rect);
2755	}
2756      // Don't draw anything if we have tiles, they are drawn
2757      // in -drawSliderBorderAndBackground:...
2758    }
2759}
2760
2761- (void) drawKnobInCell: (NSCell *)cell
2762{
2763  NSView *controlView = [cell controlView];
2764  NSSliderCell *sliderCell = (NSSliderCell *)cell;
2765
2766  [sliderCell drawKnob:
2767		[sliderCell knobRectFlipped:
2768			      [controlView isFlipped]]];
2769}
2770
2771- (NSRect) tableHeaderCellDrawingRectForBounds: (NSRect)theRect
2772{
2773  NSSize borderSize;
2774
2775  // This adjustment must match the drawn border
2776  borderSize = NSMakeSize(1, 1);
2777
2778  return NSInsetRect(theRect, borderSize.width, borderSize.height);
2779}
2780
2781- (void)drawTableHeaderRect: (NSRect)aRect
2782		     inView: (NSView *)view
2783{
2784  NSTableHeaderView *tableHeaderView = (NSTableHeaderView *)view;
2785  NSTableView *tableView = [tableHeaderView tableView];
2786  NSArray *columns;
2787  int firstColumnToDraw;
2788  int lastColumnToDraw;
2789  NSRect drawingRect;
2790  NSTableColumn *column;
2791  NSTableColumn *highlightedTableColumn;
2792  float width;
2793  int i;
2794  NSCell *cell;
2795
2796  if (tableView == nil)
2797    return;
2798
2799  firstColumnToDraw = [tableHeaderView columnAtPoint: NSMakePoint (aRect.origin.x,
2800                                                        aRect.origin.y)];
2801  if (firstColumnToDraw == -1)
2802    firstColumnToDraw = 0;
2803
2804  lastColumnToDraw = [tableHeaderView columnAtPoint: NSMakePoint (NSMaxX (aRect),
2805                                                       aRect.origin.y)];
2806  if (lastColumnToDraw == -1)
2807    lastColumnToDraw = [tableView numberOfColumns] - 1;
2808
2809  drawingRect = [tableHeaderView headerRectOfColumn: firstColumnToDraw];
2810
2811  columns = [tableView tableColumns];
2812  highlightedTableColumn = [tableView highlightedTableColumn];
2813
2814  for (i = firstColumnToDraw; i <= lastColumnToDraw; i++)
2815    {
2816      column = [columns objectAtIndex: i];
2817      width = [column width];
2818      drawingRect.size.width = width;
2819      cell = [column headerCell];
2820      if ((column == highlightedTableColumn)
2821          || [tableView isColumnSelected: i])
2822        {
2823          [cell setHighlighted: YES];
2824        }
2825      else
2826        {
2827          [cell setHighlighted: NO];
2828        }
2829      [cell drawWithFrame: drawingRect
2830                           inView: tableHeaderView];
2831      drawingRect.origin.x += width;
2832    }
2833}
2834
2835- (void) drawPopUpButtonCellInteriorWithFrame: (NSRect)cellFrame
2836				     withCell: (NSCell *)cell
2837				       inView: (NSView *)controlView
2838{
2839  // Default implementation of this method does nothing.
2840}
2841
2842- (void) drawTableViewBackgroundInClipRect: (NSRect)aRect
2843				    inView: (NSView *)view
2844		       withBackgroundColor: (NSColor *)backgroundColor
2845{
2846  NSTableView *tableView = (NSTableView *)view;
2847
2848  [backgroundColor set];
2849  NSRectFill (aRect);
2850
2851  if ([tableView usesAlternatingRowBackgroundColors])
2852    {
2853      const CGFloat rowHeight = [tableView rowHeight];
2854      NSInteger startingRow = [tableView rowAtPoint: NSMakePoint(0, NSMinY(aRect))];
2855      NSInteger endingRow;
2856      NSInteger i;
2857
2858      NSArray *rowColors = [NSColor controlAlternatingRowBackgroundColors];
2859      const NSUInteger rowColorCount = [rowColors count];
2860
2861      NSRect rowRect;
2862
2863      if (rowHeight <= 0
2864	  || rowColorCount == 0
2865	  || aRect.size.height <= 0)
2866	return;
2867
2868      if (startingRow <= 0)
2869	startingRow = 0;
2870
2871      rowRect = [tableView rectOfRow: startingRow];
2872      rowRect.origin.x = aRect.origin.x;
2873      rowRect.size.width = aRect.size.width;
2874
2875      endingRow = startingRow + ceil(aRect.size.height / rowHeight);
2876
2877      for (i = startingRow; i <= endingRow; i++)
2878	{
2879	  NSColor *color = [rowColors objectAtIndex: (i % rowColorCount)];
2880
2881	  [color set];
2882	  NSRectFill(rowRect);
2883
2884	  rowRect.origin.y += rowHeight;
2885	}
2886    }
2887}
2888
2889- (void) drawTableViewGridInClipRect: (NSRect)aRect
2890			      inView: (NSView *)view
2891{
2892  NSTableView *tableView = (NSTableView *)view;
2893
2894  // Cache some constants
2895  const CGFloat minX = NSMinX(aRect);
2896  const CGFloat maxX = NSMaxX(aRect);
2897  const CGFloat minY = NSMinY(aRect);
2898  const CGFloat maxY = NSMaxY(aRect);
2899  const NSInteger numberOfColumns = [tableView numberOfColumns];
2900  const NSInteger numberOfRows = [tableView numberOfRows];
2901
2902  NSInteger startingRow = [tableView rowAtPoint: NSMakePoint(minX, minY)];
2903  NSInteger endingRow = [tableView rowAtPoint: NSMakePoint(minX, maxY)];
2904  NSInteger startingColumn = [tableView columnAtPoint: NSMakePoint(minX, minY)];
2905  NSInteger endingColumn = [tableView columnAtPoint: NSMakePoint(maxX, minY)];
2906
2907  NSGraphicsContext *ctxt = GSCurrentContext();
2908  NSColor *gridColor = [tableView gridColor];
2909
2910
2911  if (startingRow == -1)
2912    startingRow = 0;
2913  if (endingRow == -1)
2914    endingRow = numberOfRows - 1;
2915
2916  if (startingColumn == -1)
2917    startingColumn = 0;
2918  if (endingColumn == -1)
2919    endingColumn = numberOfColumns - 1;
2920
2921
2922  DPSgsave(ctxt);
2923  [gridColor set];
2924
2925  // Draw horizontal lines
2926  if (numberOfRows > 0)
2927    {
2928      NSInteger i;
2929      for (i = startingRow; i <= endingRow; i++)
2930	{
2931	  NSRect rowRect = [tableView rectOfRow: i];
2932	  rowRect.origin.y += rowRect.size.height - 1;
2933	  rowRect.size.height = 1;
2934	  NSRectFill(rowRect);
2935	}
2936    }
2937
2938  // Draw vertical lines
2939  if (numberOfColumns > 0)
2940    {
2941     NSInteger i;
2942      for (i = startingColumn; i <= endingColumn; i++)
2943	{
2944	  NSRect colRect = [tableView rectOfColumn: i];
2945	  colRect.origin.x += colRect.size.width - 1;
2946	  colRect.size.width = 1;
2947	  NSRectFill(colRect);
2948	}
2949    }
2950
2951  DPSgrestore (ctxt);
2952}
2953
2954- (void) drawTableViewRect: (NSRect)aRect
2955		    inView: (NSView *)view
2956{
2957  NSInteger startingRow;
2958  NSInteger endingRow;
2959  NSInteger i;
2960  NSTableView *tableView = (NSTableView *)view;
2961  NSInteger numberOfRows = [tableView numberOfRows];
2962  NSInteger numberOfColumns = [tableView numberOfColumns];
2963  BOOL drawsGrid = [tableView drawsGrid];
2964
2965  /* Draw background */
2966  [tableView drawBackgroundInClipRect: aRect];
2967
2968  if ((numberOfRows == 0) || (numberOfColumns == 0))
2969    {
2970      return;
2971    }
2972
2973  /* Draw selection */
2974  [tableView highlightSelectionInClipRect: aRect];
2975
2976  /* Draw grid */
2977  if (drawsGrid)
2978    {
2979      [tableView drawGridInClipRect: aRect];
2980    }
2981
2982  /* Draw visible cells */
2983  /* Using rowAtPoint: here calls them only twice per drawn rect */
2984  startingRow = [tableView rowAtPoint: NSMakePoint (0, NSMinY (aRect))];
2985  endingRow   = [tableView rowAtPoint: NSMakePoint (0, NSMaxY (aRect))];
2986
2987  if (startingRow == -1)
2988    {
2989      startingRow = 0;
2990    }
2991  if (endingRow == -1)
2992    {
2993      endingRow = numberOfRows - 1;
2994    }
2995  //  NSLog(@"drawRect : %d-%d", startingRow, endingRow);
2996  {
2997    SEL sel = @selector(drawRow:clipRect:);
2998    void (*imp)(id, SEL, NSInteger, NSRect);
2999
3000    imp = (void (*)(id, SEL, NSInteger, NSRect))[tableView methodForSelector: sel];
3001
3002    for (i = startingRow; i <= endingRow; i++)
3003      {
3004        imp(tableView, sel, i, aRect);
3005      }
3006  }
3007}
3008
3009- (void) highlightTableViewSelectionInClipRect: (NSRect)clipRect
3010					inView: (NSView *)view
3011			      selectingColumns: (BOOL)selectingColumns
3012{
3013  NSTableView *tableView = (NSTableView *)view;
3014  NSInteger numberOfRows = [tableView numberOfRows];
3015  NSInteger numberOfColumns = [tableView numberOfColumns];
3016  NSIndexSet *selectedRows = [tableView selectedRowIndexes];
3017  NSIndexSet *selectedColumns = [tableView selectedColumnIndexes];
3018  NSColor *backgroundColor = [tableView backgroundColor];
3019
3020  // Set the fill color
3021  {
3022    NSColor *selectionColor;
3023
3024    selectionColor = [self colorNamed: @"highlightedTableRowBackgroundColor"
3025				state: GSThemeNormalState];
3026
3027    if (selectionColor == nil)
3028      {
3029	// Switch to the alternate color of the backgroundColor is white.
3030	if([backgroundColor isEqual: [NSColor whiteColor]])
3031	  {
3032	    selectionColor = [NSColor colorWithCalibratedRed: 0.86
3033						       green: 0.92
3034							blue: 0.99
3035						       alpha: 1.0];
3036	  }
3037	else
3038	  {
3039	    selectionColor = [NSColor whiteColor];
3040	  }
3041      }
3042    [selectionColor set];
3043  }
3044
3045  if (selectingColumns == NO)
3046    {
3047      NSInteger selectedRowsCount;
3048      NSUInteger row;
3049      NSInteger startingRow, endingRow;
3050
3051      selectedRowsCount = [selectedRows count];
3052      if (selectedRowsCount == 0)
3053	return;
3054
3055      /* highlight selected rows */
3056      startingRow = [tableView rowAtPoint: NSMakePoint(0, NSMinY(clipRect))];
3057      endingRow   = [tableView rowAtPoint: NSMakePoint(0, NSMaxY(clipRect))];
3058
3059      if (startingRow == -1)
3060	startingRow = 0;
3061      if (endingRow == -1)
3062	endingRow = numberOfRows - 1;
3063
3064      row = [selectedRows indexGreaterThanOrEqualToIndex: startingRow];
3065      while ((row != NSNotFound) && (row <= endingRow))
3066	{
3067	  NSRectFill(NSIntersectionRect([tableView rectOfRow: row], clipRect));
3068	  row = [selectedRows indexGreaterThanIndex: row];
3069	}
3070    }
3071  else // Selecting columns
3072    {
3073      NSUInteger selectedColumnsCount;
3074      NSUInteger column;
3075      NSInteger startingColumn, endingColumn;
3076
3077      selectedColumnsCount = [selectedColumns count];
3078
3079      if (selectedColumnsCount == 0)
3080	return;
3081
3082      /* highlight selected columns */
3083      startingColumn = [tableView columnAtPoint: NSMakePoint(NSMinX(clipRect), 0)];
3084      endingColumn = [tableView columnAtPoint: NSMakePoint(NSMaxX(clipRect), 0)];
3085
3086      if (startingColumn == -1)
3087	startingColumn = 0;
3088      if (endingColumn == -1)
3089	endingColumn = numberOfColumns - 1;
3090
3091      column = [selectedColumns indexGreaterThanOrEqualToIndex: startingColumn];
3092      while ((column != NSNotFound) && (column <= endingColumn))
3093	{
3094	  NSRectFill(NSIntersectionRect([tableView rectOfColumn: column],
3095					clipRect));
3096	  column = [selectedColumns indexGreaterThanIndex: column];
3097	}
3098    }
3099}
3100
3101- (void) drawTableViewRow: (NSInteger)rowIndex
3102		 clipRect: (NSRect)clipRect
3103		   inView: (NSView *)view
3104{
3105  NSTableView *tableView = (NSTableView *)view;
3106  // NSInteger numberOfRows = [tableView numberOfRows];
3107  NSInteger numberOfColumns = [tableView numberOfColumns];
3108  // NSIndexSet *selectedRows = [tableView selectedRowIndexes];
3109  // NSColor *backgroundColor = [tableView backgroundColor];
3110  CGFloat *columnOrigins = [tableView _columnOrigins];
3111  NSInteger editedRow = [tableView editedRow];
3112  NSInteger editedColumn = [tableView editedColumn];
3113  NSArray *tableColumns = [tableView tableColumns];
3114  NSInteger startingColumn;
3115  NSInteger endingColumn;
3116  NSTableColumn *tb;
3117  NSRect drawingRect;
3118  NSCell *cell;
3119  NSInteger i;
3120  CGFloat x_pos;
3121  const BOOL rowSelected = [[tableView selectedRowIndexes] containsIndex: rowIndex];
3122  NSColor *tempColor = nil;
3123  NSColor *selectedTextColor = [self colorNamed: @"highlightedTableRowTextColor"
3124					  state: GSThemeNormalState];
3125
3126  /* Using columnAtPoint: here would make it called twice per row per drawn
3127     rect - so we avoid it and do it natively */
3128
3129  /* Determine starting column as fast as possible */
3130  x_pos = NSMinX (clipRect);
3131  i = 0;
3132  while ((i < numberOfColumns) && (x_pos > columnOrigins[i]))
3133    {
3134      i++;
3135    }
3136  startingColumn = (i - 1);
3137
3138  if (startingColumn == -1)
3139    startingColumn = 0;
3140
3141  /* Determine ending column as fast as possible */
3142  x_pos = NSMaxX (clipRect);
3143  // Nota Bene: we do *not* reset i
3144  while ((i < numberOfColumns) && (x_pos > columnOrigins[i]))
3145    {
3146      i++;
3147    }
3148  endingColumn = (i - 1);
3149
3150  if (endingColumn == -1)
3151    endingColumn = numberOfColumns - 1;
3152
3153  /* Draw the row between startingColumn and endingColumn */
3154  for (i = startingColumn; i <= endingColumn; i++)
3155    {
3156      const BOOL columnSelected = [tableView isColumnSelected: i];
3157      const BOOL cellSelected = (rowSelected || columnSelected);
3158      tb = [tableColumns objectAtIndex: i];
3159      cell = [tb dataCellForRow: rowIndex];
3160      [tableView _willDisplayCell: cell
3161		 forTableColumn: tb
3162		 row: rowIndex];
3163      if (i == editedColumn && rowIndex == editedRow)
3164        {
3165          [cell _setInEditing: YES];
3166        }
3167      else
3168        {
3169          [cell setObjectValue: [tableView _objectValueForTableColumn: tb
3170                                                                  row: rowIndex]];
3171        }
3172      drawingRect = [tableView frameOfCellAtColumn: i
3173			       row: rowIndex];
3174
3175      // Set the cell text color if the theme provides a custom highlighted
3176      // row color.
3177      //
3178      // FIXME: This could probably be done in a cleaner way. We should
3179      // probably do -setHighlighted: YES, and the implementation of
3180      // -textColor in NSCell could use that to return a highlighted text color.
3181      if (cellSelected && (selectedTextColor != nil)
3182	  && [cell isKindOfClass: [NSTextFieldCell class]])
3183	{
3184	  tempColor = [cell textColor];
3185	  [(NSTextFieldCell *)cell setTextColor: selectedTextColor];
3186	}
3187
3188      [cell drawWithFrame: drawingRect inView: tableView];
3189
3190      if (cellSelected && (selectedTextColor != nil)
3191	  && [cell isKindOfClass: [NSTextFieldCell class]])
3192	{
3193	  // Restore the cell's text color if we changed it
3194	  [(NSTextFieldCell *)cell setTextColor: tempColor];
3195	}
3196
3197      if (i == editedColumn && rowIndex == editedRow)
3198	[cell _setInEditing: NO];
3199    }
3200}
3201
3202- (void) drawBoxInClipRect: (NSRect)clipRect
3203		   boxType: (NSBoxType)boxType
3204		borderType: (NSBorderType)borderType
3205		    inView: (NSBox *)box
3206{
3207  NSColor *color;
3208  BOOL drawTitleBackground = YES;
3209
3210  if (boxType == NSBoxCustom)
3211    {
3212      if (![box isOpaque])
3213        {
3214          color = [NSColor clearColor];
3215        }
3216      else
3217        {
3218          color = [box fillColor];
3219        }
3220    }
3221  else
3222    {
3223      color = [[box window] backgroundColor];
3224    }
3225
3226  // Draw separator boxes
3227
3228  if (boxType == NSBoxSeparator)
3229    {
3230      color = [box borderColor];
3231      if (!color || [color isEqual:[NSColor clearColor]])
3232        {
3233    	  color = [NSColor controlShadowColor];
3234        }
3235      [color set];
3236      NSRectFill([box borderRect]);
3237      return;
3238    }
3239
3240  // Draw border
3241
3242  GSDrawTiles *tiles = [[GSTheme theme] tilesNamed: GSBoxBorder state: GSThemeNormalState];
3243  if (tiles == nil
3244      || borderType == NSNoBorder
3245      || boxType == NSBoxOldStyle
3246      || boxType == NSBoxCustom)
3247    {
3248      // Fill inside
3249      [color set];
3250      NSRectFill(clipRect);
3251
3252      switch (borderType)
3253	{
3254	case NSNoBorder:
3255	  break;
3256	case NSLineBorder:
3257	  if (boxType == NSBoxCustom)
3258	    {
3259	      [[box borderColor] set];
3260	      NSFrameRectWithWidth([box borderRect], [box borderWidth]);
3261	    }
3262	  else
3263	    {
3264	      [[NSColor controlDarkShadowColor] set];
3265	      NSFrameRect([box borderRect]);
3266	    }
3267	  break;
3268	case NSBezelBorder:
3269	  [[GSTheme theme] drawDarkBezel: [box borderRect] withClip: clipRect];
3270	  break;
3271	case NSGrooveBorder:
3272	  [[GSTheme theme] drawGroove: [box borderRect] withClip: clipRect];
3273	  break;
3274	}
3275    }
3276  else
3277    {
3278      drawTitleBackground = NO;
3279
3280      // If the title is on the border, clip a hole in the later
3281
3282      [NSGraphicsContext saveGraphicsState];
3283
3284      if ((borderType != NSNoBorder)
3285	  && (([box titlePosition] == NSAtTop) || ([box titlePosition] == NSAtBottom)))
3286	{
3287	  const NSRect borderRect = [box borderRect];
3288	  const NSRect titleRect = [box titleRect];
3289	  NSBezierPath *path = [NSBezierPath bezierPath];
3290
3291	  // Left
3292	  if (NSMinX(titleRect) > NSMinX(borderRect))
3293	    {
3294	      NSRect left = borderRect;
3295	      left.size.width = NSMinX(titleRect) - NSMinX(borderRect);
3296	      [path appendBezierPathWithRect: left];
3297	    }
3298
3299	  // Right
3300	  if (NSMaxX(borderRect) > NSMaxX(titleRect))
3301	    {
3302	      NSRect right = borderRect;
3303	      right.size.width = NSMaxX(borderRect) - NSMaxX(titleRect);
3304	      right.origin.x = NSMaxX(titleRect);
3305	      [path appendBezierPathWithRect: right];
3306	    }
3307
3308	  // MinY
3309	  if (NSMinY(titleRect) > NSMinY(borderRect))
3310	    {
3311	      NSRect minY = borderRect;
3312	      minY.size.height = NSMinY(titleRect) - NSMinY(borderRect);
3313	      [path appendBezierPathWithRect: minY];
3314	    }
3315
3316	  // MaxY
3317	  if (NSMaxY(borderRect) > NSMaxY(titleRect))
3318	    {
3319	      NSRect maxY = borderRect;
3320	      maxY.size.height = NSMaxY(borderRect) - NSMaxY(titleRect);
3321	      maxY.origin.y = NSMaxY(titleRect);
3322	      [path appendBezierPathWithRect: maxY];
3323	    }
3324
3325	  if (![path isEmpty])
3326	    {
3327	      [path addClip];
3328	    }
3329	}
3330
3331
3332      [[GSTheme theme] fillRect: [box borderRect]
3333		      withTiles: tiles];
3334
3335      // Restore clipping path
3336      [NSGraphicsContext restoreGraphicsState];
3337    }
3338
3339
3340  // Draw title
3341  if ([box titlePosition] != NSNoTitle)
3342    {
3343      // If the title is on the border, clip a hole in the later
3344      if (drawTitleBackground
3345	  && (borderType != NSNoBorder)
3346	  && (([box titlePosition] == NSAtTop) || ([box titlePosition] == NSAtBottom)))
3347	{
3348	  [color set];
3349	  NSRectFill([box titleRect]);
3350	}
3351
3352      [[box titleCell] drawWithFrame: [box titleRect] inView: box];
3353    }
3354}
3355
3356- (void) drawEditorForCell: (NSCell *)cell
3357		 withFrame: (NSRect)cellFrame
3358		    inView: (NSView *)view
3359{
3360  [cell _drawEditorWithFrame: cellFrame
3361		      inView: view];
3362}
3363
3364
3365- (void) drawInCell: (NSCell *)cell
3366     attributedText: (NSAttributedString *)stringValue
3367	    inFrame: (NSRect)cellFrame
3368{
3369  [cell _drawAttributedText: stringValue
3370		    inFrame: cellFrame];
3371}
3372
3373// NSBrowserCell
3374- (void) drawBrowserInteriorWithFrame: (NSRect)cellFrame
3375			     withCell: (NSBrowserCell *)cell
3376			       inView: (NSView *)controlView
3377			    withImage: (NSImage *)theImage
3378		       alternateImage: (NSImage *)alternateImage
3379			isHighlighted: (BOOL)isHighlighted
3380				state: (int)state
3381			       isLeaf: (BOOL)isLeaf
3382{
3383  NSRect	title_rect = cellFrame;
3384  NSImage	*branch_image = nil;
3385  NSImage	*cell_image = theImage;
3386
3387  if (isHighlighted || state)
3388    {
3389      if (!isLeaf)
3390	branch_image = [self highlightedBranchImage];
3391      if (nil != alternateImage)
3392	[cell setImage: alternateImage];
3393
3394      // If we are highlighted, fill the background
3395      [[cell highlightColorInView: controlView] setFill];
3396      NSRectFill(cellFrame);
3397    }
3398  else
3399    {
3400      if (!isLeaf)
3401	branch_image = [self branchImage];
3402
3403      // (Don't fill the background)
3404    }
3405
3406  // Draw the branch image if there is one
3407  if (branch_image)
3408    {
3409      NSRect imgRect;
3410
3411      imgRect.size = [branch_image size];
3412      imgRect.origin.x = MAX(NSMaxX(title_rect) - imgRect.size.width - 4.0, 0.);
3413      imgRect.origin.y = MAX(NSMidY(title_rect) - (imgRect.size.height/2.), 0.);
3414
3415      if (controlView != nil)
3416	{
3417	  imgRect = [controlView centerScanRect: imgRect];
3418	}
3419
3420      [branch_image drawInRect: imgRect
3421		      fromRect: NSZeroRect
3422		     operation: NSCompositeSourceOver
3423		      fraction: 1.0
3424		respectFlipped: YES
3425			 hints: nil];
3426
3427      title_rect.size.width -= imgRect.size.width + 8;
3428    }
3429
3430  // Skip 2 points from the left border
3431  title_rect.origin.x += 2;
3432  title_rect.size.width -= 2;
3433
3434  // Draw the cell image if there is one
3435  if (cell_image)
3436    {
3437      NSRect imgRect;
3438
3439      imgRect.size = [cell_image size];
3440      imgRect.origin.x = NSMinX(title_rect);
3441      imgRect.origin.y = MAX(NSMidY(title_rect) - (imgRect.size.height/2.),0.);
3442
3443      if (controlView != nil)
3444	{
3445	  imgRect = [controlView centerScanRect: imgRect];
3446	}
3447
3448      [cell_image drawInRect: imgRect
3449		    fromRect: NSZeroRect
3450		   operation: NSCompositeSourceOver
3451		    fraction: 1.0
3452	      respectFlipped: YES
3453		       hints: nil];
3454
3455      title_rect.origin.x += imgRect.size.width + 4;
3456      title_rect.size.width -= imgRect.size.width + 4;
3457   }
3458
3459  // Draw the body of the cell
3460  if ([cell _inEditing])
3461    {
3462      [self drawEditorForCell: cell
3463		    withFrame: cellFrame
3464		       inView: controlView];
3465    }
3466  else
3467    {
3468      [self drawInCell: cell
3469	attributedText: [cell attributedStringValue]
3470	       inFrame: title_rect];
3471    }
3472}
3473
3474- (NSImage *) branchImage
3475{
3476  return [NSImage imageNamed: @"common_3DArrowRight"];
3477}
3478
3479- (NSImage *) highlightedBranchImage
3480{
3481  return [NSImage imageNamed: @"common_3DArrowRightH"];
3482}
3483@end
3484