============================================================= How resource tagging in Gimp works? ============================================================= GimpTagged Tagging is not limited to a concrete class hierarchy, but any class implementing GimpTagged interface can be tagged. In addition to methods for adding/removing/enumerating tags it also requires GimpTagged objects to identify themselves: * gimp_tagged_get_identifier: used to get a unique identifier of GimpTagged object. For objects which are stored in a file it will usually be a filename. * gimp_tagged_get_checksum: identifier mentioned above has a problem that it can change during sessions (for example, user moves or renames a resource file). Therefore, there needs to be a way to get other identifier from data of the tagged object, so that tags stored between session could be properly remapped. GimpTag Tags are represented by GimpTag object. There are no limitations for tags names except they cannot contain a selected set of terminal punctuations characters (used to separate tags), no whitespace at the end or front and cannot begin with a reserved prefix for internal tags ('gimp:'). These conditions are ensured when creating tag object from tag string. The only reason for tag creation to fail is when there are no characters left after applying trying to fix a tag according to the rules above. Tag names are displayed as user typed them (case sensitive), but tag comparing is done case insensitively. Tags are immutable, ie. when tag is created with one name string, it cannot be changed, but new tag has to be created instead. There are methods provided for convenient use with GLib: compare function which can be used to sort tag list and functions for storing tags in GHashTable. GimpTagCache Between sessions tags assigned to objects are stored in a cache file. Cache file is a simple XML file, which lists all resources and tags which are added to them. Resources which have no tags assigned are listed here too, so that when we check the cache we know that they have no tags assigned instead trying to find out if the resource file has been renamed. When session ends, list or all resources and tags they have assigned is constructed. Resources which were not loaded during this session, but had tags assigned are also added to the list (they are saved because they could be useful in the next session, for example, when temporarily disconnected network directory is reconnected). The list is then written to a tag cache file in user home directory. When session starts, previously saved resource and tag mapping has to be loaded and assigned to GimpTagged objects. First tag cache is loaded from file, and then containers are added (GimpContainer objects where contained items implement GimpTagged interface). After that, loaded resources are assigned tags: If resource identifier matches identifier in cache, corresponding tags are assigned to GimpTagged object. Else, if the identifier is not found in tag cache, attempt is made to check if resource file has been moved/renamed. In such case checksum is used to match the GimpTagged object with all of the records in tag cache. If match is found, identifier is updated in tag cache. Otherwise, loaded GimpTagged object is considered to be a newly added resource. GimpFilteredContainer GimpFilteredContainer is a "view" (representation) of GimpContainer. What relates it to tagging, is that it can be used to filter GimpContainer to contain only GimpTagged objects which have certain tags assigned. It is automatically updated with any changes in GimpContainer it wraps. However, items should not be added or removed from this container manually as changes do not affect original container and would be lost when GimpFilteredContainer is updated. Instead, the contents should be changed by setting tag list which would be used to filter GimpTagged objects containing all of the given GimpTags. GimpFilteredContainer can use any GimpContainer as source container. Therefore, it is possible to use decorator design pattern to implement additional container views, such as view combining items from multiple containers. GimpTagEntry widget GimpTagEntry widget extends GtkEntry and is used to either assign or query tags depending on selected mode. The widget support various usability features: * jellybeans. When tag is entered and confirmed by either separator, pressing return or otherwise, it becomes a jellybean, i.e. a single unit, not a bunch of characters. Navigating in GimpTagEntry, deleting tags, etc can be performed much quicker. However, when tag is just beeing entered (not yet confirmed), all actions operate on characters as usual. * custom auto completion is implemented in GimpTagEntry widget which allows to complete tags in the middle of tag list, doesn't offer already completed tags, tab cycles all possible completions, etc. * when GimpTagEntry is empty and unused it displays description for user regarding it's purpose. When operating in tag assignment mode, tags are assigned only when user hits return key. When operating in tag query mode, given GimpFilteredContainer is filtered as user types. GimpTagEntry also remembers recently used configurations, which can be cycled using up and down arrow keys. GimpComboTagEntry widget GimpComboTagEntry widget extends GimpTagEntry and adds ability to pick tags from a menu like list (GimpTagPopup widget). GimpTagPopup widget GimpTagPopup widget is used as a tag list menu from GimpComboTagEntry widget. It is not designed to be used with any other widget. GimpTagPopup has many visual and behavioral similarities to GtkMenu. In particular, it uses menu-like scrolling. GimpTagPopup implements various usability features, some of which are: * tags which would result in empty selection of resource are made insensitive. * closing with either keyboard or pressing outside the popup area. * underline highlighted (hovered) tags.