The icon lookup mechanism has two global settings, the list of base directories and the internal name of the current theme. Given these we need to specify how to look up an icon file from the icon name and the nominal size.
The lookup is done first in the current theme, and then recursively in each of the themes inherited the current theme, and finally in the default theme called "hicolor" (implementations may add more default themes before "hicolor", but "hicolor" must be last). As soon as there is an icon of any size that matches in a theme the search is stopped. Even if there may be an icon with a size closer to the correct one in an inherited theme we don't want to use it. Doing so may generate an inconsistant change in an icon when you change icon sizes (e.g. zoom in).
The lookup inside a theme is done in three phases. First all the directories are scanned for an exact match, e.g. one where the allowed size of the icon files match what was looked up. Then all the directories are scanned for any icon that matches the name. If that fails we finally fall back on unthemed icons. If we fail to find any icon at all it is up to the application to pick a good fallback, as the correct choice depends on the context.
The exact algorithm (in pseudocode) for looking up an icon in an implementation that supports SVG is:
LookupIcon (iconname, size):
for each subdir in $(theme subdir list) {
for each directory in $(basename list) {
for extension in ("png", "svg", "xpm") {
if DirectoryMatchesSize(subdir, size) {
filename = directory/$(themename)/subdir/iconname.extension
if exist filename
return filename
}
}
}
}
minimal_size = MAXINT
for each subdir in $(theme subdir list) {
for each directory in $(basename list) {
for extension in ("png", "svg", "xpm") {
filename = directory/$(themename)/subdir/iconname.extension
if exist filename and DirectorySizeDistance(subdir, size) < minimal_size
closest_filename = filename
minimal_size = DirectorySizeDistance(subdir, size)
}
}
}
if closest_filename set
return closest_filename
for each directory in $(basename list) {
for extension in ("png", "svg", "xpm") {
if exists directory/iconname.extension
return directory/iconname.extension
}
}
return failed icon lookup
With the following helper functions:
DirectoryMatchesSize(subdir, iconsize):
read Type and size data from subdir
if Type is Fixed
return Size == iconsize
if Type is Scaled
return MinSize <= iconsize <= MaxSize
if Type is Threshold
return Size - Threshold <= iconsize <= Size + Threshold
DirectorySizeDistance(subdir, size):
read Type and size data from subdir
if Type is Fixed
return abs(Size - iconsize)
if Type is Scaled
if iconsize < MinSize
return MinSize - iconsize
if iconsize > MaxSize
return iconsize - MaxSize
return 0
if Type is Threshold
if iconsize < Size - Threshold
return MinSize - iconsize
if iconsize > Size + Threshold
return iconsize - MaxSize
return 0