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: Alex Perez
Subject: Re: [RFC] [PATCH]: Icon themability patch for -GUI images/icons
Date: Sat, 30 Oct 2004 16:30:55 -0700
User-agent: Mozilla Thunderbird 0.8 (X11/20040913)

Quentin Mathé wrote:
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.
What do you propose to call the folder?
- a theme should be loaded only for to the user who chooses to use it
The same is true with icon loading with my patch.
- the icons themes and interface themes should use the same packaging
They are just bundles.....with Resources folders....
- the icons themes and interface themes should have the possibility to override specifics application
yes this is always necessary with apps like Gorm.

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.

Our patches address similar but different things. Everything you've written so far sounds logical, except for the "you never want to icon theme without an interface theme" which is an absolutely absurd statement, which both you and rIO made. I'm beginning to get the feeling that you feel I am encroaching on your turf.

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).
Per an IRC-discussion with Alexander Malmberg, I did express some concern about overriding the entire imageNamed class, because the performance hit of making it search through numerous other folders may or may not be negligible.


Well in the code below there is no support for nsmapping files direclty inside theme packages but it is something which can be added.
It would at least be a decent reason to keep NSMapping around, because it's used for more or less nothing as it stands right now.

***

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]