adium/adium
Clone
Summary
Browse
Changes
Graph
Updated Sparkle to 1.17.0, to fix a problem when copying broken symlinks.
adium-1.5.10.4
2017-04-23, Thijs Alkemade
5883c460b8cb
Updated Sparkle to 1.17.0, to fix a problem when copying broken symlinks.
/*
* 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 "AIAutomaticStatus.h"
#import <Adium/AIAccountControllerProtocol.h>
#import <Adium/AIStatusControllerProtocol.h>
#import <Adium/AIInterfaceControllerProtocol.h>
#import <Adium/AIChatControllerProtocol.h>
#import <Adium/ESTextAndButtonsWindowController.h>
#import <Adium/AIAccount.h>
#import <Adium/AIStatus.h>
#import <Adium/AIStatusGroup.h>
typedef
enum
{
AIAwayIdle
=
(
1
<<
1
),
AIAwayScreenSaved
=
(
1
<<
2
),
AIAwayScreenLocked
=
(
1
<<
3
),
AIAwayFastUserSwitched
=
(
1
<<
4
)
}
AIAwayAutomaticType
;
@interface
AIAutomaticStatus
()
-
(
void
)
notificationHandler
:
(
NSNotification
*
)
notification
;
-
(
void
)
triggerAutoAwayWithStatusID:
(
NSNumber
*
)
statusID
;
-
(
void
)
returnFromAutoAway
;
@end
/*!
* @class AIAutomaticStatus
*
* Automatically set accounts to certain statuses when events occur. Currently this handles:
* - Fast user switching
* - Screen(saver|lock) activation
* - Idle time
*/
@implementation
AIAutomaticStatus
/*!
* @brief Initialize the automatic status system
*/
-
(
void
)
installPlugin
{
// Ensure no idle time is set as we load
[
adium
.
preferenceController
setPreference
:
nil
forKey
:
@"idleSince"
group
:
GROUP_ACCOUNT_STATUS
];
// Initialize our state information
accountsToReconnect
=
[[
NSMutableSet
alloc
]
init
];
previousStatus
=
[[
NSMutableDictionary
alloc
]
init
];
// Register our notifications
NSNotificationCenter
*
notificationCenter
;
// FUS events are on the sharedWorkspace's notificationCenter
notificationCenter
=
[[
NSWorkspace
sharedWorkspace
]
notificationCenter
];
[
notificationCenter
addObserver
:
self
selector
:
@selector
(
notificationHandler
:
)
name
:
NSWorkspaceSessionDidBecomeActiveNotification
object
:
nil
];
[
notificationCenter
addObserver
:
self
selector
:
@selector
(
notificationHandler
:
)
name
:
NSWorkspaceSessionDidResignActiveNotification
object
:
nil
];
// Screensaver events are distributed notification events
notificationCenter
=
[
NSDistributedNotificationCenter
defaultCenter
];
[
notificationCenter
addObserver
:
self
selector
:
@selector
(
notificationHandler
:
)
name
:
AIScreensaverDidStartNotification
object
:
nil
];
[
notificationCenter
addObserver
:
self
selector
:
@selector
(
notificationHandler
:
)
name
:
AIScreensaverDidStopNotification
object
:
nil
];
[
notificationCenter
addObserver
:
self
selector
:
@selector
(
notificationHandler
:
)
name
:
AIScreenLockDidStartNotification
object
:
nil
];
[
notificationCenter
addObserver
:
self
selector
:
@selector
(
notificationHandler
:
)
name
:
AIScreenLockDidStopNotification
object
:
nil
];
// Idle events are in the Adium notification center, posted by the AdiumIdleManager
notificationCenter
=
[
NSNotificationCenter
defaultCenter
];
[
notificationCenter
addObserver
:
self
selector
:
@selector
(
notificationHandler
:
)
name
:
AIMachineIdleUpdateNotification
object
:
nil
];
[
notificationCenter
addObserver
:
self
selector
:
@selector
(
notificationHandler
:
)
name
:
AIMachineIsActiveNotification
object
:
nil
];
// Register for status preference updates
[
adium
.
preferenceController
registerPreferenceObserver
:
self
forGroup
:
PREF_GROUP_STATUS_PREFERENCES
];
}
/*!
* @brief Uninstall plugin
*
* When the plugin is uninstalled, we revert to whatever status we were previously set to.
*/
-
(
void
)
uninstallPlugin
{
// Unregister our notifications
[[[
NSWorkspace
sharedWorkspace
]
notificationCenter
]
removeObserver
:
self
];
[[
NSDistributedNotificationCenter
defaultCenter
]
removeObserver
:
self
];
[[
NSNotificationCenter
defaultCenter
]
removeObserver
:
self
];
// Unregister our preference observations
[
adium
.
preferenceController
unregisterPreferenceObserver
:
self
];
// Revert to our stored statuses
if
(
automaticStatusBitMap
!=
0
)
{
[
self
returnFromAutoAway
];
}
}
/*!
* Deallocate
*/
-
(
void
)
dealloc
{
// State information
[
accountsToReconnect
release
];
[
previousStatus
release
];
// Stored status IDs
[
fastUserSwitchID
release
];
[
screenSaverID
release
];
[
idleStatusID
release
];
[
oldStatusID
release
];
[
super
dealloc
];
}
/*!
* @brief Preferences changed
*
* Note the status IDs, interval information, and enabled information for our preferences
*/
-
(
void
)
preferencesChangedForGroup:
(
NSString
*
)
group
key
:(
NSString
*
)
key
object
:(
AIListObject
*
)
object
preferenceDict
:(
NSDictionary
*
)
prefDict
firstTime
:(
BOOL
)
firstTime
{
if
(
object
)
{
return
;
}
// Idle reporting
reportIdleEnabled
=
[[
prefDict
objectForKey
:
KEY_STATUS_REPORT_IDLE
]
boolValue
];
idleReportInterval
=
[[
prefDict
objectForKey
:
KEY_STATUS_REPORT_IDLE_INTERVAL
]
doubleValue
];
// Idle status change
[
idleStatusID
release
];
idleStatusID
=
[[
prefDict
objectForKey
:
KEY_STATUS_AUTO_AWAY_STATUS_STATE_ID
]
retain
];
idleStatusEnabled
=
[[
prefDict
objectForKey
:
KEY_STATUS_AUTO_AWAY
]
boolValue
];
idleStatusInterval
=
[[
prefDict
objectForKey
:
KEY_STATUS_AUTO_AWAY_INTERVAL
]
doubleValue
];
// Fast user switch
[
fastUserSwitchID
release
];
fastUserSwitchID
=
[[
prefDict
objectForKey
:
KEY_STATUS_FUS_STATUS_STATE_ID
]
retain
];
fastUserSwitchEnabled
=
[[
prefDict
objectForKey
:
KEY_STATUS_FUS
]
boolValue
];
// Screensaver
[
screenSaverID
release
];
screenSaverID
=
[[
prefDict
objectForKey
:
KEY_STATUS_SS_STATUS_STATE_ID
]
retain
];
screenSaverEnabled
=
[[
prefDict
objectForKey
:
KEY_STATUS_SS
]
boolValue
];
}
/*!
* @brief Handle a notification
*
* @param notification The notification to process
*
* When a notification comes in, this checks if it's a start or end event
*
* If this event changes one of the automatic statusses, the user is set again
* to the highest priority automatic away status, or if none, returns from autoaway.
*
* Priorities: Fast User Switch'ed > Screen(saver|lock) > Idle.
*/
-
(
void
)
notificationHandler:
(
NSNotification
*
)
notification
{
NSString
*
notificationName
=
[
notification
name
];
unsigned
oldBitMap
=
automaticStatusBitMap
;
// Start events
if
([
notificationName
isEqualToString
:
NSWorkspaceSessionDidResignActiveNotification
])
{
AILogWithSignature
(
@"Fast user switch (start) detected"
);
if
(
fastUserSwitchEnabled
)
automaticStatusBitMap
|=
AIAwayFastUserSwitched
;
}
else
if
([
notificationName
isEqualToString
:
AIScreensaverDidStartNotification
])
{
AILogWithSignature
(
@"Screensaver (start) detected."
);
if
(
screenSaverEnabled
)
automaticStatusBitMap
|=
AIAwayScreenSaved
;
}
else
if
([
notificationName
isEqualToString
:
AIMachineIdleUpdateNotification
])
{
double
duration
=
[[[
notification
userInfo
]
objectForKey
:
@"Duration"
]
doubleValue
];
if
(
reportIdleEnabled
&&
duration
>=
idleReportInterval
)
{
NSDate
*
idleSince
=
[[
notification
userInfo
]
objectForKey
:
@"idleSince"
];
if
((
NSInteger
)[[
adium
.
preferenceController
preferenceForKey
:
@"idleSince"
group
:
GROUP_ACCOUNT_STATUS
]
timeIntervalSince1970
]
!=
(
NSInteger
)[
idleSince
timeIntervalSince1970
])
{
AILogWithSignature
(
@"Idle (start) detected. %@ -> %@"
,
[
adium
.
preferenceController
preferenceForKey
:
@"idleSince"
group
:
GROUP_ACCOUNT_STATUS
],
idleSince
);
// Update our idle time
[
adium
.
preferenceController
setPreference
:
[[
notification
userInfo
]
objectForKey
:
@"idleSince"
]
forKey
:
@"idleSince"
group
:
GROUP_ACCOUNT_STATUS
];
}
}
if
(
idleStatusEnabled
&&
duration
>=
idleStatusInterval
&&
!
(
automaticStatusBitMap
&
AIAwayIdle
))
{
AILogWithSignature
(
@"Auto-away (start) detected."
);
automaticStatusBitMap
|=
AIAwayIdle
;
}
}
if
([
notificationName
isEqualToString
:
AIScreenLockDidStartNotification
])
{
AILogWithSignature
(
@"Screenlock (start) detected."
);
if
(
screenSaverEnabled
)
automaticStatusBitMap
|=
AIAwayScreenLocked
;
}
// End events
if
([
notificationName
isEqualToString
:
NSWorkspaceSessionDidBecomeActiveNotification
])
{
AILogWithSignature
(
@"Fast user switch (end) detected."
);
automaticStatusBitMap
&=
~
AIAwayFastUserSwitched
;
}
else
if
([
notificationName
isEqualToString
:
AIScreensaverDidStopNotification
])
{
AILogWithSignature
(
@"Screensaver (end) detected."
);
automaticStatusBitMap
&=
~
AIAwayScreenSaved
;
}
else
if
([
notificationName
isEqualToString
:
AIMachineIsActiveNotification
])
{
if
(
automaticStatusBitMap
&
AIAwayIdle
)
{
AILogWithSignature
(
@"Auto-away (end) detected."
);
automaticStatusBitMap
&=
~
AIAwayIdle
;
}
if
(
reportIdleEnabled
)
{
AILogWithSignature
(
@"Idle (end) detected."
);
[
adium
.
preferenceController
setPreference
:
nil
forKey
:
@"idleSince"
group
:
GROUP_ACCOUNT_STATUS
];
}
}
else
if
([
notificationName
isEqualToString
:
AIScreenLockDidStopNotification
])
{
AILogWithSignature
(
@"Screenlock (end) detected."
);
automaticStatusBitMap
&=
~
AIAwayScreenLocked
;
}
// Check if a change in status is required: if so, look for the one with the highest priority
if
(
oldBitMap
!=
automaticStatusBitMap
)
{
NSNumber
*
statusID
=
nil
;
if
(
automaticStatusBitMap
&
AIAwayFastUserSwitched
)
statusID
=
fastUserSwitchID
;
else
if
((
automaticStatusBitMap
&
AIAwayScreenLocked
)
||
(
automaticStatusBitMap
&
AIAwayScreenSaved
))
statusID
=
screenSaverID
;
else
if
(
automaticStatusBitMap
&
AIAwayIdle
)
statusID
=
idleStatusID
;
else
[
self
returnFromAutoAway
];
if
(
statusID
)
[
self
triggerAutoAwayWithStatusID
:
statusID
];
}
}
/*!
* @brief Automatically set an account as away
*
* @param statusID The status ID to change account status to
*
* Sets all available accounts to the status type statusID, while storing the old one if necessary
*/
-
(
void
)
triggerAutoAwayWithStatusID:
(
NSNumber
*
)
statusID
{
AIStatusItem
*
targetStatusState
=
[
adium
.
statusController
statusStateWithUniqueStatusID
:
statusID
];
// Grab any group member if possible
if
([
targetStatusState
isKindOfClass
:
[
AIStatusGroup
class
]])
{
targetStatusState
=
[(
AIStatusGroup
*
)
targetStatusState
anyContainedStatus
];
}
// If we weren't given a valid and new state, fail.
if
(
!
targetStatusState
||
[
oldStatusID
isEqualToNumber
:
statusID
])
{
return
;
}
for
(
AIAccount
*
account
in
adium
.
accountController
.
accounts
)
{
AIStatus
*
currentStatusState
=
account
.
statusState
;
// Store the state of the account if there is no previous one saved
if
(
!
[
previousStatus
objectForKey
:
[
account
internalObjectID
]])
{
// Don't modify or store the status of (originally!) non-available accounts
if
(
currentStatusState
.
statusType
!=
AIAvailableStatusType
)
{
continue
;
}
[
previousStatus
setObject
:
currentStatusState
forKey
:[
account
internalObjectID
]];
}
AILogWithSignature
(
@"Setting %@ to status %@"
,
account
,
targetStatusState
);
if
(
account
.
online
)
{
// Set the account's status to our new value
[
account
setStatusState
:
(
AIStatus
*
)
targetStatusState
];
// If this status brought the account offline, add it to the list to reconnect.
if
(
targetStatusState
.
statusType
==
AIOfflineStatusType
)
{
[
accountsToReconnect
addObject
:
account
];
}
}
else
{
[
account
setStatusStateAndRemainOffline
:
(
AIStatus
*
)
targetStatusState
];
}
}
[
oldStatusID
release
];
oldStatusID
=
[
statusID
retain
];
}
/*!
* @brief Return from automatic away
*
* Returns all accounts with stored status information to their previous status.
*/
-
(
void
)
returnFromAutoAway
{
for
(
AIAccount
*
account
in
adium
.
accountController
.
accounts
)
{
AIStatus
*
previousStatusState
=
[
previousStatus
objectForKey
:
[
account
internalObjectID
]];
// Skip accounts without stored information.
if
(
!
previousStatusState
)
{
continue
;
}
AILogWithSignature
(
@"Returning %@ to status %@"
,
account
,
previousStatusState
);
if
(
account
.
online
||
[
accountsToReconnect
containsObject
:
account
])
{
//If online or needs to be reconnected, set the previous state, going online if necessary
[
account
setStatusState
:
previousStatusState
];
}
else
{
//If offline, set the state without coming online
[
account
setStatusStateAndRemainOffline
:
previousStatusState
];
}
}
[
accountsToReconnect
removeAllObjects
];
[
previousStatus
removeAllObjects
];
automaticStatusBitMap
=
0
;
[
oldStatusID
release
];
oldStatusID
=
nil
;
}
@end