Adium

Map of Adium

This is a map to the internal structure of Adium. You'll need this if you want to write a plug-in or patch.

WARNING: THE FOLLOWING INFORMATION HAS NOT YET BEEN REVIEWED BY EVERY DEVELOPER. IT SHOULD NOT BE CONSIDERED FINAL NOR COMPLETE NOR CORRECT. PUPPIES MAY DIE IF YOU RELY ON THIS INFORMATION BEFORE THIS WARNING HAS BEEN REMOVED.

Creating a Plugin

There are a few pages about this:

First principles

Most access to the internals of Adium is done through AIAdium's assortment of controllers and handy methods. Anything using Adium's precompiled header automatically gets access to the global variable

       id<AIAdium> adium;

If your code doesn't get automatic access (for example if it's in an external plugin), you can do the following to get access:

       #import <Adium/AISharedAdium.h>

Notifications

Much of the information that comes to you from elsewhere in Adium will arrive by means of a notification through the local notification center.

  • NSObject
    • NSNotificationCenter

The notification names are listed in <Adium/AIAdiumProtocol.h>, as well as here.

In the 1.3 series and earlier versions, Adium used a separate notification center owned by and accessed through the AIAdium object. We removed this for Adium 1.4 and later.


Contacts

  • NSObject
    • ESObjectWithStatus
      • AIListObject
        • AIListContact
    • AIContactController <AIContactController>
  • [adium contactController]

Creating a new contact from an account+UID

Call [contactController contactWithService:service account:account UID:UID]. This method creates a new contact if a matching one didn't previously exist. Note that this contact will not show up anywhere as long as it's not in a group.

Adding a contact to the contact list

See Groups/Adding a contact to a group.

Removing a contact from the contact list

Call [contactController removeListObjects:[NSArray arrayWithObject:contactToRemove]]. Adium does not delete data associated with the contact; chat transcripts remain on the hard drive, and settings (such as alias or notes) will be present but unused such that if the user adds the contact back the information will be 'restored'.

Finding what group a contact is in

Call [contactController remoteGroupForContact:contact]. [contact containingObject] won't always work; for example, the contact may be within a metacontact. [contact parentContact] returns the outermost contact, so if the contact is within a metacontact, the metacontact will be returned, but if it isn't self will be returned.

Sending messages or anything else to a contact

XXX - do we really follow this process when sending? I think the core handles determination of the preferred account -evands

First, call [accountController preferredAccountForSendingContentType: CONTENT_FOO_TYPE toContact:destContact]. Content types are:

  • CONTENT_CONTEXT_TYPE
  • CONTENT_EVENT_TYPE
  • CONTENT_FILE_TRANSFER_TYPE
  • CONTENT_MESSAGE_TYPE
  • CONTENT_STATUS_TYPE
  • CONTENT_TYPING_TYPE

Most of the time, you'll use CONTENT_MESSAGE_TYPE.

Next, create a chat. See Chats/Creating a chat with a contact.

Next, create a content object. For a message, use [[AIContentMessage alloc] initWithChat:chat source:XXX destination:destContact date:[NSDate date] message:messageAttrStr]. For anything else, use [[contentObjectClass alloc] initWithChat:chat source:XXX destination:destContact date:[NSDate date]]. Content object classes are:

  • AIContentContext
  • AIContentEvent
  • AIContentMessage
  • AIContentStatus
  • AIContentTyping
  • ESFileTransfer

Finally, send the content object with [contentController sendContentObject:contentObject]. This returns a BOOL indicating whether it was successfully sent.

Sending raw text to a contact

First, call [accountController preferredAccountForSendingContentType: CONTENT_MESSAGE_TYPE toContact:contact]. Then, call [contentController sendRawMessage:messageString toContact:contact].

This should not be used for sending messages to users, generally. The only place it is currently used within Adium is for sending OTR encryption messages which are postprocessed on the other side and therefore never displayed. This will not give any visual indication to the local user that a message was sent.

Storing Anything Along With a Contact

For runtime data: [contact setStatusObject:forKey:notify:] and [contact statusObjectForKey:] For permanent data: [contact setPreference:forKey:group:] and [contact preferenceForKey:group:]


Metacontacts

NOTE: Metacontacts are horribly broken and have to be fixed/rewritten for a future version, so this might change.

  • NSObject
    • ESObjectWithStatus
      • AIListObject
        • AIListContact
          • AIMetaContact <AIContainingObject>
    • AIContactController <AIContactController>
  • [adium contactController]

Creating a new metacontact

Call [contactController groupListContacts:contactsToGroup]. This returns the created AIMetaContact. XXX Is there anything to do after this? For example, putting the metacontact in a group?

Getting an array of every contact in the metacontact

Call [metacontact containedObjects] or [metacontact listObjects].

Adding contacts to the metacontact

Call [contactController moveContact:contactToMove intoObject:metacontact]. If you want to specify the position within the metacontact (e.g. drag-and-drop), or move multiple objects at a time, use [contactController moveListObjects:[NSArray arrayWithObject:contactToMove] intoObject:metacontact index:desiredIndex].

Removing contacts from the metacontact

Call [contactController removeAllListObjectsMatching:contactToRemove fromMetaContact:metaContact].

Finding what group a metacontact is in

Call [contactController remoteGroupForContact:metacontact]. [metacontact parentObject] should work as well, but the former is better for consistency.


Groups

NOTE: Since Metacontacts are horribly broken right now and have to be fixed/rewritten for a future version, this might change a bit.

  • NSObject
    • ESObjectWithStatus
      • AIListObject
        • AIListGroup <AIContainingObject>
    • AIContactController <AIContactController>
  • [adium contactController]

Getting the root group

Call [contactController contactList].

Creating a group

Call [contactController groupWithUID:groupName].

Deleting a group

Call [contactController removeListObjects:[NSArray arrayWithObject:groupToRemove]].

Getting an array of every contact in the group

Call [group containedObjects]. That array may contain duplicates (e.g. on different accounts without being in a metacontact); for a duplicate-free array, use [group listObjects].

Adding a contact to a group

Call [contactController addContacts:[NSArray arrayWithObject:contactToAdd] toGroup:group].

Moving a contact from one group to another

Call [contactController moveContact:contactToMove intoObject:group]. If you want to specify the position within the group (e.g. drag-and-drop), or move multiple objects at a time, use [contactController moveListObjects:[NSArray arrayWithObject:contactToMove] intoObject:group index:desiredIndex].

Removing a contact from a group

This is equivalent to removing them from the contact list, so call [contactController removeListObjects:[NSArray arrayWithObject:contactToRemove]]. XXX Safe to remove them while they are in a metacontact?


Services

  • NSObject
    • AIService
      • <service-specific subclass>

Adding a new service

  1. Subclass AIService.
  2. Instantiate the subclass.
  3. Call [accountController registerService:service].

This is best done from a plug-in (in -installPlugin).

Subclassing AIService

XXX


Accounts

  • NSObject
    • AIAccount (Note: Some methods are in AIAbstractAccount.[hm], but they are categoried onto AIAccount)
      • <service-specific subclass>
    • AIAccountController <AIAccountController>

Adding a new account

Call [accountController createAccountWithService:service UID:UID]. This returns an AIAccount. Then call [accountController addAccount:account].

Removing an account

Call [accountController deleteAccount:account].


Chats

AIChatController handles almost everything chat related; Creating new single-user & group chats, returning the approriate chat for a given indentifier type, the addition of contacts to a chat, etc.

  • NSObject
    • ESObjectWithStatus
      • AIChat <AIContainingObject>
    • AIChatController <AIChatController>
  • To call methods in AIChatController from any part of adium, you can use: [[adium chatController] methodName]

Creating a chat with a contact

To create a chat with a contact (ie. a "single user chat"), call [AIChatController chatWithContact:contactName]. This method takes an AIListContact as a parameters, and returns an AIChat, which is the newly created chat.

Finding all chats that involve a contact

To find all chats containing (involving) a contact, call [AIChatController existingChatWithContact:contact] This method takes an AIListContact as a parameter, and returns the first chat with the contact that is found. If no chats are found, the method returns nil.

Creating a group chat

To open a group chat, call [AIChatController chatWithName:name identifier:anIdentifier onAccount:account creationInfo:inInfo] The parameters to this method are:

  • name - an NSString object representing the name of the chat
  • identifier - an object of type id by which the contactController can search for an existing chat. If this is not given, the contactController will attempt

to find the chat by searching by name.

  • account - the account to open the chat on
  • creationInfo - A dictionary of information which may be used by the account when joining the chat serverside

Inviting a contact to a group chat

XXX

Receiving messages from a chat

XXX

Sending a message to a chat

XXX

Sending an event to a chat

XXX


Preferences

  • NSObject
    • AIPreferenceController <AIPreferenceController>
    • AIModularPane
      • AIPreferencePane
        • <plug-in-specific subclass>
  • [adium preferenceController]

Preferences exist either globally or by-object. For example, all the settings that you see in the Account Editor sheet are by-object on the account object being edited.

Adding a new preferences group

XXX

Observing preference changes

XXX

Opening the Preferences window to a specific category

Call [preferenceController openPreferencesToCategory:], passing any of:

  • AIPref_General
  • AIPref_Accounts
  • AIPref_Personal
  • AIPref_Appearance
  • AIPref_Messages
  • AIPref_Status
  • AIPref_Events
  • AIPref_FileTransfer

XXX The above method does not exist! I made it up. But it should, since there is no defined list of identifier strings. Alternatively, we can define such a list. —Peter

If you want a pane in the Advanced category, use -openPreferencesToAdvancedPane: and pass the localized name of the pane you want to open it to.

Adding a category to the Preferences window

You'll need to create a subclass of AIPreferencePane, and then instantiate that subclass, and then add that pane using [preferenceController addPreferencePane:].

Custom panes go in the Advanced category. It is theoretically possible to override -category and return some other category, but your pane will not be visible. The window is not wide enough, and we don't modify the toolbar anyway.

In your subclass, you'll need to override -label (localized display name), -image (display icon) and -nibName (for loading the view).

You should be able to use Cocoa Bindings to store the values of all your preference controls. If this doesn't work and you need to work around it, or you simply haven't learned Bindings yet, use the -changePreference: action. You'll need to override it, obviously, but you should also call super.

When your pane is displayed, or when -changePreference: is received, -configureControlDimming is sent to it. If you're not using Bindings for everything (you're mixing and matching or not using Bindings at all), you should enable/disable your controls here. If you are using Bindings for everything, you can skip overriding this method.


Plug-ins

  • NSObject
    • AIPlugin <AIPlugin, AIPluginInfo>
      • <subclass>

Anatomy of a .AdiumPlugin bundle

You need these in your Info.plist:

  • CFBundleSignature: AdiM
  • NSPrincipalClass: <name of your subclass of AIPlugin>

When your plug-in is loaded, Adium will call [instanceOfYourPrincipalClass installPlugin]. When your plug-in is unloaded, Adium will call [instanceOfYourPrincipalClass uninstallPlugin].

Doing things when your plug-in is loaded

Override -(void)installPlugin.

Doing things when your plug-in is uninstalled

Override -(void)uninstallPlugin.

Supplying information about your plug-in

Override -pluginAuthor, -pluginVersion, -pluginDescription, and -pluginURL. Each of these returns an NSString (yes, including -pluginURL).

Page last modified by Robby, 5 years ago