diff --git a/ChangeLog b/ChangeLog index 08bde2142..99e50313c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2006-09-22 Richard Frith-Macdonald + + * Source/GSWindowDecorationView.m: Merge in theme branch changes. + * Source/NSColorList.m: Cooperate with NSColor to change system + color list when a new theme is activated. + * Source/GSDrawFunctions.m: Merge in theme branch changes. + Reorganize, add theme loading functionality, add image tiling + methods. + * Source/NSButtonCell.m: Merge in theme branch changes. + * Source/NSColor.m: Take notice of themes changing the system + color list and interface with NSColorList to make the chanes and + then send out the notification to tell all observers. + * Source/GSTitleView.m: call instance method rather than class method + * Source/DocMakefile: Document theme changes + * Source/NSTabView.m: call instance method rather than class method + * Documentation/GuiAdditions.gsdoc: link to theme documentation + * Documentation/General/GNUmakefile: make theme documentation + * Headers/AppKit/NSWindow.h: improve comments + * Headers/Additions/GNUstepGUI/GSDrawFunctions.h: reorganize + 2006-09-21 Matt Rice * Source/NSTextFieldCell.m (-setEnabled:): Revert previous patch. diff --git a/Documentation/GNUmakefile b/Documentation/GNUmakefile index 19a12068f..a2ebc2013 100644 --- a/Documentation/GNUmakefile +++ b/Documentation/GNUmakefile @@ -18,7 +18,8 @@ # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free -# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02111 USA. # Install into the system root by default GNUSTEP_INSTALLATION_DIR = $(GNUSTEP_SYSTEM_ROOT) diff --git a/Documentation/General/GNUmakefile b/Documentation/General/GNUmakefile index 136f6d225..7907ba5c0 100644 --- a/Documentation/General/GNUmakefile +++ b/Documentation/General/GNUmakefile @@ -19,7 +19,8 @@ # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free -# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02111 USA # # Install into the system root by default diff --git a/Documentation/GuiAdditions.gsdoc b/Documentation/GuiAdditions.gsdoc index d90808ecf..94bf20854 100644 --- a/Documentation/GuiAdditions.gsdoc +++ b/Documentation/GuiAdditions.gsdoc @@ -25,6 +25,7 @@

GSDisplayServer + GSDrawFunctions GSHbox GSTable GSVbox diff --git a/Headers/Additions/GNUstepGUI/GSDrawFunctions.h b/Headers/Additions/GNUstepGUI/GSDrawFunctions.h index 0fdf9384c..19f7d46b4 100644 --- a/Headers/Additions/GNUstepGUI/GSDrawFunctions.h +++ b/Headers/Additions/GNUstepGUI/GSDrawFunctions.h @@ -2,9 +2,10 @@ Useful/configurable drawing functions - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004-2006 Free Software Foundation, Inc. Author: Adam Fedor + Author: Richard Frith-Macdonald Date: Jan 2004 This file is part of the GNU Objective C User interface library. @@ -21,7 +22,8 @@ You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02111 USA. */ #ifndef _GNUstep_H_GSDrawFunctions @@ -31,39 +33,277 @@ // For gradient types #include "AppKit/NSButtonCell.h" +@class NSBundle; @class NSColor; +@class GSDrawTiles; +/** + * This defines how the center middle image in a tile array should be + * used when drawing a rectangle. + */ +typedef enum { + FillStyleNone, /** The image is not drawn */ + FillStyleScale, /** The image is scaled to fit */ + FillStyleRepeat, /** The image is tiled from bottom left */ + FillStyleCenter /** The image is tiled from the center */ +} GSDrawFunctionsFillStyle; + + +/** Notification sent when a theme has just become active. + */ +APPKIT_EXPORT NSString *GSThemeDidActivateNotification; + +/** Notification sent when a theme has become inactive. + */ +APPKIT_EXPORT NSString *GSThemeDidDeactivateNotification; + + +/** +

This interface is HIGHLY unstable + and incomplete at present. +

+

+ This is a class used for 'theming', which is mostly a matter of + encapsulating common drawing behaviors so that GUI appearance can + be easily modified, but also includes mechanisms for altering + some GUI behavior (such mas orientation and position of menus). +

+

+ Methods in this class standardize drawing of buttons, borders + and other common GUI elements, so that all other classes within + the GUI will provide a consistent appearance by using these + methods. +

+

+ The default implementation uses the standard configurable colors + defined in NSColor, such as controlLightHighlightColor, + controlShadowColor and controlDarkShadowColor.
+ Themes are expected to override the default system color list with their + own versions, and this class cooperates with [NSColor] and [NSColorList] + to establish the correct system color list when a theme is activated. +

+

+ The class provides a mechanism for automatic loading of theme bundles + consisting of resources used to define how drawing is done, plus an + optional binary subclass of this class (to replace/extend the drawing + methods this class provides). +

+

+ In future this class should provide mechanisms to draw controls by + tiling of images, and provide control over GUI behavior by controlling + the values returned by NSInterfaceStyleForKey() so that controls + use the appropriate behavior. +

+*/ @interface GSDrawFunctions : NSObject +{ +@private + NSBundle *_bundle; + NSMutableArray *_images; + NSMutableDictionary *_tiles; +} -+ (id) theme; -+ (void) setTheme: (id)theme; +/** + * Set the currently active theme to be the instance specified.
+ * You do not normally need to call this method as it is called + * automatically when the user default which specifies the current + * theme (GSTheme) is updated. + */ ++ (void) setTheme: (GSDrawFunctions*)theme; -+ (NSRect) drawButton: (NSRect)border : (NSRect)clip; -+ (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip; -+ (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip; -+ (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip; -+ (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip; -+ (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip; -+ (NSRect) drawGroove: (NSRect)border : (NSRect)clip; -+ (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip; +/** + * Returns the currently active theme instance. This is the value most + * recently set using +setTheme: or (if none has been set) is a default + * instance of the base class. + */ ++ (GSDrawFunctions*) theme; -+ (NSRect) drawGradientBorder: (NSGradientType)gradientType - inRect: (NSRect)border - withClip: (NSRect)clip; +/** + *

This method is called automatically when the receiver is made into + * the currently active theme by the +setTheme: method. Subclasses may + * override it to perform startup operations, but should call the super + * class implementation before doing their own thing. + *

+ *

The base implementation handles setup and caching of certain image + * information and then sends a GSThemeDidActivateNotification to allow + * other parts of the GUI library to update themselves from the new theme.
+ * If the theme sets an alternative system color list, the notification + * userInfo dictionary will contain that list keyed on Colors. + *

+ */ +- (void) activate; -- (NSRect) drawButton: (NSRect)border : (NSRect)clip; -- (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip; -- (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip; -- (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip; -- (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip; -- (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip; -- (NSRect) drawGroove: (NSRect)border : (NSRect)clip; -- (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip; +/** + * Return the bundle containing the resources used by the current theme. + */ +- (NSBundle*) bundle; -- (NSRect) drawGradientBorder: (NSGradientType)gradientType - inRect: (NSRect)border - withClip: (NSRect)clip; +/** + *

This method is called automatically when the receiver is stopped from + * being the currently active theme by the use of the +setTheme: method + * to make another theme active. Subclasses may override it to perform + * shutdown operations, but should call the super class implementation + * after their own. + *

+ *

The base implementation handles some cleanup and then sends a + * GSThemeDidDeactivateNotification to allow other parts of the GUI library + * to update themselves. + *

+ */ +- (void) deactivate; + +/** + * Initialise an instance of a theme with the specified resource bundle.
+ * You don't need to call this method directly, but if you are subclassing + * you may need to override this to provide additional initialisation. + */ +- (id) initWithBundle: (NSBundle*)bundle; + +/** + * Returns the tile image information for a particular image name, + * or nil if there is no such information.
+ * The GUI library uses this internally to handling tiling of image + * information to draw user interface elements. The tile information + * returned by this method can be passed to the + * -fillRect:withTiles:background:fillStyle: method. + */ +- (GSDrawTiles*) tilesNamed: (NSString*)aName; +@end + +/** + * Theme drawing methods + */ +@interface GSDrawFunctions (Drawing) + +/** + * Draws a button frame and background (not its content) for the specified + * cell and view.
+ * Returns the rectangle into which the cell contents should be drawn. + */ +- (NSRect) drawButton: (NSRect)frame + in: (NSButtonCell*)cell + view: (NSView*)view + style: (int)style + state: (int)state; + +/** Draws the indicator (normally a dotted rectangle) to show that + * the view currently has keyboard focus. + */ +- (void) drawFocusFrame: (NSRect)frame view: (NSView*)view; + +/** + * Draws the background of a window ... normally a simple fill with the + * the window's background color. + */ +- (void) drawWindowBackground: (NSRect)frame view: (NSView*)view; @end +/** + * Helper functions for drawing standard items. + */ +@interface GSDrawFunctions (MidLevelDrawing) +/** Draw a standard button */ +- (NSRect) drawButton: (NSRect)border withClip: (NSRect)clip; + +/** Draw a dark bezel border */ +- (NSRect) drawDarkBezel: (NSRect)border withClip: (NSRect)clip; + +/** Draw a "dark" button border (used in tableviews) */ +- (NSRect) drawDarkButton: (NSRect)border withClip: (NSRect)clip; + +/** Draw a frame photo border. Used in NSImageView. */ +- (NSRect) drawFramePhoto: (NSRect)border withClip: (NSRect)clip; + +/** Draw a gradient border. */ +- (NSRect) drawGradientBorder: (NSGradientType)gradientType + inRect: (NSRect)border + withClip: (NSRect)clip; + +/** Draw a grey bezel border */ +- (NSRect) drawGrayBezel: (NSRect)border withClip: (NSRect)clip; + +/** Draw a groove border */ +- (NSRect) drawGroove: (NSRect)border withClip: (NSRect)clip; + +/** Draw a light bezel border */ +- (NSRect) drawLightBezel: (NSRect)border withClip: (NSRect)clip; + +/** Draw a white bezel border */ +- (NSRect) drawWhiteBezel: (NSRect)border withClip: (NSRect)clip; + +@end + +/** + * Low level drawiong methods ... themes may use these for drawing, + * but should not normally override them. + */ +@interface GSDrawFunctions (LowLevelDrawing) +/** + * Method to tile the supplied image to fill the horizontal rectangle. + */ +- (void) fillHorizontalRect: (NSRect)rect + withImage: (NSImage*)image + fromRect: (NSRect)source + flipped: (BOOL)flipped; + +/** + * Tile rect with image. The tiling starts with the origin of the + * first copy of the image at the bottom left corner of the rect + * unless center is YES, in which case the image is centered in rect + * and tiled outwards from that. + */ +- (void) fillRect: (NSRect)rect +withRepeatedImage: (NSImage*)image + fromRect: (NSRect)source + center: (BOOL)center; + +/** + * Method to tile a rectangle given an array of nine tile images.
+ * This draws the left, right, top and bottom borders by tiling the + * images at TileCL, TileCR, TileTM and TileBM respectively. It then + * draws the four corner images and finally deals with the remaining + * space in the middle according to the specified style.
+ * The background color specified is used where style is FillStyleNone. + */ +- (void) fillRect: (NSRect)rect + withTiles: (GSDrawTiles*)tiles + background: (NSColor*)color + fillStyle: (GSDrawFunctionsFillStyle)style; + +/** + * Method to tile the supplied image to fill the vertical rectangle. + */ +- (void) fillVerticalRect: (NSRect)rect + withImage: (NSImage*)image + fromRect: (NSRect)source + flipped: (BOOL)flipped; +@end + + +/** + * Deprecated methods ... do not use + */ +@interface GSDrawFunctions (deprecated) ++ (NSRect) drawButton: (NSRect)border : (NSRect)clip; ++ (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip; ++ (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip; ++ (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip; ++ (NSRect) drawGradientBorder: (NSGradientType)gradientType + inRect: (NSRect)border + withClip: (NSRect)clip; ++ (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip; ++ (NSRect) drawGroove: (NSRect)border : (NSRect)clip; ++ (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip; ++ (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip; +- (NSRect) drawButton: (NSRect)border : (NSRect)clip; +- (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip; +- (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip; +- (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip; +- (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip; +- (NSRect) drawGroove: (NSRect)border : (NSRect)clip; +- (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip; +- (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip; +@end + #endif /* _GNUstep_H_GSDrawFunctions */ diff --git a/Headers/AppKit/NSWindow.h b/Headers/AppKit/NSWindow.h index 34aa63323..123bcb637 100644 --- a/Headers/AppKit/NSWindow.h +++ b/Headers/AppKit/NSWindow.h @@ -111,7 +111,7 @@ APPKIT_EXPORT NSSize NSTokenSize; *

A window has a frame. This is the frame of the entire * window on the screen, including all decorations and borders. The origin * of the frame represents its bottom left corner and the frame is expressed - * in screen coordinates (see [NSScreen]).
+ * in screen coordinates (see [NSScreen]). *

*

When a window is created, it has a private [NSView] instance * which fills the entire window frame and whose coordinate system is the @@ -122,7 +122,7 @@ APPKIT_EXPORT NSSize NSTokenSize; * draw window decorations if the backend library is not handling the * window decorations. *

- *

A window always contains a content view which is the highest + *

A window always contains a content view which is the highest * level view available for public (application) use. This view fills the * area of the window inside any decoration/border.
* This is the only part of the window that application programmers are diff --git a/Source/DocMakefile b/Source/DocMakefile index e99603f82..249ab28ae 100644 --- a/Source/DocMakefile +++ b/Source/DocMakefile @@ -19,7 +19,8 @@ # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free -# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02111 USA # MAKEFILE_NAME = DocMakefile @@ -147,6 +148,7 @@ NSWorkspace.h AUTOGSDOC_HEADERS_GUIADD = \ GSDisplayServer.h \ +GSDrawFunctions.h \ GSFusedSilica.h \ GSTable.h \ GSHbox.h \ diff --git a/Source/GSDrawFunctions.m b/Source/GSDrawFunctions.m index c558514b0..706bc3489 100644 --- a/Source/GSDrawFunctions.m +++ b/Source/GSDrawFunctions.m @@ -21,112 +21,441 @@ You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02111 USA. */ +#include "Foundation/NSBundle.h" +#include "Foundation/NSDictionary.h" +#include "Foundation/NSFileManager.h" +#include "Foundation/NSNotification.h" +#include "Foundation/NSNull.h" +#include "Foundation/NSPathUtilities.h" +#include "Foundation/NSUserDefaults.h" #include "GNUstepGUI/GSDrawFunctions.h" #include "AppKit/NSColor.h" +#include "AppKit/NSColorList.h" #include "AppKit/NSGraphics.h" +#include "AppKit/NSImage.h" #include "AppKit/NSView.h" +#include "AppKit/NSBezierPath.h" #include "AppKit/PSOperators.h" +NSString *GSThemeDidActivateNotification + = @"GSThemeDidActivateNotification"; +NSString *GSThemeDidDeactivateNotification + = @"GSThemeDidDeactivateNotification"; + +/** These are the nine types of tile used to draw a rectangular object. + */ +typedef enum { + TileTL = 0, /** Top left corner */ + TileTM = 1, /** Top middle section */ + TileTR = 2, /** Top right corner */ + TileCL = 3, /** Centerj left corner */ + TileCM = 4, /** Centerj middle section */ + TileCR = 5, /** Centerj right corner */ + TileBL = 6, /** Bottom left corner */ + TileBM = 7, /** Bottom middle section */ + TileBR = 8 /** Bottom right corner */ +} GSDrawFunctionsTileOffset; + +/** This is a trivial class to hold the nine tiles needed to draw a rectangle + */ +@interface GSDrawTiles : NSObject +{ +@public + NSImage *images[9]; /** The tile images */ + NSRect rects[9]; /** The rectangles to use when drawing */ +} +- (id) initWithImage: (NSImage*)image; +@end + +@implementation GSDrawTiles +- (void) dealloc +{ + unsigned i; + + for (i = 0; i < 9; i++) + { + RELEASE(images[i]); + } + [super dealloc]; +} /** - - Class Description -

- This is a simple class used for encapsulating common drawing behaviors. - These methods standardize drawing of buttons, borders and other common - GUI elements. The drawing functions are encapsulated in a class to - allow overriding of the methods so that these elements can be drawn - in different ways (e.g. with themes). -

-

- The default implementation uses the standard configurable colors defined in - NSColor, such as controlLightHighlightColor, - controlShadowColor and controlDarkShadowColor. -

- -*/ + * Simple initialiser, assume the single image is split into nine equal tiles. + * If the image size is not divisible by three, the corners are made equal + * in size and the central parts slightly smaller. + */ +- (id) initWithImage: (NSImage*)image +{ + unsigned i; + unsigned j; + NSSize s = [image size]; + + for (i = 0; i < 9; i++) + { + images[i] = RETAIN(image); + } + i = s.width / 3; + j = s.height / 3; + rects[TileTL] = NSMakeRect(0.0, s.height - j, i, j); + rects[TileTM] = NSMakeRect(i, s.height - j, s.width - 2 * i, j); + rects[TileTR] = NSMakeRect(s.width - i, s.height - j, i, j); + rects[TileCL] = NSMakeRect(0.0, j, i, s.height - 2 * j); + rects[TileCM] = NSMakeRect(i, j, s.width - 2 * i, s.height - 2 * j); + rects[TileCR] = NSMakeRect(s.width - i, j, i, s.height - 2 * j); + rects[TileBL] = NSMakeRect(0.0, 0.0, i, j); + rects[TileBM] = NSMakeRect(i, 0.0, s.width - 2 * i, j); + rects[TileBR] = NSMakeRect(s.width - i, 0.0, i, j); + return self; +} +@end + +@interface GSDrawFunctions (internal) +/** + * Called whenever user defaults are changed ... this checks for the + * GSTheme user default and ensures that the specified theme is the + * current active theme. + */ ++ (void) defaultsDidChange: (NSNotification*)n; + +/** + * Called to load and make active the specified theme.
+ * If aName is nil or an empty string, this reverts to the default theme.
+ * If the named theme is already active, this has no effect.
+ * Returns YES on success, NO if the theme could not be loaded. + */ ++ (BOOL) loadThemeNamed: (NSString*)aName; +@end + + @implementation GSDrawFunctions -static id theTheme = nil; +static GSDrawFunctions *defaultTheme = nil; +static GSDrawFunctions *theTheme = nil; +static NSString *theThemeName = nil; +static NSMutableDictionary *themes = nil; +static NSNull *null = nil; - -+ (id) theme ++ (void) defaultsDidChange: (NSNotification*)n { - if (theTheme == nil) + NSUserDefaults *defs; + NSString *name; + + defs = [NSUserDefaults standardUserDefaults]; + name = [defs stringForKey: @"GSTheme"]; + if (name != theThemeName && [name isEqual: theThemeName] == NO) { - theTheme = [self new]; + [self loadThemeNamed: name]; } +} + ++ (void) initialize +{ + if (themes == nil) + { + themes = [NSMutableDictionary new]; + [self theme]; // Initialise/create the default theme + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(defaultsDidChange:) + name: NSUserDefaultsDidChangeNotification + object: nil]; + } + if (null == nil) + { + null = RETAIN([NSNull null]); + } + if (defaultTheme == nil) + { + NSBundle *aBundle = [NSBundle bundleForClass: self]; + + defaultTheme = [[self alloc] initWithBundle: aBundle]; + ASSIGN(theTheme, defaultTheme); + } +} + ++ (BOOL) loadThemeNamed: (NSString*)aName +{ + NSBundle *bundle; + Class cls; + GSDrawFunctions *instance; + NSString *theme; + + if ([aName length] == 0) + { + [self setTheme: nil]; + [self theme]; + return YES; + } + + /* Ensure that the theme name does not contain path components + * and has the 'theme' extension. + */ + aName = [aName lastPathComponent]; + if ([[aName pathExtension] isEqualToString: @"theme"] == YES) + { + theme = aName; + } + else + { + theme = [aName stringByAppendingPathExtension: @"theme"]; + } + + bundle = [themes objectForKey: theme]; + if (bundle == nil) + { + NSString *path; + NSEnumerator *enumerator; + NSFileManager *mgr = [NSFileManager defaultManager]; + + enumerator = [NSSearchPathForDirectoriesInDomains + (NSAllLibrariesDirectory, NSAllDomainsMask, YES) objectEnumerator]; + while ((path = [enumerator nextObject]) != nil) + { + BOOL isDir; + + path = [path stringByAppendingPathComponent: @"Themes"]; + path = [path stringByAppendingPathComponent: theme]; + if ([mgr fileExistsAtPath: path isDirectory: &isDir]) + { + break; + } + } + + if (path == nil) + { + NSLog (@"No theme named '%@' found", aName); + return NO; + } + else + { + bundle = [NSBundle bundleWithPath: path]; + [themes setObject: bundle forKey: theme]; + [bundle load]; // Ensure code is loaded. + } + } + + cls = [bundle principalClass]; + if (cls == 0) + { + cls = self; + } + instance = [[cls alloc] initWithBundle: bundle]; + [self setTheme: instance]; + RELEASE(instance); + return YES; +} + ++ (void) setTheme: (GSDrawFunctions*)theme +{ + if (theme == nil) + { + theme = defaultTheme; + } + if (theme != theTheme) + { + [theTheme deactivate]; + ASSIGN (theTheme, theme); + [theTheme activate]; + } +} + ++ (GSDrawFunctions*) theme +{ return theTheme; } -+ (void) setTheme: (id) aTheme + +- (void) activate { - ASSIGN (theTheme, aTheme); + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + NSArray *imagePaths; + NSEnumerator *enumerator; + NSString *imagePath; + NSString *colorsPath; + + colorsPath = [_bundle pathForResource: @"ThemeColors" ofType: @"clr"]; + if (colorsPath != nil) + { + NSColorList *list = nil; + + list = [[NSColorList alloc] initWithName: @"System" + fromFile: colorsPath]; + if (list != nil) + { + [userInfo setObject: list forKey: @"Colors"]; + RELEASE(list); + } + } + + /* + * We step through all the bundle image resources and load them in + * to memory, setting their names so that they are visible to + * [NSImage+imageNamed:] and storing them in our local array. + */ + imagePaths = [_bundle pathsForResourcesOfType: nil + inDirectory: @"ThemeImages"]; + enumerator = [imagePaths objectEnumerator]; + while ((imagePath = [enumerator nextObject]) != nil) + { + NSImage *image; + + image = [[NSImage alloc] initWithContentsOfFile: imagePath]; + if (image != nil) + { + NSString *imageName; + + imageName = [imagePath lastPathComponent]; + imageName = [imageName stringByDeletingPathExtension]; + [_images addObject: image]; + [image setName: imageName]; + RELEASE(image); + } + } + + [[NSNotificationCenter defaultCenter] + postNotificationName: GSThemeDidActivateNotification + object: self + userInfo: userInfo]; } -/** Tell current theme to draw a button border */ -+ (NSRect) drawButton: (NSRect)border : (NSRect)clip +- (NSBundle*) bundle { - return [[self theme] drawButton: border : clip]; + return _bundle; } -/** Tell current theme to draw a "dark" button border (used in tableviews) */ -+ (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip +- (void) deactivate { - return [[self theme] drawDarkButton: border : clip]; + NSEnumerator *enumerator; + NSImage *image; + + /* + * Remove all cached bundle images from both NSImage's name dictionary + * and our cache array. + */ + enumerator = [_images objectEnumerator]; + while ((image = [enumerator nextObject]) != nil) + { + [image setName: nil]; + } + [_images removeAllObjects]; + + [[NSNotificationCenter defaultCenter] + postNotificationName: GSThemeDidDeactivateNotification + object: self + userInfo: nil]; + } -/** Tell current theme to draw a dark bezel border */ -+ (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip +- (void) dealloc { - return [[self theme] drawDarkBezel: border : clip]; + RELEASE(_bundle); + RELEASE(_images); + RELEASE(_tiles); + [super dealloc]; } -/** Tell current theme to draw a light bezel border */ -+ (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip +- (id) initWithBundle: (NSBundle*)bundle { - return [[self theme] drawLightBezel: border : clip]; + ASSIGN(_bundle, bundle); + _images = [NSMutableArray new]; + _tiles = [NSMutableDictionary new]; + return self; } -/** Tell current theme to draw a white bezel border */ -+ (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip +- (GSDrawTiles*) tilesNamed: (NSString*)aName { - return [[self theme] drawWhiteBezel: border : clip]; + GSDrawTiles *tiles = [_tiles objectForKey: aName]; + + if (tiles == nil) + { + NSImage *image = [NSImage imageNamed: aName]; + + if (image != nil) + { + tiles = [[GSDrawTiles alloc] initWithImage: image]; + } + else + { + tiles = RETAIN(null); + } + [_tiles setObject: tiles forKey: aName]; + RELEASE(_tiles); + } + if (tiles == (id)null) + { + tiles = nil; + } + return tiles; } -/** Tell current theme to draw a grey bezel border */ -+ (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip +@end + + +@implementation GSDrawFunctions (Drawing) + +- (NSRect) drawButton: (NSRect) frame + in: (NSButtonCell*) cell + view: (NSView*) view + style: (int) style + state: (int) state { - return [[self theme] drawGrayBezel: border : clip]; + /* computes the interior frame rect */ + + NSRect interiorFrame = [cell drawingRectForBounds: frame]; + + /* Draw the button background */ + + if (state == 0) /* default state, unpressed */ + { + [[NSColor controlBackgroundColor] set]; + NSRectFill(frame); + [GSDrawFunctions drawButton: frame : NSZeroRect]; + } + else if (state == 1) /* highlighted state */ + { + [[NSColor selectedControlColor] set]; + NSRectFill(frame); + [GSDrawFunctions drawGrayBezel: frame : NSZeroRect]; + } + else if (state == 2) /* pushed state */ + { + [[NSColor selectedControlColor] set]; + NSRectFill(frame); + [GSDrawFunctions drawGrayBezel: frame : NSZeroRect]; + interiorFrame + = NSOffsetRect(interiorFrame, 1.0, [view isFlipped] ? 1.0 : -1.0); + } + + /* returns the interior frame rect */ + + return interiorFrame; } -/** Tell current theme to draw a groove border */ -+ (NSRect) drawGroove: (NSRect)border : (NSRect)clip +- (void) drawFocusFrame: (NSRect) frame view: (NSView*) view { - return [[self theme] drawGroove: border : clip]; + NSDottedFrameRect(frame); } -/** Tell current theme to draw a frame photo border. Used in NSImageView. */ -+ (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip +- (void) drawWindowBackground: (NSRect) frame view: (NSView*) view { - return [[self theme] drawFramePhoto: border : clip]; + NSColor *c; + + c = [[view window] backgroundColor]; + [c set]; + NSRectFill (frame); } -/** Tell current theme to draw a gradient border. */ -+ (NSRect) drawGradientBorder: (NSGradientType)gradientType - inRect: (NSRect)border - withClip: (NSRect)clip -{ - return [[self theme] drawGradientBorder: gradientType - inRect: border - withClip: clip]; -} +@end + -- (NSRect) drawButton: (NSRect)border : (NSRect)clip +@implementation GSDrawFunctions (MidLevelDrawing) + +- (NSRect) drawButton: (NSRect)border withClip: (NSRect)clip { NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge, @@ -138,8 +467,7 @@ static id theTheme = nil; NSColor *black = [NSColor controlDarkShadowColor]; NSColor *dark = [NSColor controlShadowColor]; NSColor *white = [NSColor controlLightHighlightColor]; - NSColor *colors[] = {black, black, white, white, - dark, dark}; + NSColor *colors[] = {black, black, white, white, dark, dark}; if ([[NSView focusView] isFlipped] == YES) { @@ -151,30 +479,7 @@ static id theTheme = nil; } } -/** Draw a "dark" button border (used in tableviews) */ -- (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip -{ - NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, - NSMinXEdge, NSMaxYEdge}; - NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, - NSMinXEdge, NSMinYEdge}; - // These names are role names not the actual colours - NSColor *black = [NSColor controlDarkShadowColor]; - NSColor *white = [NSColor controlHighlightColor]; - NSColor *colors[] = {black, black, white, white}; - - if ([[NSView focusView] isFlipped] == YES) - { - return NSDrawColorTiledRects(border, clip, dn_sides, colors, 4); - } - else - { - return NSDrawColorTiledRects(border, clip, up_sides, colors, 4); - } -} - -/** Draw a dark bezel border */ -- (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip +- (NSRect) drawDarkBezel: (NSRect)border withClip: (NSRect)clip { NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge, NSMinXEdge, NSMaxYEdge, NSMaxXEdge, NSMinYEdge}; @@ -185,8 +490,7 @@ static id theTheme = nil; NSColor *dark = [NSColor controlShadowColor]; NSColor *light = [NSColor controlColor]; NSColor *white = [NSColor controlLightHighlightColor]; - NSColor *colors[] = {white, white, dark, dark, - black, black, light, light}; + NSColor *colors[] = {white, white, dark, dark, black, black, light, light}; NSRect rect; if ([[NSView focusView] isFlipped] == YES) @@ -208,113 +512,28 @@ static id theTheme = nil; return rect; } -/** Draw a light bezel border */ -- (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip +- (NSRect) drawDarkButton: (NSRect)border withClip: (NSRect)clip { - NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge, - NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge}; - NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge, - NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge}; - // These names are role names not the actual colours - NSColor *dark = [NSColor controlShadowColor]; - NSColor *light = [NSColor controlColor]; - NSColor *white = [NSColor controlLightHighlightColor]; - NSColor *colors[] = {white, white, dark, dark, - light, light, dark, dark}; - - if ([[NSView focusView] isFlipped] == YES) - { - return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8); - } - else - { - return NSDrawColorTiledRects(border, clip, up_sides, colors, 8); - } -} - -/** Draw a white bezel border */ -- (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip -{ - NSRectEdge up_sides[] = {NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge, - NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge}; - NSRectEdge dn_sides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge, - NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge}; - // These names are role names not the actual colours - NSColor *dark = [NSColor controlShadowColor]; - NSColor *light = [NSColor controlColor]; - NSColor *white = [NSColor controlLightHighlightColor]; - NSColor *colors[] = {dark, white, white, dark, - dark, light, light, dark}; - - if ([[NSView focusView] isFlipped] == YES) - { - return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8); - } - else - { - return NSDrawColorTiledRects(border, clip, up_sides, colors, 8); - } -} - -/** Draw a grey bezel border */ -- (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip -{ - NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge, - NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge}; - NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge, - NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge}; + NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, + NSMinXEdge, NSMaxYEdge}; + NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, + NSMinXEdge, NSMinYEdge}; // These names are role names not the actual colours NSColor *black = [NSColor controlDarkShadowColor]; - NSColor *dark = [NSColor controlShadowColor]; - NSColor *light = [NSColor controlColor]; - NSColor *white = [NSColor controlLightHighlightColor]; - NSColor *colors[] = {white, white, dark, dark, - light, light, black, black}; - NSRect rect; + NSColor *white = [NSColor controlHighlightColor]; + NSColor *colors[] = {black, black, white, white}; if ([[NSView focusView] isFlipped] == YES) { - rect = NSDrawColorTiledRects(border, clip, dn_sides, colors, 8); - [dark set]; - PSrectfill(NSMinX(border) + 1., NSMaxY(border) - 2., 1., 1.); - PSrectfill(NSMaxX(border) - 2., NSMinY(border) + 1., 1., 1.); + return NSDrawColorTiledRects(border, clip, dn_sides, colors, 4); } else { - rect = NSDrawColorTiledRects(border, clip, up_sides, colors, 8); - [dark set]; - PSrectfill(NSMinX(border) + 1., NSMinY(border) + 1., 1., 1.); - PSrectfill(NSMaxX(border) - 2., NSMaxY(border) - 2., 1., 1.); - } - return rect; -} - -/** Draw a groove border */ -- (NSRect) drawGroove: (NSRect)border : (NSRect)clip -{ - // go clockwise from the top twice -- makes the groove come out right - NSRectEdge up_sides[] = {NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge, - NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge}; - NSRectEdge dn_sides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge, - NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge}; - // These names are role names not the actual colours - NSColor *dark = [NSColor controlShadowColor]; - NSColor *white = [NSColor controlLightHighlightColor]; - NSColor *colors[] = {dark, white, white, dark, - white, dark, dark, white}; - - if ([[NSView focusView] isFlipped] == YES) - { - return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8); - } - else - { - return NSDrawColorTiledRects(border, clip, up_sides, colors, 8); + return NSDrawColorTiledRects(border, clip, up_sides, colors, 4); } } -/** Draw a frame photo border. Used in NSImageView. */ -- (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip +- (NSRect) drawFramePhoto: (NSRect)border withClip: (NSRect)clip { NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge, @@ -338,7 +557,6 @@ static id theTheme = nil; } } -/** Draw a gradient border. */ - (NSRect) drawGradientBorder: (NSGradientType)gradientType inRect: (NSRect)border withClip: (NSRect)clip @@ -351,14 +569,10 @@ static id theTheme = nil; NSColor *dark = [NSColor controlShadowColor]; NSColor *light = [NSColor controlColor]; NSColor **colors; - NSColor *concaveWeak[] = {dark, dark, - light, light}; - NSColor *concaveStrong[] = {black, black, - light, light}; - NSColor *convexWeak[] = {light, light, - dark, dark}; - NSColor *convexStrong[] = {light, light, - black, black}; + NSColor *concaveWeak[] = {dark, dark, light, light}; + NSColor *concaveStrong[] = {black, black, light, light}; + NSColor *convexWeak[] = {light, light, dark, dark}; + NSColor *convexStrong[] = {light, light, black, black}; NSRect rect; switch (gradientType) @@ -392,4 +606,500 @@ static id theTheme = nil; return rect; } +- (NSRect) drawGrayBezel: (NSRect)border withClip: (NSRect)clip +{ + NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge, + NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge}; + NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge, + NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge}; + // These names are role names not the actual colours + NSColor *black = [NSColor controlDarkShadowColor]; + NSColor *dark = [NSColor controlShadowColor]; + NSColor *light = [NSColor controlColor]; + NSColor *white = [NSColor controlLightHighlightColor]; + NSColor *colors[] = {white, white, dark, dark, + light, light, black, black}; + NSRect rect; + + if ([[NSView focusView] isFlipped] == YES) + { + rect = NSDrawColorTiledRects(border, clip, dn_sides, colors, 8); + [dark set]; + PSrectfill(NSMinX(border) + 1., NSMaxY(border) - 2., 1., 1.); + PSrectfill(NSMaxX(border) - 2., NSMinY(border) + 1., 1., 1.); + } + else + { + rect = NSDrawColorTiledRects(border, clip, up_sides, colors, 8); + [dark set]; + PSrectfill(NSMinX(border) + 1., NSMinY(border) + 1., 1., 1.); + PSrectfill(NSMaxX(border) - 2., NSMaxY(border) - 2., 1., 1.); + } + return rect; +} + +- (NSRect) drawGroove: (NSRect)border withClip: (NSRect)clip +{ + // go clockwise from the top twice -- makes the groove come out right + NSRectEdge up_sides[] = {NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge, + NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge}; + NSRectEdge dn_sides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge, + NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge}; + // These names are role names not the actual colours + NSColor *dark = [NSColor controlShadowColor]; + NSColor *white = [NSColor controlLightHighlightColor]; + NSColor *colors[] = {dark, white, white, dark, + white, dark, dark, white}; + + if ([[NSView focusView] isFlipped] == YES) + { + return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8); + } + else + { + return NSDrawColorTiledRects(border, clip, up_sides, colors, 8); + } +} + +- (NSRect) drawLightBezel: (NSRect)border withClip: (NSRect)clip +{ + NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge, + NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge}; + NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge, + NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge}; + // These names are role names not the actual colours + NSColor *dark = [NSColor controlShadowColor]; + NSColor *light = [NSColor controlColor]; + NSColor *white = [NSColor controlLightHighlightColor]; + NSColor *colors[] = {white, white, dark, dark, + light, light, dark, dark}; + + if ([[NSView focusView] isFlipped] == YES) + { + return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8); + } + else + { + return NSDrawColorTiledRects(border, clip, up_sides, colors, 8); + } +} + +- (NSRect) drawWhiteBezel: (NSRect)border withClip: (NSRect)clip +{ + NSRectEdge up_sides[] = {NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge, + NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge}; + NSRectEdge dn_sides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge, + NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge}; + // These names are role names not the actual colours + NSColor *dark = [NSColor controlShadowColor]; + NSColor *light = [NSColor controlColor]; + NSColor *white = [NSColor controlLightHighlightColor]; + NSColor *colors[] = {dark, white, white, dark, + dark, light, light, dark}; + + if ([[NSView focusView] isFlipped] == YES) + { + return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8); + } + else + { + return NSDrawColorTiledRects(border, clip, up_sides, colors, 8); + } +} + @end + + + +@implementation GSDrawFunctions (LowLevelDrawing) + +- (void) fillHorizontalRect: (NSRect)rect + withImage: (NSImage*)image + fromRect: (NSRect)source + flipped: (BOOL)flipped +{ + NSGraphicsContext *ctxt = GSCurrentContext(); + NSBezierPath *path; + unsigned repetitions; + unsigned count; + float y; + + DPSgsave (ctxt); + path = [NSBezierPath bezierPathWithRect: rect]; + [path addClip]; + repetitions = (rect.size.width / source.size.width) + 1; + y = rect.origin.y; + + if (flipped) y = rect.origin.y + rect.size.height; + + for (count = 0; count < repetitions; count++) + { + NSPoint p = NSMakePoint (rect.origin.x + count * source.size.width, y); + + [image compositeToPoint: p + fromRect: source + operation: NSCompositeSourceOver]; + } + DPSgrestore (ctxt); +} + +- (void) fillRect: (NSRect)rect +withRepeatedImage: (NSImage*)image + fromRect: (NSRect)source + center: (BOOL)center +{ + NSGraphicsContext *ctxt = GSCurrentContext (); + NSBezierPath *path; + NSSize size; + unsigned xrepetitions; + unsigned yrepetitions; + unsigned x; + unsigned y; + + DPSgsave (ctxt); + path = [NSBezierPath bezierPathWithRect: rect]; + [path addClip]; + size = [image size]; + xrepetitions = (rect.size.width / size.width) + 1; + yrepetitions = (rect.size.height / size.height) + 1; + + for (x = 0; x < xrepetitions; x++) + { + for (y = 0; y < yrepetitions; y++) + { + NSPoint p; + + p = NSMakePoint (rect.origin.x + x * size.width, + rect.origin.y + y * size.height); + [image compositeToPoint: p + fromRect: source + operation: NSCompositeSourceOver]; + } + } + DPSgrestore (ctxt); +} + +- (void) fillRect: (NSRect)rect + withTiles: (GSDrawTiles*)tiles + background: (NSColor*)color + fillStyle: (GSDrawFunctionsFillStyle)style +{ + NSGraphicsContext *ctxt = GSCurrentContext(); + NSSize tls = tiles->rects[TileTL].size; + NSSize tms = tiles->rects[TileTM].size; + NSSize trs = tiles->rects[TileTR].size; + NSSize cls = tiles->rects[TileCL].size; + NSSize crs = tiles->rects[TileCR].size; + NSSize bls = tiles->rects[TileBL].size; + NSSize bms = tiles->rects[TileBM].size; + NSSize brs = tiles->rects[TileBR].size; + NSRect inFill; + BOOL flipped = [[ctxt focusView] isFlipped]; + + if (color == nil) + { + [[NSColor redColor] set]; + } + else + { + [color set]; + } + NSRectFill(rect); + + if (flipped) + { + [self fillHorizontalRect: + NSMakeRect (rect.origin.x + bls.width, + rect.origin.y + rect.size.height - bms.height, + rect.size.width - bls.width - brs.width, + bms.height) + withImage: tiles->images[TileBM] + fromRect: tiles->rects[TileBM] + flipped: YES]; + [self fillHorizontalRect: + NSMakeRect (rect.origin.x + tls.width, + rect.origin.y, + rect.size.width - tls.width - trs.width, + tms.height) + withImage: tiles->images[TileTM] + fromRect: tiles->rects[TileTM] + flipped: YES]; + [self fillVerticalRect: + NSMakeRect (rect.origin.x, + rect.origin.y + bls.height, + cls.width, + rect.size.height - bls.height - tls.height) + withImage: tiles->images[TileCL] + fromRect: tiles->rects[TileCL] + flipped: NO]; + [self fillVerticalRect: + NSMakeRect (rect.origin.x + rect.size.width - crs.width, + rect.origin.y + brs.height, + crs.width, + rect.size.height - brs.height - trs.height) + withImage: tiles->images[TileCR] + fromRect: tiles->rects[TileCR] + flipped: NO]; + + [tiles->images[TileTL] compositeToPoint: + NSMakePoint (rect.origin.x, + rect.origin.y) + fromRect: tiles->rects[TileTL] + operation: NSCompositeSourceOver]; + [tiles->images[TileTR] compositeToPoint: + NSMakePoint (rect.origin.x + rect.size.width - tls.width, + rect.origin.y) + fromRect: tiles->rects[TileTR] + operation: NSCompositeSourceOver]; + [tiles->images[TileBL] compositeToPoint: + NSMakePoint (rect.origin.x, + rect.origin.y + rect.size.height - tls.height) + fromRect: tiles->rects[TileBL] + operation: NSCompositeSourceOver]; + [tiles->images[TileBR] compositeToPoint: + NSMakePoint (rect.origin.x + rect.size.width - brs.width, + rect.origin.y + rect.size.height - tls.height) + fromRect: tiles->rects[TileBR] + operation: NSCompositeSourceOver]; + + inFill = NSMakeRect (rect.origin.x +cls.width, + rect.origin.y + bms.height, + rect.size.width - cls.width - crs.width, + rect.size.height - bms.height - tms.height); + if (style == FillStyleCenter) + { + [self fillRect: inFill + withRepeatedImage: tiles->images[TileCM] + fromRect: tiles->rects[TileCM] + center: NO]; + } + else if (style == FillStyleRepeat) + { + [self fillRect: inFill + withRepeatedImage: tiles->images[TileCM] + fromRect: tiles->rects[TileCM] + center: NO]; + } + else if (style == FillStyleScale) + { + [tiles->images[TileCM] setScalesWhenResized: YES]; + [tiles->images[TileCM] setSize: inFill.size]; + [tiles->images[TileCM] compositeToPoint: inFill.origin + fromRect: tiles->rects[TileCM] + operation: NSCompositeSourceOver]; + } + } + else + { + [self fillHorizontalRect: + NSMakeRect( + rect.origin.x + tls.width, + rect.origin.y + rect.size.height - tms.height, + rect.size.width - bls.width - brs.width, + tms.height) + withImage: tiles->images[TileTM] + fromRect: tiles->rects[TileTM] + flipped: NO]; + [self fillHorizontalRect: + NSMakeRect( + rect.origin.x + bls.width, + rect.origin.y, + rect.size.width - bls.width - brs.width, + bms.height) + withImage: tiles->images[TileBM] + fromRect: tiles->rects[TileBM] + flipped: NO]; + [self fillVerticalRect: + NSMakeRect( + rect.origin.x, + rect.origin.y + bls.height, + cls.width, + rect.size.height - tls.height - bls.height) + withImage: tiles->images[TileCL] + fromRect: tiles->rects[TileCL] + flipped: NO]; + [self fillVerticalRect: + NSMakeRect( + rect.origin.x + rect.size.width - crs.width, + rect.origin.y + brs.height, + crs.width, + rect.size.height - trs.height - brs.height) + withImage: tiles->images[TileCR] + fromRect: tiles->rects[TileCR] + flipped: NO]; + + [tiles->images[TileTL] compositeToPoint: + NSMakePoint ( + rect.origin.x, + rect.origin.y + rect.size.height - tls.height) + fromRect: tiles->rects[TileTL] + operation: NSCompositeSourceOver]; + [tiles->images[TileTR] compositeToPoint: + NSMakePoint( + rect.origin.x + rect.size.width - trs.width, + rect.origin.y + rect.size.height - trs.height) + fromRect: tiles->rects[TileTR] + operation: NSCompositeSourceOver]; + [tiles->images[TileBL] compositeToPoint: + NSMakePoint( + rect.origin.x, + rect.origin.y) + fromRect: tiles->rects[TileBL] + operation: NSCompositeSourceOver]; + [tiles->images[TileBR] compositeToPoint: + NSMakePoint( + rect.origin.x + rect.size.width - brs.width, + rect.origin.y) + fromRect: tiles->rects[TileBR] + operation: NSCompositeSourceOver]; + + inFill = NSMakeRect (rect.origin.x +cls.width, + rect.origin.y + bms.height, + rect.size.width - cls.width - crs.width, + rect.size.height - bms.height - tms.height); + + if (style == FillStyleCenter) + { + [self fillRect: inFill + withRepeatedImage: tiles->images[TileCM] + fromRect: tiles->rects[TileCM] + center: NO]; + } + else if (style == FillStyleRepeat) + { + [self fillRect: inFill + withRepeatedImage: tiles->images[TileCM] + fromRect: tiles->rects[TileCM] + center: YES]; + } + else if (style == FillStyleScale) + { + [tiles->images[TileCM] setScalesWhenResized: YES]; + [tiles->images[TileCM] setSize: inFill.size]; + [tiles->images[TileCM] compositeToPoint: inFill.origin + fromRect: tiles->rects[TileCM] + operation: NSCompositeSourceOver]; + } + } +} + +- (void) fillVerticalRect: (NSRect)rect + withImage: (NSImage*)image + fromRect: (NSRect)source + flipped: (BOOL)flipped +{ + NSGraphicsContext *ctxt = GSCurrentContext(); + NSBezierPath *path; + unsigned repetitions; + unsigned count; + NSPoint p; + + DPSgsave (ctxt); + path = [NSBezierPath bezierPathWithRect: rect]; + [path addClip]; + repetitions = (rect.size.height / source.size.height) + 1; + + if (flipped) + { + for (count = 0; count < repetitions; count++) + { + p = NSMakePoint (rect.origin.x, + rect.origin.y + rect.size.height - count * source.size.height); + [image compositeToPoint: p + fromRect: source + operation: NSCompositeSourceOver]; + } + } + else + { + for (count = 0; count < repetitions; count++) + { + p = NSMakePoint (rect.origin.x, + rect.origin.y + count * source.size.height); + [image compositeToPoint: p + fromRect: source + operation: NSCompositeSourceOver]; + } + } + DPSgrestore (ctxt); +} + +@end + + + +@implementation GSDrawFunctions (deprecated) ++ (NSRect) drawButton: (NSRect)border : (NSRect)clip +{ + return [[self theme] drawButton: border : clip]; +} ++ (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip +{ + return [[self theme] drawDarkButton: border : clip]; +} ++ (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip +{ + return [[self theme] drawDarkBezel: border : clip]; +} ++ (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip +{ + return [[self theme] drawLightBezel: border : clip]; +} ++ (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip +{ + return [[self theme] drawWhiteBezel: border : clip]; +} ++ (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip +{ + return [[self theme] drawGrayBezel: border : clip]; +} ++ (NSRect) drawGroove: (NSRect)border : (NSRect)clip +{ + return [[self theme] drawGroove: border : clip]; +} ++ (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip +{ + return [[self theme] drawFramePhoto: border : clip]; +} ++ (NSRect) drawGradientBorder: (NSGradientType)gradientType + inRect: (NSRect)border + withClip: (NSRect)clip +{ + return [[self theme] drawGradientBorder: gradientType + inRect: border + withClip: clip]; +} +- (NSRect) drawButton: (NSRect)border : (NSRect)clip +{ + return [self drawButton: border withClip: clip]; +} +- (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip +{ + return [self drawDarkButton: border withClip: clip]; +} +- (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip +{ + return [self drawDarkBezel: border withClip: clip]; +} +- (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip +{ + return [self drawLightBezel: border withClip: clip]; +} +- (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip +{ + return [self drawWhiteBezel: border withClip: clip]; +} +- (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip +{ + return [self drawGrayBezel: border withClip: clip]; +} +- (NSRect) drawGroove: (NSRect)border : (NSRect)clip +{ + return [self drawGroove: border withClip: clip]; +} +- (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip +{ + return [self drawFramePhoto: border withClip: clip]; +} +@end + diff --git a/Source/GSTitleView.m b/Source/GSTitleView.m index 304dade63..d6f0b4260 100644 --- a/Source/GSTitleView.m +++ b/Source/GSTitleView.m @@ -195,6 +195,7 @@ - (void) drawRect: (NSRect)rect { + GSDrawFunctions *theme = [GSDrawFunctions theme]; NSRect workRect = [self bounds]; NSSize titleSize; NSRectEdge top_left[] = {NSMinXEdge, NSMaxYEdge}; @@ -213,7 +214,7 @@ // Rectangle 2 // Draw the title box's button. - [GSDrawFunctions drawButton: workRect :workRect]; + [theme drawButton: workRect withClip: workRect]; // Overdraw white top and left lines with light gray lines for window title workRect.origin.y += 1; diff --git a/Source/GSWindowDecorationView.m b/Source/GSWindowDecorationView.m index 391ed4de1..9096ac3fa 100644 --- a/Source/GSWindowDecorationView.m +++ b/Source/GSWindowDecorationView.m @@ -30,6 +30,7 @@ #include "AppKit/NSColor.h" #include "AppKit/NSWindow.h" #include "GNUstepGUI/GSDisplayServer.h" +#include "GNUstepGUI/GSDrawFunctions.h" struct NSWindow_struct @@ -215,12 +216,9 @@ struct NSWindow_struct - (void) drawRect: (NSRect)rect { - NSColor *c = [_window backgroundColor]; - if (NSIntersectsRect(rect, contentRect)) { - [c set]; - NSRectFill(contentRect); + [[GSDrawFunctions theme] drawWindowBackground: contentRect view: self]; } } diff --git a/Source/NSButtonCell.m b/Source/NSButtonCell.m index 41232b6cb..e172394f5 100644 --- a/Source/NSButtonCell.m +++ b/Source/NSButtonCell.m @@ -836,6 +836,9 @@ typedef struct _GSButtonCellFlags - (void) drawWithFrame: (NSRect)cellFrame inView: (NSView*)controlView { + unsigned mask; + int buttonState = 0; + // Save last view drawn to if (_control_view != controlView) _control_view = controlView; @@ -848,22 +851,59 @@ typedef struct _GSButtonCellFlags if (NSIsEmptyRect(cellFrame)) return; - // draw the border if needed - if ((_cell.is_bordered) && - (!_shows_border_only_while_mouse_inside || _mouse_inside)) + // set the mask + if (_cell.is_highlighted) { - // FIXME Should check the bezel and gradient style - if (_cell.is_highlighted && (_highlightsByMask & NSPushInCellMask)) + mask = _highlightsByMask; + if (_cell.state) { - [GSDrawFunctions drawGrayBezel: cellFrame : NSZeroRect]; - } - else + mask &= ~_showAltStateMask; + } + } + else if (_cell.state) + mask = _showAltStateMask; + else + mask = NSNoCellMask; + + /* Draw the cell's background color. + We draw when there is a border or when highlightsByMask + is NSChangeBackgroundCellMask or NSChangeGrayCellMask, + as required by our nextstep-like look and feel. */ + if (_cell.is_bordered + || (_highlightsByMask & NSChangeBackgroundCellMask) + || (_highlightsByMask & NSChangeGrayCellMask)) + { + /* Determine the background color. */ + if (mask & (NSChangeGrayCellMask | NSChangeBackgroundCellMask)) { - [GSDrawFunctions drawButton: cellFrame : NSZeroRect]; + buttonState = 1; /* highlighted state */ } } + /* Pushed in buttons contents are displaced to the bottom right 1px. */ + if (_cell.is_bordered && (mask & NSPushInCellMask)) + { + buttonState = 2; // pushed button + } + + // draw the border if needed + if ((_cell.is_bordered) + && (!_shows_border_only_while_mouse_inside || _mouse_inside)) + { + cellFrame = [[GSDrawFunctions theme] + drawButton: cellFrame in: self view: controlView + style: _bezel_style + state: buttonState]; + } + [self drawInteriorWithFrame: cellFrame inView: controlView]; + + // Draw first responder + if (_cell.shows_first_responder + && [[controlView window] firstResponder] == controlView) + { + [[GSDrawFunctions theme] drawFocusFrame: cellFrame view: controlView]; + } } - (void) drawGradientWithFrame: (NSRect)cellFrame inView: (NSView *)controlView @@ -884,47 +924,47 @@ typedef struct _GSButtonCellFlags switch (_gradient_type) { - case NSGradientNone: - return; - break; + case NSGradientNone: + return; + break; - case NSGradientConcaveWeak: - [gray getHue: &h saturation: &s brightness: &v alpha: &a]; - start_white = [lightGray brightnessComponent]; - end_white = [gray brightnessComponent]; - break; - - case NSGradientConvexWeak: - [darkGray getHue: &h saturation: &s brightness: &v alpha: &a]; - start_white = [gray brightnessComponent]; - end_white = [lightGray brightnessComponent]; - break; - - case NSGradientConcaveStrong: - [lightGray getHue: &h saturation: &s brightness: &v alpha: &a]; - start_white = [lightGray brightnessComponent]; - end_white = [darkGray brightnessComponent]; - break; - - case NSGradientConvexStrong: - [darkGray getHue: &h saturation: &s brightness: &v alpha: &a]; - start_white = [darkGray brightnessComponent]; - end_white = [lightGray brightnessComponent]; - break; + case NSGradientConcaveWeak: + [gray getHue: &h saturation: &s brightness: &v alpha: &a]; + start_white = [lightGray brightnessComponent]; + end_white = [gray brightnessComponent]; + break; + + case NSGradientConvexWeak: + [darkGray getHue: &h saturation: &s brightness: &v alpha: &a]; + start_white = [gray brightnessComponent]; + end_white = [lightGray brightnessComponent]; + break; + + case NSGradientConcaveStrong: + [lightGray getHue: &h saturation: &s brightness: &v alpha: &a]; + start_white = [lightGray brightnessComponent]; + end_white = [darkGray brightnessComponent]; + break; + + case NSGradientConvexStrong: + [darkGray getHue: &h saturation: &s brightness: &v alpha: &a]; + start_white = [darkGray brightnessComponent]; + end_white = [lightGray brightnessComponent]; + break; - default: - break; + default: + break; } white = start_white; - white_step = fabs(start_white - end_white)/ - (cellFrame.size.width + cellFrame.size.height); + white_step = fabs(start_white - end_white) + / (cellFrame.size.width + cellFrame.size.height); // Start from top left - p1 = NSMakePoint(cellFrame.origin.x, - cellFrame.size.height + cellFrame.origin.y); + p1 = NSMakePoint(cellFrame.origin.x, + cellFrame.size.height + cellFrame.origin.y); p2 = NSMakePoint(cellFrame.origin.x, - cellFrame.size.height + cellFrame.origin.y); + cellFrame.size.height + cellFrame.origin.y); // Move by Y while (p1.y > cellFrame.origin.y) @@ -974,7 +1014,6 @@ typedef struct _GSButtonCellFlags NSRect titleRect; NSSize imageSize = {0, 0}; NSSize titleSize = {0, 0}; - NSColor *backgroundColor = nil; BOOL flippedView = [controlView isFlipped]; NSCellImagePosition ipos = _cell.image_position; @@ -998,38 +1037,6 @@ typedef struct _GSButtonCellFlags else mask = NSNoCellMask; - /* Pushed in buttons contents are displaced to the bottom right 1px. */ - if (_cell.is_bordered && (mask & NSPushInCellMask)) - { - cellFrame = NSOffsetRect(cellFrame, 1., flippedView ? 1. : -1.); - } - - /* Draw the cell's background color. - We draw when there is a border or when highlightsByMask - is NSChangeBackgroundCellMask or NSChangeGrayCellMask, - as required by our nextstep-like look and feel. */ - if (_cell.is_bordered - || (_highlightsByMask & NSChangeBackgroundCellMask) - || (_highlightsByMask & NSChangeGrayCellMask)) - { - /* Determine the background color. */ - if (mask & (NSChangeGrayCellMask | NSChangeBackgroundCellMask)) - { - backgroundColor = [NSColor selectedControlColor]; - } - else if (_cell.is_bordered) - { - backgroundColor = [NSColor controlBackgroundColor]; - } - - if (backgroundColor != nil) - { - [backgroundColor set]; - NSRectFill (cellFrame); - } - - } - /* * Determine the image and the title that will be * displayed. If the NSContentsCellMask is set the @@ -1195,7 +1202,8 @@ typedef struct _GSButtonCellFlags imageRect.origin.x = cellFrame.origin.x; imageRect.origin.y = cellFrame.origin.y; imageRect.size.width = cellFrame.size.width; - imageRect.size.height = titleRect.origin.y - GSCellTextImageYDist - imageRect.origin.y; + imageRect.size.height + = titleRect.origin.y - GSCellTextImageYDist - imageRect.origin.y; if (_cell.is_bordered || _cell.is_bezeled) { @@ -1229,7 +1237,9 @@ typedef struct _GSButtonCellFlags // Draw image if (imageToDisplay != nil) { - [self _drawImage: imageToDisplay inFrame: imageRect isFlipped: flippedView]; + [self _drawImage: imageToDisplay + inFrame: imageRect + isFlipped: flippedView]; } // Draw title @@ -1237,13 +1247,6 @@ typedef struct _GSButtonCellFlags { [self _drawAttributedText: titleToDisplay inFrame: titleRect]; } - - // Draw first responder - if (_cell.shows_first_responder - && [[controlView window] firstResponder] == controlView) - { - NSDottedFrameRect(cellFrame); - } } - (NSSize) cellSize diff --git a/Source/NSColor.m b/Source/NSColor.m index a2c2e4e52..470c55d28 100644 --- a/Source/NSColor.m +++ b/Source/NSColor.m @@ -45,9 +45,19 @@ #include "AppKit/NSImage.h" #include "AppKit/NSGraphics.h" #include "AppKit/PSOperators.h" +#include "GNUstepGUI/GSDrawFunctions.h" static Class NSColorClass; +/* This interface must be provided in NSColorList to let us manage + * system colors. + */ +@interface NSColorList (GNUstepPrivate) ++ (void) _setDefaultSystemColorList: (NSColorList*)aList; ++ (void) _setThemeSystemColorList: (NSColorList*)aList; +@end + + @interface GSNamedColor : NSColor { NSString *_catalog_name; @@ -153,12 +163,14 @@ static Class NSColorClass; + (NSColor*) colorFromString: (NSString*)string; + (void) defaultsDidChange: (NSNotification*)notification; ++ (void) themeDidActivate: (NSNotification*)notification; @end // Class variables static BOOL gnustep_gui_ignores_alpha = YES; static NSColorList *systemColors = nil; +static NSColorList *defaultSystemColors = nil; static NSMutableDictionary *colorStrings = nil; static NSMutableDictionary *systemDict = nil; @@ -224,9 +236,11 @@ void initSystemColors(void) nil]; systemColors = [NSColorList colorListNamed: @"System"]; + defaultSystemColors = [[NSColorList alloc] initWithName: @"System"]; + [NSColorList _setDefaultSystemColorList: defaultSystemColors]; if (systemColors == nil) { - systemColors = [[NSColorList alloc] initWithName: @"System"]; + ASSIGN(systemColors, defaultSystemColors); } { @@ -240,24 +254,24 @@ void initSystemColors(void) while ((key = (NSString *)[enumerator nextObject])) { - NSString *aColorString; NSColor *color; - if ([systemColors colorWithKey: key]) - continue; + if ((color = [systemColors colorWithKey: key]) == nil) + { + NSString *aColorString; - aColorString = [colorStrings objectForKey: key]; - color = [NSColorClass colorFromString: aColorString]; + aColorString = [colorStrings objectForKey: key]; + color = [NSColorClass colorFromString: aColorString]; - NSCAssert1(color, @"couldn't get default system color %@", key); - - [systemColors setColor: color forKey: key]; - - changed = YES; + NSCAssert1(color, @"couldn't get default system color %@", key); + [systemColors setColor: color forKey: key]; + changed = YES; + } + if (defaultSystemColors != systemColors) + { + [defaultSystemColors setColor: color forKey: key]; + } } - - if (changed) - [systemColors writeToFile: nil]; } systemDict = [NSMutableDictionary new]; @@ -309,6 +323,12 @@ systemColorWithName(NSString *name) selector: @selector(defaultsDidChange:) name: NSUserDefaultsDidChangeNotification object: nil]; + // watch for themes which may provide new system color lists + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(themeDidActivate:) + name: GSThemeDidActivateNotification + object: nil]; } } @@ -1636,6 +1656,35 @@ systemColorWithName(NSString *name) } } +/* + * Handle activation of a new theme ... look for a 'System' color list + * in the theme bundle and use it instead of the default system color + * list if it is present. + */ ++ (void) themeDidActivate: (NSNotification*)notification +{ + NSDictionary *userInfo = [notification userInfo]; + NSColorList *list = [userInfo objectForKey: @"Colors"]; + + if (list == nil) + { + list = defaultSystemColors; + } + NSAssert([[list name] isEqual: @"System"], NSInvalidArgumentException); + [NSColorList _setThemeSystemColorList: list]; + + /* We always update the system dictionary and send a notification, since + * the theme may have gicen us a pre-existing color list, but have changed + * one or more of the colors in it. + */ + list = [NSColorList colorListNamed: @"System"]; + ASSIGN(systemColors, list); + [systemDict removeAllObjects]; +NSLog(@"Theme activation with control background %@", [self controlBackgroundColor]); + [[NSNotificationCenter defaultCenter] + postNotificationName: NSSystemColorsDidChangeNotification object: nil]; +} + @end diff --git a/Source/NSColorList.m b/Source/NSColorList.m index 4cdfddaf3..2d78b6dbb 100644 --- a/Source/NSColorList.m +++ b/Source/NSColorList.m @@ -29,6 +29,7 @@ #include "config.h" #include +#include #include #include #include @@ -43,82 +44,51 @@ #include "AppKit/NSColor.h" #include "AppKit/AppKitExceptions.h" -// The list of available color lists is created only once -- this has -// a drawback, that you need to restart your program to get the color -// lists read again. -static NSMutableArray *_gnustep_available_color_lists = nil; -static NSLock *_gnustep_color_list_lock = nil; +// The list of available color lists is cached and re-loaded only +// after a time. +static NSMutableArray *_availableColorLists = nil; +static NSLock *_colorListLock = nil; + +static NSColorList *defaultSystemColorList = nil; +static NSColorList *themeColorList = nil; + +@interface NSColorList (GNUstepPrivate) + +/* Loads the available color lists from standard directories.
+ * If called with a nil argument, this will check to see if the + * lists have already been loaded, and only load if they haven't been. + */ ++ (void) _loadAvailableColorLists: (NSNotification*)aNotification; + +/* Set the default system color list ... to be used if no system color + * list has been loaded from file. This should always be the last of + * the array of available color lists even though it has noit been + * written to file. + */ ++ (void) _setDefaultSystemColorList: (NSColorList*)aList; + +/* Set the theme system color list ... if this is not nil, it is placed + * at the start of the array of available lists and is used as the system + * color list. + */ ++ (void) _setThemeSystemColorList: (NSColorList*)aList; + +@end @implementation NSColorList // // Class methods // -+ (void)initialize ++ (void) initialize { if (self == [NSColorList class]) { [self setVersion: 2]; + _colorListLock = [NSRecursiveLock new]; } } -/* - * Private Method which loads the color lists. - * Invoke if _gnustep_available_color_lists == nil - * before any operation with that object or its lock. - * - * The aim is to defer reading the available color lists - * till we really need to, so that only programs which really use - * this feature get the overhead. - */ -+ (void) _loadAvailableColorLists -{ - NSString *dir; - NSString *file; - NSEnumerator *e; - NSFileManager *fm = [NSFileManager defaultManager]; - NSDirectoryEnumerator *de; - NSColorList *newList; - - // Create the global array of color lists - _gnustep_available_color_lists = [[NSMutableArray alloc] init]; - - /* - * Load color lists found in standard paths into the array - * FIXME: Check exactly where in the directory tree we should scan. - */ - e = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, - NSAllDomainsMask, YES) objectEnumerator]; - - while ((dir = (NSString *)[e nextObject])) - { - BOOL flag; - - dir = [dir stringByAppendingPathComponent: @"Colors"]; - if (![fm fileExistsAtPath: dir isDirectory: &flag] || !flag) - { - // Only process existing directories - continue; - } - - de = [fm enumeratorAtPath: dir]; - while ((file = [de nextObject])) - { - if ([[file pathExtension] isEqualToString: @"clr"]) - { - NSString *name = [file stringByDeletingPathExtension]; - newList = [[NSColorList alloc] initWithName: name - fromFile: [dir stringByAppendingPathComponent: file]]; - [_gnustep_available_color_lists addObject: newList]; - RELEASE(newList); - } - } - } - - // And create its access lock - _gnustep_color_list_lock = [[NSLock alloc] init]; -} - /* * Getting All Color Lists */ @@ -126,15 +96,11 @@ static NSLock *_gnustep_color_list_lock = nil; { NSArray *a; - if (_gnustep_available_color_lists == nil) - [NSColorList _loadAvailableColorLists]; - // Serialize access to color list - [_gnustep_color_list_lock lock]; - - a = [NSArray arrayWithArray: _gnustep_available_color_lists]; - - [_gnustep_color_list_lock unlock]; + [_colorListLock lock]; + [NSColorList _loadAvailableColorLists: nil]; + a = [NSArray arrayWithArray: _availableColorLists]; + [_colorListLock unlock]; return a; } @@ -146,31 +112,25 @@ static NSLock *_gnustep_color_list_lock = nil; { NSColorList *r; NSEnumerator *e; - BOOL found = NO; - - if (_gnustep_available_color_lists == nil) - [NSColorList _loadAvailableColorLists]; // Serialize access to color list - [_gnustep_color_list_lock lock]; + [_colorListLock lock]; - e = [_gnustep_available_color_lists objectEnumerator]; + [NSColorList _loadAvailableColorLists: nil]; + e = [_availableColorLists objectEnumerator]; - while ((r = (NSColorList *)[e nextObject])) + while ((r = (NSColorList *)[e nextObject]) != nil) { if ([[r name] isEqualToString: name]) { - found = YES; + RETAIN(r); break; } } - [_gnustep_color_list_lock unlock]; + [_colorListLock unlock]; - if (found) - return r; - else - return nil; + return AUTORELEASE(r); } @@ -371,6 +331,8 @@ static NSLock *_gnustep_color_list_lock = nil; key: (NSString *)key atIndex: (unsigned)location { + NSNotification *n; + if (_is_editable == NO) [NSException raise: NSColorListNotEditableException format: @"Color list cannot be edited\n"]; @@ -379,13 +341,20 @@ static NSLock *_gnustep_color_list_lock = nil; [_orderedColorKeys removeObject: key]; [_orderedColorKeys insertObject: key atIndex: location]; - [[NSNotificationCenter defaultCenter] - postNotificationName: NSColorListChangedNotification - object: self]; + n = [NSNotification notificationWithName: NSColorListChangedNotification + object: self + userInfo: nil]; + [[NSNotificationQueue defaultQueue] + enqueueNotification: n + postingStyle: NSPostASAP + coalesceMask: NSNotificationCoalescingOnSender + forModes: nil]; } - (void) removeColorWithKey: (NSString *)key { + NSNotification *n; + if (_is_editable == NO) [NSException raise: NSColorListNotEditableException format: @"Color list cannot be edited\n"]; @@ -393,14 +362,21 @@ static NSLock *_gnustep_color_list_lock = nil; [_colorDictionary removeObjectForKey: key]; [_orderedColorKeys removeObject: key]; - [[NSNotificationCenter defaultCenter] - postNotificationName: NSColorListChangedNotification - object: self]; + n = [NSNotification notificationWithName: NSColorListChangedNotification + object: self + userInfo: nil]; + [[NSNotificationQueue defaultQueue] + enqueueNotification: n + postingStyle: NSPostASAP + coalesceMask: NSNotificationCoalescingOnSender + forModes: nil]; } - (void) setColor: (NSColor *)aColor forKey: (NSString *)key { + NSNotification *n; + if (_is_editable == NO) [NSException raise: NSColorListNotEditableException format: @"Color list cannot be edited\n"]; @@ -410,9 +386,14 @@ static NSLock *_gnustep_color_list_lock = nil; if ([_orderedColorKeys containsObject: key] == NO) [_orderedColorKeys addObject: key]; - [[NSNotificationCenter defaultCenter] - postNotificationName: NSColorListChangedNotification - object: self]; + n = [NSNotification notificationWithName: NSColorListChangedNotification + object: self + userInfo: nil]; + [[NSNotificationQueue defaultQueue] + enqueueNotification: n + postingStyle: NSPostASAP + coalesceMask: NSNotificationCoalescingOnSender + forModes: nil]; } /* @@ -438,8 +419,7 @@ static NSLock *_gnustep_color_list_lock = nil; * We need to initialize before saving, to avoid the new file being * counted as a different list thus making us appear twice */ - if (_gnustep_available_color_lists == nil) - [NSColorList _loadAvailableColorLists]; + [NSColorList _loadAvailableColorLists: nil]; if (path == nil || ([fm fileExistsAtPath: path isDirectory: &isDir] == NO)) { @@ -513,10 +493,10 @@ static NSLock *_gnustep_color_list_lock = nil; if (success && path_is_standard) { - [_gnustep_color_list_lock lock]; - if ([_gnustep_available_color_lists containsObject: self] == NO) - [_gnustep_available_color_lists addObject: self]; - [_gnustep_color_list_lock unlock]; + [_colorListLock lock]; + if ([_availableColorLists containsObject: self] == NO) + [_availableColorLists addObject: self]; + [_colorListLock unlock]; return YES; } @@ -532,12 +512,9 @@ static NSLock *_gnustep_color_list_lock = nil; handler: nil]; // Remove the color list from the global list of colors - if (_gnustep_available_color_lists == nil) - [NSColorList _loadAvailableColorLists]; - - [_gnustep_color_list_lock lock]; - [_gnustep_available_color_lists removeObject: self]; - [_gnustep_color_list_lock unlock]; + [_colorListLock lock]; + [_availableColorLists removeObject: self]; + [_colorListLock unlock]; // Reset file name _fullFileName = nil; @@ -562,3 +539,117 @@ static NSLock *_gnustep_color_list_lock = nil; @end +@implementation NSColorList (GNUstepPrivate) + ++ (void) _loadAvailableColorLists: (NSNotification*)aNotification +{ + [_colorListLock lock]; + /* FIXME ... we should ensure that we get housekeeping notifications */ + if (_availableColorLists != nil && aNotification == nil) + { + // Nothing to do ... already loaded + [_colorListLock unlock]; + } + else + { + NSString *dir; + NSString *file; + NSEnumerator *e; + NSFileManager *fm = [NSFileManager defaultManager]; + NSDirectoryEnumerator *de; + NSColorList *newList; + + if (_availableColorLists == nil) + { + // Create the global array of color lists + _availableColorLists = [[NSMutableArray alloc] init]; + } + else + { + [_availableColorLists removeAllObjects]; + } + + /* + * Keep any pre-loaded system color list. + */ + if (themeColorList != nil) + { + [_availableColorLists addObject: themeColorList]; + } + + /* + * Load color lists found in standard paths into the array + * FIXME: Check exactly where in the directory tree we should scan. + */ + e = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, + NSAllDomainsMask, YES) objectEnumerator]; + + while ((dir = (NSString *)[e nextObject])) + { + BOOL flag; + + dir = [dir stringByAppendingPathComponent: @"Colors"]; + if (![fm fileExistsAtPath: dir isDirectory: &flag] || !flag) + { + // Only process existing directories + continue; + } + + de = [fm enumeratorAtPath: dir]; + while ((file = [de nextObject])) + { + if ([[file pathExtension] isEqualToString: @"clr"]) + { + NSString *name; + + name = [file stringByDeletingPathExtension]; + newList = [[NSColorList alloc] initWithName: name + fromFile: [dir stringByAppendingPathComponent: file]]; + [_availableColorLists addObject: newList]; + RELEASE(newList); + } + } + } + + if (defaultSystemColorList != nil) + { + [_availableColorLists addObject: defaultSystemColorList]; + } + [_colorListLock unlock]; + } +} + ++ (void) _setDefaultSystemColorList: (NSColorList*)aList +{ + [_colorListLock lock]; + if (defaultSystemColorList != aList) + { + if (defaultSystemColorList != nil + && [_availableColorLists lastObject] == defaultSystemColorList) + { + [_availableColorLists removeLastObject]; + } + ASSIGN(defaultSystemColorList, aList); + [_availableColorLists addObject: aList]; + } + [_colorListLock unlock]; +} + ++ (void) _setThemeSystemColorList: (NSColorList*)aList +{ + [_colorListLock lock]; + if (themeColorList != aList) + { + if (themeColorList != nil && [_availableColorLists count] > 0 + && [_availableColorLists objectAtIndex: 0] == themeColorList) + { + [_availableColorLists removeObjectAtIndex: 0]; + } + ASSIGN(themeColorList, aList); + [_availableColorLists insertObject: aList atIndex: 0]; + } + [_colorListLock unlock]; +} + +@end + diff --git a/Source/NSTabView.m b/Source/NSTabView.m index 7a9988941..fd9d7147a 100644 --- a/Source/NSTabView.m +++ b/Source/NSTabView.m @@ -365,6 +365,7 @@ - (void) drawRect: (NSRect)rect { NSGraphicsContext *ctxt = GSCurrentContext(); + GSDrawFunctions *theme = [GSDrawFunctions theme]; int howMany = [_items count]; int i; NSRect previousRect = NSMakeRect(0, 0, 0, 0); @@ -378,18 +379,18 @@ default: case NSTopTabsBezelBorder: aRect.size.height -= 16; - [GSDrawFunctions drawButton: aRect : NSZeroRect]; + [theme drawButton: aRect withClip: NSZeroRect]; break; case NSBottomTabsBezelBorder: aRect.size.height -= 16; aRect.origin.y += 16; - [GSDrawFunctions drawButton: aRect : rect]; + [theme drawButton: aRect withClip: rect]; aRect.origin.y -= 16; break; case NSNoTabsBezelBorder: - [GSDrawFunctions drawButton: aRect : rect]; + [theme drawButton: aRect withClip: rect]; break; case NSNoTabsLineBorder: