adium/adium
Clone
Summary
Browse
Changes
Graph
Merge.
adium-1.5.8
1.5.8
2013-10-13, Thijs Alkemade
e0467f507718
Merge.
/*
* 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/AIInterfaceControllerProtocol.h>
#import "AIWebKitMessageViewController.h"
#import "AIWebKitMessageViewPlugin.h"
#import "ESWebKitMessageViewPreferences.h"
#import <AIUtilities/AIDictionaryAdditions.h>
#import <AIUtilities/AIBundleAdditions.h>
#import "AIWebkitMessageViewStyle.h"
#import <Adium/AIChat.h>
#import <AIUtilities/AIStringAdditions.h>
#define NEW_CONTENT_RETRY_DELAY 0.01
#define MESSAGE_STYLES_SUBFOLDER_OF_APP_SUPPORT @"Message Styles"
@interface
AIWebKitMessageViewPlugin
()
-
(
void
)
resetStylesForType
:
(
AIWebkitStyleType
)
styleType
;
-
(
void
)
xtrasChanged:
(
NSNotification
*
)
notification
;
-
(
void
)
clearHardcodedBuiltInStylePaths
;
-
(
void
)
performAdium14PreferenceUpdates
;
@end
@implementation
AIWebKitMessageViewPlugin
/*!
* @brief Install plugin
*/
-
(
void
)
installPlugin
{
styleDictionary
=
nil
;
[
adium
createResourcePathForName
:
MESSAGE_STYLES_SUBFOLDER_OF_APP_SUPPORT
];
/* If this isn't done, Bad Things happen, so check it each launch (cheap!) in case the user reverted to a
* pre-Adium 1.4 version. This check can be removed ~Adium 1.6.
*/
[
self
clearHardcodedBuiltInStylePaths
];
[
self
performAdium14PreferenceUpdates
];
//Setup our preferences
[
adium
.
preferenceController
registerDefaults
:
[
NSDictionary
dictionaryNamed
:
WEBKIT_DEFAULT_PREFS
forClass
:
[
self
class
]]
forGroup
:
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
];
[
adium
.
preferenceController
registerDefaults
:
[
NSDictionary
dictionaryNamed
:
WEBKIT_DEFAULT_PREFS
forClass
:
[
self
class
]]
forGroup
:
PREF_GROUP_WEBKIT_GROUP_MESSAGE_DISPLAY
];
preferences
=
[(
ESWebKitMessageViewPreferences
*
)[
ESWebKitMessageViewPreferences
preferencePaneForPlugin
:
self
]
retain
];
//Observe for installation of new styles
[[
NSNotificationCenter
defaultCenter
]
addObserver
:
self
selector
:
@selector
(
xtrasChanged
:
)
name
:
AIXtrasDidChangeNotification
object
:
nil
];
[
adium
.
preferenceController
registerPreferenceObserver
:
self
forGroup
:
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
];
[
adium
.
preferenceController
registerPreferenceObserver
:
self
forGroup
:
PREF_GROUP_WEBKIT_GROUP_MESSAGE_DISPLAY
];
//Register ourself as a message view plugin
[
adium
.
interfaceController
registerMessageDisplayPlugin
:
self
];
}
-
(
void
)
uninstallPlugin
{
[
adium
.
preferenceController
unregisterPreferenceObserver
:
self
];
[
adium
.
interfaceController
unregisterMessageDisplayPlugin
:
self
];
[
adium
.
preferenceController
removePreferencePane
:
preferences
];
[[
NSNotificationCenter
defaultCenter
]
removeObserver
:
self
];
[
styleDictionary
release
];
styleDictionary
=
nil
;
[
preferences
release
];
preferences
=
nil
;
[
currentRegularStyle
release
];
currentRegularStyle
=
nil
;
[
currentGroupStyle
release
];
currentGroupStyle
=
nil
;
[
lastStyleLoadDate
release
];
lastStyleLoadDate
=
nil
;
[
super
uninstallPlugin
];
}
-
(
id
<
AIMessageDisplayController
>
)
messageDisplayControllerForChat:
(
AIChat
*
)
inChat
{
return
[
AIWebKitMessageViewController
messageDisplayControllerForChat
:
inChat
withPlugin
:
self
];
}
-
(
void
)
preferencesChangedForGroup:
(
NSString
*
)
group
key
:(
NSString
*
)
key
object
:(
AIListObject
*
)
object
preferenceDict
:(
NSDictionary
*
)
prefDict
firstTime
:(
BOOL
)
firstTime
{
if
([
key
isEqualToString
:
KEY_WEBKIT_STYLE
])
{
if
([
group
isEqualToString
:
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
])
{
[
self
resetStylesForType
:
AIWebkitRegularChat
];
}
else
{
[
self
resetStylesForType
:
AIWebkitGroupChat
];
}
}
if
([
group
isEqualToString
:
PREF_GROUP_WEBKIT_GROUP_MESSAGE_DISPLAY
])
{
useRegularForGroupChat
=
[[
prefDict
objectForKey
:
KEY_WEBKIT_USE_REGULAR_PREFERENCES
]
boolValue
];
}
}
-
(
NSDictionary
*
)
availableMessageStyles
{
if
(
!
styleDictionary
)
{
NSArray
*
stylesArray
=
[
adium
allResourcesForName
:
MESSAGE_STYLES_SUBFOLDER_OF_APP_SUPPORT
withExtensions
:
@"AdiumMessageStyle"
];
NSBundle
*
style
;
NSString
*
resourcePath
;
//Clear the current dictionary of styles and ready a new mutable dictionary
styleDictionary
=
[[
NSMutableDictionary
alloc
]
init
];
//Get all resource paths to search
for
(
resourcePath
in
stylesArray
)
{
if
((
style
=
[
NSBundle
bundleWithPath
:
resourcePath
]))
{
NSString
*
styleIdentifier
=
[
style
bundleIdentifier
];
if
(
styleIdentifier
&&
[
styleIdentifier
length
])
{
[
styleDictionary
setObject
:
style
forKey
:
styleIdentifier
];
}
}
}
NSAssert
([
styleDictionary
count
]
>
0
,
@"No message styles available"
);
//Abort if we have no message styles
}
return
[
NSDictionary
dictionaryWithDictionary
:
styleDictionary
];
//returning mutable private variables == nuh uh
}
-
(
NSBundle
*
)
defaultMessageStyleBundleBasedOnFailedIdentifier:
(
NSString
*
)
identifier
{
NSDictionary
*
styles
=
[
self
availableMessageStyles
];
NSBundle
*
defaultMessageStyleBundle
=
nil
;
if
(
!
defaultMessageStyleBundle
)
{
defaultMessageStyleBundle
=
[
styles
objectForKey
:
KEY_WEBKIT_STYLE
];
}
if
(
!
defaultMessageStyleBundle
)
{
defaultMessageStyleBundle
=
[[
styles
allValues
]
lastObject
];
}
return
defaultMessageStyleBundle
;
}
-
(
NSBundle
*
)
messageStyleBundleWithIdentifier:
(
NSString
*
)
identifier
{
NSDictionary
*
styles
=
[
self
availableMessageStyles
];
NSBundle
*
bundle
=
[
styles
objectForKey
:
identifier
];
//If the style isn't available, use our default. Or, failing that, any available style
if
(
!
bundle
)
{
bundle
=
[
self
defaultMessageStyleBundleBasedOnFailedIdentifier
:
identifier
];
}
return
bundle
;
}
-
(
AIWebkitMessageViewStyle
*
)
currentMessageStyleForChat:
(
AIChat
*
)
chat
{
NSString
*
loadFromGroup
=
nil
;
AIWebkitMessageViewStyle
**
thisStyle
=
nil
;
if
(
!
chat
.
isGroupChat
||
useRegularForGroupChat
)
{
if
(
!
currentRegularStyle
)
{
loadFromGroup
=
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
;
}
thisStyle
=
&
currentRegularStyle
;
}
else
if
(
chat
.
isGroupChat
)
{
if
(
!
currentGroupStyle
)
{
loadFromGroup
=
PREF_GROUP_WEBKIT_GROUP_MESSAGE_DISPLAY
;
}
thisStyle
=
&
currentGroupStyle
;
}
if
(
loadFromGroup
&&
thisStyle
)
{
id
<
AIPreferenceController
>
prefs
=
adium
.
preferenceController
;
/* We use the path directly, if possible, to avoid a relatively expensive search through multiple folders */
*
thisStyle
=
[
AIWebkitMessageViewStyle
messageViewStyleFromPath
:
[
prefs
preferenceForKey
:
KEY_CURRENT_WEBKIT_STYLE_PATH
group
:
loadFromGroup
]];
if
(
!*
thisStyle
)
{
/* If the path isn't cached yet, load the style and then store the path */
*
thisStyle
=
[
AIWebkitMessageViewStyle
messageViewStyleFromBundle
:
[
self
messageStyleBundleWithIdentifier
:
[
prefs
preferenceForKey
:
KEY_WEBKIT_STYLE
group
:
loadFromGroup
]]];
if
(
*
thisStyle
)
{
[
prefs
setPreference
:
[[[
*
thisStyle
bundle
]
bundlePath
]
stringByCollapsingBundlePath
]
forKey
:
KEY_CURRENT_WEBKIT_STYLE_PATH
group
:
loadFromGroup
];
}
else
{
/* If the style failed to load, clear our preference to fall back to the default */
/* XXX An error message could potentially be displayed here */
[
prefs
setPreference
:
nil
forKey
:
KEY_WEBKIT_STYLE
group
:
loadFromGroup
];
*
thisStyle
=
[
AIWebkitMessageViewStyle
messageViewStyleFromBundle
:
[
self
messageStyleBundleWithIdentifier
:
[
prefs
preferenceForKey
:
KEY_WEBKIT_STYLE
group
:
loadFromGroup
]]];
}
}
[
*
thisStyle
retain
];
}
if
(
thisStyle
)
{
NSDictionary
*
fileAttrs
=
[[
NSFileManager
defaultManager
]
attributesOfItemAtPath
:
[[
*
thisStyle
bundle
]
bundlePath
]
error
:
NULL
];
NSDate
*
modDate
=
[
fileAttrs
objectForKey
:
NSFileModificationDate
];
if
(
lastStyleLoadDate
&&
[
modDate
timeIntervalSinceDate
:
lastStyleLoadDate
]
>
0
)
{
[
currentGroupStyle
reloadStyle
];
[
currentRegularStyle
reloadStyle
];
}
[
lastStyleLoadDate
release
];
lastStyleLoadDate
=
[[
NSDate
date
]
retain
];
}
else
{
[
lastStyleLoadDate
release
];
lastStyleLoadDate
=
nil
;
}
return
(
thisStyle
?
*
thisStyle
:
nil
);
}
-
(
void
)
resetStylesForType:
(
AIWebkitStyleType
)
styleType
{
[
styleDictionary
release
];
styleDictionary
=
nil
;
switch
(
styleType
)
{
case
AIWebkitRegularChat
:
{
[
currentRegularStyle
release
];
currentRegularStyle
=
nil
;
[
adium
.
preferenceController
setPreference
:
nil
forKey
:
KEY_CURRENT_WEBKIT_STYLE_PATH
group
:
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
];
break
;
}
case
AIWebkitGroupChat
:
{
[
currentGroupStyle
release
];
currentGroupStyle
=
nil
;
[
adium
.
preferenceController
setPreference
:
nil
forKey
:
KEY_CURRENT_WEBKIT_STYLE_PATH
group
:
PREF_GROUP_WEBKIT_GROUP_MESSAGE_DISPLAY
];
}
}
}
/*!
* @brief Rebuild our list of available styles when the installed xtras change
*/
-
(
void
)
xtrasChanged:
(
NSNotification
*
)
notification
{
if
([[
notification
object
]
caseInsensitiveCompare
:
@"AdiumMessageStyle"
]
==
NSOrderedSame
)
{
[
self
resetStylesForType
:
AIWebkitRegularChat
];
[
self
resetStylesForType
:
AIWebkitGroupChat
];
[
preferences
messageStyleXtrasDidChange
];
}
}
/*!
* @brief The preference group this chat should use.
*/
-
(
NSString
*
)
preferenceGroupForChat:
(
AIChat
*
)
chat
{
if
(
useRegularForGroupChat
||
!
chat
.
isGroupChat
)
{
return
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
;
}
else
{
return
PREF_GROUP_WEBKIT_GROUP_MESSAGE_DISPLAY
;
}
}
-
(
NSString
*
)
styleSpecificKey:
(
NSString
*
)
key
forStyle:
(
NSString
*
)
style
{
return
[
NSString
stringWithFormat
:
@"%@:%@"
,
style
,
key
];
}
#pragma mark -
/*!
* @brief Clears cached style bundle pathes if they are built-in but not bundle-relative; Adium 1.4b18 and prior made these.
*/
-
(
void
)
clearHardcodedBuiltInStylePaths
{
if
([[
adium
.
preferenceController
preferenceForKey
:
KEY_CURRENT_WEBKIT_STYLE_PATH
group
:
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
]
rangeOfString
:
@".app"
].
location
!=
NSNotFound
)
[
self
resetStylesForType
:
AIWebkitRegularChat
];
if
([[
adium
.
preferenceController
preferenceForKey
:
KEY_CURRENT_WEBKIT_STYLE_PATH
group
:
PREF_GROUP_WEBKIT_GROUP_MESSAGE_DISPLAY
]
rangeOfString
:
@".app"
].
location
!=
NSNotFound
)
[
self
resetStylesForType
:
AIWebkitGroupChat
];
}
-
(
void
)
performAdium14PreferenceUpdates
{
if
(
!
[[
adium
.
preferenceController
preferenceForKey
:
@"Adium 1.4:Updated Preferences"
group
:
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
]
boolValue
])
{
NSDictionary
*
dict
=
[
adium
.
preferenceController
preferencesForGroup
:
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
];
NSMutableDictionary
*
newDict
=
[[
dict
mutableCopy
]
autorelease
];
NSMutableSet
*
keysToRemove
=
[
NSMutableSet
set
];
NSDictionary
*
conversionDict
=
[
NSDictionary
dictionaryWithObjectsAndKeys
:
/* complete style changes */
@"im.adium.Gone Dark.style"
,
@"com.adiumx.eclipse.style"
,
@"im.adium.Stockholm.style"
,
@"com.adiumx.plastic.style"
,
@"im.adium.minimal_mod.style"
,
@"com.adiumx.minimal_2.0.style"
,
@"im.adium.Renkoo.style"
,
@"com.adiumx.renkooNaked.style"
,
@"im.adium.minimal_mod.style"
,
@"com.adiumx.minimal.style"
,
/* bundle identifier changes */
@"im.adium.Gone Dark.style"
,
@"com.adiumx.gonedark.style"
,
@"im.adium.minimal_mod.style"
,
@"com.adiumx.minimal_mod.style"
,
@"im.adium.Mockie.style"
,
@"com.adiumx.mockie.style"
,
@"im.adium.Renkoo.style"
,
@"com.adiumx.renkoo.style"
,
@"im.adium.Smooth Operator.style"
,
@"com.adiumx.smooth.operator.style"
,
@"im.adium.Stockholm.style"
,
@"com.adiumx.stockholm.style"
,
@"im.adium.yMous.style"
,
@"mathuaerknedam.yMous.style"
,
nil
];
/* Upgrade the style ID itself (that is, the style that Adium will be displaying) if it changed */
NSString
*
upgradedStyleID
=
[
conversionDict
objectForKey
:
[
dict
objectForKey
:
KEY_WEBKIT_STYLE
]];
if
(
upgradedStyleID
)
[
newDict
setObject
:
upgradedStyleID
forKey
:
KEY_WEBKIT_STYLE
];
/* Now update style-specific preferences, whose keys are prefixed with style names, as needed */
for
(
NSString
*
key
in
[
dict
keyEnumerator
])
{
/* For each changed bundle, check each key */
for
(
NSString
*
oldBundleID
in
[
conversionDict
keyEnumerator
])
{
if
([
key
hasPrefix
:
oldBundleID
])
{
NSString
*
newBundleID
=
[
conversionDict
objectForKey
:
oldBundleID
];
NSString
*
newKey
=
[
newBundleID
stringByAppendingString
:
[
key
substringFromIndex
:
oldBundleID
.
length
]];
/* Store with the new bundle ID in the key */
[
newDict
setObject
:
[
dict
objectForKey
:
key
]
forKey
:
newKey
];
/* Remove the obsolete preference; we'll want it not in the newDict but also need to manually
* remove it when we're done, since AIPreferenceController's setPreferences:inGroup: is nondestructive. */
[
newDict
removeObjectForKey
:
key
];
[
keysToRemove
addObject
:
key
];
}
}
}
[
adium
.
preferenceController
setPreferences
:
newDict
inGroup
:
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
];
for
(
NSString
*
key
in
keysToRemove
)
{
[
adium
.
preferenceController
setPreference
:
nil
forKey
:
key
group
:
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
];
}
[
adium
.
preferenceController
setPreference
:
[
NSNumber
numberWithBool
:
YES
]
forKey
:
@"Adium 1.4:Updated Preferences"
group
:
PREF_GROUP_WEBKIT_REGULAR_MESSAGE_DISPLAY
];
}
}
@end