1/** <title>GSThemeTools</title>
2
3   <abstract>Useful/configurable drawing functions</abstract>
4
5   Copyright (C) 2004 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 <Foundation/NSException.h>
30#import "AppKit/NSBezierPath.h"
31#import "AppKit/NSGraphics.h"
32#import "AppKit/NSImage.h"
33#import "AppKit/PSOperators.h"
34#import "GSThemePrivate.h"
35
36#include <math.h>
37#include <float.h>
38
39
40@implementation	GSTheme (MidLevelDrawing)
41
42- (NSRect) drawButton: (NSRect)border withClip: (NSRect)clip
43{
44  NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge,
45			   NSMinXEdge, NSMaxYEdge,
46			   NSMaxXEdge, NSMinYEdge};
47  NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge,
48			   NSMinXEdge, NSMinYEdge,
49			   NSMaxXEdge, NSMaxYEdge};
50  // These names are role names not the actual colours
51  NSColor *black = [NSColor controlDarkShadowColor];
52  NSColor *dark = [NSColor controlShadowColor];
53  NSColor *white = [NSColor controlLightHighlightColor];
54  NSColor *colors[] = {black, black, white, white, dark, dark};
55
56  if ([[NSView focusView] isFlipped] == YES)
57    {
58      return NSDrawColorTiledRects(border, clip, dn_sides, colors, 6);
59    }
60  else
61    {
62      return NSDrawColorTiledRects(border, clip, up_sides, colors, 6);
63    }
64}
65
66- (NSRect) drawDarkBezel: (NSRect)border withClip: (NSRect)clip
67{
68  NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge,
69			   NSMinXEdge, NSMaxYEdge, NSMaxXEdge, NSMinYEdge};
70  NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge,
71			   NSMinXEdge, NSMinYEdge, NSMaxXEdge, NSMaxYEdge};
72  // These names are role names not the actual colours
73  NSColor *black = [NSColor controlDarkShadowColor];
74  NSColor *dark = [NSColor controlShadowColor];
75  NSColor *light = [NSColor controlColor];
76  NSColor *white = [NSColor controlLightHighlightColor];
77  NSColor *colors[] = {white, white, dark, dark, black, black, light, light};
78  NSRect rect;
79
80  if ([[NSView focusView] isFlipped] == YES)
81    {
82      rect = NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
83
84      [dark set];
85      PSrectfill(NSMinX(border) + 1., NSMinY(border) - 2., 1., 1.);
86      PSrectfill(NSMaxX(border) - 2., NSMaxY(border) + 1., 1., 1.);
87    }
88  else
89    {
90      rect = NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
91
92      [dark set];
93      PSrectfill(NSMinX(border) + 1., NSMinY(border) + 1., 1., 1.);
94      PSrectfill(NSMaxX(border) - 2., NSMaxY(border) - 2., 1., 1.);
95    }
96  return rect;
97}
98
99- (NSRect) drawDarkButton: (NSRect)border withClip: (NSRect)clip
100{
101  NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge,
102			   NSMinXEdge, NSMaxYEdge};
103  NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge,
104			   NSMinXEdge, NSMinYEdge};
105  // These names are role names not the actual colours
106  NSColor *black = [NSColor controlDarkShadowColor];
107  NSColor *white = [NSColor controlHighlightColor];
108  NSColor *colors[] = {black, black, white, white};
109
110  if ([[NSView focusView] isFlipped] == YES)
111    {
112      return NSDrawColorTiledRects(border, clip, dn_sides, colors, 4);
113    }
114  else
115    {
116      return NSDrawColorTiledRects(border, clip, up_sides, colors, 4);
117    }
118}
119
120- (NSRect) drawFramePhoto: (NSRect)border withClip: (NSRect)clip
121{
122  NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge,
123			   NSMinXEdge, NSMaxYEdge,
124			   NSMaxXEdge, NSMinYEdge};
125  NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge,
126			   NSMinXEdge, NSMinYEdge,
127			   NSMaxXEdge, NSMaxYEdge};
128  // These names are role names not the actual colours
129  NSColor *black = [NSColor controlDarkShadowColor];
130  NSColor *dark = [NSColor controlShadowColor];
131  NSColor *colors[] = {dark, dark, dark, dark, black,black};
132
133  if ([[NSView focusView] isFlipped] == YES)
134    {
135      return NSDrawColorTiledRects(border, clip, dn_sides, colors, 6);
136    }
137  else
138    {
139      return NSDrawColorTiledRects(border, clip, up_sides, colors, 6);
140    }
141}
142
143#if 1
144- (NSRect) drawGradientBorder: (NSGradientType)gradientType
145                       inRect: (NSRect)border
146                     withClip: (NSRect)clip
147{
148  NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge,
149			   NSMinXEdge, NSMaxYEdge};
150  NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge,
151			   NSMinXEdge, NSMinYEdge};
152  NSColor *black = [NSColor controlDarkShadowColor];
153  NSColor *dark = [NSColor controlShadowColor];
154  NSColor *light = [NSColor controlColor];
155  NSColor **colors;
156  NSColor *concaveWeak[] = {dark, dark, light, light};
157  NSColor *concaveStrong[] = {black, black, light, light};
158  NSColor *convexWeak[] = {light, light, dark, dark};
159  NSColor *convexStrong[] = {light, light, black, black};
160  NSRect rect;
161
162  switch (gradientType)
163    {
164      case NSGradientConcaveWeak:
165	colors = concaveWeak;
166	break;
167      case NSGradientConcaveStrong:
168	colors = concaveStrong;
169	break;
170      case NSGradientConvexWeak:
171	colors = convexWeak;
172	break;
173      case NSGradientConvexStrong:
174	colors = convexStrong;
175	break;
176      case NSGradientNone:
177      default:
178	return border;
179    }
180
181  if ([[NSView focusView] isFlipped] == YES)
182    {
183      rect = NSDrawColorTiledRects(border, clip, dn_sides, colors, 4);
184    }
185  else
186    {
187      rect = NSDrawColorTiledRects(border, clip, up_sides, colors, 4);
188    }
189
190  return rect;
191}
192
193#else
194// FIXME: I think this method is wrong.
195- (NSRect) drawGradientBorder: (NSGradientType)gradientType
196                       inRect: (NSRect)cellFrame
197                     withClip: (NSRect)clip
198{
199  float   start_white = 0.0;
200  float   end_white = 0.0;
201  float   white = 0.0;
202  float   white_step = 0.0;
203  float   h, s, v, a;
204  NSPoint p1, p2;
205  NSColor *gray = nil;
206  NSColor *darkGray = nil;
207  NSColor *lightGray = nil;
208
209  lightGray = [NSColor colorWithDeviceRed: NSLightGray
210                       green: NSLightGray
211                       blue: NSLightGray
212                       alpha:1.0];
213  gray = [NSColor colorWithDeviceRed: NSGray
214                  green: NSGray
215                  blue: NSGray
216                  alpha:1.0];
217  darkGray = [NSColor colorWithDeviceRed: NSDarkGray
218                      green: NSDarkGray
219                      blue: NSDarkGray
220                      alpha:1.0];
221
222  switch (gradientType)
223    {
224      case NSGradientNone:
225        return NSZeroRect;
226        break;
227
228      case NSGradientConcaveWeak:
229        [gray getHue: &h saturation: &s brightness: &v alpha: &a];
230        start_white = [lightGray brightnessComponent];
231        end_white = [gray brightnessComponent];
232        break;
233
234      case NSGradientConvexWeak:
235        [darkGray getHue: &h saturation: &s brightness: &v alpha: &a];
236        start_white = [gray brightnessComponent];
237        end_white = [lightGray brightnessComponent];
238        break;
239
240      case NSGradientConcaveStrong:
241        [lightGray getHue: &h saturation: &s brightness: &v alpha: &a];
242        start_white = [lightGray brightnessComponent];
243        end_white = [darkGray brightnessComponent];
244        break;
245
246      case NSGradientConvexStrong:
247        [darkGray getHue: &h saturation: &s brightness: &v alpha: &a];
248        start_white = [darkGray brightnessComponent];
249        end_white = [lightGray brightnessComponent];
250        break;
251
252      default:
253        break;
254    }
255
256  white = start_white;
257  white_step = fabs(start_white - end_white)
258    / (cellFrame.size.width + cellFrame.size.height);
259
260  // Start from top left
261  p1 = NSMakePoint(cellFrame.origin.x,
262    cellFrame.size.height + cellFrame.origin.y);
263  p2 = NSMakePoint(cellFrame.origin.x,
264    cellFrame.size.height + cellFrame.origin.y);
265
266  // Move by Y
267  while (p1.y > cellFrame.origin.y)
268    {
269      [[NSColor
270        colorWithDeviceHue: h saturation: s brightness: white alpha: 1.0] set];
271      [NSBezierPath strokeLineFromPoint: p1 toPoint: p2];
272
273      if (start_white > end_white)
274        white -= white_step;
275      else
276        white += white_step;
277
278      p1.y -= 1.0;
279      if (p2.x < (cellFrame.size.width + cellFrame.origin.x))
280        p2.x += 1.0;
281      else
282        p2.y -= 1.0;
283    }
284
285  // Move by X
286  while (p1.x < (cellFrame.size.width + cellFrame.origin.x))
287    {
288      [[NSColor
289        colorWithDeviceHue: h saturation: s brightness: white alpha: 1.0] set];
290      [NSBezierPath strokeLineFromPoint: p1 toPoint: p2];
291
292      if (start_white > end_white)
293        white -= white_step;
294      else
295        white += white_step;
296
297      p1.x += 1.0;
298      if (p2.x >= (cellFrame.size.width + cellFrame.origin.x))
299        p2.y -= 1.0;
300      else
301        p2.x += 1.0;
302    }
303
304  return NSZeroRect;
305}
306
307#endif
308
309- (NSRect) drawGrayBezel: (NSRect)border withClip: (NSRect)clip
310{
311  NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge,
312			   NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge};
313  NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge,
314			     NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge};
315  // These names are role names not the actual colours
316  NSColor *black = [NSColor controlDarkShadowColor];
317  NSColor *dark = [NSColor controlShadowColor];
318  NSColor *light = [NSColor controlColor];
319  NSColor *white = [NSColor controlLightHighlightColor];
320  NSColor *colors[] = {white, white, dark, dark,
321		       light, light, black, black};
322  NSRect rect;
323
324  if ([[NSView focusView] isFlipped] == YES)
325    {
326      rect = NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
327      [dark set];
328      PSrectfill(NSMinX(border) + 1., NSMaxY(border) - 2., 1., 1.);
329      PSrectfill(NSMaxX(border) - 2., NSMinY(border) + 1., 1., 1.);
330    }
331  else
332    {
333      rect = NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
334      [dark set];
335      PSrectfill(NSMinX(border) + 1., NSMinY(border) + 1., 1., 1.);
336      PSrectfill(NSMaxX(border) - 2., NSMaxY(border) - 2., 1., 1.);
337    }
338  return rect;
339}
340
341- (NSRect) drawGroove: (NSRect)border withClip: (NSRect)clip
342{
343  // go clockwise from the top twice -- makes the groove come out right
344  NSRectEdge up_sides[] = {NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge,
345			   NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge};
346  NSRectEdge dn_sides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge,
347			   NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge};
348  // These names are role names not the actual colours
349  NSColor *dark = [NSColor controlShadowColor];
350  NSColor *white = [NSColor controlLightHighlightColor];
351  NSColor *colors[] = {dark, white, white, dark,
352		       white, dark, dark, white};
353
354  if ([[NSView focusView] isFlipped] == YES)
355    {
356      return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
357    }
358  else
359    {
360      return NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
361    }
362}
363
364- (NSRect) drawLightBezel: (NSRect)border withClip: (NSRect)clip
365{
366  NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge,
367  			   NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge};
368  NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge,
369			   NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge};
370  // These names are role names not the actual colours
371  NSColor *dark = [NSColor controlShadowColor];
372  NSColor *light = [NSColor controlColor];
373  NSColor *white = [NSColor controlLightHighlightColor];
374  NSColor *colors[] = {white, white, dark, dark,
375		       light, light, dark, dark};
376
377  if ([[NSView focusView] isFlipped] == YES)
378    {
379      return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
380    }
381  else
382    {
383      return NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
384    }
385}
386
387- (NSRect) drawWhiteBezel: (NSRect)border withClip: (NSRect)clip
388{
389  NSRectEdge up_sides[] = {NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge,
390  			   NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge};
391  NSRectEdge dn_sides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge,
392  			     NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge};
393  // These names are role names not the actual colours
394  NSColor *dark = [NSColor controlShadowColor];
395  NSColor *light = [NSColor controlColor];
396  NSColor *white = [NSColor controlLightHighlightColor];
397  NSColor *colors[] = {dark, white, white, dark,
398		       dark, light, light, dark};
399
400  if ([[NSView focusView] isFlipped] == YES)
401    {
402      return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
403    }
404  else
405    {
406      return NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
407    }
408}
409
410- (void) drawRoundBezel: (NSRect)cellFrame withColor: (NSColor*)backgroundColor
411{
412  NSBezierPath *p;
413  NSPoint point;
414  CGFloat radius;
415
416  // make smaller than enclosing frame
417  cellFrame = NSInsetRect(cellFrame, 4, 4);
418  radius = cellFrame.size.height / 2.0;
419  point = cellFrame.origin;
420  point.x += radius;
421  point.y += radius - 0.5;
422
423  // Draw initial path to enclose the button...
424  // left half-circle
425  p = [NSBezierPath bezierPath];
426  [p appendBezierPathWithArcWithCenter: point
427				radius: radius
428			    startAngle: 90.0
429			      endAngle: 270.0];
430
431  // line to first point and right halfcircle
432  point.x += cellFrame.size.width - cellFrame.size.height;
433  [p appendBezierPathWithArcWithCenter: point
434				radius: radius
435			    startAngle: 270.0
436			      endAngle: 90.0];
437  [p closePath];
438
439  // fill with background color
440  [backgroundColor set];
441  [p fill];
442
443  // and stroke rounded button
444  [[NSColor shadowColor] set];
445  [p stroke];
446
447  // Add highlights...
448  point = cellFrame.origin;
449  point.x += radius - 0.5;
450  point.y += radius - 0.5;
451  p = [NSBezierPath bezierPath];
452  [p setLineWidth: 1.0];
453  [p appendBezierPathWithArcWithCenter: point
454				radius: radius
455			    startAngle: 135.0
456			      endAngle: 270.0];
457
458  // line to first point and right halfcircle
459  point.x += cellFrame.size.width - cellFrame.size.height;
460  [p appendBezierPathWithArcWithCenter: point
461				radius: radius
462			    startAngle: 270.0
463			      endAngle: 315.0];
464  [[NSColor controlLightHighlightColor] set];
465  [p stroke];
466}
467
468- (void) drawCircularBezel: (NSRect)cellFrame
469		 withColor: (NSColor*)backgroundColor
470{
471  // make smaller so that it does not touch frame
472  NSBezierPath *oval;
473
474  oval = [NSBezierPath bezierPathWithOvalInRect: NSInsetRect(cellFrame, 1, 1)];
475
476  // fill oval with background color
477  [backgroundColor set];
478  [oval fill];
479
480  // and stroke rounded button
481  [[NSColor shadowColor] set];
482  [oval stroke];
483}
484
485@end
486
487
488
489@implementation	GSTheme (LowLevelDrawing)
490
491- (void) fillHorizontalRect: (NSRect)rect
492		  withImage: (NSImage*)image
493		   fromRect: (NSRect)source
494		    flipped: (BOOL)flipped
495{
496  NSGraphicsContext	*ctxt = GSCurrentContext();
497  NSBezierPath		*path;
498  unsigned		repetitions;
499  float			remainder;
500  unsigned		count;
501  NSPoint		p;
502  float			y;
503
504  if (rect.size.width <= 0.0)
505    [NSException raise: NSInvalidArgumentException
506		format: @"[%@-%@] rect width is not positive",
507      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
508  if (rect.size.height <= 0.0)
509    [NSException raise: NSInvalidArgumentException
510		format: @"[%@-%@] rect height is not positive",
511      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
512  if (source.size.width <= 0.0)
513    [NSException raise: NSInvalidArgumentException
514		format: @"[%@-%@] source width is not positive",
515      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
516  if (source.size.height <= 0.0)
517    [NSException raise: NSInvalidArgumentException
518		format: @"[%@-%@] source height is not positive",
519      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
520  if (image == nil)
521    [NSException raise: NSInvalidArgumentException
522		format: @"[%@-%@] image is nil",
523      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
524
525  ctxt = GSCurrentContext();
526  DPSgsave (ctxt);
527  path = [NSBezierPath bezierPathWithRect: rect];
528  [path addClip];
529  repetitions = rect.size.width / source.size.width;
530  remainder = rect.size.width - repetitions * source.size.width;
531  y = rect.origin.y;
532
533  if (flipped) y = rect.origin.y + rect.size.height;
534
535  for (count = 0; count < repetitions; count++)
536    {
537      p = NSMakePoint (rect.origin.x + count * source.size.width, y);
538      [image compositeToPoint: p
539		     fromRect: source
540		    operation: NSCompositeSourceOver];
541    }
542  if (remainder > 0)
543    {
544      p = NSMakePoint (rect.origin.x + repetitions * source.size.width, y);
545      source.size.width = remainder;
546      [image compositeToPoint: p
547		     fromRect: source
548		    operation: NSCompositeSourceOver];
549    }
550  DPSgrestore (ctxt);
551}
552
553- (void) fillRect: (NSRect)rect
554withRepeatedImage: (NSImage*)image
555	 fromRect: (NSRect)source
556	   center: (BOOL)center
557{
558  NSGraphicsContext	*ctxt;
559  NSBezierPath		*path;
560  NSSize		size;
561  unsigned		xrepetitions;
562  unsigned		yrepetitions;
563  unsigned		x;
564  unsigned		y;
565
566  if (rect.size.width <= 0.0)
567    [NSException raise: NSInvalidArgumentException
568		format: @"[%@-%@] rect width is not positive",
569      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
570  if (rect.size.height <= 0.0)
571    [NSException raise: NSInvalidArgumentException
572		format: @"[%@-%@] rect height is not positive",
573      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
574  if (source.size.width <= 0.0)
575    [NSException raise: NSInvalidArgumentException
576		format: @"[%@-%@] source width is not positive",
577      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
578  if (source.size.height <= 0.0)
579    [NSException raise: NSInvalidArgumentException
580		format: @"[%@-%@] source height is not positive",
581      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
582  if (image == nil)
583    [NSException raise: NSInvalidArgumentException
584		format: @"[%@-%@] image is nil",
585      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
586
587  ctxt = GSCurrentContext ();
588  DPSgsave (ctxt);
589  path = [NSBezierPath bezierPathWithRect: rect];
590  [path addClip];
591  size = [image size];
592  xrepetitions = (rect.size.width / size.width) + 1;
593  yrepetitions = (rect.size.height / size.height) + 1;
594
595  for (x = 0; x < xrepetitions; x++)
596    {
597      for (y = 0; y < yrepetitions; y++)
598	{
599	  NSPoint p;
600
601	  p = NSMakePoint (rect.origin.x + x * size.width,
602	    rect.origin.y + y * size.height);
603	  [image compositeToPoint: p
604			 fromRect: source
605			operation: NSCompositeSourceOver];
606      }
607  }
608  DPSgrestore (ctxt);
609}
610
611- (NSRect) fillRect: (NSRect)rect
612	  withTiles: (GSDrawTiles*)tiles
613	 background: (NSColor*)color
614{
615  return [self fillRect: rect
616	      withTiles: tiles
617	     background: color
618	      fillStyle: [tiles fillStyle]];
619}
620
621- (NSRect) fillRect: (NSRect)rect
622	  withTiles: (GSDrawTiles*)tiles
623{
624  return [self fillRect: rect
625	      withTiles: tiles
626	     background: [NSColor clearColor]
627	      fillStyle: [tiles fillStyle]];
628}
629
630- (NSRect) fillRect: (NSRect)rect
631	  withTiles: (GSDrawTiles*)tiles
632	 background: (NSColor*)color
633	  fillStyle: (GSThemeFillStyle)style
634{
635  if (rect.size.width <= 0.0)
636    [NSException raise: NSInvalidArgumentException
637		format: @"[%@-%@] rect width is not positive",
638      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
639  if (rect.size.height <= 0.0)
640    [NSException raise: NSInvalidArgumentException
641		format: @"[%@-%@] rect height is not positive",
642      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
643  if (tiles == nil)
644    [NSException raise: NSInvalidArgumentException
645		format: @"[%@-%@] tiles is nil",
646      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
647
648  return [tiles fillRect: rect background: color fillStyle: style];
649}
650
651- (void) fillVerticalRect: (NSRect)rect
652		withImage: (NSImage*)image
653		 fromRect: (NSRect)source
654		  flipped: (BOOL)flipped
655{
656  NSGraphicsContext	*ctxt;
657  NSBezierPath		*path;
658  unsigned		repetitions;
659  float			remainder;
660  unsigned		count;
661  NSPoint		p;
662
663  if (rect.size.width <= 0.0)
664    [NSException raise: NSInvalidArgumentException
665		format: @"[%@-%@] rect width is not positive",
666      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
667  if (rect.size.height <= 0.0)
668    [NSException raise: NSInvalidArgumentException
669		format: @"[%@-%@] rect height is not positive",
670      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
671  if (source.size.width <= 0.0)
672    [NSException raise: NSInvalidArgumentException
673		format: @"[%@-%@] source width is not positive",
674      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
675  if (source.size.height <= 0.0)
676    [NSException raise: NSInvalidArgumentException
677		format: @"[%@-%@] source height is not positive",
678      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
679  if (image == nil)
680    [NSException raise: NSInvalidArgumentException
681		format: @"[%@-%@] image is nil",
682      NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
683  ctxt = GSCurrentContext();
684  DPSgsave (ctxt);
685  path = [NSBezierPath bezierPathWithRect: rect];
686  [path addClip];
687  repetitions = rect.size.height / source.size.height;
688  remainder = rect.size.height - repetitions * source.size.height;
689
690  if (flipped)
691    {
692      for (count = 0; count < repetitions; count++)
693	{
694	  p = NSMakePoint (rect.origin.x,
695	    rect.origin.y + rect.size.height - count * source.size.height);
696	  [image compositeToPoint: p
697			 fromRect: source
698			operation: NSCompositeSourceOver];
699	}
700      if (remainder > 0)
701	{
702	  p = NSMakePoint (rect.origin.x,
703	    rect.origin.y + rect.size.height
704	    - repetitions * source.size.height);
705	  source.origin.y += source.size.height - remainder;
706	  source.size.height = remainder;
707	  [image compositeToPoint: p
708			 fromRect: source
709			operation: NSCompositeSourceOver];
710	}
711    }
712  else
713    {
714      for (count = 0; count < repetitions; count++)
715	{
716	  p = NSMakePoint (rect.origin.x,
717	    rect.origin.y + count * source.size.height);
718	  [image compositeToPoint: p
719			 fromRect: source
720			operation: NSCompositeSourceOver];
721	}
722      if (remainder > 0)
723	{
724	  p = NSMakePoint (rect.origin.x,
725	    rect.origin.y + repetitions * source.size.height);
726	  source.size.height = remainder;
727	  [image compositeToPoint: p
728			 fromRect: source
729			operation: NSCompositeSourceOver];
730	}
731    }
732  DPSgrestore (ctxt);
733}
734
735@end
736
737
738
739@implementation	GSDrawTiles
740- (id) copyWithZone: (NSZone*)zone
741{
742  GSDrawTiles	*c = (GSDrawTiles*)NSCopyObject(self, 0, zone);
743  unsigned	i;
744
745  for (i = 0; i < 9; i++)
746    {
747      c->images[i] = [images[i] copyWithZone: zone];
748    }
749  c->style = style;
750  return c;
751}
752
753- (void) dealloc
754{
755  unsigned	i;
756
757  for (i = 0; i < 9; i++)
758    {
759      RELEASE(images[i]);
760    }
761  [super dealloc];
762}
763
764- (NSString*) description
765{
766  NSMutableString	*d = [[[super description] mutableCopy] autorelease];
767  unsigned		i;
768
769  for (i = 0; i < 9; i++)
770    {
771      [d appendFormat: @"\n  %@ %@", NSStringFromRect(rects[i]), images[i]];
772    }
773
774  return d;
775}
776
777/**
778 * Simple initialiser, assume the single image is split into nine equal tiles.
779 * If the image size is not divisible by three, the corners are made equal
780 * in size and the central parts slightly smaller.
781 */
782- (id) initWithImage: (NSImage*)image
783{
784  NSSize	s = [image size];
785
786  return [self initWithImage: image
787		  horizontal: s.width / 3.0
788		    vertical: s.height / 3.0];
789}
790
791- (id) initWithNinePatchImage: (NSImage*)image
792{
793  int i;
794  CGFloat r,g,b,a;
795  int x1 = -1; // x1, x2, y1, y2, are in flipped coordinates
796  int x2 = -1; // 0,0 is the top-left pixel
797  int y1 = -1;
798  int y2 = -1;
799  NSSize s = [image size];
800  NSBitmapImageRep* rep = [[image representations] objectAtIndex: 0];
801
802  for (i = 0; i < s.width; i++)
803    {
804      NSColor	*pixelColor = [rep colorAtX: i y: 0];
805
806      [pixelColor getRed: &r green: &g blue: &b alpha: &a];
807      if (a > 0 && x1 == -1)
808        {
809          x1 = i;
810        }
811      else if (a == 0 && x1 != -1)
812        {
813          x2 = i - 1;
814          break;
815        }
816    }
817
818  for (i = 0; i < s.height; i++)
819    {
820      NSColor	*pixelColor = [rep colorAtX: 0 y: i];
821
822      [pixelColor getRed: &r green: &g blue: &b alpha: &a];
823      if (a > 0 && y1 == -1)
824        {
825          y1 = i;
826        }
827      else if (a == 0 && y1 != -1)
828        {
829          y2 = i - 1;
830          break;
831        }
832    }
833
834  scaleFactor  = 1.0f;
835  style = GSThemeFillStyleScaleAll;
836
837  // These are all in _unflipped_ coordinates
838  rects[TileTL] = NSMakeRect(1, s.height - y1, x1 - 1, y1 - 1);
839  rects[TileTM] = NSMakeRect(x1, s.height - y1, 1 + x2 - x1, y1 - 1);
840  rects[TileTR] = NSMakeRect(x2 + 1, s.height - y1, s.width - x2 - 2, y1 - 1);
841  rects[TileCL] = NSMakeRect(1, s.height - y2 - 1, x1 - 1, 1 + y2 - y1);
842  rects[TileCM] = NSMakeRect(x1, s.height - y2 - 1, 1 + x2 - x1, 1 + y2 - y1);
843  rects[TileCR] = NSMakeRect(x2 + 1, s.height - y2 - 1, s.width - x2 - 2, 1 + y2 - y1);
844  rects[TileBL] = NSMakeRect(1, 1, x1 - 1, s.height - y2 - 2);
845  rects[TileBM] = NSMakeRect(x1, 1, 1 + x2 - x1, s.height - y2 - 2);
846  rects[TileBR] = NSMakeRect(x2 + 1, 1, s.width - x2 - 2, s.height - y2 - 2);
847
848  // Measure the content rect (the right and bottom edges of the nine-patch
849  // data)
850
851  x1 = -1;
852  x2 = -1;
853  y1 = -1;
854  y2 = -1;
855
856  for (i = 0; i < s.width; i++)
857    {
858      NSColor	*pixelColor = [rep colorAtX: i y: s.height - 1];
859
860      [pixelColor getRed: &r green: &g blue: &b alpha: &a];
861      if ((a == 1 && r == 0 && g == 0 && b == 0) && x1 == -1)
862        {
863          x1 = i;
864        }
865      else if (!(a == 1 && r == 0 && g == 0 && b == 0) && x1 != -1)
866        {
867          x2 = i - 1;
868          break;
869        }
870    }
871
872  for (i = 0; i < s.height; i++)
873    {
874      NSColor	*pixelColor = [rep colorAtX: s.width - 1 y: i];
875
876      [pixelColor getRed: &r green: &g blue: &b alpha: &a];
877      if ((a == 1 && r == 0 && g == 0 && b == 0) && y1 == -1)
878        {
879          y1 = i;
880        }
881      else if (!(a == 1 && r == 0 && g == 0 && b == 0) && y1 != -1)
882        {
883          y2 = i - 1;
884          break;
885        }
886    }
887
888  // Specifying the content rect part of the nine-patch image is optional
889  // ; if either the horizontal or vertical information is missing, use the
890  // geometry from rects[TileCM]
891
892  // contentRect is in unflipped coordinates, like rects[]
893
894  if (x1 == -1)
895    {
896      contentRect.origin.x = rects[TileCM].origin.x;
897      contentRect.size.width = rects[TileCM].size.width;
898    }
899  else
900    {
901      contentRect.origin.x = x1;
902      contentRect.size.width = 1 + x2 - x1;
903    }
904
905  if (y1 == -1)
906    {
907      contentRect.origin.y = rects[TileCM].origin.y;
908      contentRect.size.height = rects[TileCM].size.height;
909    }
910  else
911    {
912      contentRect.origin.y = s.height - y2 - 1;
913      contentRect.size.height = 1 + y2 - y1;
914    }
915
916  // Measure the layout rect (the right and bottom edges of the nine-patch
917  // data which  _isn't_ red pixels)
918
919  x1 = -1;
920  x2 = -1;
921  y1 = -1;
922  y2 = -1;
923
924  for (i = 1; i < (s.width - 1); i++)
925    {
926      NSColor	*pixelColor = [rep colorAtX: i y: s.height - 1];
927
928      [pixelColor getRed: &r green: &g blue: &b alpha: &a];
929      if (!(a == 1 && r == 1 && g == 0 && b == 0) && x1 == -1)
930        {
931          x1 = i;
932        }
933      else if ((a == 1 && r == 1 && g == 0 && b == 0) && x1 != -1)
934        {
935          x2 = i - 1;
936          break;
937        }
938    }
939
940  for (i = 1; i < (s.height - 1); i++)
941    {
942      NSColor	*pixelColor = [rep colorAtX: s.width - 1 y: i];
943
944      [pixelColor getRed: &r green: &g blue: &b alpha: &a];
945      if (!(a == 1 && r == 1 && g == 0 && b == 0) && y1 == -1)
946        {
947          y1 = i;
948        }
949      else if ((a == 1 && r == 1 && g == 0 && b == 0) && y1 != -1)
950        {
951          y2 = i - 1;
952          break;
953        }
954    }
955
956
957  if (x2 == -1)
958    {
959      x2 = s.width - 2;
960    }
961
962  if (y2 == -1)
963    {
964      y2 = s.height - 2;
965    }
966
967  layoutRect = NSMakeRect(x1, s.height - y2 - 1, 1 + x2 - x1, 1 + y2 - y1);
968
969  [self validateTilesSizeWithImage: image];
970  return self;
971}
972
973- (NSImage*) extractImageFrom: (NSImage*) image withRect: (NSRect) rect
974{
975  NSImage *img = [[NSImage alloc] initWithSize: rect.size];
976
977  [img lockFocus];
978  [image drawAtPoint: NSMakePoint(0, 0)
979	    fromRect: rect
980	   operation: NSCompositeSourceOver
981	    fraction: 1.0];
982  [img unlockFocus];
983  [img TIFFRepresentation]; // creates a proper NSBitmapImageRep
984  return [img autorelease];
985}
986
987- (void) validateTilesSizeWithImage: (NSImage*)image
988{
989  int i;
990
991  originalRectCM = rects[TileCM];
992
993  for (i = 0; i < 9; i++)
994    {
995      if (rects[i].origin.x < 0.0 || rects[i].origin.y < 0.0
996        || rects[i].size.width <= 0.0 || rects[i].size.height <= 0.0)
997        {
998          images[i] = nil;
999          rects[i] = NSZeroRect;
1000        }
1001      else
1002        {
1003          images[i]
1004	    = [[self extractImageFrom: image withRect: rects[i]] retain];
1005	  // FIXME: This makes no sense to me, why not leave the
1006	  // rect origins at their original values?
1007          rects[i].origin.x = 0;
1008          rects[i].origin.y = 0;
1009        }
1010    }
1011
1012    if (contentRect.origin.x < 0.0 || contentRect.origin.y < 0.0
1013      || contentRect.size.width <= 0.0 || contentRect.size.height <= 0.0)
1014      {
1015        contentRect = rects[TileCM];
1016      }
1017}
1018
1019- (id) initWithImage: (NSImage*)image horizontal: (float)x vertical: (float)y
1020{
1021  NSSize s = [image size];
1022
1023  x = floor(x);
1024  y = floor(y);
1025
1026  scaleFactor  = 1.0;
1027
1028  rects[TileTL] = NSMakeRect(0.0, s.height - y, x, y);
1029  rects[TileTM] = NSMakeRect(x, s.height - y, s.width - 2.0 * x, y);
1030  rects[TileTR] = NSMakeRect(s.width - x, s.height - y, x, y);
1031  rects[TileCL] = NSMakeRect(0.0, y, x, s.height - 2.0 * y);
1032  rects[TileCM] = NSMakeRect(x, y, s.width - 2.0 * x, s.height - 2.0 * y);
1033  rects[TileCR] = NSMakeRect(s.width - x, y, x, s.height - 2.0 * y);
1034  rects[TileBL] = NSMakeRect(0.0, 0.0, x, y);
1035  rects[TileBM] = NSMakeRect(x, 0.0, s.width - 2.0 * x, y);
1036  rects[TileBR] = NSMakeRect(s.width - x, 0.0, x, y);
1037  contentRect = rects[TileCM];
1038
1039  style = GSThemeFillStyleNone;
1040
1041  [self validateTilesSizeWithImage: image];
1042  return self;
1043}
1044
1045- (void) scaleTo: (float)scale
1046{
1047  unsigned	i;
1048  NSSize	s;
1049
1050  if (scale == scaleFactor)
1051    {
1052      return;
1053    }
1054
1055  [images[0] setScalesWhenResized: YES];
1056  s = [images[0] size];
1057  s.width *= scale;
1058  s.height *= scale;
1059  [images[0] setSize: s];
1060  rects[0].size.height *= scale;
1061  rects[0].size.width *= scale;
1062  rects[0].origin.x *= scale;
1063  rects[0].origin.y *= scale;
1064  for (i = 1; i < 9; i++)
1065    {
1066      unsigned	j;
1067
1068      for (j = 0; j < i; j++)
1069	{
1070	  if (images[i] == images[j])
1071	    {
1072	      break;
1073	    }
1074	}
1075      if (j == i)
1076	{
1077	  [images[i] setScalesWhenResized: YES];
1078	  s = [images[i] size];
1079	  s.width *= scale;
1080	  s.height *= scale;
1081	  [images[i] setSize: s];
1082	}
1083      rects[i].size.height *= scale;
1084      rects[i].size.width *= scale;
1085      rects[i].origin.x *= scale;
1086      rects[i].origin.y *= scale;
1087    }
1088  contentRect.size.height *= scale;
1089  contentRect.size.width *= scale;
1090  contentRect.origin.x *= scale;
1091  contentRect.origin.y *= scale;
1092}
1093
1094- (NSRect) fillRect: (NSRect)rect
1095         background: (NSColor*)color
1096{
1097  return [self fillRect: rect background: color fillStyle: style];
1098}
1099
1100- (NSRect) fillRect: (NSRect)rect
1101         background: (NSColor*)color
1102          fillStyle: (GSThemeFillStyle)aStyle
1103{
1104  if (color == nil)
1105    {
1106      [[NSColor redColor] set];
1107    }
1108  else
1109    {
1110      [color set];
1111    }
1112//  NSRectFill(rect);
1113
1114  switch (aStyle)
1115    {
1116      case GSThemeFillStyleNone:
1117           return [self noneStyleFillRect: rect];
1118      case GSThemeFillStyleScale:
1119           return [self scaleStyleFillRect: rect];
1120      case GSThemeFillStyleRepeat:
1121           return [self repeatStyleFillRect: rect];
1122      case GSThemeFillStyleCenter:
1123           return [self centerStyleFillRect: rect];
1124      case GSThemeFillStyleMatrix:
1125           return [self matrixStyleFillRect: rect];
1126      case GSThemeFillStyleScaleAll:
1127           return [self scaleAllStyleFillRect: rect];
1128    }
1129
1130  return NSZeroRect;
1131}
1132
1133- (NSSize) computeTotalTilesSize
1134{
1135  NSSize tsz;
1136
1137  tsz.width = rects[TileTL].size.width + rects[TileTR].size.width;
1138  if (images[TileTM] != nil)
1139    {
1140      tsz.width += rects[TileTM].size.width;
1141    }
1142  tsz.height = rects[TileTL].size.height + rects[TileBL].size.height;
1143  if (images[TileCL] != nil)
1144    {
1145      tsz.height += rects[TileCL].size.height;
1146    }
1147  return tsz;
1148}
1149
1150- (GSThemeMargins) themeMargins
1151{
1152  NSRect cm = originalRectCM;
1153  GSThemeMargins margins;
1154
1155  margins.left = rects[TileCL].size.width;
1156  margins.right = rects[TileCR].size.width;
1157  margins.top = rects[TileTM].size.height;
1158  margins.bottom = rects[TileBM].size.height;
1159
1160  // Adjust for contentRect != cm
1161
1162  margins.left += (contentRect.origin.x - cm.origin.x);
1163  margins.bottom += (contentRect.origin.y - cm.origin.y);
1164
1165  margins.right += (NSMaxX(cm) - NSMaxX(contentRect));
1166  margins.top += (NSMaxY(cm) - NSMaxY(contentRect));
1167
1168  return margins;
1169}
1170
1171- (NSRect) contentRectForRect: (NSRect)rect
1172		    isFlipped: (BOOL)flipped
1173{
1174  GSThemeMargins margins = [self themeMargins];
1175
1176  rect.origin.x += margins.left;
1177  rect.origin.y += flipped ? margins.top : margins.bottom;
1178
1179  rect.size.width -= (margins.left + margins.right);
1180  rect.size.height -= (margins.top + margins.bottom);
1181
1182  return rect;
1183}
1184
1185- (NSRect) noneStyleFillRect: (NSRect)rect
1186{
1187  NSRect inFill = [self contentRectForRect: rect isFlipped: NO];
1188  [self repeatFillRect: rect];
1189  [self drawCornersRect: rect];
1190  return inFill;
1191}
1192
1193- (NSRect) centerStyleFillRect: (NSRect)rect
1194{
1195  BOOL flipped = [[GSCurrentContext() focusView] isFlipped];
1196
1197  NSRect r = rects[TileCM];
1198  NSRect inFill = [self contentRectForRect: rect isFlipped: flipped];
1199  [self repeatFillRect: rect];
1200  [self drawCornersRect: rect];
1201
1202  r.origin.x = inFill.origin.x + (inFill.size.width - r.size.width) / 2;
1203  r.origin.y = inFill.origin.y + (inFill.size.height - r.size.height) / 2;
1204
1205  if (flipped)
1206    {
1207      r.origin.y += r.size.height;
1208    }
1209
1210  [images[TileCM] compositeToPoint: r.origin
1211                          fromRect: rects[TileCM]
1212                         operation: NSCompositeSourceOver];
1213
1214  return inFill;
1215}
1216
1217- (NSRect) repeatStyleFillRect: (NSRect)rect
1218{
1219  BOOL flipped = [[GSCurrentContext() focusView] isFlipped];
1220
1221  NSSize tsz = [self computeTotalTilesSize];
1222  NSRect inFill = [self contentRectForRect: rect isFlipped: flipped];
1223  [self repeatFillRect: rect];
1224  [self drawCornersRect: rect];
1225
1226  [[GSTheme theme] fillRect: inFill
1227    withRepeatedImage: images[TileCM]
1228    fromRect: rects[TileCM]
1229    center: !flipped];
1230
1231  NSLog(@"rect %@ too small for tiles %@",
1232    NSStringFromSize(rect.size), NSStringFromSize(tsz));
1233
1234  return inFill;
1235}
1236
1237- (NSRect) scaleStyleFillRect: (NSRect)rect
1238{
1239  BOOL flipped = [[GSCurrentContext() focusView] isFlipped];
1240
1241  NSRect inFill = [self contentRectForRect: rect isFlipped: flipped];
1242
1243  NSImage *im = [images[TileCM] copy];
1244  NSRect r =  rects[TileCM];
1245  NSSize s = [images[TileCM] size];
1246  NSPoint p = inFill.origin;
1247  float sx = inFill.size.width / r.size.width;
1248  float sy = inFill.size.height / r.size.height;
1249
1250  [self repeatFillRect: rect];
1251  [self drawCornersRect: rect];
1252
1253  r.size.width = inFill.size.width;
1254  r.size.height = inFill.size.height;
1255  r.origin.x *= sx;
1256  r.origin.y *= sy;
1257  s.width *= sx;
1258  s.height *= sy;
1259
1260  if (flipped)
1261    {
1262      p.y += inFill.size.height;
1263    }
1264
1265  [im setScalesWhenResized: YES];
1266  [im setSize: s];
1267  [im compositeToPoint: p
1268              fromRect: r
1269             operation: NSCompositeSourceOver];
1270  RELEASE(im);
1271
1272  return inFill;
1273}
1274
1275- (NSRect) scaleAllStyleFillRect: (NSRect)rect
1276{
1277  BOOL flipped = [[GSCurrentContext() focusView] isFlipped];
1278  NSSize cls = rects[TileCL].size;
1279  NSSize bms = rects[TileBM].size;
1280  NSSize crs = rects[TileCR].size;
1281  NSSize tms = rects[TileTM].size;
1282  NSImage *img;
1283  NSRect imgRect;
1284
1285  NSRect inFill = [self contentRectForRect: rect isFlipped: flipped];
1286  [self scaleFillRect: rect];
1287  [self drawCornersRect: rect];
1288
1289  // Draw center scaled
1290
1291  img = images[TileCM];
1292  imgRect = NSMakeRect(0, 0,
1293    rect.size.width - cls.width - crs.width,
1294    rect.size.height - tms.height - bms.height);
1295
1296  if (imgRect.size.width > 0 && imgRect.size.height > 0)
1297    {
1298      NSPoint p;
1299
1300      [img setScalesWhenResized: YES];
1301      [img setSize: imgRect.size];
1302      p = NSMakePoint(rect.origin.x + cls.width,
1303	rect.origin.y + bms.height);
1304      if (flipped)
1305        {
1306          p.y = rect.origin.y + rect.size.height - bms.height;
1307        }
1308      [img compositeToPoint: p
1309                   fromRect: imgRect
1310                  operation: NSCompositeSourceOver];
1311    }
1312
1313  return inFill;
1314}
1315
1316- (NSRect) matrixStyleFillRect: (NSRect)rect
1317{
1318  NSSize tsz = [self computeTotalTilesSize];
1319  NSRect grid = NSZeroRect;
1320  float x;
1321  float y;
1322  float space = 3.0;
1323  float scale;
1324  BOOL flipped = [[GSCurrentContext() focusView] isFlipped];
1325
1326  if (images[TileTM] == nil)
1327    {
1328      grid.size.width = tsz.width + space * 3.0;
1329    }
1330  else
1331    {
1332      grid.size.width = tsz.width + space * 4.0;
1333    }
1334  scale = rect.size.width / grid.size.width;
1335
1336  if (images[TileCL] == nil)
1337    {
1338      grid.size.height = tsz.height + space * 3.0;
1339    }
1340  else
1341    {
1342      grid.size.height = tsz.height + space * 4.0;
1343    }
1344
1345  if ((rect.size.height / grid.size.height) < scale)
1346    {
1347      scale = rect.size.height / grid.size.height;
1348    }
1349
1350  if (floor(scale) >= 1)
1351    {
1352      scale = floor(scale);
1353    }
1354
1355  if (scale != 1)
1356    {
1357      // We need to scale the tiles down to fit.
1358      grid.size.width *= scale;
1359      grid.size.height *= scale;
1360      space *= scale;
1361      [self scaleTo: scale];
1362    }
1363
1364  grid.origin.x = rect.origin.x + (rect.size.width - grid.size.width) / 2;
1365  x = grid.origin.x;
1366  if (flipped)
1367    {
1368      grid.origin.y = NSMaxY(rect) - (rect.size.height - grid.size.height) / 2;
1369      y = NSMaxY(grid);
1370    }
1371  else
1372    {
1373      grid.origin.y = rect.origin.y + (rect.size.height - grid.size.height) / 2;
1374      y = grid.origin.y;
1375    }
1376
1377  // Draw bottom row
1378  if (flipped)
1379    {
1380      y -= (rects[TileBL].size.height + space);
1381    }
1382  else
1383    {
1384      y += space;
1385    }
1386  [images[TileBL] compositeToPoint: NSMakePoint(x, y)
1387                                 fromRect: rects[TileBL]
1388                                operation: NSCompositeSourceOver];
1389  x += rects[TileBL].size.width + space;
1390  if (images[TileBM] != nil)
1391    {
1392      [images[TileBM] compositeToPoint: NSMakePoint(x, y)
1393                                     fromRect: rects[TileBM]
1394	                            operation: NSCompositeSourceOver];
1395      x += rects[TileBM].size.width + space;
1396    }
1397  [images[TileBR] compositeToPoint: NSMakePoint(x, y)
1398	                         fromRect: rects[TileBR]
1399                                operation: NSCompositeSourceOver];
1400  if (!flipped)
1401    {
1402      y += rects[TileBL].size.height;
1403    }
1404
1405  // Draw middle row
1406  if (images[TileCL] != nil)
1407    {
1408      x = grid.origin.x;
1409      if (flipped)
1410        {
1411          y -= (rects[TileCL].size.height + space);
1412	}
1413      else
1414	{
1415          y += space;
1416	}
1417      [images[TileCL] compositeToPoint: NSMakePoint(x, y)
1418                                     fromRect: rects[TileCL]
1419                                    operation: NSCompositeSourceOver];
1420      x += rects[TileCL].size.width + space;
1421      if (images[TileCM] != nil)
1422        {
1423	  [images[TileCM] compositeToPoint: NSMakePoint(x, y)
1424                                         fromRect: rects[TileCM]
1425                                        operation: NSCompositeSourceOver];
1426          x += rects[TileCM].size.width + space;
1427        }
1428      [images[TileCR] compositeToPoint: NSMakePoint(x, y)
1429                                     fromRect: rects[TileCR]
1430                                    operation: NSCompositeSourceOver];
1431      if (!flipped)
1432        {
1433          y += rects[TileCL].size.height;
1434        }
1435    }
1436
1437  // Draw top row
1438  x = grid.origin.x;
1439  if (flipped)
1440    {
1441      y -= (rects[TileTL].size.height + space);
1442    }
1443  else
1444    {
1445      y += space;
1446    }
1447  [images[TileTL] compositeToPoint: NSMakePoint(x, y)
1448                                 fromRect: rects[TileTL]
1449                                operation: NSCompositeSourceOver];
1450  x += rects[TileTL].size.width + space;
1451  if (images[TileTM] != nil)
1452    {
1453      [images[TileTM] compositeToPoint: NSMakePoint(x, y)
1454                                     fromRect: rects[TileTM]
1455                                    operation: NSCompositeSourceOver];
1456      x += rects[TileTM].size.width + space;
1457    }
1458  [images[TileTR] compositeToPoint: NSMakePoint(x, y)
1459                                 fromRect: rects[TileTR]
1460                                 operation: NSCompositeSourceOver];
1461  if (scale != 1)
1462    {
1463      [self scaleTo: 1.0f/scale];
1464    }
1465  return NSZeroRect;
1466}
1467
1468- (void) repeatFillRect: (NSRect)rect
1469{
1470  BOOL flipped = [[GSCurrentContext() focusView] isFlipped];
1471
1472  NSSize tls = rects[TileTL].size;
1473  NSSize tms = rects[TileTM].size;
1474  NSSize trs = rects[TileTR].size;
1475  NSSize cls = rects[TileCL].size;
1476  NSSize crs = rects[TileCR].size;
1477  NSSize bls = rects[TileBL].size;
1478  NSSize bms = rects[TileBM].size;
1479  NSSize brs = rects[TileBR].size;
1480
1481  // Draw Top-Middle image repeated
1482  if (rect.size.width > tls.width + trs.width && tms.height > 0)
1483    {
1484      float y = rect.origin.y + rect.size.height - tms.height;
1485
1486      if (flipped)
1487        {
1488          y = rect.origin.y;
1489        }
1490
1491      [[GSTheme theme] fillHorizontalRect:
1492        NSMakeRect (rect.origin.x + tls.width, y,
1493	    rect.size.width - tls.width - trs.width,
1494	    tms.height)
1495	withImage: images[TileTM]
1496	fromRect: rects[TileTM]
1497	flipped: flipped];
1498    }
1499
1500  // Draw Bottom-Middle image repeated
1501  if (rect.size.width > bls.width + brs.width && bms.height > 0)
1502    {
1503      float y = rect.origin.y;
1504
1505      if (flipped)
1506        {
1507          y = rect.origin.y + rect.size.height - bms.height;
1508        }
1509      [[GSTheme theme] fillHorizontalRect:
1510	NSMakeRect (rect.origin.x + bls.width, y,
1511	      rect.size.width - bls.width - brs.width,
1512	      bms.height)
1513	withImage: images[TileBM]
1514	fromRect: rects[TileBM]
1515	flipped: flipped];
1516    }
1517
1518  // Draw Center-Left image repeated
1519
1520  if (rect.size.height > bls.height + tls.height && cls.width > 0)
1521    {
1522      [[GSTheme theme] fillVerticalRect:
1523	NSMakeRect (rect.origin.x,
1524	      rect.origin.y + bls.height,
1525	      cls.width,
1526	      rect.size.height - bls.height - tls.height)
1527	withImage: images[TileCL]
1528	fromRect: rects[TileCL]
1529	flipped: flipped];
1530    }
1531
1532  // Draw Center-Right image repeated
1533
1534  if (rect.size.height > brs.height + trs.height && crs.width > 0)
1535    {
1536      [[GSTheme theme] fillVerticalRect:
1537	NSMakeRect (rect.origin.x + rect.size.width - crs.width,
1538	      rect.origin.y + brs.height,
1539	      crs.width,
1540	      rect.size.height - brs.height - trs.height)
1541	withImage: images[TileCR]
1542	fromRect: rects[TileCR]
1543	flipped: flipped];
1544    }
1545}
1546
1547- (void) scaleFillRect: (NSRect)rect
1548{
1549  BOOL flipped = [[GSCurrentContext() focusView] isFlipped];
1550  NSImage *img;
1551  NSRect imgRect;
1552  NSPoint p;
1553
1554  NSSize tls = rects[TileTL].size;
1555  NSSize tms = rects[TileTM].size;
1556  NSSize trs = rects[TileTR].size;
1557  NSSize cls = rects[TileCL].size;
1558  NSSize crs = rects[TileCR].size;
1559  NSSize bls = rects[TileBL].size;
1560  NSSize bms = rects[TileBM].size;
1561  NSSize brs = rects[TileBR].size;
1562
1563  // Draw Top-Middle image scaled
1564
1565  img = images[TileTM];
1566  imgRect = NSMakeRect(0, 0,
1567    rect.size.width - tls.width - trs.width, tms.height);
1568  if (imgRect.size.width > 0 && imgRect.size.height > 0)
1569    {
1570      [img setScalesWhenResized: YES];
1571      [img setSize: imgRect.size];
1572      p = NSMakePoint(rect.origin.x + tls.width,
1573	rect.origin.y + rect.size.height - tms.height);
1574      if (flipped)
1575        {
1576          p.y = rect.origin.y + tms.height;
1577        }
1578      [img compositeToPoint: p
1579                   fromRect: imgRect
1580                  operation: NSCompositeSourceOver];
1581    }
1582
1583  // Draw Bottom-Middle image scaled
1584
1585  img = images[TileBM];
1586  imgRect = NSMakeRect(0, 0,
1587    rect.size.width - bls.width - brs.width, bms.height);
1588  if (imgRect.size.width > 0 && imgRect.size.height > 0)
1589    {
1590      [img setScalesWhenResized: YES];
1591      [img setSize: imgRect.size];
1592      p = NSMakePoint(rect.origin.x + bls.width, rect.origin.y);
1593      if (flipped)
1594        {
1595          p.y = rect.origin.y + rect.size.height;
1596        }
1597      [img compositeToPoint: p
1598                   fromRect: imgRect
1599                  operation: NSCompositeSourceOver];
1600    }
1601
1602  // Draw Center-Left image scaled
1603
1604  img = images[TileCL];
1605  imgRect = NSMakeRect(0, 0,
1606    cls.width, rect.size.height - tls.height - bls.height);
1607  if (imgRect.size.width > 0 && imgRect.size.height > 0)
1608    {
1609      [img setScalesWhenResized: YES];
1610      [img setSize: imgRect.size];
1611      p = NSMakePoint(rect.origin.x, rect.origin.y + bls.height);
1612      if (flipped)
1613        {
1614          p.y = rect.origin.y + rect.size.height - bls.height;
1615        }
1616      [img compositeToPoint: p
1617                   fromRect: imgRect
1618                  operation: NSCompositeSourceOver];
1619    }
1620
1621  // Draw Center-Right image scaled
1622
1623  img = images[TileCR];
1624  imgRect = NSMakeRect(0, 0,
1625    crs.width, rect.size.height - trs.height - brs.height);
1626  if (imgRect.size.width > 0 && imgRect.size.height > 0)
1627    {
1628      [img setScalesWhenResized: YES];
1629      [img setSize: imgRect.size];
1630      p = NSMakePoint(rect.origin.x + rect.size.width - crs.width,
1631	rect.origin.y + brs.height);
1632      if (flipped)
1633        {
1634          p.y = rect.origin.y + rect.size.height - brs.height;
1635        }
1636      [img compositeToPoint: p
1637                   fromRect: imgRect
1638                  operation: NSCompositeSourceOver];
1639    }
1640}
1641
1642
1643- (void) drawCornersRect: (NSRect)rect
1644{
1645  BOOL flipped = [[GSCurrentContext() focusView] isFlipped];
1646
1647  NSSize tls = rects[TileTL].size;
1648  NSSize trs = rects[TileTR].size;
1649  NSSize brs = rects[TileBR].size;
1650  NSPoint p;
1651
1652  p = NSMakePoint (rect.origin.x,
1653    rect.origin.y + rect.size.height - tls.height);
1654  if (flipped)
1655    {
1656      p.y = rect.origin.y + tls.height;
1657    }
1658  [images[TileTL] compositeToPoint: p
1659                          fromRect: rects[TileTL]
1660                         operation: NSCompositeSourceOver];
1661// Is this right?
1662//  p = NSMakePoint(rect.origin.x + rect.size.width - trs.width + 1,
1663  p = NSMakePoint(rect.origin.x + rect.size.width - trs.width,
1664    rect.origin.y + rect.size.height - trs.height);
1665  if (flipped)
1666    {
1667      p.y = rect.origin.y + tls.height;
1668    }
1669  [images[TileTR] compositeToPoint: p
1670                          fromRect: rects[TileTR]
1671                         operation: NSCompositeSourceOver];
1672
1673  p = NSMakePoint(rect.origin.x, rect.origin.y);
1674  if (flipped)
1675    {
1676      p.y = rect.origin.y + rect.size.height;
1677    }
1678  [images[TileBL] compositeToPoint: p
1679                          fromRect: rects[TileBL]
1680                         operation: NSCompositeSourceOver];
1681
1682// Is this right?
1683//  p = NSMakePoint(rect.origin.x + rect.size.width - brs.width + 1,
1684  p = NSMakePoint(rect.origin.x + rect.size.width - brs.width,
1685    rect.origin.y);
1686  if (flipped)
1687    {
1688      p.y = rect.origin.y + rect.size.height;
1689    }
1690  [images[TileBR] compositeToPoint: p
1691                          fromRect: rects[TileBR]
1692                         operation: NSCompositeSourceOver];
1693}
1694
1695- (GSThemeFillStyle) fillStyle
1696{
1697  return style;
1698}
1699
1700- (void) setFillStyle: (GSThemeFillStyle)aStyle
1701{
1702  style = aStyle;
1703}
1704
1705- (NSSize) size
1706{
1707  const CGFloat width = rects[TileCL].size.width
1708    + rects[TileCM].size.width
1709    + rects[TileCR].size.width;
1710
1711  const CGFloat height = rects[TileTM].size.height
1712    + rects[TileCM].size.height
1713    + rects[TileBM].size.height;
1714
1715  return NSMakeSize(width, height);
1716}
1717
1718@end
1719
1720