summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/oscar/icq/icqpresence.cpp
blob: ab6bb6701782b6904a99954437f13b1328b75f81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/*
    icqpresence.cpp  -  ICQ online status and presence management
    
    Copyright (c) 2004      by Richard Smith          <[email protected]>
    Kopete    (c) 2002-2004 by the Kopete developers  <[email protected]>

    *************************************************************************
    *                                                                       *
    * 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.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <utility>
#include <vector>

#include <kdebug.h>
#include <klocale.h>
#include <kstaticdeleter.h>

#include <kopeteonlinestatus.h>
#include <kopeteonlinestatusmanager.h>

#include "icqprotocol.h"

#include "icqpresence.h"

namespace ICQ
{

//BEGIN struct PresenceTypeData

struct PresenceTypeData
{
	Presence::Type type;
	Kopete::OnlineStatus::StatusType onlineStatusType;
	unsigned long setFlag;
	unsigned long getFlag;
	QString caption;
	QString visibleName;
	QString invisibleName;
	const char *visibleIcon;
	const char *invisibleIcon;
	unsigned int categories;
	unsigned int options;

	static const PresenceTypeData *all();
	static const PresenceTypeData &forType( Presence::Type type );
	static const PresenceTypeData &forStatus( unsigned long status );
	static const PresenceTypeData &forOnlineStatusType( const Kopete::OnlineStatus::StatusType statusType );
};

const PresenceTypeData *PresenceTypeData::all()
{
	using namespace Kopete;
	using namespace ICQ::StatusCode;
	/**
	 * The order here is important - this is the order the IS_XXX flags will be checked for in.
	 * That, in particular, means that NA, Occupied and DND must appear before Away, and that
	 * DND must appear before Occupied. Offline (testing all bits) must go first, and Online
	 * (testing no bits - will always match a status) must go last.
	 * 
	 * Free For Chat is currently listed after Away, since if someone is Away + Free For Chat we
	 * want to show them as Away more than we want to show them FFC.
	 */
	static const PresenceTypeData data[] =
	{
		{ Presence::Offline,      OnlineStatus::Offline, OFFLINE,  OFFLINE, i18n( "O&ffline" ),        i18n("Offline"),        i18n("Offline"),                    0,                      "contact_invisible_overlay", Kopete::OnlineStatusManager::Offline,      0 },
		{ Presence::DoNotDisturb, OnlineStatus::Away,    SET_DND,  IS_DND,  i18n( "&Do Not Disturb" ), i18n("Do Not Disturb"), i18n("Do Not Disturb (Invisible)"), "contact_busy_overlay", "contact_invisible_overlay", Kopete::OnlineStatusManager::Busy,         Kopete::OnlineStatusManager::HasAwayMessage },
		{ Presence::Occupied,     OnlineStatus::Away,    SET_OCC,  IS_OCC,  i18n( "O&ccupied" ),       i18n("Occupied"),       i18n("Occupied (Invisible)"),       "contact_busy_overlay", "contact_invisible_overlay", 0,                                         Kopete::OnlineStatusManager::HasAwayMessage },
		{ Presence::NotAvailable, OnlineStatus::Away,    SET_NA,   IS_NA,   i18n( "Not A&vailable" ),  i18n("Not Available"),  i18n("Not Available (Invisible)"),  "contact_xa_overlay",   "contact_invisible_overlay", Kopete::OnlineStatusManager::ExtendedAway, Kopete::OnlineStatusManager::HasAwayMessage },
		{ Presence::Away,         OnlineStatus::Away,    SET_AWAY, IS_AWAY, i18n( "&Away" ),           i18n("Away"),           i18n("Away (Invisible)"),           "contact_away_overlay", "contact_invisible_overlay", Kopete::OnlineStatusManager::Away,         Kopete::OnlineStatusManager::HasAwayMessage },
		{ Presence::FreeForChat,  OnlineStatus::Online,  SET_FFC,  IS_FFC,  i18n( "&Free for Chat" ),  i18n("Free For Chat"),  i18n("Free For Chat (Invisible)"),  "icq_ffc",              "contact_invisible_overlay", Kopete::OnlineStatusManager::FreeForChat,  0 },
		{ Presence::Online,       OnlineStatus::Online,  ONLINE,   ONLINE,  i18n( "O&nline" ),         i18n("Online"),         i18n("Online (Invisible)"),         0,                      "contact_invisible_overlay", Kopete::OnlineStatusManager::Online,       0 }
	};
	return data;
}

const PresenceTypeData &PresenceTypeData::forType( Presence::Type type )
{
	const PresenceTypeData *array = all();
	for ( uint n = 0; n < Presence::TypeCount; ++n )
		if ( array[n].type == type )
			return array[n];
	kdWarning(14153) << k_funcinfo << "type " << (int)type << " not found! Returning Offline" << endl;
	return array[0];
}

const PresenceTypeData &PresenceTypeData::forStatus( unsigned long status )
{
	const PresenceTypeData *array = all();
	for ( uint n = 0; n < Presence::TypeCount; ++n )
	{
		//kdDebug(14153) << k_funcinfo << "array[n].getFlag is " << array[n].getFlag << ", status is " << status << ", & is " << (array[n].getFlag & status) << endl;
		if ( (array[n].getFlag & status) == array[n].getFlag )
			return array[n];
	}
	kdWarning(14153) << k_funcinfo << "status " << (int)status << " not found! Returning Offline. This should not happen." << endl;
	return array[0];
}

const PresenceTypeData &PresenceTypeData::forOnlineStatusType( const Kopete::OnlineStatus::StatusType statusType )
{
	const PresenceTypeData *array = all();
	for ( int n = Presence::TypeCount - 1; n >= 0; --n )
	{
		if ( array[n].onlineStatusType == statusType )
			return array[n];
	}
	kdWarning(14153) << k_funcinfo << "online status " << (int)statusType << " not found! Returning Offline. This should not happen." << endl;
	return array[0];
}

//END struct PresenceTypeData

//BEGIN class OnlineStatusManager

class OnlineStatusManager::Private
{
public:
	typedef std::vector<Kopete::OnlineStatus> StatusList;
	
	// connecting and unknown should have the same internal status as offline, so converting to a Presence gives an Offline one
	Private()
		: connecting(     Kopete::OnlineStatus::Connecting, 99, ICQProtocol::protocol(),
					      99,                "icq_connecting", i18n("Connecting...") )
		, unknown(        Kopete::OnlineStatus::Unknown,     0, ICQProtocol::protocol(),
					      Presence::Offline, "status_unknown", i18n("Unknown") )
		, waitingForAuth( Kopete::OnlineStatus::Unknown,     1, ICQProtocol::protocol(),
				          Presence::Offline, "button_cancel",  i18n("Waiting for Authorization") )
		, invisible(      Kopete::OnlineStatus::Invisible,   2, ICQProtocol::protocol(),
						  Presence::Offline, QString::null,    QString::null,
						  QString::null, Kopete::OnlineStatusManager::Invisible,
						  Kopete::OnlineStatusManager::HideFromMenu )

	{
		createStatusList( false, 0, visibleStatusList );
		createStatusList( true, Presence::TypeCount, invisibleStatusList );
	}
	void createStatusList( bool invisible, const uint invisibleOffset, StatusList &statusList )
	{
		//weight 0, 1 and 2 are used by KOS unknown, waitingForAuth and invisible
		const uint firstUsableWeight = 3;
		statusList.reserve( Presence::TypeCount );
		for ( uint n = 0; n < Presence::TypeCount; ++n )
		{
			const PresenceTypeData &data = PresenceTypeData::forType( static_cast<Presence::Type>(n) );
			const uint weight = n + firstUsableWeight;
			const uint internalStatus = n + invisibleOffset;
			QStringList overlayIcons( data.visibleIcon );
			QString description( data.visibleName );
			Kopete::OnlineStatus status;
			if ( invisible )
			{
				overlayIcons << data.invisibleIcon;
				description = data.invisibleName;
				//don't add invisible KOS to account's context menu
				status = Kopete::OnlineStatus( data.onlineStatusType, weight,
											   ICQProtocol::protocol(), internalStatus,
											   overlayIcons, description );
			}
			else
			{
				//add visible KOS
				status = Kopete::OnlineStatus( data.onlineStatusType, weight,
											   ICQProtocol::protocol(), internalStatus,
											   overlayIcons, description,
											   data.caption, data.categories, data.options );
			}
			statusList.push_back( status );
		}
	}
	
	StatusList visibleStatusList, invisibleStatusList;
	Kopete::OnlineStatus connecting;
	Kopete::OnlineStatus unknown;
	Kopete::OnlineStatus waitingForAuth;
	Kopete::OnlineStatus invisible;
};

OnlineStatusManager::OnlineStatusManager()
	: d( new Private )
{
}

OnlineStatusManager::~OnlineStatusManager()
{
	delete d;
}

Presence OnlineStatusManager::presenceOf( uint internalStatus )
{
	if ( internalStatus < Presence::TypeCount )
	{
		return Presence( static_cast<Presence::Type>( internalStatus ), Presence::Visible );
	}
	else if ( internalStatus < 2 * Presence::TypeCount )
	{
		return Presence( static_cast<Presence::Type>( internalStatus - Presence::TypeCount ), Presence::Invisible );
	}
	else
	{
		kdWarning(14153) << k_funcinfo << "No presence exists for internal status " << internalStatus << "! Returning Offline" << endl;
		return Presence( Presence::Offline, Presence::Visible );
	}	
}

Kopete::OnlineStatus OnlineStatusManager::onlineStatusOf( const Presence &presence )
{
	if ( presence.visibility() == Presence::Visible )
		return d->visibleStatusList[ presence.type() ];
	else
		return d->invisibleStatusList[ presence.type() ];
}

Kopete::OnlineStatus OnlineStatusManager::connectingStatus()
{
	return d->connecting;
}

Kopete::OnlineStatus OnlineStatusManager::unknownStatus()
{
	return d->unknown;
}

Kopete::OnlineStatus OnlineStatusManager::waitingForAuth()
{
	return d->waitingForAuth;
}

//END class OnlineStatusManager

//BEGIN class Presence

Presence Presence::fromOnlineStatus( const Kopete::OnlineStatus &status )
{
	if ( status.protocol() == ICQProtocol::protocol() )
	{
		OnlineStatusManager *store = ICQProtocol::protocol()->statusManager();
		return store->presenceOf( status.internalStatus() );
	}
	else
	{
		//status is a libkopete builtin status object
		//don't even think about converting it to ICQ::Presence using presenceOf!
		return Presence( PresenceTypeData::forOnlineStatusType( status.status() ).type,
						 Presence::Visible );
	}
}

Kopete::OnlineStatus Presence::toOnlineStatus() const
{
	OnlineStatusManager *store = ICQProtocol::protocol()->statusManager();
	return store->onlineStatusOf( *this );
}


unsigned long Presence::toOscarStatus() const
{
	unsigned long basicStatus = basicOscarStatus();
	if ( _visibility == Invisible )
		basicStatus |= StatusCode::INVISIBLE;
	return basicStatus;
}

Presence Presence::fromOscarStatus( unsigned long code )
{
	Type type = typeFromOscarStatus( code & ~StatusCode::INVISIBLE );
	bool invisible = (code & StatusCode::INVISIBLE) == StatusCode::INVISIBLE;
	return Presence( type, invisible ? Invisible : Visible );
}


unsigned long Presence::basicOscarStatus() const
{
	const PresenceTypeData &data = PresenceTypeData::forType( _type );
	return data.setFlag;
}

Presence::Type Presence::typeFromOscarStatus( unsigned long status )
{
	const PresenceTypeData &data = PresenceTypeData::forStatus( status );
	return data.type;
}

//END class Presence

} // end namespace ICQ

// vim: set noet ts=4 sts=4 sw=4:
// kate: indent-mode: csands; space-indent off; tab-width 4;