Massive changes to dock icon drawing:
authorFrank Dowsett <wixardy@adium.im>
Fri, 18 Feb 2011 15:44:06 -0500
changeset 3670 add0c83648a5
parent 3669 7905c08bc4d8
child 3671 855cbc039a80
Massive changes to dock icon drawing:
- Use NSDockTile for badging fixes #13439
- Remove flashing of the contact name* fixes #14452
- Use a constant size instead of the current screen's size fixes #13088

Fixes #616

*It's superfluous and may have been broken in 1.3 based on the reports from after 1.4 was released.
Adium.xcodeproj/project.pbxproj
Frameworks/Adium Framework/Source/AIDockControllerProtocol.h
Plugins/Contact Status Dock Overlays/AIContactStatusDockOverlaysPlugin.h
Plugins/Contact Status Dock Overlays/AIContactStatusDockOverlaysPlugin.m
Plugins/Contact Status Dock Overlays/DockOverlayDefaults.plist
Plugins/Dock Icon Badging/AIDockBadger.h
Plugins/Dock Icon Badging/AIDockBadger.m
Plugins/Dock Icon Badging/AIDockNameOverlay.h
Plugins/Dock Icon Badging/AIDockNameOverlay.m
Plugins/Dock Icon Badging/newContentThreeDigits.png
Plugins/Dock Icon Badging/newContentTwoDigits.png
Resources/BadgerDefaults.plist
Resources/DockPrefs.plist
Resources/DockUnviewedContentDefaults.plist
Source/AICoreComponentLoader.m
Source/AIDockController.h
Source/AIDockController.m
Source/AIDockUnviewedContentPlugin.h
Source/AIDockUnviewedContentPlugin.m
--- a/Adium.xcodeproj/project.pbxproj	Fri Feb 18 01:01:13 2011 +0100
+++ b/Adium.xcodeproj/project.pbxproj	Fri Feb 18 15:44:06 2011 -0500
@@ -301,8 +301,6 @@
 		341BD5700965EC3500CF83F5 /* OTRFingerprintDetailsWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = 341BD56E0965EC3500CF83F5 /* OTRFingerprintDetailsWindow.nib */; };
 		341BD5710965EC3500CF83F5 /* OTRPrivateKeyGenerationWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = 341BD56F0965EC3500CF83F5 /* OTRPrivateKeyGenerationWindow.nib */; };
 		341BD57B0965EC4700CF83F5 /* OTRPrefs.nib in Resources */ = {isa = PBXBuildFile; fileRef = 341BD5790965EC4700CF83F5 /* OTRPrefs.nib */; };
-		341BDF5E0969814A00CF83F5 /* BadgerDefaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 341BDF5D0969814900CF83F5 /* BadgerDefaults.plist */; };
-		341BDF700969829E00CF83F5 /* DockUnviewedContentDefaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 341BDF6F0969829E00CF83F5 /* DockUnviewedContentDefaults.plist */; };
 		342353D709D45726009FF634 /* AILogViewerWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 342353D509D45725009FF634 /* AILogViewerWindowController.m */; };
 		3428328907D2643F004F01C9 /* ESStatusPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 3428328507D2643F004F01C9 /* ESStatusPreferences.m */; };
 		3428328B07D2643F004F01C9 /* ESStatusPreferencesPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 3428328707D2643F004F01C9 /* ESStatusPreferencesPlugin.m */; };
@@ -1105,9 +1103,6 @@
 		4BD6742E06001D1D0049CAF7 /* AIEmoticonPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = F5F0FE9D04B12CAC01A80106 /* AIEmoticonPreferences.m */; };
 		4BD6742F06001D1D0049CAF7 /* EmoticonPrefs.nib in Resources */ = {isa = PBXBuildFile; fileRef = F5F0FEA004B1336C01A80106 /* EmoticonPrefs.nib */; };
 		4BD6743106001D1E0049CAF7 /* EmoticonDefaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = F5F0FEA204B133AB01A80106 /* EmoticonDefaults.plist */; };
-		4BD6744506001D430049CAF7 /* AIContactStatusDockOverlaysPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B18A52C0459F8F200A8010A /* AIContactStatusDockOverlaysPlugin.m */; };
-		4BD6744906001D460049CAF7 /* DockOverlayDefaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4BB826B504C8876C00A8010A /* DockOverlayDefaults.plist */; };
-		4BD6745806001D5E0049CAF7 /* AIDockUnviewedContentPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B13322C0459BAB800A8010A /* AIDockUnviewedContentPlugin.m */; };
 		4BD6745E06001D740049CAF7 /* ESOpenMessageWindowContactAlertPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 344AB2C40579136900384CEE /* ESOpenMessageWindowContactAlertPlugin.m */; };
 		4BD6746306001D760049CAF7 /* ESSendMessageContactAlertPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 342DC5BF05783DE7000E1D83 /* ESSendMessageContactAlertPlugin.m */; };
 		4BD6746606001D770049CAF7 /* SendMessageContactAlert.nib in Resources */ = {isa = PBXBuildFile; fileRef = 342DC7FE05784463000E1D83 /* SendMessageContactAlert.nib */; };
@@ -1150,13 +1145,12 @@
 		5A1781860EC1215D00BA1E04 /* AIAutoScrollTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A1781850EC1215D00BA1E04 /* AIAutoScrollTextView.m */; };
 		5A5F601D12962EC0007A2232 /* AISegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A5F601A12962D06007A2232 /* AISegmentedControl.m */; };
 		5A5F601E12962ECE007A2232 /* AISegmentedControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A5F601912962D06007A2232 /* AISegmentedControl.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		5A5F8BBD12D560E400019727 /* AIDockNameOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A5F8BBC12D560E400019727 /* AIDockNameOverlay.m */; };
 		5A804FEA12BE9E84007CDC1B /* scandate.m in Sources */ = {isa = PBXBuildFile; fileRef = 31DDDA6F12BDD5CE0048F6C0 /* scandate.m */; };
 		5A80508512BE9F00007CDC1B /* scandate.m in Sources */ = {isa = PBXBuildFile; fileRef = 31DDDA6F12BDD5CE0048F6C0 /* scandate.m */; };
 		5A94397B1279ECB800FDD81D /* AIImgurImageUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A94397A1279ECB800FDD81D /* AIImgurImageUploader.m */; };
 		5A9A9F8911F2951400328DF9 /* AIDoNothingContactAlertPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A9A9F8811F2951400328DF9 /* AIDoNothingContactAlertPlugin.m */; };
 		5A9A9F8B11F295EB00328DF9 /* Stop sign.png in Resources */ = {isa = PBXBuildFile; fileRef = 5A9A9F8A11F295EB00328DF9 /* Stop sign.png */; };
-		63093C2F0892143500F118D3 /* newContentThreeDigits.png in Resources */ = {isa = PBXBuildFile; fileRef = 63093C2D0892143500F118D3 /* newContentThreeDigits.png */; };
-		63093C300892143500F118D3 /* newContentTwoDigits.png in Resources */ = {isa = PBXBuildFile; fileRef = 63093C2E0892143500F118D3 /* newContentTwoDigits.png */; };
 		6311F4710E340DD2004234B8 /* AISharedAdium.h in Headers */ = {isa = PBXBuildFile; fileRef = 6311F46F0E340DD2004234B8 /* AISharedAdium.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		6311F4720E340DD2004234B8 /* AISharedAdium.m in Sources */ = {isa = PBXBuildFile; fileRef = 6311F4700E340DD2004234B8 /* AISharedAdium.m */; };
 		6318DBA10F8D93F200C5E584 /* WebKit Defaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 347065E406015DC5004F0D20 /* WebKit Defaults.plist */; };
@@ -1433,7 +1427,6 @@
 		63EA62F0094B8705009C8453 /* AIXtraInfoController.m in Sources */ = {isa = PBXBuildFile; fileRef = 63EA62EE094B8705009C8453 /* AIXtraInfoController.m */; };
 		63EA63DD094BB79A009C8453 /* StatusIconPreviewView.nib in Resources */ = {isa = PBXBuildFile; fileRef = 63336B3F094994A800970871 /* StatusIconPreviewView.nib */; };
 		63EA6409094BB9A9009C8453 /* AIStatusIconPreviewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 63EA6407094BB9A9009C8453 /* AIStatusIconPreviewController.m */; };
-		63FBAA480887779D00026246 /* AIDockBadger.m in Sources */ = {isa = PBXBuildFile; fileRef = 636AB7F2087E2B6700A87670 /* AIDockBadger.m */; };
 		6615618B0D84B875004B7946 /* AIFilterBarBackgroundBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 6615618A0D84B874004B7946 /* AIFilterBarBackgroundBox.m */; };
 		6E4D961406B739AC005235E9 /* DCJoinChatPanelPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E4D961206B739AC005235E9 /* DCJoinChatPanelPlugin.m */; };
 		6EC1684D06C170A000F9FAD3 /* DCInviteToChatPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EC1684806C170A000F9FAD3 /* DCInviteToChatPlugin.m */; };
@@ -2309,8 +2302,6 @@
 		341BD5890965ECC200CF83F5 /* de */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = de; path = Resources/de.lproj/OTRPrefs.nib; sourceTree = "<group>"; };
 		341BD58A0965ECC800CF83F5 /* cs */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = cs; path = Resources/cs.lproj/OTRPrefs.nib; sourceTree = "<group>"; };
 		341BD58B0965ECCD00CF83F5 /* ca */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = ca; path = Resources/ca.lproj/OTRPrefs.nib; sourceTree = "<group>"; };
-		341BDF5D0969814900CF83F5 /* BadgerDefaults.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.xml; name = BadgerDefaults.plist; path = Resources/BadgerDefaults.plist; sourceTree = SOURCE_ROOT; };
-		341BDF6F0969829E00CF83F5 /* DockUnviewedContentDefaults.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.xml; name = DockUnviewedContentDefaults.plist; path = Resources/DockUnviewedContentDefaults.plist; sourceTree = "<group>"; };
 		341DE02F07FE104700EB7B06 /* de */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = de; path = Resources/de.lproj/ESFileTransferProgressView.nib; sourceTree = "<group>"; };
 		341DE03407FE105600EB7B06 /* ja */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = ja; path = Resources/ja.lproj/ESFileTransferProgressView.nib; sourceTree = "<group>"; };
 		341DE03507FE106400EB7B06 /* ja */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = ja; path = Resources/ja.lproj/PasswordPrompt.nib; sourceTree = "<group>"; };
@@ -4000,16 +3991,12 @@
 		4B08399B07D235190034A001 /* DockIconSelectionSheet.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = DockIconSelectionSheet.nib; path = Resources/DockIconSelectionSheet.nib; sourceTree = "<group>"; };
 		4B09D5C005D5265800BC00F0 /* AIMetaContact.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = AIMetaContact.h; path = "Frameworks/Adium Framework/Source/AIMetaContact.h"; sourceTree = "<group>"; };
 		4B09D5C105D5265800BC00F0 /* AIMetaContact.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = AIMetaContact.m; path = "Frameworks/Adium Framework/Source/AIMetaContact.m"; sourceTree = "<group>"; };
-		4B13322B0459BAB800A8010A /* AIDockUnviewedContentPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIDockUnviewedContentPlugin.h; path = Source/AIDockUnviewedContentPlugin.h; sourceTree = "<group>"; };
-		4B13322C0459BAB800A8010A /* AIDockUnviewedContentPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIDockUnviewedContentPlugin.m; path = Source/AIDockUnviewedContentPlugin.m; sourceTree = "<group>"; };
 		4B14CF24085FBC5800C88949 /* AdiumSound.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = AdiumSound.h; path = Source/AdiumSound.h; sourceTree = "<group>"; };
 		4B14CF25085FBC5800C88949 /* AdiumSound.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = AdiumSound.m; path = Source/AdiumSound.m; sourceTree = "<group>"; };
 		4B14CF26085FBC5800C88949 /* AdiumSoundSets.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = AdiumSoundSets.h; path = Source/AdiumSoundSets.h; sourceTree = "<group>"; };
 		4B14CF27085FBC5900C88949 /* AdiumSoundSets.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = AdiumSoundSets.m; path = Source/AdiumSoundSets.m; sourceTree = "<group>"; };
 		4B14CF28085FBC5900C88949 /* AdiumSpeech.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = AdiumSpeech.h; path = Source/AdiumSpeech.h; sourceTree = "<group>"; };
 		4B14CF29085FBC5900C88949 /* AdiumSpeech.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = AdiumSpeech.m; path = Source/AdiumSpeech.m; sourceTree = "<group>"; };
-		4B18A52B0459F8F200A8010A /* AIContactStatusDockOverlaysPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIContactStatusDockOverlaysPlugin.h; path = "Plugins/Contact Status Dock Overlays/AIContactStatusDockOverlaysPlugin.h"; sourceTree = "<group>"; };
-		4B18A52C0459F8F200A8010A /* AIContactStatusDockOverlaysPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIContactStatusDockOverlaysPlugin.m; path = "Plugins/Contact Status Dock Overlays/AIContactStatusDockOverlaysPlugin.m"; sourceTree = "<group>"; };
 		4B191D880611C10800E8A4E7 /* License.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = License.txt; sourceTree = "<group>"; };
 		4B191D890611C10800E8A4E7 /* ReadMe.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = ReadMe.rtf; sourceTree = "<group>"; };
 		4B22E15C085CAE2E001864DE /* AIAbstractListObjectMenu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = AIAbstractListObjectMenu.h; path = "Frameworks/Adium Framework/Source/AIAbstractListObjectMenu.h"; sourceTree = "<group>"; };
@@ -4134,7 +4121,6 @@
 		4BB2EB200D88D76100CA7A7D /* timestamp32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = timestamp32.png; path = Resources/timestamp32.png; sourceTree = "<group>"; };
 		4BB2EB210D88D76100CA7A7D /* timestamp32_transparent.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = timestamp32_transparent.png; path = Resources/timestamp32_transparent.png; sourceTree = "<group>"; };
 		4BB5B78107D2DC4900CFE06F /* pref-status.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "pref-status.png"; path = "Resources/pref-status.png"; sourceTree = "<group>"; };
-		4BB826B504C8876C00A8010A /* DockOverlayDefaults.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist; name = DockOverlayDefaults.plist; path = "Plugins/Contact Status Dock Overlays/DockOverlayDefaults.plist"; sourceTree = "<group>"; };
 		4BB99726083D1A5F006ACD6F /* SMContactListShowBehaviorPlugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = SMContactListShowBehaviorPlugin.h; path = Source/SMContactListShowBehaviorPlugin.h; sourceTree = SOURCE_ROOT; };
 		4BB99727083D1A5F006ACD6F /* SMContactListShowBehaviorPlugin.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = SMContactListShowBehaviorPlugin.m; path = Source/SMContactListShowBehaviorPlugin.m; sourceTree = SOURCE_ROOT; };
 		4BB99728083D1A5F006ACD6F /* SMContactListShowDetailsPane.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = SMContactListShowDetailsPane.h; path = Source/SMContactListShowDetailsPane.h; sourceTree = SOURCE_ROOT; };
@@ -4219,6 +4205,8 @@
 		5A1781850EC1215D00BA1E04 /* AIAutoScrollTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIAutoScrollTextView.m; path = Source/AIAutoScrollTextView.m; sourceTree = "<group>"; };
 		5A5F601912962D06007A2232 /* AISegmentedControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AISegmentedControl.h; path = "Frameworks/AIUtilities Framework/Source/AISegmentedControl.h"; sourceTree = "<group>"; };
 		5A5F601A12962D06007A2232 /* AISegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AISegmentedControl.m; path = "Frameworks/AIUtilities Framework/Source/AISegmentedControl.m"; sourceTree = "<group>"; };
+		5A5F8BBB12D560E400019727 /* AIDockNameOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIDockNameOverlay.h; path = "Plugins/Dock Icon Badging/AIDockNameOverlay.h"; sourceTree = "<group>"; };
+		5A5F8BBC12D560E400019727 /* AIDockNameOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIDockNameOverlay.m; path = "Plugins/Dock Icon Badging/AIDockNameOverlay.m"; sourceTree = "<group>"; };
 		5A7642A811E044B900E5E0AF /* sk_SK */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = sk_SK; path = Resources/sk_SK.lproj/AccountProxy.nib; sourceTree = "<group>"; };
 		5A7642A911E044B900E5E0AF /* sk_SK */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = sk_SK; path = Resources/sk_SK.lproj/AIAdvancedInspectorPane.nib; sourceTree = "<group>"; };
 		5A7642AA11E044B900E5E0AF /* sk_SK */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = sk_SK; path = Resources/sk_SK.lproj/AppearancePrefs.nib; sourceTree = "<group>"; };
@@ -4310,8 +4298,6 @@
 		5A9A9F8711F2951400328DF9 /* AIDoNothingContactAlertPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIDoNothingContactAlertPlugin.h; sourceTree = "<group>"; };
 		5A9A9F8811F2951400328DF9 /* AIDoNothingContactAlertPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AIDoNothingContactAlertPlugin.m; sourceTree = "<group>"; };
 		5A9A9F8A11F295EB00328DF9 /* Stop sign.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Stop sign.png"; path = "Resources/Stop sign.png"; sourceTree = SOURCE_ROOT; };
-		63093C2D0892143500F118D3 /* newContentThreeDigits.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = newContentThreeDigits.png; sourceTree = "<group>"; };
-		63093C2E0892143500F118D3 /* newContentTwoDigits.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = newContentTwoDigits.png; sourceTree = "<group>"; };
 		6311F46F0E340DD2004234B8 /* AISharedAdium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AISharedAdium.h; path = "Frameworks/Adium Framework/Source/AISharedAdium.h"; sourceTree = "<group>"; };
 		6311F4700E340DD2004234B8 /* AISharedAdium.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AISharedAdium.m; path = "Frameworks/Adium Framework/Source/AISharedAdium.m"; sourceTree = "<group>"; };
 		63125D8D0A75EBF000303EE9 /* AIPurpleLiveJournalAccount.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = AIPurpleLiveJournalAccount.h; path = "Plugins/Purple Service/AIPurpleLiveJournalAccount.h"; sourceTree = "<group>"; };
@@ -4581,8 +4567,6 @@
 		634DC5560F9C3B2F007B6479 /* AIStringUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIStringUtilities.h; path = "Frameworks/AIUtilities Framework/Source/AIStringUtilities.h"; sourceTree = "<group>"; };
 		6360B2580BF2EB93004CD99B /* AIWebKitDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIWebKitDelegate.h; path = "Plugins/WebKit Message View/AIWebKitDelegate.h"; sourceTree = "<group>"; };
 		6360B2590BF2EB93004CD99B /* AIWebKitDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIWebKitDelegate.m; path = "Plugins/WebKit Message View/AIWebKitDelegate.m"; sourceTree = "<group>"; };
-		636AB7F1087E2B6700A87670 /* AIDockBadger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIDockBadger.h; sourceTree = "<group>"; };
-		636AB7F2087E2B6700A87670 /* AIDockBadger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AIDockBadger.m; sourceTree = "<group>"; };
 		636C6E5D0ED56D0100E0E528 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = /usr/lib/libcrypto.dylib; sourceTree = "<absolute>"; };
 		636D8C970E4E95A500E5F558 /* AIAddressBookController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIAddressBookController.m; path = "Frameworks/Adium Framework/Source/AIAddressBookController.m"; sourceTree = "<group>"; };
 		636D93660E4E9FD300E5F558 /* AdiumAddressBookAction_Yahoo.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.scpt; name = AdiumAddressBookAction_Yahoo.scpt; path = "Frameworks/Adium Framework/Resources/AdiumAddressBookAction_Yahoo.scpt"; sourceTree = "<group>"; };
@@ -6059,8 +6043,6 @@
 				34F8A7FA05A8927F00577775 /* Applescript Filters */,
 				4BF4813B049A802E00A8010A /* Auto Linking */,
 				3436CD940CB5AB5B003A026E /* Bookmarking */,
-				636AB7EC087E2A2E00A87670 /* Dock Icon Badging */,
-				4B1332130459B98E00A8010A /* Dock Unviewed Content */,
 				11C157D504A88DE0008E0C76 /* Emoticons */,
 				3429BAF70703789D00EED705 /* File Transfer Messages */,
 				34CF9DEF085B8B3D00D87E37 /* iTunes Now Playing */,
@@ -6879,16 +6861,6 @@
 			name = "Contact Status Coloring";
 			sourceTree = "<group>";
 		};
-		4B1332130459B98E00A8010A /* Dock Unviewed Content */ = {
-			isa = PBXGroup;
-			children = (
-				341BDF6F0969829E00CF83F5 /* DockUnviewedContentDefaults.plist */,
-				4B13322B0459BAB800A8010A /* AIDockUnviewedContentPlugin.h */,
-				4B13322C0459BAB800A8010A /* AIDockUnviewedContentPlugin.m */,
-			);
-			name = "Dock Unviewed Content";
-			sourceTree = "<group>";
-		};
 		4B1332140459B9A000A8010A /* Dock Account Status */ = {
 			isa = PBXGroup;
 			children = (
@@ -6915,9 +6887,8 @@
 		4B18A51F0459F89300A8010A /* Contact and Chat Dock Overlays */ = {
 			isa = PBXGroup;
 			children = (
-				4B18A52B0459F8F200A8010A /* AIContactStatusDockOverlaysPlugin.h */,
-				4B18A52C0459F8F200A8010A /* AIContactStatusDockOverlaysPlugin.m */,
-				4BB826B504C8876C00A8010A /* DockOverlayDefaults.plist */,
+				5A5F8BBB12D560E400019727 /* AIDockNameOverlay.h */,
+				5A5F8BBC12D560E400019727 /* AIDockNameOverlay.m */,
 			);
 			name = "Contact and Chat Dock Overlays";
 			sourceTree = "<group>";
@@ -8322,19 +8293,6 @@
 			name = "Xtras Preview Views";
 			sourceTree = "<group>";
 		};
-		636AB7EC087E2A2E00A87670 /* Dock Icon Badging */ = {
-			isa = PBXGroup;
-			children = (
-				341BDF5D0969814900CF83F5 /* BadgerDefaults.plist */,
-				636AB7F1087E2B6700A87670 /* AIDockBadger.h */,
-				636AB7F2087E2B6700A87670 /* AIDockBadger.m */,
-				63093C2D0892143500F118D3 /* newContentThreeDigits.png */,
-				63093C2E0892143500F118D3 /* newContentTwoDigits.png */,
-			);
-			name = "Dock Icon Badging";
-			path = "Plugins/Dock Icon Badging";
-			sourceTree = "<group>";
-		};
 		636D930E0E4E9DDD00E5F558 /* Address Book Integration */ = {
 			isa = PBXGroup;
 			children = (
@@ -9897,7 +9855,6 @@
 				34A48A1006F64382002C7C5F /* ContactListWindowBorderless.nib in Resources */,
 				4BE9FFE8055981AB00359B15 /* Adium.pch in Resources */,
 				4BD6743106001D1E0049CAF7 /* EmoticonDefaults.plist in Resources */,
-				4BD6744906001D460049CAF7 /* DockOverlayDefaults.plist in Resources */,
 				4BD6755406001E1E0049CAF7 /* DualWindowDefaults.plist in Resources */,
 				4BD6756B06001E320049CAF7 /* EventSoundDefaults.plist in Resources */,
 				4B191D8A0611C10800E8A4E7 /* License.txt in Resources */,
@@ -9967,8 +9924,6 @@
 				071C56330827933100399C0F /* Shared Dock Icon Images in Resources */,
 				4BB9972F083D1A74006ACD6F /* ContactListShowBehavior.nib in Resources */,
 				110763FA08676D05005987A5 /* BlockEditorWindow.nib in Resources */,
-				63093C2F0892143500F118D3 /* newContentThreeDigits.png in Resources */,
-				63093C300892143500F118D3 /* newContentTwoDigits.png in Resources */,
 				EE147A6D0896B18800A21377 /* ABSearch.png in Resources */,
 				EE147A790896B32400A21377 /* ABSearch.nib in Resources */,
 				632D801708E7E0A40032C187 /* XtrasManager.nib in Resources */,
@@ -9985,8 +9940,6 @@
 				341BD5700965EC3500CF83F5 /* OTRFingerprintDetailsWindow.nib in Resources */,
 				341BD5710965EC3500CF83F5 /* OTRPrivateKeyGenerationWindow.nib in Resources */,
 				341BD57B0965EC4700CF83F5 /* OTRPrefs.nib in Resources */,
-				341BDF5E0969814A00CF83F5 /* BadgerDefaults.plist in Resources */,
-				341BDF700969829E00CF83F5 /* DockUnviewedContentDefaults.plist in Resources */,
 				349710DD096C4C7700699948 /* Buttons.strings in Resources */,
 				343EC6C1096F7DEA00349098 /* StatusPreferencesAdvanced.nib in Resources */,
 				343EC9810970C29400349098 /* OfflineContactHidingDefaults.plist in Resources */,
@@ -10447,8 +10400,6 @@
 				4BD673B706001C5F0049CAF7 /* AIContactStatusColoringPlugin.m in Sources */,
 				4BD6741C06001CEF0049CAF7 /* AIMessageAliasPlugin.m in Sources */,
 				4BD6742E06001D1D0049CAF7 /* AIEmoticonPreferences.m in Sources */,
-				4BD6744506001D430049CAF7 /* AIContactStatusDockOverlaysPlugin.m in Sources */,
-				4BD6745806001D5E0049CAF7 /* AIDockUnviewedContentPlugin.m in Sources */,
 				4BD6745E06001D740049CAF7 /* ESOpenMessageWindowContactAlertPlugin.m in Sources */,
 				4BD6746306001D760049CAF7 /* ESSendMessageContactAlertPlugin.m in Sources */,
 				4BD6753006001E0D0049CAF7 /* AIDualWindowInterfacePlugin.m in Sources */,
@@ -10560,7 +10511,6 @@
 				34CF9DF5085B8B7900D87E37 /* ESiTunesPlugin.m in Sources */,
 				110763F608676CC9005987A5 /* RAFBlockEditorPlugin.m in Sources */,
 				110763F808676CC9005987A5 /* RAFBlockEditorWindowController.m in Sources */,
-				63FBAA480887779D00026246 /* AIDockBadger.m in Sources */,
 				EE147A800896B33400A21377 /* OWABSearchWindowController.m in Sources */,
 				633F175D08F905A800C76383 /* AIXtrasManager.m in Sources */,
 				348F57940936F3CC00288E41 /* ESEditStatusGroupWindowController.m in Sources */,
@@ -10761,6 +10711,7 @@
 				5A80508512BE9F00007CDC1B /* scandate.m in Sources */,
 				1154F50B12E1476900B8CA27 /* AILogByAccountWindowController.m in Sources */,
 				766ABAB61306D1020049FFB7 /* AIUnreadMessagesTooltip.m in Sources */,
+				5A5F8BBD12D560E400019727 /* AIDockNameOverlay.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
--- a/Frameworks/Adium Framework/Source/AIDockControllerProtocol.h	Fri Feb 18 01:01:13 2011 +0100
+++ b/Frameworks/Adium Framework/Source/AIDockControllerProtocol.h	Fri Feb 18 15:44:06 2011 -0500
@@ -34,12 +34,12 @@
 - (void)setIconStateNamed:(NSString *)inName;
 - (void)removeIconStateNamed:(NSString *)inName;
 - (void)setIconState:(AIIconState *)iconState named:(NSString *)inName;
-- (CGFloat)dockIconScale;
 - (NSImage *)baseApplicationIconImage;
+- (void)setOverlay:(NSImage *)image;
 
 //Special access to icon pack loading
 - (NSArray *)availableDockIconPacks;
-- (BOOL)currentIconSupportsIconStateNamed:(NSString *)inName;;
+- (BOOL)currentIconSupportsIconStateNamed:(NSString *)inName;
 - (NSMutableDictionary *)iconPackAtPath:(NSString *)folderPath;
 - (void)getName:(NSString **)outName previewState:(AIIconState **)outIconState forIconPackAtPath:(NSString *)folderPath;
 - (AIIconState *)previewStateForIconPackAtPath:(NSString *)folderPath;
--- a/Plugins/Contact Status Dock Overlays/AIContactStatusDockOverlaysPlugin.h	Fri Feb 18 01:01:13 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/* 
- * Adium is the legal property of its developers, whose names are listed in the copyright file included
- * with this source distribution.
- * 
- * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
- * General Public License as published by the Free Software Foundation; either version 2 of the License,
- * or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
- * Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License along with this program; if not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#import <Adium/AIContactObserverManager.h>
-#import <Adium/AIContactAlertsControllerProtocol.h>
-#import <Adium/AIChatControllerProtocol.h>
-
-#define PREF_GROUP_DOCK_OVERLAYS		@"Dock Overlays"
-#define DOCK_OVERLAY_DEFAULT_PREFS		@"DockOverlayDefaults"
-
-#define	KEY_DOCK_SHOW_STATUS			@"Show status overlays"
-#define	KEY_DOCK_SHOW_CONTENT			@"Show content overlays"
-#define	KEY_DOCK_OVERLAY_POSITION		@"Overlay Position"
-
-#define DOCK_OVERLAY_ALERT_IDENTIFIER		@"DockOverlay"
-
-@class AIIconState;
-
-@interface AIContactStatusDockOverlaysPlugin : AIPlugin <AIListObjectObserver, AIChatObserver, AIActionHandler> {
-    NSMutableArray				*overlayObjectsArray;
-    AIIconState					*overlayState;
-
-    NSColor	*signedOffColor;
-    NSColor	*signedOnColor;
-    NSColor	*unviewedContentColor;
-
-    NSColor	*backSignedOffColor;
-    NSColor	*backSignedOnColor;
-    NSColor	*backUnviewedContentColor;
-
-    BOOL	showStatus;
-    BOOL	showContent;
-    BOOL	overlayPosition;
-	BOOL	shouldAnimate;
-
-    NSImage	*image1;
-    NSImage	*image2;
-}
-
-@end
--- a/Plugins/Contact Status Dock Overlays/AIContactStatusDockOverlaysPlugin.m	Fri Feb 18 01:01:13 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,455 +0,0 @@
-/* 
- * Adium is the legal property of its developers, whose names are listed in the copyright file included
- * with this source distribution.
- * 
- * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
- * General Public License as published by the Free Software Foundation; either version 2 of the License,
- * or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
- * Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License along with this program; if not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#import "AIContactStatusDockOverlaysPlugin.h"
-#import <Adium/AIChatControllerProtocol.h>
-#import <Adium/AIContactControllerProtocol.h>
-#import <Adium/AIContentControllerProtocol.h>
-#import "AIDockController.h"
-#import <Adium/AIInterfaceControllerProtocol.h>
-#import <Adium/AIContactAlertsControllerProtocol.h>
-#import <AIUtilities/AIColorAdditions.h>
-#import <AIUtilities/AIDictionaryAdditions.h>
-#import <AIUtilities/AIParagraphStyleAdditions.h>
-#import <AIUtilities/AIArrayAdditions.h>
-#import <AIUtilities/AIImageAdditions.h>
-#import <Adium/AIAbstractListController.h>
-#import <Adium/AIAccount.h>
-#import <Adium/AIChat.h>
-#import <Adium/AIIconState.h>
-
-#define SMALLESTRADIUS				15
-#define RADIUSRANGE					36
-#define SMALLESTFONTSIZE			14
-#define FONTSIZERANGE				30
-
-#define	DOCK_OVERLAY_ALERT_SHORT	AILocalizedString(@"Display name in the dock icon",nil)
-#define DOCK_OVERLAY_ALERT_LONG		DOCK_OVERLAY_ALERT_SHORT
-
-@interface AIContactStatusDockOverlaysPlugin ()
-- (void)_setOverlay;
-- (NSImage *)overlayImageFlash:(BOOL)flash;
-- (void)flushPreferenceColorCache;
-- (void)chatClosed:(NSNotification *)notification;
-- (void)removeDockOverlay:(NSTimer *)removeTimer;
-@end
-
-@implementation AIContactStatusDockOverlaysPlugin
-
-/*!
-* @brief Install
- */
-- (void)installPlugin
-{
-	overlayObjectsArray = [[NSMutableArray alloc] init];
-    overlayState = nil;
-
-    //Register as a contact observer (For signed on / signed off)
-    [[AIContactObserverManager sharedManager] registerListObjectObserver:self];
-	
-	//Register as a chat observer (for unviewed content)
-	[adium.chatController registerChatObserver:self];
-	
-	[[NSNotificationCenter defaultCenter] addObserver:self
-								   selector:@selector(chatClosed:)
-									   name:Chat_WillClose
-									 object:nil];
-	
-    //Prefs
-	[adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_LIST_THEME];
-	[adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_APPEARANCE];
-	
-    //
-    image1 = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
-    image2 = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
-	
-	//Install our contact alert
-	[adium.contactAlertsController registerActionID:DOCK_OVERLAY_ALERT_IDENTIFIER withHandler:self];
-}
-
-- (void)uninstallPlugin
-{
-	[[AIContactObserverManager sharedManager] unregisterListObjectObserver:self];
-	[adium.chatController unregisterChatObserver:self];
-	[[NSNotificationCenter defaultCenter] removeObserver:self];
-	[adium.preferenceController unregisterPreferenceObserver:self];
-}
-
-/*!
-* @brief Short description
- * @result A short localized description of the action
- */
-- (NSString *)shortDescriptionForActionID:(NSString *)actionID
-{
-	return DOCK_OVERLAY_ALERT_SHORT;
-}
-
-/*!
-* @brief Long description
- * @result A longer localized description of the action which should take into account the details dictionary as appropraite.
- */
-- (NSString *)longDescriptionForActionID:(NSString *)actionID withDetails:(NSDictionary *)details
-{
-	return DOCK_OVERLAY_ALERT_LONG;
-}
-
-/*!
-* @brief Image
- */
-- (NSImage *)imageForActionID:(NSString *)actionID
-{
-	//XXX
-	return [NSImage imageNamed:@"DockAlert" forClass:[self class]];
-}
-
-/*!
- * @brief Details pane
- * @result An <tt>AIModularPane</tt> to use for configuring this action, or nil if no configuration is possible.
- */
-- (AIModularPane *)detailsPaneForActionID:(NSString *)actionID
-{
-	return nil;
-}
-
-/*!
-* @brief Perform an action
- *
- * @param actionID The ID of the action to perform
- * @param listObject The listObject associated with the event triggering the action. It may be nil
- * @param details If set by the details pane when the action was created, the details dictionary for this particular action
- * @param eventID The eventID which triggered this action
- * @param userInfo Additional information associated with the event; userInfo's type will vary with the actionID.
- */
-- (BOOL)performActionID:(NSString *)actionID forListObject:(AIListObject *)listObject withDetails:(NSDictionary *)details triggeringEventID:(NSString *)eventID userInfo:(id)userInfo
-{
-	BOOL isMessageEvent = [adium.contactAlertsController isMessageEvent:eventID];
-	
-	if (isMessageEvent) {
-		AIChat	*chat;
-
-		if ((chat = [userInfo objectForKey:@"AIChat"]) &&
-		   (chat != adium.interfaceController.activeChat) &&
-		   (![overlayObjectsArray containsObjectIdenticalTo:chat])) {
-			[overlayObjectsArray addObject:chat];
-			
-			//Wait until the next run loop so this event is done processing (and our unviewed content count is right)
-			[self performSelector:@selector(_setOverlay)
-					   withObject:nil
-					   afterDelay:0];
-
-			/* The chat observer method is responsible for removing this overlay later */
-		}
-
-	} else if (listObject) {
-		NSTimer	*removeTimer;
-		
-		//Clear any current timer for this object o ahve its overlay removed
-		if ((removeTimer = [listObject valueForProperty:@"DockOverlayRemoveTimer"])) [removeTimer invalidate];
-		
-		//Add a timer to remove this overlay
-		removeTimer = [NSTimer scheduledTimerWithTimeInterval:5
-													   target:self
-													 selector:@selector(removeDockOverlay:)
-													 userInfo:listObject
-													  repeats:NO];
-		[listObject setValue:removeTimer
-							 forProperty:@"DockOverlayRemoveTimer"
-							 notify:NotifyNever];
-
-		if (![overlayObjectsArray containsObject:listObject]) {
-			[overlayObjectsArray addObject:listObject];
-		}
-
-		//Wait until the next run loop so this event is done processing
-		[self performSelector:@selector(_setOverlay)
-				   withObject:nil
-				   afterDelay:0];
-	}
-	
-	return YES;
-}
-
-- (void)removeDockOverlay:(NSTimer *)removeTimer
-{
-	AIListObject	*inObject = [removeTimer userInfo];
-
-	[overlayObjectsArray removeObjectIdenticalTo:inObject];
-	
-	[inObject setValue:nil
-					   forProperty:@"DockOverlayRemoveTimer"
-					   notify:NotifyNever];
-	
-	[self _setOverlay];
-}
-
-- (void)chatClosed:(NSNotification *)notification
-{
-	AIChat	*chat = [notification object];
-	
-	[overlayObjectsArray removeObjectIdenticalTo:chat];
-	
-	[self _setOverlay];
-}
-
-/*!
-* @brief Allow multiple actions?
- *
- * If this method returns YES, every one of this action associated with the triggering event will be executed.
- * If this method returns NO, only the first will be.
- *
- * Don't allow multiple dock actions to occur.  While a series of "Bounce every 5 seconds," "Bounce every 10 seconds,"
- * and so on actions could be combined sanely, a series of "Bounce once" would make the dock go crazy.
- */
-- (BOOL)allowMultipleActionsWithID:(NSString *)actionID
-{
-	return NO;
-}
-
-- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
-							object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
-{	
-	if ([group isEqualToString:PREF_GROUP_LIST_THEME]) {
-		//Grab colors from status coloring plugin's prefs    
-		[self flushPreferenceColorCache];
-		signedOffColor = [[[prefDict objectForKey:KEY_SIGNED_OFF_COLOR] representedColor] retain];
-		signedOnColor = [[[prefDict objectForKey:KEY_SIGNED_ON_COLOR] representedColor] retain];
-		unviewedContentColor = [[[prefDict objectForKey:KEY_UNVIEWED_COLOR] representedColor] retain];
-		
-		backSignedOffColor = [[[prefDict objectForKey:KEY_LABEL_SIGNED_OFF_COLOR] representedColor] retain];
-		backSignedOnColor = [[[prefDict objectForKey:KEY_LABEL_SIGNED_ON_COLOR] representedColor] retain];
-		backUnviewedContentColor = [[[prefDict objectForKey:KEY_LABEL_UNVIEWED_COLOR] representedColor] retain];
-
-	} else if ([group isEqualToString:PREF_GROUP_APPEARANCE]) {
-		if (!key || [key isEqualToString:KEY_ANIMATE_DOCK_ICON]) {
-			BOOL newShouldAnimate = [[prefDict objectForKey:KEY_ANIMATE_DOCK_ICON] boolValue];
-			if (newShouldAnimate != shouldAnimate) {
-				shouldAnimate = newShouldAnimate;
-
-				//Redo our overlay to respect our new preference
-				if (!firstTime) [self _setOverlay];
-			}
-		}
-	}
-}
-
-
-- (void)flushPreferenceColorCache
-{
-	[signedOffColor release]; signedOffColor = nil;
-	[signedOnColor release]; signedOnColor = nil;
-	[unviewedContentColor release]; unviewedContentColor = nil;
-	[backSignedOffColor release]; backSignedOffColor = nil;
-	[backSignedOnColor release]; backSignedOnColor = nil;
-	[backUnviewedContentColor release]; backUnviewedContentColor = nil;	
-}
-
-- (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
-{
-	if ([inObject isKindOfClass:[AIAccount class]]) {
-		//When an account signs on or off, force an overlay update as it may have silently changed
-		//contacts' statuses
-		if ([inModifiedKeys containsObject:@"isOnline"]) {
-			BOOL			madeChanges = NO;
-			
-			for (AIListObject *listObject in [[overlayObjectsArray copy] autorelease]) {
-				if (([listObject respondsToSelector:@selector(account)]) &&
-				   ([(id)listObject account] == inObject) &&
-				   ([overlayObjectsArray containsObjectIdenticalTo:listObject])) {
-					[overlayObjectsArray removeObject:listObject];
-					madeChanges = YES;
-				}
-			}
-			
-			if (madeChanges) [self _setOverlay];
-		}
-	}
-	
-	return nil;
-}
-
-/*!
- * @brief When a chat no longer has unviewed content, remove it from display
- */
-- (NSSet *)updateChat:(AIChat *)inChat keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
-{
-	if (inModifiedKeys == nil || [inModifiedKeys containsObject:KEY_UNVIEWED_CONTENT]) {
-		
-		if (![inChat unviewedContentCount]) {
-			if ([overlayObjectsArray containsObjectIdenticalTo:inChat]) {
-				[overlayObjectsArray removeObjectIdenticalTo:inChat];
-				[self _setOverlay];
-			}
-		}
-	}
-	
-	return nil;
-}
-
-- (void)_setOverlay
-{
-    //Remove & release the current overlay state
-    if (overlayState) {
-        [adium.dockController removeIconStateNamed:@"ContactStatusOverlay"];
-        [overlayState release]; overlayState = nil;
-    }
-
-    //Create & set the new overlay state
-    if ([overlayObjectsArray count] != 0) {
-        //Set the state
-		if (shouldAnimate) {
-			overlayState = [[AIIconState alloc] initWithImages:[NSArray arrayWithObjects:[self overlayImageFlash:NO], [self overlayImageFlash:YES], nil]
-														 delay:0.5f
-												       looping:YES 
-													   overlay:YES];
-		} else {
-			overlayState = [[AIIconState alloc] initWithImage:[self overlayImageFlash:NO]
-													  overlay:YES];
-		}
-
-        [adium.dockController setIconState:overlayState named:@"ContactStatusOverlay"];
-    }   
-}
-
-- (NSImage *)overlayImageFlash:(BOOL)flash
-{
-    NSEnumerator		*enumerator;
-    ESObjectWithProperties  *object;
-    NSFont				*font;
-    NSParagraphStyle	*paragraphStyle;
-    CGFloat				dockIconScale;
-    CGFloat					iconHeight;
-    CGFloat				top, bottom;
-    NSImage				*image = (flash ? image1 : image2);
-	
-    //Pre-calc some sizes
-    dockIconScale = 1- [adium.dockController dockIconScale];
-    iconHeight = (SMALLESTRADIUS + (RADIUSRANGE * dockIconScale));
-
-	top = 126;
-	bottom = top - iconHeight;
-
-    //Set up the string details
-    font = [NSFont boldSystemFontOfSize:(SMALLESTFONTSIZE + (FONTSIZERANGE * dockIconScale))];
-    paragraphStyle = [NSParagraphStyle styleWithAlignment:NSCenterTextAlignment lineBreakMode:NSLineBreakByClipping];
-	
-    //Clear our image
-    [image lockFocus];
-    [[NSColor clearColor] set];
-    NSRectFillUsingOperation(NSMakeRect(0, 0, 128, 128), NSCompositeCopy);
-	
-    //Draw overlays for each contact
-    enumerator = [overlayObjectsArray reverseObjectEnumerator];
-    while ((object = [enumerator nextObject]) && !(top < 0) && bottom < 128) {
-        CGFloat			left, right, arcRadius, stringInset;
-        NSBezierPath	*path;
-        NSColor			*backColor = nil, *textColor = nil, *borderColor = nil;
-		
-        //Create the pill frame
-        arcRadius = (iconHeight / 2.0f);
-        stringInset = (iconHeight / 4.0f);
-        left = 1 + arcRadius;
-        right = 127 - arcRadius;
-		
-        path = [NSBezierPath bezierPath];
-        [path setLineWidth:((iconHeight/2.0f) * 0.13333f)];
-        //Top
-        [path moveToPoint: NSMakePoint(left, top)];
-        [path lineToPoint: NSMakePoint(right, top)];
-		
-        //Right rounded cap
-        [path appendBezierPathWithArcWithCenter:NSMakePoint(right, top - arcRadius) 
-										 radius:arcRadius
-									 startAngle:90
-									   endAngle:0
-									  clockwise:YES];
-        [path lineToPoint: NSMakePoint(right + arcRadius, bottom + arcRadius)];
-        [path appendBezierPathWithArcWithCenter:NSMakePoint(right, bottom + arcRadius) 
-										 radius:arcRadius
-									 startAngle:0
-									   endAngle:270
-									  clockwise:YES];
-		
-        //Bottom
-        [path moveToPoint: NSMakePoint(right, bottom)];
-        [path lineToPoint: NSMakePoint(left, bottom)];
-		
-        //Left rounded cap
-        [path appendBezierPathWithArcWithCenter:NSMakePoint(left, bottom + arcRadius)
-										 radius:arcRadius
-									 startAngle:270
-									   endAngle:180
-									  clockwise:YES];
-        [path lineToPoint: NSMakePoint(left - arcRadius, top - arcRadius)];
-        [path appendBezierPathWithArcWithCenter:NSMakePoint(left, top - arcRadius) radius:arcRadius startAngle:180 endAngle:90 clockwise:YES];
-
-        if ([object integerValueForProperty:KEY_UNVIEWED_CONTENT]) { //Unviewed
-			if (flash) {
-                backColor = [NSColor whiteColor];
-                textColor = [NSColor blackColor];
-            } else {
-                backColor = backUnviewedContentColor;
-                textColor = unviewedContentColor;
-            }
-        } else if ([object boolValueForProperty:@"signedOn"]) { //Signed on
-            backColor = backSignedOnColor;
-            textColor = signedOnColor;
-			
-        } else if ([object boolValueForProperty:@"signedOff"]) { //Signed off
-            backColor = backSignedOffColor;
-            textColor = signedOffColor;
-			
-        }
-		
-		if (!backColor) {
-			backColor = [NSColor whiteColor];
-		}
-		if (!textColor) {
-			textColor = [NSColor blackColor];
-		}
-		
-        //Lighten/Darken the back color slightly
-        if ([backColor colorIsDark]) {
-            backColor = [backColor darkenBy:-0.15f];
-            borderColor = [backColor darkenBy:-0.3f];
-        } else {
-            backColor = [backColor darkenBy:0.15f];
-            borderColor = [backColor darkenBy:0.3f];
-        }
-		
-        //Draw
-        [backColor set];
-        [path fill];
-        [borderColor set];
-        [path stroke];
-		
-        //Get the object's display name
-        [object.displayName drawInRect:NSMakeRect(0 + stringInset, bottom + 1, 128 - (stringInset * 2), top - bottom)
-                           withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, paragraphStyle, NSParagraphStyleAttributeName, textColor, NSForegroundColorAttributeName, nil]];
-		/*        
-			nameString = [[[NSAttributedString alloc] initWithString:contact.displayName attributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, paragraphStyle, NSParagraphStyleAttributeName, textColor, NSForegroundColorAttributeName, nil]] autorelease];
-        [nameString drawInRect:NSMakeRect(0 + stringInset, bottom + 1, 128 - (stringInset * 2), top - bottom)];*/
-		
-        //Move down to the next pill
-		top -= (iconHeight + 7.0f * dockIconScale);
-		bottom = top - iconHeight;
-    }
-	
-    [image unlockFocus];
-    
-    return image;
-}
-
-@end
--- a/Plugins/Contact Status Dock Overlays/DockOverlayDefaults.plist	Fri Feb 18 01:01:13 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>Show status overlays</key>
-	<true/>
-	<key>Show content overlays</key>
-	<true/>
-	<key>Overlay Position</key>
-	<true/>
- </dict>
-</plist>
--- a/Plugins/Dock Icon Badging/AIDockBadger.h	Fri Feb 18 01:01:13 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/* 
- * Adium is the legal property of its developers, whose names are listed in the copyright file included
- * with this source distribution.
- * 
- * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
- * General Public License as published by the Free Software Foundation; either version 2 of the License,
- * or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
- * Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License along with this program; if not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#import <Adium/AIChatControllerProtocol.h>
-
-@class AIIconState;
-
-@interface AIDockBadger : AIPlugin <AIChatObserver> {
-	AIIconState					*overlayState;
-	NSImage						*badgeTwoDigits, *badgeThreeDigits;
-	NSInteger							lastUnviewedContentCount;
-	
-	BOOL						shouldBadge;
-	BOOL						showConversationCount;
-}
-
-@end
--- a/Plugins/Dock Icon Badging/AIDockBadger.m	Fri Feb 18 01:01:13 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +0,0 @@
-/* 
- * Adium is the legal property of its developers, whose names are listed in the copyright file included
- * with this source distribution.
- * 
- * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
- * General Public License as published by the Free Software Foundation; either version 2 of the License,
- * or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
- * Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License along with this program; if not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#import "AIDockBadger.h"
-#import <Adium/AIChatControllerProtocol.h>
-#import <Adium/AIContactControllerProtocol.h>
-#import <Adium/AIContentControllerProtocol.h>
-#import <Adium/AIStatusControllerProtocol.h>
-#import "AIDockController.h"
-#import <Adium/AIInterfaceControllerProtocol.h>
-#import <Adium/AIContactAlertsControllerProtocol.h>
-#import <AIUtilities/AIColorAdditions.h>
-#import <AIUtilities/AIDictionaryAdditions.h>
-#import <AIUtilities/AIParagraphStyleAdditions.h>
-#import <AIUtilities/AIArrayAdditions.h>
-#import <AIUtilities/AIImageAdditions.h>
-#import <Adium/AIAbstractListController.h>
-#import <Adium/AIAccount.h>
-#import <Adium/AIChat.h>
-#import <Adium/AIIconState.h>
-
-@interface AIDockBadger ()
-- (void)removeOverlay;
-- (void)_setOverlay;
-@end
-
-@implementation AIDockBadger
-
-#pragma mark Birth and death
-
-/*!
- * @brief Install
- */
-- (void)installPlugin
-{
-	overlayState = nil;
-
-	//Register our default preferences
-    [adium.preferenceController registerDefaults:[NSDictionary dictionaryNamed:@"BadgerDefaults"
-																		forClass:[self class]] 
-										  forGroup:PREF_GROUP_APPEARANCE];
-
-	//Observe pref changes
-	[adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_APPEARANCE];
-	
-	// Register as an observer of the status preferences for unread conversation count
-	[adium.preferenceController registerPreferenceObserver:self
-													forGroup:PREF_GROUP_STATUS_PREFERENCES];
-}
-
-/*!
- * @brief Uninstall
- */
-- (void)uninstallPlugin
-{
-	[adium.chatController unregisterChatObserver:self];
-	[[NSNotificationCenter defaultCenter] removeObserver:self];
-	[adium.preferenceController unregisterPreferenceObserver:self];
-}
-
-#pragma mark Signals to update
-
-/*!
- * @brief Update our overlay when a chat updates with a relevant key
- */
-- (NSSet *)updateChat:(AIChat *)inChat keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
-{
-	if (!inModifiedKeys || [inModifiedKeys containsObject:KEY_UNVIEWED_CONTENT]) {
-		[self performSelector:@selector(_setOverlay)
-				   withObject:nil
-				   afterDelay:0];
-	}
-	
-	return nil;
-}
-
-/*!
- * @brief Update our overlay when a chat closes
- */
-- (void)chatClosed:(NSNotification *)notification
-{	
-	[self performSelector:@selector(_setOverlay)
-			   withObject:nil
-			   afterDelay:0];
-}
-
-#pragma mark Preference observing
-- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
-							object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
-{
-	if ([group isEqualToString:PREF_GROUP_APPEARANCE] && (!key || [key isEqualToString:KEY_BADGE_DOCK_ICON])) {
-		BOOL	newShouldBadge = [[prefDict objectForKey:KEY_BADGE_DOCK_ICON] boolValue];
-		if (newShouldBadge != shouldBadge) {
-			shouldBadge = newShouldBadge;
-			
-			if (shouldBadge) {
-				//Register as a chat observer (for unviewed content). If there is any unviewed content, our overlay will be set.
-				[adium.chatController registerChatObserver:self];
-				
-				[[NSNotificationCenter defaultCenter] addObserver:self
-											   selector:@selector(chatClosed:)
-												   name:Chat_WillClose
-												 object:nil];
-			} else {
-				//Remove any existing overlay
-				[self removeOverlay];
-				
-				//Stop observing
-				[adium.chatController unregisterChatObserver:self];
-				[[NSNotificationCenter defaultCenter] removeObserver:self];
-			}
-		}
-	}
-	
-	if ([group isEqualToString:PREF_GROUP_STATUS_PREFERENCES]) {
-		showConversationCount = [[prefDict objectForKey:KEY_STATUS_CONVERSATION_COUNT] boolValue];
-		
-		[self _setOverlay];
-	}
-}	
-
-#pragma mark Work methods
-
-- (NSImage *)numberedBadge:(NSInteger)count
-{
-	if(!badgeTwoDigits) {
-		badgeTwoDigits = [[NSImage imageNamed:@"newContentTwoDigits"] retain];
-		badgeThreeDigits = [[NSImage imageNamed:@"newContentThreeDigits"] retain];
-	}
-
-	NSImage		*badge = nil, *badgeToComposite = nil;
-	NSString	*numString = nil;
-
-	//999 unread messages should be enough for anyone
-	if (count >= 1000) {
-		count = 999;
-	}
-
-	badgeToComposite = ((count < 10) ? badgeTwoDigits : badgeThreeDigits);
-	numString = [[NSNumber numberWithInteger:count] description];
-
-	NSRect rect = { NSZeroPoint, [badgeToComposite size] };
-	NSFont *font = [NSFont fontWithName:@"Helvetica-Bold" size:24];
-	
-	if (!font) font = [NSFont systemFontOfSize:24];
-	
-	NSDictionary *atts = [NSDictionary dictionaryWithObjectsAndKeys:
-		[NSColor whiteColor], NSForegroundColorAttributeName,
-		font, NSFontAttributeName,
-		nil];
-	
-	NSSize numSize = [numString sizeWithAttributes:atts];
-	rect.origin.x = (rect.size.width / 2) - (numSize.width / 2);
-	rect.origin.y = (rect.size.height / 2) - (numSize.height / 2);
-
-	badge = [[NSImage alloc] initWithSize:rect.size];
-	[badge setFlipped:YES];
-	[badge lockFocus];
-	[badgeToComposite compositeToPoint:NSMakePoint(0, rect.size.height) operation:NSCompositeSourceOver];
-
-	[numString drawInRect:rect
-		   withAttributes:atts];
-	
-	[badge unlockFocus];
-		
-	return [badge autorelease];
-}
-
-/*!
- * @brief Remove any existing dock overlay
- */
-- (void)removeOverlay
-{
-	if (overlayState) {
-		[adium.dockController removeIconStateNamed:@"UnviewedContentCount"];
-		[overlayState release]; overlayState = nil;
-	}
-}
-
-/*!
- * @brief Update our overlay to the current unviewed content count
- */
-- (void)_setOverlay
-{
-	NSInteger contentCount = (showConversationCount ?
-					   [adium.chatController unviewedConversationCount] : [adium.chatController unviewedContentCount]);
-
-	if (contentCount != lastUnviewedContentCount) {
-		//Remove & release the current overlay state
-		[self removeOverlay];
-
-		//Create & set the new overlay state
-		if (contentCount > 0) {
-			//Set the state
-			overlayState = [[AIIconState alloc] initWithImage:[self numberedBadge:contentCount] 
-													  overlay:YES];
-			[adium.dockController setIconState:overlayState named:@"UnviewedContentCount"];
-		}
-
-		lastUnviewedContentCount = contentCount;
-	}
-}
-
-@end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Dock Icon Badging/AIDockNameOverlay.h	Fri Feb 18 15:44:06 2011 -0500
@@ -0,0 +1,20 @@
+#import <Adium/AIContactAlertsControllerProtocol.h>
+#import <Adium/AIContactObserverManager.h>
+#import <Adium/AIChatControllerProtocol.h>
+
+#define DOCK_OVERLAY_ALERT_IDENTIFIER		@"DockOverlay"
+
+@interface AIDockNameOverlay : AIPlugin <AIActionHandler, AIListObjectObserver, AIChatObserver> {
+@private
+	NSMutableArray *overlayObjectsArray;
+	
+	NSColor *signedOffColor;
+	NSColor *signedOnColor;
+	NSColor *unviewedContentColor;
+	
+	NSColor *backSignedOffColor;
+	NSColor *backSignedOnColor;
+	NSColor *backUnviewedContentColor;
+}
+
+@end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Dock Icon Badging/AIDockNameOverlay.m	Fri Feb 18 15:44:06 2011 -0500
@@ -0,0 +1,342 @@
+#import "AIDockNameOverlay.h"
+#import "AIDockController.h"
+#import <Adium/AIChatControllerProtocol.h>
+#import <Adium/AIContactAlertsControllerProtocol.h>
+#import <Adium/AIStatusControllerProtocol.h>
+#import <AIUtilities/AIParagraphStyleAdditions.h>
+#import <AIUtilities/AIImageAdditions.h>
+#import <Adium/AIChat.h>
+#import <Adium/AIAccount.h>
+#import <AIUtilities/AIArrayAdditions.h>
+#import <Adium/AIAbstractListController.h>
+#import <AIUtilities/AIColorAdditions.h>
+
+
+#define DOCK_OVERLAY_ALERT_SHORT	AILocalizedString(@"Display name in the dock icon",nil)
+#define DOCK_OVERLAY_ALERT_LONG		DOCK_OVERLAY_ALERT_SHORT
+
+@interface AIDockNameOverlay ()
+- (void)flushPreferenceColorCache;
+- (void)drawOverlay;
+- (void)removeDockOverlay:(NSTimer *)removeTimer;
+@end
+
+@implementation AIDockNameOverlay
+- (void)installPlugin
+{
+	//Install our contact alert
+	[adium.contactAlertsController registerActionID:DOCK_OVERLAY_ALERT_IDENTIFIER withHandler:self];
+
+	overlayObjectsArray = [[NSMutableArray alloc] init];
+	
+	//Register as a contact observer (For signed on / signed off)
+	[[AIContactObserverManager sharedManager] registerListObjectObserver:self];
+	
+	[adium.chatController registerChatObserver:self];
+	
+	//Observe pref changes
+	[adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_APPEARANCE];
+	[adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_LIST_THEME];
+}
+
+- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
+							object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
+{	
+	if ([group isEqualToString:PREF_GROUP_LIST_THEME]) {
+		//Grab colors from status coloring plugin's prefs
+		[self flushPreferenceColorCache];
+		signedOffColor = [[[prefDict objectForKey:KEY_SIGNED_OFF_COLOR] representedColor] retain];
+		signedOnColor = [[[prefDict objectForKey:KEY_SIGNED_ON_COLOR] representedColor] retain];
+		unviewedContentColor = [[[prefDict objectForKey:KEY_UNVIEWED_COLOR] representedColor] retain];
+		
+		backSignedOffColor = [[[prefDict objectForKey:KEY_LABEL_SIGNED_OFF_COLOR] representedColor] retain];
+		backSignedOnColor = [[[prefDict objectForKey:KEY_LABEL_SIGNED_ON_COLOR] representedColor] retain];
+		backUnviewedContentColor = [[[prefDict objectForKey:KEY_LABEL_UNVIEWED_COLOR] representedColor] retain];
+	}
+}
+
+- (void)flushPreferenceColorCache
+{
+	[signedOffColor release]; signedOffColor = nil;
+	[signedOnColor release]; signedOnColor = nil;
+	[unviewedContentColor release]; unviewedContentColor = nil;
+	[backSignedOffColor release]; backSignedOffColor = nil;
+	[backSignedOnColor release]; backSignedOnColor = nil;
+	[backUnviewedContentColor release]; backUnviewedContentColor = nil;
+}
+
+- (void)uninstallPlugin
+{
+	[adium.preferenceController unregisterPreferenceObserver:self];
+	[adium.chatController unregisterChatObserver:self];
+}
+
+/*!
+ * @brief Short description
+ * @result A short localized description of the action
+ */
+- (NSString *)shortDescriptionForActionID:(NSString *)actionID
+{
+	return DOCK_OVERLAY_ALERT_SHORT;
+}
+
+/*!
+ * @brief Long description
+ * @result A longer localized description of the action which should take into account the details dictionary as appropraite.
+ */
+- (NSString *)longDescriptionForActionID:(NSString *)actionID withDetails:(NSDictionary *)details
+{
+	return DOCK_OVERLAY_ALERT_LONG;
+}
+
+/*!
+ * @brief Image
+ */
+- (NSImage *)imageForActionID:(NSString *)actionID
+{
+	return [NSImage imageNamed:@"DockAlert" forClass:[self class]];
+}
+
+/*!
+ * @brief Details pane
+ * @result An <tt>AIModularPane</tt> to use for configuring this action, or nil if no configuration is possible.
+ */
+- (AIModularPane *)detailsPaneForActionID:(NSString *)actionID
+{
+	return nil;
+}
+
+/*!
+ * @brief Perform an action
+ *
+ * @param actionID The ID of the action to perform
+ * @param listObject The listObject associated with the event triggering the action. It may be nil
+ * @param details If set by the details pane when the action was created, the details dictionary for this particular action
+ * @param eventID The eventID which triggered this action
+ * @param userInfo Additional information associated with the event; userInfo's type will vary with the actionID.
+ */
+- (BOOL)performActionID:(NSString *)actionID forListObject:(AIListObject *)listObject withDetails:(NSDictionary *)details triggeringEventID:(NSString *)eventID userInfo:(id)userInfo
+{
+	BOOL isMessageEvent = [adium.contactAlertsController isMessageEvent:eventID];
+	
+	if (isMessageEvent) {
+		AIChat *chat;
+		
+		if ((chat = [userInfo objectForKey:@"AIChat"]) &&
+			(chat != adium.interfaceController.activeChat)) {
+			if (![overlayObjectsArray containsObjectIdenticalTo:chat])
+				[overlayObjectsArray addObject:chat];
+			
+			//Wait until the next run loop so that this event is done processing and our unviewed content count is correct
+			[self performSelector:@selector(drawOverlay)
+					   withObject:nil
+					   afterDelay:0];
+		}
+		
+	} else if (listObject) {
+		NSTimer *removeTimer;
+		
+		//Clear any current timer for this object to have its overlay removed
+		if ((removeTimer = [listObject valueForProperty:@"DockOverlayRemoveTimer"])) [removeTimer invalidate];
+		
+		//Add a timer to remove this overlay
+		removeTimer = [NSTimer scheduledTimerWithTimeInterval:5
+													   target:self
+													 selector:@selector(removeDockOverlay:)
+													 userInfo:listObject
+													  repeats:NO];
+		[listObject setValue:removeTimer
+				 forProperty:@"DockOverlayRemoveTimer"
+					  notify:NotifyNever];
+		
+		if (![overlayObjectsArray containsObjectIdenticalTo:listObject])
+			[overlayObjectsArray addObject:listObject];
+		
+		[self drawOverlay];
+	}
+	
+	return YES;
+}
+
+/*!
+ * @brief Allow multiple actions?
+ *
+ * If this method returns YES, every one of this action associated with the triggering event will be executed.
+ * If this method returns NO, only the first will be.
+ */
+- (BOOL)allowMultipleActionsWithID:(NSString *)actionID
+{
+	return NO;
+}
+
+/*!
+ * @brief When a chat no longer has unviewed content remove it from display
+ */
+- (NSSet *)updateChat:(AIChat *)inChat keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
+{
+	if (inModifiedKeys == nil || [inModifiedKeys containsObject:KEY_UNVIEWED_CONTENT]) {
+		if (![inChat unviewedContentCount]) {
+			[overlayObjectsArray removeObjectIdenticalTo:inChat];
+			[self drawOverlay];
+		}
+	}
+	
+	return nil;
+}
+
+/*!
+ * @brief When an account signs on or off force an overlay update as its contacts statuses may have silently changed
+ */
+- (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
+{
+	if ([inObject isKindOfClass:[AIAccount class]]) {
+		if ([inModifiedKeys containsObject:@"isOnline"]) {
+			BOOL madeChanges = NO;
+			
+			for (AIListObject *listObject in [[overlayObjectsArray copy] autorelease]) {
+				if (([listObject respondsToSelector:@selector(account)]) &&
+					([(id)listObject account] == inObject) &&
+					([overlayObjectsArray containsObjectIdenticalTo:listObject])) {
+					[overlayObjectsArray removeObjectIdenticalTo:listObject];
+					madeChanges = YES;
+				}
+			}
+			
+			if (madeChanges) [self drawOverlay];
+		}
+	}
+	
+	return nil;
+}
+
+- (void)removeDockOverlay:(NSTimer *)removeTimer
+{
+	AIListObject *inObject = [removeTimer userInfo];
+	[overlayObjectsArray removeObjectIdenticalTo:inObject];
+	
+	[inObject setValue:nil
+		   forProperty:@"DockOverlayRemoveTimer"
+				notify:NotifyNever];
+	
+	[self drawOverlay];
+}
+
+- (void)drawOverlay
+{
+	NSFont				*font;
+	NSParagraphStyle	*paragraphStyle;
+	CGFloat				iconHeight;
+	CGFloat				top, bottom;
+	NSImage				*image = [[NSImage alloc] initWithSize:NSMakeSize(128, 128)];
+	
+	iconHeight = 30.0f;
+	bottom = 2;
+	top = bottom + iconHeight;
+	
+	//Set up the string details
+	font = [NSFont boldSystemFontOfSize:24.0f];
+	paragraphStyle = [NSParagraphStyle styleWithAlignment:NSCenterTextAlignment lineBreakMode:NSLineBreakByClipping];
+	
+	[image lockFocus];
+	
+	//Clear our image
+	[[NSColor clearColor] set];
+	NSRectFillUsingOperation(NSMakeRect(0, 0, 128, 128), NSCompositeCopy);
+	
+	//Draw overlays for each contact
+	for (ESObjectWithProperties *object in [overlayObjectsArray reverseObjectEnumerator]) {
+		if (top >= 128)
+			break;
+		
+		CGFloat			left, right, arcRadius, stringInset;
+		NSBezierPath	*path;
+		NSColor			*backColor = nil, *textColor = nil, *borderColor = nil;
+		
+		//Create the pill frame
+		arcRadius = (iconHeight / 2.0f);
+		stringInset = (iconHeight / 4.0f);
+		left = 1 + arcRadius;
+		right = 127 - arcRadius;
+		
+		path = [NSBezierPath bezierPath];
+		[path setLineWidth:((iconHeight/2.0f) * 0.13333f)];
+		//Top
+		[path moveToPoint: NSMakePoint(left, top)];
+		[path lineToPoint: NSMakePoint(right, top)];
+		
+		//Right rounded cap
+		[path appendBezierPathWithArcWithCenter:NSMakePoint(right, top - arcRadius)
+										 radius:arcRadius
+									 startAngle:90
+									   endAngle:0
+									  clockwise:YES];
+		[path lineToPoint: NSMakePoint(right + arcRadius, bottom + arcRadius)];
+		[path appendBezierPathWithArcWithCenter:NSMakePoint(right, bottom + arcRadius)
+										 radius:arcRadius
+									 startAngle:0
+									   endAngle:270
+									  clockwise:YES];
+		
+		//Bottom
+		[path moveToPoint: NSMakePoint(right, bottom)];
+		[path lineToPoint: NSMakePoint(left, bottom)];
+		
+		//Left rounded cap
+		[path appendBezierPathWithArcWithCenter:NSMakePoint(left, bottom + arcRadius)
+										 radius:arcRadius
+									 startAngle:270
+									   endAngle:180
+									  clockwise:YES];
+		[path lineToPoint: NSMakePoint(left - arcRadius, top - arcRadius)];
+		[path appendBezierPathWithArcWithCenter:NSMakePoint(left, top - arcRadius)
+										 radius:arcRadius
+									 startAngle:180
+									   endAngle:90
+									  clockwise:YES];
+		
+		if ([object integerValueForProperty:KEY_UNVIEWED_CONTENT]) { //Unviewed
+			backColor = backUnviewedContentColor;
+			textColor = unviewedContentColor;
+		} else if ([object boolValueForProperty:@"signedOn"]) { //Signed on
+			backColor = backSignedOnColor;
+			textColor = signedOnColor;
+		} else if ([object boolValueForProperty:@"signedOff"]) { //Signed off
+			backColor = backSignedOffColor;
+			textColor = signedOffColor;
+		}
+		
+		if (!backColor)
+			backColor = [NSColor whiteColor];
+		if (!textColor)
+			textColor = [NSColor blackColor];
+		
+		//Lighten/Darken the back color slightly
+		if ([backColor colorIsDark]) {
+			backColor = [backColor darkenBy:-0.15f];
+			borderColor = [backColor darkenBy:-0.3f];
+		} else {
+			backColor = [backColor darkenBy:0.15f];
+			borderColor = [backColor darkenBy:0.3f];
+		}
+		
+		//Draw
+		[backColor set];
+		[path fill];
+		[borderColor set];
+		[path stroke];
+		
+		//Get the object's display name
+		[object.displayName drawInRect:NSMakeRect(stringInset + 1, bottom - 1, 127 - (stringInset * 2), top - bottom)
+						withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, paragraphStyle, NSParagraphStyleAttributeName, textColor, NSForegroundColorAttributeName, nil]];
+		
+		//Move up to the next pill
+		bottom = top + 3.0f;
+		top = bottom + iconHeight;
+	}
+	
+	[image unlockFocus];
+	
+	[adium.dockController setOverlay:image];
+}
+
+@end
Binary file Plugins/Dock Icon Badging/newContentThreeDigits.png has changed
Binary file Plugins/Dock Icon Badging/newContentTwoDigits.png has changed
--- a/Resources/BadgerDefaults.plist	Fri Feb 18 01:01:13 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>Badge Dock Icon on Unread Messages</key>
-	<true/>
-</dict>
-</plist>
--- a/Resources/DockPrefs.plist	Fri Feb 18 01:01:13 2011 +0100
+++ b/Resources/DockPrefs.plist	Fri Feb 18 15:44:06 2011 -0500
@@ -1,7 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>Badge Dock Icon on Unread Messages</key>
+	<true/>
+	<key>Animate Dock Icon on Unread Messages</key>
+	<true/>
 	<key>Dock Icon</key>
 	<string>Adiumy Green</string>
 </dict>
--- a/Resources/DockUnviewedContentDefaults.plist	Fri Feb 18 01:01:13 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>Animate Dock Icon on Unread Messages</key>
-	<true/>
-</dict>
-</plist>
--- a/Source/AICoreComponentLoader.m	Fri Feb 18 01:01:13 2011 +0100
+++ b/Source/AICoreComponentLoader.m	Fri Feb 18 15:44:06 2011 -0500
@@ -81,11 +81,10 @@
 		@"AIContactOnlineSincePlugin",
 		@"AIContactSortSelectionPlugin",
 		@"AIContactStatusColoringPlugin",
-		@"AIContactStatusDockOverlaysPlugin",
+		@"AIDockNameOverlay",
 		@"AIContactStatusEventsPlugin",
 		@"AIDockAccountStatusPlugin",
 		@"AIDockBehaviorPlugin",
-		@"AIDockUnviewedContentPlugin",
 		@"AIDualWindowInterfacePlugin",
 		@"AIEventSoundsPlugin",
 		@"AIExtendedStatusPlugin",
@@ -136,7 +135,6 @@
 		@"RAFBlockEditorPlugin",
 		@"SMContactListShowBehaviorPlugin",
 		@"ESiTunesPlugin",
-		@"AIDockBadger",
 		@"ESPersonalPreferencesPlugin",
 		@"AIXtrasManager",
 		@"OWSpellingPerContactPlugin",
--- a/Source/AIDockController.h	Fri Feb 18 01:01:13 2011 +0100
+++ b/Source/AIDockController.h	Fri Feb 18 15:44:06 2011 -0500
@@ -16,11 +16,14 @@
 
 #import <Adium/AIDockControllerProtocol.h>
 #import <Adium/AIInterfaceControllerProtocol.h>
+#import <Adium/AIChatControllerProtocol.h>
 
-@interface AIDockController: NSObject <AIDockController, AIFlashObserver> {
+
+@interface AIDockController: NSObject <AIDockController, AIFlashObserver, AIChatObserver> {
 @private
     NSTimer 				*animationTimer;
     NSTimer					*bounceTimer;
+    NSTimeInterval			currentBounceInterval;
     
     NSMutableDictionary		*availableIconStateDict;
     NSMutableDictionary		*availableDynamicIconStateDict;
@@ -30,9 +33,16 @@
     NSInteger						currentAttentionRequest;
 	
     BOOL					observingFlash;
-    BOOL					needsDisplay;
+	BOOL					needsDisplay;
 	
-	NSTimeInterval			currentBounceInterval;
+	NSDockTile *dockTile;
+	NSImageView *view;
+	NSImage *overlay;
+	
+	BOOL showConversationCount;
+	BOOL shouldBadge;
+	BOOL unviewedState;
+	BOOL animateDockIcon;
 }
 
 @end
--- a/Source/AIDockController.m	Fri Feb 18 01:01:13 2011 +0100
+++ b/Source/AIDockController.m	Fri Feb 18 15:44:06 2011 -0500
@@ -21,7 +21,10 @@
 #import <AIUtilities/AIDictionaryAdditions.h>
 #import <AIUtilities/AIFileManagerAdditions.h>
 #import <AIUtilities/AIApplicationAdditions.h>
+#import <Adium/AIChatControllerProtocol.h>
 #import <Adium/AIIconState.h>
+#import <Adium/AIChat.h>
+#import <Adium/AIStatusControllerProtocol.h>
 
 #define DOCK_DEFAULT_PREFS			@"DockPrefs"
 #define ICON_DISPLAY_DELAY			0.1
@@ -32,6 +35,9 @@
 #define SINGLE_BOUNCE_INTERVAL		999
 #define NO_BOUNCE_INTERVAL			1000
 
+#define DOCK_ICON_INTERNAL_PATH		@"../Shared Images/"
+#define DOCK_ICON_SHARED_IMAGES		@"Shared Dock Icon Images"
+
 @interface AIDockController ()
 - (void)_setNeedsDisplay;
 - (void)_buildIcon;
@@ -42,6 +48,9 @@
 - (BOOL)_bounceWithInterval:(double)delay;
 - (AIIconState *)iconStateFromStateDict:(NSDictionary *)stateDict folderPath:(NSString *)folderPath;
 - (void)updateAppBundleIcon;
+- (void)updateDockView;
+- (void)updateDockBadge;
+- (void)animateDockIcon;
 
 - (void)appWillChangeActive:(NSNotification *)notification;
 - (void)bounceWithTimer:(NSTimer *)timer;
@@ -61,6 +70,7 @@
 		animationTimer = nil;
 		bounceTimer = nil;
 		needsDisplay = NO;
+		unviewedState = NO;
 	}
 	
 	return self;
@@ -68,18 +78,28 @@
 
 - (void)controllerDidLoad
 {
+	dockTile = [NSApp dockTile];
+	view = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 128, 128)];
+	
+	[dockTile setContentView:view];
+
+	//Register our default preferences
+	[adium.preferenceController registerDefaults:[NSDictionary dictionaryNamed:DOCK_DEFAULT_PREFS
+																	  forClass:[self class]] 
+										forGroup:PREF_GROUP_APPEARANCE];
+	
+	//Observe pref changes
+	[adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_APPEARANCE];
+	// Register as an observer of the status preferences for unread conversation count
+	[adium.preferenceController registerPreferenceObserver:self
+												  forGroup:PREF_GROUP_STATUS_PREFERENCES];
+	
+	[adium.chatController registerChatObserver:self];
+	
 	NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
 	
-    //Register our default preferences
-    [adium.preferenceController registerDefaults:[NSDictionary dictionaryNamed:DOCK_DEFAULT_PREFS
-																		forClass:[self class]] 
-										  forGroup:PREF_GROUP_APPEARANCE];
-    
-    //Observe pref changes
-	[adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_APPEARANCE];
-	
-    //We always want to stop bouncing when Adium is made active
-    [notificationCenter addObserver:self
+	//We always want to stop bouncing when Adium is made active
+	[notificationCenter addObserver:self
 	                       selector:@selector(appWillChangeActive:) 
 	                           name:NSApplicationWillBecomeActiveNotification 
 	                         object:nil];
@@ -90,7 +110,7 @@
 	                           name:NSApplicationWillResignActiveNotification 
 	                         object:nil];
 	
-	//If Adium has been upgraded since the last time we ran, re-apply the user's custom icon
+	//If Adium has been upgraded since the last time we ran re-apply the user's custom icon
 	NSString	*lastVersion = [[NSUserDefaults standardUserDefaults] objectForKey:LAST_ICON_UPDATE_VERSION];
 	if (![[NSApp applicationVersion] isEqualToString:lastVersion]) {
 		[self updateAppBundleIcon];
@@ -101,6 +121,7 @@
 - (void)controllerWillClose
 {
 	[adium.preferenceController unregisterPreferenceObserver:self];
+	[adium.chatController unregisterChatObserver:self];
 
 	//Reset our icon by removing all icon states (except for the base state)
 	NSArray *stateArrayCopy = [[activeIconStateArray copy] autorelease]; //Work with a copy, since this array will change as we remove states
@@ -115,6 +136,7 @@
 }
 
 
+#pragma mark Dock Icon Packs
 /*!
  * @brief Returns an array of available dock icon pack paths
  */
@@ -130,69 +152,6 @@
 	return iconPackPaths;
 }
 
-
-
-- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
-							object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
-{
-	if (!key || [key isEqualToString:KEY_ACTIVE_DOCK_ICON]) {		
-		//Load the new icon pack
-		NSString *iconPath = [adium pathOfPackWithName:[prefDict objectForKey:KEY_ACTIVE_DOCK_ICON]
-											 extension:@"AdiumIcon"
-									resourceFolderName:FOLDER_DOCK_ICONS];
-
-		if (iconPath) {
-			NSMutableDictionary	*newAvailableIconStateDict = [self iconPackAtPath:iconPath];
-			if (newAvailableIconStateDict) {
-				[availableIconStateDict autorelease]; 
-				availableIconStateDict = [newAvailableIconStateDict retain];
-			}
-		}
-		
-		//Write the icon to the Adium application bundle so finder will see it
-		//On launch we only need to update the icon file if this is a new version of Adium.  When preferences
-		//change we always want to update it
-		if (!firstTime) {
-			[self updateAppBundleIcon];
-		}
-
-		//Recomposite the icon
-		[self _setNeedsDisplay];
-	}
-}
-
-- (void)updateAppBundleIcon
-{	
-	NSImage *image = [[[availableIconStateDict objectForKey:@"State"] objectForKey:@"ApplicationIcon"] image];
-	if (!image) 
-		image = [[[availableIconStateDict objectForKey:@"State"] objectForKey:@"Base"] image];
-
-	if (image) {
-		[[NSWorkspace sharedWorkspace] setIcon:image 
-									   forFile:[[NSBundle mainBundle] bundlePath]
-									   options:0];
-
-		//Finder won't update Adium's icon to match the new one until it is restarted if we don't
-		//tell NSWorkspace to note the change.
-		[[NSWorkspace sharedWorkspace] noteFileSystemChanged:[[NSBundle mainBundle] bundlePath]];
-	}
-}
-
-//Icons ------------------------------------------------------------------------------------
-- (void)_setNeedsDisplay
-{
-	if (!needsDisplay) {
-		needsDisplay = YES;
-
-		//Invoke a display after a short delay
-		[NSTimer scheduledTimerWithTimeInterval:ICON_DISPLAY_DELAY
-										 target:self
-									   selector:@selector(_buildIcon)
-									   userInfo:nil
-										repeats:NO];
-	}
-}
-
 //Load an icon pack
 - (NSMutableDictionary *)iconPackAtPath:(NSString *)folderPath
 {
@@ -212,15 +171,24 @@
 	return [NSMutableDictionary dictionaryWithObjectsAndKeys:[iconPackDict objectForKey:@"Description"], @"Description", iconStateDict, @"State", nil];
 }
 
+- (AIIconState *)previewStateForIconPackAtPath:(NSString *)folderPath
+{
+	AIIconState	*previewState = nil;
+	
+	[self getName:NULL previewState:&previewState forIconPackAtPath:folderPath];
+	
+	return previewState;
+}
+
 /*!
- * @brief Get the name and preview steate for a dock icon pack
+ * @brief Get the name and preview state for a dock icon pack
  *
  * @param outName Reference to an NSString, or NULL if this information is not needed
  * @param outIconState Reference to an AIIconState, or NULL if this information is not needed
  * @param folderPath The path to the dock icon pack
  */
 - (void)getName:(NSString **)outName previewState:(AIIconState **)outIconState forIconPackAtPath:(NSString *)folderPath
-{	
+{
 	//Load the icon pack
 	NSDictionary *iconPackDict = [NSDictionary dictionaryWithContentsOfFile:[folderPath stringByAppendingPathComponent:@"IconPack.plist"]];
 	
@@ -231,25 +199,16 @@
 	if (outName) *outName = [[iconPackDict objectForKey:@"Description"] objectForKey:@"Title"];
 }
 
-- (AIIconState *)previewStateForIconPackAtPath:(NSString *)folderPath
-{
-	AIIconState	*previewState = nil;
-	
-	[self getName:NULL previewState:&previewState forIconPackAtPath:folderPath];
-	
-	return previewState;
-}
-
 - (AIIconState *)iconStateFromStateDict:(NSDictionary *)stateDict folderPath:(NSString *)folderPath
 {
 	AIIconState		*iconState = nil;
+	//Get the state information
+	BOOL _overlay = [[stateDict objectForKey:@"Overlay"] boolValue];
+	BOOL looping = [[stateDict objectForKey:@"Looping"] boolValue];
 	
 	if ([[stateDict objectForKey:@"Animated"] integerValue]) { //Animated State
 		NSMutableDictionary	*tempIconCache = [NSMutableDictionary dictionary];
 		
-		//Get the state information
-		BOOL overlay = [[stateDict objectForKey:@"Overlay"] boolValue];
-		BOOL looping = [[stateDict objectForKey:@"Looping"] boolValue];
 		CGFloat delay   = (CGFloat)[[stateDict objectForKey:@"Delay"] doubleValue];
 		NSArray *imageNameArray = [stateDict objectForKey:@"Images"];
 
@@ -258,20 +217,18 @@
 		for (NSString *imageName in imageNameArray) {
 			NSString	*imagePath;
 			
-#define DOCK_ICON_INTERNAL_PATH @"../Shared Images/"
 			if ([imageName hasPrefix:DOCK_ICON_INTERNAL_PATH]) {
 				//Special hack for all the incorrectly made icon packs we have floating around out there :P
 				imageName = [imageName substringFromIndex:[DOCK_ICON_INTERNAL_PATH length]];
 				imagePath = [[NSBundle mainBundle] pathForResource:[[[imageName stringByDeletingPathExtension] stringByAppendingString:@"-localized"] stringByAppendingPathExtension:[imageName pathExtension]]
 				                                            ofType:@""
-				                                       inDirectory:@"Shared Dock Icon Images"];
+				                                       inDirectory:DOCK_ICON_SHARED_IMAGES];
 				
 				if (!imagePath) {
 					imagePath = [[NSBundle mainBundle] pathForResource:imageName
 																ofType:@""
-														   inDirectory:@"Shared Dock Icon Images"];
+														   inDirectory:DOCK_ICON_SHARED_IMAGES];
 				}
-
 			} else {
 				imagePath = [folderPath stringByAppendingPathComponent:imageName];
 			}
@@ -279,10 +236,12 @@
 			NSImage *image = [tempIconCache objectForKey:imagePath]; //We re-use the same images for each state if possible to lower memory usage.
 			if (!image && imagePath) {
 				image = [[[NSImage alloc] initByReferencingFile:imagePath] autorelease];
-				if (image) [tempIconCache setObject:image forKey:imagePath];
+				if (image)
+					[tempIconCache setObject:image forKey:imagePath];
 			}
 			
-			if (image) [imageArray addObject:image];
+			if (image)
+				[imageArray addObject:image];
 		}
 		
 		//Create the state
@@ -290,16 +249,14 @@
 			iconState = [[AIIconState alloc] initWithImages:imageArray
 													  delay:delay
 													looping:looping
-													overlay:overlay];
+													overlay:_overlay];
 		} else {
 			NSLog(@"Invalid animated icon state");
 		}
-		
 	} else { //Static State
 		NSString	*imageName;
 		NSString	*imagePath;
 		NSImage		*image;
-		BOOL		overlay;
 		
 		imageName = [stateDict objectForKey:@"Image"];
 		
@@ -308,11 +265,11 @@
 			imageName = [imageName substringFromIndex:[DOCK_ICON_INTERNAL_PATH length]];
 			imagePath = [[NSBundle mainBundle] pathForResource:[[[imageName stringByDeletingPathExtension] stringByAppendingString:@"-localized"] stringByAppendingPathExtension:[imageName pathExtension]]
 														ofType:@""
-												   inDirectory:@"Shared Dock Icon Images"];
+												   inDirectory:DOCK_ICON_SHARED_IMAGES];
 			if (!imagePath) {
 				imagePath = [[NSBundle mainBundle] pathForResource:imageName
-			                                            ofType:@""
-			                                       inDirectory:@"Shared Dock Icon Images"];
+															ofType:@""
+													   inDirectory:DOCK_ICON_SHARED_IMAGES];
 			}
 		} else {
 			imagePath = [folderPath stringByAppendingPathComponent:imageName];
@@ -320,35 +277,15 @@
 
 		//Get the state information
 		image = [[NSImage alloc] initByReferencingFile:imagePath];
-		overlay = [[stateDict objectForKey:@"Overlay"] boolValue];
 		
 		//Create the state
-		iconState = [[AIIconState alloc] initWithImage:image overlay:overlay];		
+		iconState = [[AIIconState alloc] initWithImage:image overlay:_overlay];
 		[image release];
 	}
 
 	return [iconState autorelease];
 }
 
-//Set an icon state from our currently loaded icon pack
-- (void)setIconStateNamed:(NSString *)inName
-{
-	if (![activeIconStateArray containsObject:inName]) {
-		[activeIconStateArray addObject:inName]; 	//Add the name to our array
-		[self _setNeedsDisplay];			//Redisplay our icon
-	}
-}
-
-//Remove an active icon state
-- (void)removeIconStateNamed:(NSString *)inName
-{
-	if ([activeIconStateArray containsObject:inName]) {
-		[activeIconStateArray removeObject:inName]; 	//Remove the name from our array
-		
-		[self _setNeedsDisplay];			//Redisplay our icon
-	}
-}
-
 /*!
  * @brief Does the current icon know how to display a given state?
  */
@@ -357,11 +294,147 @@
 	return ([[availableIconStateDict objectForKey:@"State"] objectForKey:inName] != nil);
 }
 
+//Set an icon state from our currently loaded icon pack
+- (void)setIconStateNamed:(NSString *)inName
+{
+	if (![activeIconStateArray containsObject:inName]) {
+		[activeIconStateArray addObject:inName];
+		[self _setNeedsDisplay];
+	}
+}
+
+//Remove an active icon state
+- (void)removeIconStateNamed:(NSString *)inName
+{
+	if ([activeIconStateArray containsObject:inName]) {
+		[activeIconStateArray removeObject:inName];
+		[self _setNeedsDisplay];
+	}
+}
+
 //Set a custom icon state
 - (void)setIconState:(AIIconState *)iconState named:(NSString *)inName
 {
-    [availableDynamicIconStateDict setObject:iconState forKey:inName]; 	//Add the new state to our available dict
-    [self setIconStateNamed:inName];					//Set it
+	[availableDynamicIconStateDict setObject:iconState forKey:inName]; //Add the new state to our available dict
+	[self setIconStateNamed:inName]; //Set it
+}
+
+#pragma mark Controller
+- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
+							object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
+{
+	if ([group isEqualToString:PREF_GROUP_APPEARANCE]) {
+		if (!key || [key isEqualToString:KEY_ACTIVE_DOCK_ICON]) {
+			//Load the new icon pack
+			NSString *iconPath = [adium pathOfPackWithName:[prefDict objectForKey:KEY_ACTIVE_DOCK_ICON]
+												 extension:@"AdiumIcon"
+										resourceFolderName:FOLDER_DOCK_ICONS];
+
+			if (iconPath) {
+				NSMutableDictionary	*newAvailableIconStateDict = [self iconPackAtPath:iconPath];
+				if (newAvailableIconStateDict) {
+					[availableIconStateDict autorelease];
+					availableIconStateDict = [newAvailableIconStateDict retain];
+				}
+			}
+			
+			//Write the icon to the Adium application bundle so that Finder will see it.
+			//On launch we only need to update the icon file if this is a new version of Adium.
+			//When preferences change we always want to update it
+			if (!firstTime) {
+				[self updateAppBundleIcon];
+			}
+
+			//Recomposite the icon
+			[self _setNeedsDisplay];
+		}
+		else if (!key || [key isEqualToString:KEY_BADGE_DOCK_ICON]) {
+			BOOL newShouldBadge = [[prefDict objectForKey:KEY_BADGE_DOCK_ICON] boolValue];
+			if (newShouldBadge != shouldBadge) {
+				shouldBadge = newShouldBadge;
+				
+				[self updateDockBadge];
+			}
+		}
+		else if (!key || [key isEqualToString:KEY_ANIMATE_DOCK_ICON]) {
+			BOOL newAnimateDockIcon = [[prefDict objectForKey:KEY_ANIMATE_DOCK_ICON] boolValue];
+			if (newAnimateDockIcon != animateDockIcon) {
+				animateDockIcon = newAnimateDockIcon;
+				
+				[self animateDockIcon];
+			}
+		}
+	}
+	
+	if ([group isEqualToString:PREF_GROUP_STATUS_PREFERENCES]) {
+		if (!key || [key isEqualToString:KEY_STATUS_CONVERSATION_COUNT]) {
+			BOOL newShowConversationCount = [[prefDict objectForKey:KEY_STATUS_CONVERSATION_COUNT] boolValue];
+			if (newShowConversationCount != showConversationCount) {
+				showConversationCount = newShowConversationCount;
+				
+				[self updateDockBadge];
+			}
+		}
+		else if ([key isEqualToString:KEY_STATUS_MENTION_COUNT]) {
+			//Just update as the counting is handled elsewhere
+			[self updateDockBadge];
+		}
+	}
+}
+
+//Icons ------------------------------------------------------------------------------------
+- (void)_setNeedsDisplay
+{
+	if (!needsDisplay) {
+		needsDisplay = YES;
+
+		//Invoke a display after a short delay
+		[NSTimer scheduledTimerWithTimeInterval:ICON_DISPLAY_DELAY
+										 target:self
+									   selector:@selector(_buildIcon)
+									   userInfo:nil
+										repeats:NO];
+	}
+}
+
+- (void)updateAppBundleIcon
+{	
+	NSImage *image = [[[availableIconStateDict objectForKey:@"State"] objectForKey:@"ApplicationIcon"] image];
+	if (!image)
+		image = [[[availableIconStateDict objectForKey:@"State"] objectForKey:@"Base"] image];
+	
+	if (image) {
+		[[NSWorkspace sharedWorkspace] setIcon:image
+									   forFile:[[NSBundle mainBundle] bundlePath]
+									   options:0];
+		
+		//Finder won't update Adium's icon to match the new one until it is restarted if we don't
+		//tell NSWorkspace to note the change.
+		[[NSWorkspace sharedWorkspace] noteFileSystemChanged:[[NSBundle mainBundle] bundlePath]];
+	}
+}
+
+/*!
+ * @brief Return the dock icon image without any auxiliary states
+ */
+- (NSImage *)baseApplicationIconImage
+{
+	NSDictionary	*availableIcons = [availableIconStateDict objectForKey:@"State"];
+	AIIconState		*baseState = [availableIcons objectForKey:@"Base"];
+	
+	if (baseState) {
+		AIIconState		*iconState = [[[AIIconState alloc] initByCompositingStates:[NSArray arrayWithObject:baseState]] autorelease];
+		return [iconState image];
+	}
+	
+	return nil;
+}
+
+- (void)setOverlay:(NSImage *)newImage
+{
+	[overlay release];
+	overlay = [newImage retain];
+	[self updateDockView];
 }
 
 //Build/Pre-render the icon images, start/stop animation
@@ -392,10 +465,7 @@
 		currentIconState = [[AIIconState alloc] initByCompositingStates:iconStates];
 		
 		if (![currentIconState animated]) { //Static icon
-			NSImage *image = [currentIconState image];
-			if (image) {
-				[[NSApplication sharedApplication] setApplicationIconImage:image];
-			}
+			[self updateDockView];
 		} else { //Animated icon
 			//Our dock icon can run its animation at any speed, but we want to try and sync it with the global Adium flashing.  To do this, we delay starting our timer until the next flash occurs.
 			[adium.interfaceController registerFlashObserver:self];
@@ -437,62 +507,10 @@
 	//Move to the next image
 	if (timer) {
 		[currentIconState nextFrame];
-	}
-
-	//Set the image
-	NSImage *image = [currentIconState image];
-	if (image) {
-		[[NSApplication sharedApplication] setApplicationIconImage:image];
+		[self updateDockView];
 	}
 }
 
-//returns the % of the dock icon's full size that it currently is (0.0 - 1.0)
-- (CGFloat)dockIconScale
-{
-	NSScreen *screen = [NSScreen mainScreen];
-	NSSize trueSize = screen.visibleFrame.size;
-	NSSize availableSize = screen.frame.size;
-
-	CGFloat	dHeight = availableSize.height - trueSize.height;
-	CGFloat dWidth = availableSize.width - trueSize.width;
-	CGFloat dockScale = 0;
-
-	if (dHeight != 22) { //dock is on the bottom
-		if (dHeight != 26) { //dock is not hidden
-			dockScale = (dHeight-22)/128;
-		}
-	} else if (dWidth != 0) { //dock is on the side
-		if (dWidth != 4) { //dock is not hidden
-			dockScale = (dWidth)/128;
-		}
-	} else {
-		//multiple monitors?
-		//Add support for multiple monitors
-	}
-
-	if (dockScale <= 0 || dockScale > 1.0f) {
-		dockScale = 0.3f;
-	}
-
-	return dockScale;
-}
-
-/*!
- * @brief Return the dock icon image without any auxiliary states
- */
-- (NSImage *)baseApplicationIconImage
-{
-	NSDictionary	*availableIcons = [availableIconStateDict objectForKey:@"State"];
-	AIIconState		*baseState = [availableIcons objectForKey:@"Base"];
-
-	if (baseState) {
-		AIIconState		*iconState = [[[AIIconState alloc] initByCompositingStates:[NSArray arrayWithObject:baseState]] autorelease];
-		return [iconState image];
-	}
-	
-	return nil;
-}
-
 //Bouncing -------------------------------------------------------------------------------------------------------------
 #pragma mark Bouncing
 
@@ -605,8 +623,8 @@
 {
 	//Stop any timer
 	if (bounceTimer) {
-		[bounceTimer invalidate]; 
-		[bounceTimer release]; 
+		[bounceTimer invalidate];
+		[bounceTimer release];
 		bounceTimer = nil;
 	}
 
@@ -624,4 +642,68 @@
     [self _stopBouncing]; //Stop any bouncing
 }
 
+
+#pragma mark Dock Drawing
+- (void)updateDockView
+{
+	NSImage *image = [[[currentIconState image] copy] autorelease];
+	if (overlay) {
+		[image lockFocus];
+		[overlay drawInRect:[view frame] fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
+		[image unlockFocus];
+	}
+	
+	[view setImage:image];
+	[dockTile setContentView:view];
+	[dockTile display];
+}
+
+- (void)updateDockBadge
+{
+	NSInteger contentCount = (showConversationCount ?
+							 [adium.chatController unviewedConversationCount] : [adium.chatController unviewedContentCount]);
+	if (contentCount > 0 && shouldBadge)
+		[dockTile setBadgeLabel:[NSString stringWithFormat:@"%d", contentCount]];
+	else
+		[dockTile setBadgeLabel:nil];
+}
+
+- (void)animateDockIcon
+{
+	[self updateDockBadge];
+	
+	if (adium.chatController.unviewedContentCount && animateDockIcon) {
+		//If this is the first contact with unviewed content, animate the dock
+		if (!unviewedState) {
+			NSString *iconState;
+			if (([adium.statusController.activeStatusState statusType] == AIInvisibleStatusType) &&
+				[self currentIconSupportsIconStateNamed:@"InvisibleAlert"]) {
+				iconState = @"InvisibleAlert";
+			} else {
+				iconState = @"Alert";
+			}
+			
+			[self setIconStateNamed:iconState];
+			unviewedState = YES;
+		}
+	} else if (unviewedState) {
+		//If there are no more contacts with unviewed content, stop animating the dock
+		[self removeIconStateNamed:@"Alert"];
+		[self removeIconStateNamed:@"InvisibleAlert"];
+		unviewedState = NO;
+	}
+}
+
+/*!
+ * @brief When a chat has unviewed content update the badge and maybe start/stop the animation
+ */
+- (NSSet *)updateChat:(AIChat *)inChat keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
+{
+	if (inModifiedKeys == nil || [inModifiedKeys containsObject:KEY_UNVIEWED_CONTENT]) {
+		[self animateDockIcon];
+	}
+	
+	return nil;
+}
+
 @end
--- a/Source/AIDockUnviewedContentPlugin.h	Fri Feb 18 01:01:13 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-/* 
- * Adium is the legal property of its developers, whose names are listed in the copyright file included
- * with this source distribution.
- * 
- * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
- * General Public License as published by the Free Software Foundation; either version 2 of the License,
- * or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
- * Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License along with this program; if not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#import <Adium/AIChatControllerProtocol.h>
-
-@interface AIDockUnviewedContentPlugin : AIPlugin <AIChatObserver> {
-    BOOL				unviewedState;
-	BOOL				animateDockIcon;
-}
-
-@end
--- a/Source/AIDockUnviewedContentPlugin.m	Fri Feb 18 01:01:13 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-/* 
- * Adium is the legal property of its developers, whose names are listed in the copyright file included
- * with this source distribution.
- * 
- * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
- * General Public License as published by the Free Software Foundation; either version 2 of the License,
- * or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
- * Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License along with this program; if not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#import "AIDockUnviewedContentPlugin.h"
-#import <Adium/AIChatControllerProtocol.h>
-#import <Adium/AIContentControllerProtocol.h>
-#import <Adium/AIStatusControllerProtocol.h>
-#import <Adium/AIDockControllerProtocol.h>
-#import <AIUtilities/AIArrayAdditions.h>
-#import <AIUtilities/AIDictionaryAdditions.h>
-#import <Adium/AIChat.h>
-
-@interface AIDockUnviewedContentPlugin ()
-- (void)removeAlert;
-- (void)chatWillClose:(NSNotification *)notification;
-@end
-
-/*!
- * @class AIDockUnviewedContentPlugin
- * @brief Component responsible for triggering and removing the Alert dock icon state for unviewed content
- */
-@implementation AIDockUnviewedContentPlugin
-
-/*!
- * @brief Install
- */
-- (void)installPlugin
-{
-    //init
-    unviewedState = NO;
-
-	//Register our default preferences
-    [adium.preferenceController registerDefaults:[NSDictionary dictionaryNamed:@"DockUnviewedContentDefaults"
-																		forClass:[self class]] 
-										  forGroup:PREF_GROUP_APPEARANCE];
-	
-	//Observe pref changes
-	[adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_APPEARANCE];
-}
-
-/*!
- * @brief Uninstall
- */
-- (void)uninstallPlugin
-{
-	[adium.chatController unregisterChatObserver:self];
-	[[NSNotificationCenter defaultCenter] removeObserver:self];
-}
-
-#pragma mark Preference observing
-- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
-							object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
-{
-	if (!key || [key isEqualToString:KEY_ANIMATE_DOCK_ICON]) {
-		BOOL newAnimateDockIcon = [[prefDict objectForKey:KEY_ANIMATE_DOCK_ICON] boolValue];
-
-		if (newAnimateDockIcon != animateDockIcon) {
-			animateDockIcon = newAnimateDockIcon;
-			
-			if (animateDockIcon) {
-				//Register as a chat observer (So we can catch the unviewed content status flag)
-				[adium.chatController registerChatObserver:self];
-				
-				[[NSNotificationCenter defaultCenter] addObserver:self
-											   selector:@selector(chatWillClose:)
-												   name:Chat_WillClose object:nil];
-				
-			} else {
-				[self removeAlert];
-
-				[adium.chatController unregisterChatObserver:self];
-				[[NSNotificationCenter defaultCenter] removeObserver:self];
-			}
-		}
-	}
-}
-/*!
- * @brief Chat was updated
- *
- * Check for whether inModifiedKeys contains a change to unviewed content. If so, put the dock in the Alert state
- * if it isn't already and there is unviewed content, or take it out of the Alert state if it is and there is none.
- *
- * The alert state, in the default dock icon set, is the Adium duck flapping its wings.
- */
-- (NSSet *)updateChat:(AIChat *)inChat keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
-{
-    if ([inModifiedKeys containsObject:KEY_UNVIEWED_CONTENT]) {
-        if (adium.chatController.unviewedContentCount) {
-            //If this is the first contact with unviewed content, animate the dock
-            if (!unviewedState) {
-				NSString *iconState;
-
-				if (([adium.statusController.activeStatusState statusType] == AIInvisibleStatusType) &&
-					[adium.dockController currentIconSupportsIconStateNamed:@"InvisibleAlert"]) {
-					iconState = @"InvisibleAlert";					
-				} else {
-					iconState = @"Alert";
-				}
-
-                [adium.dockController setIconStateNamed:iconState];
-                unviewedState = YES;
-            }
-        } else if (unviewedState) {
-			//If there are no more contacts with unviewed content, stop animating the dock
-			[self removeAlert];
-        }
-    }
-
-    return nil;
-}
-
-/*!
- * @brief Remove any existing alert state
- */
-- (void)removeAlert
-{
-	[adium.dockController removeIconStateNamed:@"Alert"];
-	[[adium dockController] removeIconStateNamed:@"InvisibleAlert"];
-	unviewedState = NO;
-}
-
-/*!
- * @brief Respond to a chat closing
- *
- * Ensure that when a chat closes we remove the Alert state if necessary.
- */
-- (void)chatWillClose:(NSNotification *)notification
-{
-	if (!adium.chatController.unviewedContentCount && unviewedState) {
-		//If there are no more contacts with unviewed content, stop animating the dock
-		[self removeAlert];
-	}
-}
-
-@end