gnustep-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC] [PATCH]: Icon themability patch for -GUI images/icons


From: Quentin Mathé
Subject: Re: [RFC] [PATCH]: Icon themability patch for -GUI images/icons
Date: Sun, 31 Oct 2004 00:44:08 +0200

Le 30 oct. 04, à 15:35, Alex Perez a écrit :

This patch builds upon a previous non-behavior-changing patch which I wrote and Alexander Malmberg comitted this morning on my behalf. For some background:

NSWorkspace had a private convenience class called _getImageWithName: which, with the permission of Alexander Malmberg, we moved to NSImage and renamed to -standardImageWithName: and modified slightly so the alternate: argument was not necessary (it now intelligently looks for "ImageName" and then for "common_ImageName" (and anything else for that matter, like "camaelon_ImageName", if you override the method.) In any event, What this patch does is enable icon themability. You can have sets of icons which you can use by simply having a very small bundle override the -getImageWithName: method.

This patch builds upon the previous one, by making nearly every single image which was previously loaded directly via NSImage -imageNamed: "common_ImageName" load with the new convenience NSImage method.

Unless you want your own icon set, you will notice zero behavioral change.

Everyone, please comment on this...If I don't hear anything negative from anyone by tuesday or so of next week, I will commit it as-is. It works on my machine.

Alexander Malmberg, anything you feel you need to add?

Hi Alex,

it's nice to want to improve GNUstep theme support, but I must said I strongly disagree with your patch. - in my opinion, the themes support for icons or interface should not be supported at the GNUstep level but by a separate framework which can eventually be included in /gnustep/core and built when wanted. - trying to implement icons themes support without taking in account interface themes is a waste time because icons themes and interface themes have a lot in common, and they should be implemented with some coordination (although two frameworks can be used) - a correct icons themes support can be tricky to do especially if you consider the fallback issues or the file types relation (see the icons vs MIME discussion in the October 2004 FreeDesktop xdg list). - your patch reimplements something which already exists but in a way which is worst I think, because to create a theme now you need to write some code (I never saw a theme engine which involves such requirement) : nsmappings solution is way more easy (just a file to edit) and can eventually modified to support exactly what you doing in your code (with something like GSImagePatternName = "common_*" included at the beginning of the nsmapping file). Anyway I see with your solution that you want to allow with extra code what I would call a hack : the possibility to have theme icons anywhere on your computer, then why not install libraries in the same way… ;-) it is true that nsmapping doesn't permit that, but that doesn't mean that your solution is right…
- it gives no facility to support FreeDesktop icons themes.

Well, because you will probably ask what is my solution, here it is:

Icons themes implemented the right way should meet the following requirements : - a special folder should exist in ~/GNUstep/Library, /GNUstep/System/Library, /GNUstep/Local/Library, GNUstep/Network/Library where you can install themes in the same way you add fonts or whatever resources.
- a theme should be loaded only for to the user who chooses to use it
- the icons themes and interface themes should use the same packaging
- the icons themes and interface themes should have the possibility to override specifics application

An interface theme will be a collection of images used to draw the widgets (a pixmap theme if you want) and eventually icons, an icon theme would be an interface theme which contains just icons.

I propose to have the special folder called "Themes".
The theme you want to install should go directly in this folder.
Each theme is subdivided in a folder called "Icons" and a folder called "Interface" (for the images used by the widgets), the two folders "Icons" and "Interface" can also contain folders named by applications bundle identifiers or applications names to have the possibility to customize applications specific icons or even to have them use a special interface look. With defaults, you specify the themes you want to use with the following keys :
* User domain *
- IconThemeName
- InterfaceThemeName
* Application domain *
- IconThemeName
- InterfaceThemeName
In case of a theme "plop" containing interface and icons where you want both, you just set IconThemeName="plop" and InterfaceThemeName="plop".With this solution, you can mix the icons theme and the interface theme from two different theme packages, you can even override the icons theme and the interface theme for each application.

Now here is a basic implementation of it with a modified NSImage class in a way to make -imageNamed: more modular and easy to override, and a category to NSImage which would be included in the theme engine (like Camaelon or whatever).

Well in the code below there is no support for nsmapping files direclty inside theme packages but it is something which can be added.

***

The modified NSImage code :

+ (id) imageNamed: (NSString *)aName
{
  NSString      *realName = [nsmapping objectForKey: aName];
  NSImage       *image;

  if (realName)
    aName = realName;

  image = (NSImage*)[nameDict objectForKey: aName];

  if (image == nil)
    {
      NSString  *ext;
      NSString  *path = nil;
      NSBundle  *main_bundle;
      NSArray   *array;
      NSString  *the_name = aName;

// FIXME: This should use [NSBundle pathForImageResource], but this will
      // only allow imageUnfilteredFileTypes.
/* If there is no image with that name, search in the main bundle */
      main_bundle = [NSBundle mainBundle];
      ext = [aName pathExtension];
      if (ext != nil && [ext length] == 0)
        {
          ext = nil;
        }

      /* Check if extension is one of the image types */
      array = [self imageFileTypes];
      if ([array indexOfObject: ext] != NSNotFound)
        {
          /* Extension is one of the image types so remove from the name */
          the_name = [aName stringByDeletingPathExtension];
        }
      else
       {
          /* Otherwise extension is not an image type so leave it alone */
          the_name = aName;
          ext = nil;
        }

      // Now look for the images in the current application
      path = [self _pathForApplicationImageNamed: the_name];

      // Now in the system
      if (!path)
        {
          path = [self _pathForSystemImageNamed: the_name];
        }

      if ([path length] != 0)
        {
          image = [[self allocWithZone: NSDefaultMallocZone()]
                 initByReferencingFile: path];
          if (image != nil)
            {
              [image setName: aName];
              RELEASE(image);           // Retained in dictionary.
              image->_flags.archiveByName = YES;
            }
          return image;
        }
    }

  return image;
}

- (NSString *) _pathForApplicationImageNamed: (NSString *)name
{
  NSString *ext = [name pathExtension];
  NSString *path;
  NSBundle *main_bundle = [NSBundle mainBundle];

  if (ext)
    {
      path = [main_bundle pathForResource: name ofType: ext];
    }
  else
    {
      id o, e;

       e = [[self fileTypes] objectEnumerator];
       while ((o = [e nextObject]))
         {
           path = [main_bundle pathForResource: name
                                        ofType: o];
           if (path != nil && [path length] != 0)
             break;
         }
     }

  return path;
}

- (NSString *) _pathForSystemImageNamed: (NSString *)name
{
  NSString *ext = [name pathExtension];
  NSString *path;

  /* If not found then search in system */
  if (name)
    {
      if (ext)
        {
          path = [NSBundle pathForLibraryResource: the_name
                                           ofType: ext
                                                      inDirectory: @"Images"];
        }
  else
        {
          id o, e;

          e = [array objectEnumerator];
          while ((o = [e nextObject]))
            {
              path = [NSBundle pathForLibraryResource: the_name
                                                       ofType: o
                                                      inDirectory: @"Images"];
              if (path != nil && [path length] != 0)
                break;
            }
        }


   return path;
}

***

Now the method rewritten in the theme engine bundle :

NSString *ThemeIconResource = @"ThemeIconResource";
NSString *ThemeInterfaceResource = @"ThemeInterfaceResource";
NSString *ThemeNotDefinedResource = @"ThemeNotDefinedResource";

- (NSString *) _imageNameThemeResource: (NSString *)name
{
// iconResourceNames and interfaceResourceNames are set by loading a plist like file from // the system which describe if an image name (system defined) is associated to an icon
  // or an interface image.

  if ([iconResourceNames containsObject: name])
    {
      return ThemeIconResource;
    }
  else if ([interfaceResourceNames containsObject: name])
    {
      return ThemeInterfaceResource;
    }

  return ThemeNotDefinedResource;
}

- (NSString *) _pathForApplicationImageNamed: (NSString *)name
{
  NSBundle *main_bundle = [NSBundle mainBundle];
  NSString *path;

  // We try with the bundle identifier first
path = [self _pathForThemeImageNamed: name application: [main_bundle bundleIdentifier]];

  // If no result, we try with the application name
  if (path == nil)
    {
path = [self _pathThemeImageNamed: name application: [NSApp applicationName]];
    }

  return path;
}

- (NSString *) _pathForSystemImageNamed: (NSString *)name
{
  return [self _pathForThemeImageNamed: name application: nil];
}

- (NSString *) _pathForThemeImageNamed: (NSString *)name application: (BOOL)app
{
  NSUserDefaults = [NSUserDefaults standardUserDefaults];
  NSString *ext = [name pathExtension];
  NSString *path;
  NSString *themeResource;
  NSString *themeResourceDir;
  NSString *themeName;
  NSString *baseDir = @"Themes";

  if (app == nil)
    {
       themeResource = [self _imageNameThemeResource: name];
       if ([themeResource isEqualToString: ThemeNotDefinedResource])
         {
return nil; // May be we should look in the System Images directory
         }

       if ([themeResource isEqualToString: ThemeIconResource])
         {
           themeResourceDir = @"Icons";
         }
       else if ([themeResource isEqualToString: ThemeInterfaceResource])
        {
          themeResourceDir = @"Interface";
        }
    }
  else
    {
      themeResourceDir = @"Icons";
    }

  themeName = [userDefaults objectForKey: themeResource];
  if (app != nil)
    {
      NSString *applicationThemeName =
[userDefaults objectForKey: [app stringByAppendingString: @"ThemeIconResource"]];

      if (applicationThemeName != nil)
        themeName = applicationThemeName;
    }

path = [[baseDir stringByAppendingPathComponent: themeName] stringByAppendingPathComponent: themeResourceDir];
  if (app != nil)
    {
      // App is equal to the bundle identifier or the application name
      path = [path stringByAppendingPathComponent: app];
    }

  if (ext)
    {
      path = [NSBundle pathForLibraryResource: name
                                       ofType: ext
                                  inDirectory: path];
      if (path == nil)
        {
          path = [NSBundle pathForLibraryResource: name
                                           ofType: ext
                                      inDirectory: @"Images"];
        }
    }
  else
    {
      id o, e;
      NSArray *fileTypes = [self fileTypes];

       e = [filesTypes objectEnumerator];
       while ((o = [e nextObject]))
         {
           path = [NSBundle pathForLibraryResource: name
                                            ofType: o
                                       inDirectory: path];
           if (path != nil && [path length] != 0)
             break;
         }

       e = [filesTypes objectEnumerator];
       while ((o = [e nextObject]))
         {
           path = [NSBundle pathForLibraryResource: name
                                            ofType: o
                                       inDirectory: @"Images"];
           if (path != nil && [path length] != 0)
             break;
         }
    }

  return path;
}

--
Quentin Mathé
address@hidden




reply via email to

[Prev in Thread] Current Thread [Next in Thread]