1 /*
2  * ContactTree
3  *
4  * Copyright (C) 2002 Barnaby Gray <barnaby@beedesign.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19  *
20  */
21 
22 #include <cstdlib>
23 
24 #include "ContactTree.h"
25 #include "events.h"
26 
27 namespace ICQ2000 {
28 
29   using std::string;
30 
31 #define ITERATE_CONTACTS_BEGIN \
32   iterator curr = begin();     \
33   while (curr != end()) {
34 
35 #define ITERATE_CONTACTS_END \
36     ++curr;                  \
37   }
38 
39 #define ITERATE_GROUPS_BEGIN        \
40   iterator curr = m_groups.begin(); \
41   while (curr != m_groups.end()) {
42 
43 #define ITERATE_GROUPS_END \
44     ++curr;                \
45   }
46 
47 #define CONST_ITERATE_GROUPS_BEGIN        \
48   const_iterator curr = m_groups.begin(); \
49   while (curr != m_groups.end()) {
50 
51 #define CONST_ITERATE_GROUPS_END \
52     ++curr;                      \
53   }
54 
55   // ============================================================================
56   //  ContactTree Group object
57   // ============================================================================
58 
_ContactTree_Group(const std::string & l,unsigned short id)59   _ContactTree_Group::_ContactTree_Group(const std::string& l, unsigned short id)
60     : m_id(id), m_label(l)
61   { }
62 
_ContactTree_Group()63   _ContactTree_Group::_ContactTree_Group()
64     : m_id(0), m_label("")
65   { }
66 
_ContactTree_Group(const _ContactTree_Group & gp)67   _ContactTree_Group::_ContactTree_Group(const _ContactTree_Group& gp)
68     : m_crefs( gp.m_crefs ), m_id( gp.m_id ), m_label( gp.m_label )
69   { }
70 
get_id() const71   unsigned short _ContactTree_Group::get_id() const
72   {
73     return m_id;
74   }
75 
get_label() const76   string _ContactTree_Group::get_label() const
77   {
78     return m_label;
79   }
80 
set_label(const string & l)81   void _ContactTree_Group::set_label(const string& l)
82   {
83     if (m_label == l) return;
84     m_label = l;
85     GroupChangeEvent ev( *this );
86     contactlist_signal.emit( &ev );
87   }
88 
operator [](unsigned int uin)89   ContactRef _ContactTree_Group::operator[](unsigned int uin)
90   {
91     return lookup_uin(uin);
92   }
93 
lookup_uin(unsigned int uin)94   ContactRef _ContactTree_Group::lookup_uin(unsigned int uin)
95   {
96     if (m_crefs.count(uin) != 0) return (*(m_crefs.find(uin))).second;
97     return NULL;
98   }
99 
lookup_mobile(const string & m)100   ContactRef _ContactTree_Group::lookup_mobile(const string& m)
101   {
102     ITERATE_CONTACTS_BEGIN
103     if ((*curr)->getNormalisedMobileNo() == m) return (*curr);
104     ITERATE_CONTACTS_END
105 
106     return NULL;
107   }
108 
lookup_email(const string & em)109   ContactRef _ContactTree_Group::lookup_email(const string& em)
110   {
111     ITERATE_CONTACTS_BEGIN
112     if ((*curr)->getEmail() == em) return (*curr);
113     ITERATE_CONTACTS_END
114 
115     return NULL;
116   }
117 
add(ContactRef ct)118   ContactRef _ContactTree_Group::add(ContactRef ct) {
119     m_crefs.insert( std::make_pair(ct->getUIN(), ct) );
120 
121     // fire off signal
122     UserAddedEvent uev( ct, *this );
123     contactlist_signal.emit( &uev );
124 
125     // connect up contact's status signals to propagate up group
126     ct->status_change_signal.connect( contact_status_change_signal );
127     ct->userinfo_change_signal.connect( contact_userinfo_change_signal );
128 
129     return ct;
130   }
131 
relocate_from(ContactRef ct)132   void _ContactTree_Group::relocate_from(ContactRef ct) {
133     if (m_crefs.count(ct->getUIN()) != 0)
134       m_crefs.erase(ct->getUIN());
135   }
136 
relocate_to(ContactRef ct)137   void _ContactTree_Group::relocate_to(ContactRef ct) {
138     m_crefs.insert( std::make_pair(ct->getUIN(), ct) );
139   }
140 
remove(unsigned int uin)141   void _ContactTree_Group::remove(unsigned int uin) {
142     if (m_crefs.count(uin) != 0) {
143       // first fire off signal
144       UserRemovedEvent uev( m_crefs[uin], *this );
145       contactlist_signal.emit( &uev );
146 
147       m_crefs.erase(uin);
148     }
149   }
150 
empty() const151   bool _ContactTree_Group::empty() const
152   {
153     return m_crefs.empty();
154   }
155 
size() const156   unsigned int _ContactTree_Group::size() const
157   {
158     return m_crefs.size();
159   }
160 
exists(unsigned int uin)161   bool _ContactTree_Group::exists(unsigned int uin)
162   {
163     return (m_crefs.count(uin) != 0);
164   }
165 
mobile_exists(const string & m)166   bool _ContactTree_Group::mobile_exists(const string& m)
167   {
168     ITERATE_CONTACTS_BEGIN
169     if ((*curr)->getNormalisedMobileNo() == m) return true;
170     ITERATE_CONTACTS_END
171     return false;
172   }
173 
email_exists(const string & em)174   bool _ContactTree_Group::email_exists(const string& em)
175   {
176     ITERATE_CONTACTS_BEGIN
177     if ((*curr)->getEmail() == em) return true;
178     ITERATE_CONTACTS_END
179     return false;
180   }
181 
begin()182   _ContactTree_Group::iterator _ContactTree_Group::begin()
183   {
184     return iterator(m_crefs.begin());
185   }
186 
end()187   _ContactTree_Group::iterator _ContactTree_Group::end()
188   {
189     return iterator(m_crefs.end());
190   }
191 
begin() const192   _ContactTree_Group::const_iterator _ContactTree_Group::begin() const
193   {
194     return const_iterator(m_crefs.begin());
195   }
196 
end() const197   _ContactTree_Group::const_iterator _ContactTree_Group::end() const
198   {
199     return const_iterator(m_crefs.end());
200   }
201 
202   // ============================================================================
203   //  ContactTree
204   // ============================================================================
205 
ContactTree()206   ContactTree::ContactTree() { }
207 
ContactTree(const ContactTree & ct)208   ContactTree::ContactTree(const ContactTree& ct)
209     : m_groups( ct.m_groups )
210   { }
211 
operator [](unsigned int uin)212   ContactRef ContactTree::operator[](unsigned int uin)
213   {
214     return lookup_uin(uin);
215   }
216 
lookup_uin(unsigned int uin)217   ContactRef ContactTree::lookup_uin(unsigned int uin)
218   {
219     ITERATE_GROUPS_BEGIN
220     if ((*curr).exists(uin)) return (*curr).lookup_uin(uin);
221     ITERATE_GROUPS_END
222     return NULL;
223   }
224 
lookup_mobile(const std::string & m)225   ContactRef ContactTree::lookup_mobile(const std::string& m)
226   {
227     ITERATE_GROUPS_BEGIN
228     if ((*curr).mobile_exists(m)) return (*curr).lookup_mobile(m);
229     ITERATE_GROUPS_END
230     return NULL;
231   }
232 
lookup_email(const std::string & em)233   ContactRef ContactTree::lookup_email(const std::string& em)
234   {
235     ITERATE_GROUPS_BEGIN
236     if ((*curr).email_exists(em)) return (*curr).lookup_email(em);
237     ITERATE_GROUPS_END
238     return NULL;
239   }
240 
add_group(const string & l)241   ContactTree::Group& ContactTree::add_group(const string& l)
242   {
243     return add_group(l, get_unique_group_id() );
244   }
245 
add_group(const string & l,unsigned short group_id)246   ContactTree::Group& ContactTree::add_group(const string& l, unsigned short group_id)
247   {
248     if (exists_group(group_id)) {
249       // oops.. trying to add one with a duplicate group_id
250       group_id = get_unique_group_id();
251     }
252 
253     Group gp( l, group_id );
254     m_groups.push_back(gp);
255 
256     // propagate signals up to ContactTree object
257     m_groups.back().contactlist_signal.connect( contactlist_signal );
258     m_groups.back().contact_status_change_signal.connect( contact_status_change_signal );
259     m_groups.back().contact_userinfo_change_signal.connect( contact_userinfo_change_signal );
260 
261     // fireoff event
262     GroupAddedEvent ev(m_groups.back());
263     contactlist_signal.emit( &ev );
264 
265     return m_groups.back();
266   }
267 
remove_group(Group & gp)268   void ContactTree::remove_group(Group& gp)
269   {
270     remove_group(gp.get_id());
271   }
272 
remove_group(unsigned short group_id)273   void ContactTree::remove_group(unsigned short group_id)
274   {
275     ITERATE_GROUPS_BEGIN
276     if ((*curr).get_id() == group_id) {
277       // emit event
278       GroupRemovedEvent ev(*curr);
279       contactlist_signal.emit( &ev );
280 
281       // remove from list
282       m_groups.erase(curr);
283 
284       break;
285     }
286     ITERATE_GROUPS_END
287   }
288 
exists_group(unsigned short group_id)289   bool ContactTree::exists_group(unsigned short group_id)
290   {
291     ITERATE_GROUPS_BEGIN
292     if ((*curr).get_id() == group_id) return true;
293     ITERATE_GROUPS_END
294     return false;
295   }
296 
lookup_group(unsigned short group_id)297   ContactTree::Group& ContactTree::lookup_group(unsigned short group_id)
298   {
299     ITERATE_GROUPS_BEGIN
300     if ((*curr).get_id() == group_id) return (*curr);
301     ITERATE_GROUPS_END
302     return add_group("");
303   }
304 
lookup_group_containing_contact(ContactRef ct)305   ContactTree::Group& ContactTree::lookup_group_containing_contact(ContactRef ct)
306   {
307     unsigned int uin = ct->getUIN();
308     ITERATE_GROUPS_BEGIN
309     if ((*curr).exists(uin)) return (*curr);
310     ITERATE_GROUPS_END
311     return add_group("");
312   }
313 
relocate_contact(ContactRef ct,Group & from,Group & to)314   void ContactTree::relocate_contact(ContactRef ct, Group& from, Group& to)
315   {
316     from.relocate_from(ct);
317     to.relocate_to(ct);
318 
319     UserRelocatedEvent ev(ct, to, from);
320     contactlist_signal.emit( &ev );
321   }
322 
remove(unsigned int uin)323   void ContactTree::remove(unsigned int uin)
324   {
325     ITERATE_GROUPS_BEGIN
326     if ((*curr).exists(uin)) {
327       (*curr).remove(uin);
328       break;
329     }
330     ITERATE_GROUPS_END
331   }
332 
size() const333   unsigned int ContactTree::size() const
334   {
335     unsigned int ret = 0;
336     CONST_ITERATE_GROUPS_BEGIN
337     ret += (*curr).size();
338     CONST_ITERATE_GROUPS_END
339     return ret;
340   }
341 
group_size() const342   unsigned int ContactTree::group_size() const
343   {
344     return m_groups.size();
345   }
346 
empty() const347   bool ContactTree::empty() const
348   {
349     return m_groups.empty();
350   }
351 
exists(unsigned int uin)352   bool ContactTree::exists(unsigned int uin)
353   {
354     ITERATE_GROUPS_BEGIN
355     if ((*curr).exists(uin)) return true;
356     ITERATE_GROUPS_END
357     return false;
358   }
359 
mobile_exists(const std::string & m)360   bool ContactTree::mobile_exists(const std::string& m)
361   {
362     ITERATE_GROUPS_BEGIN
363     if ((*curr).mobile_exists(m)) return true;
364     ITERATE_GROUPS_END
365     return false;
366   }
367 
email_exists(const std::string & em)368   bool ContactTree::email_exists(const std::string& em)
369   {
370     ITERATE_GROUPS_BEGIN
371     if ((*curr).email_exists(em)) return true;
372     ITERATE_GROUPS_END
373     return false;
374   }
375 
begin()376   ContactTree::iterator ContactTree::begin()
377   {
378     return m_groups.begin();
379   }
380 
end()381   ContactTree::iterator ContactTree::end()
382   {
383     return m_groups.end();
384   }
385 
begin() const386   ContactTree::const_iterator ContactTree::begin() const
387   {
388     return m_groups.begin();
389   }
390 
end() const391   ContactTree::const_iterator ContactTree::end() const
392   {
393     return m_groups.end();
394   }
395 
get_unique_group_id() const396   unsigned short ContactTree::get_unique_group_id() const
397   {
398     unsigned short ret;
399     while(1) {
400       ret = (unsigned short) rand();
401       // this is fine for our simple purposes..
402 
403       bool uniq = true;
404       CONST_ITERATE_GROUPS_BEGIN
405       if ((*curr).get_id() == ret) {
406 	uniq = false;
407 	break;
408       }
409       CONST_ITERATE_GROUPS_END
410 
411       if (uniq) break;
412     }
413 
414     return ret;
415   }
416 }
417