~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to kde/src/lib/phonedirectorymodel.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-01-07 14:51:16 UTC
  • mfrom: (4.3.5 sid)
  • Revision ID: package-import@ubuntu.com-20150107145116-yxnafinf4lrdvrmx
Tags: 1.4.1-0.1ubuntu1
* Merge with Debian, remaining changes:
 - Drop soprano, nepomuk build-dep
* Drop ubuntu patches, now upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
//SFLPhone
24
24
#include "phonenumber.h"
25
25
#include "call.h"
 
26
#include "uri.h"
26
27
#include "account.h"
27
28
#include "contact.h"
28
29
#include "accountlistmodel.h"
29
30
#include "numbercategory.h"
30
31
#include "numbercategorymodel.h"
31
 
#include "abstractcontactbackend.h"
 
32
#include "abstractitembackend.h"
32
33
#include "dbus/presencemanager.h"
33
34
#include "visitors/pixmapmanipulationvisitor.h"
 
35
#include "contactmodel.h"
34
36
 
35
37
PhoneDirectoryModel* PhoneDirectoryModel::m_spInstance = nullptr;
36
38
 
45
47
PhoneDirectoryModel::~PhoneDirectoryModel()
46
48
{
47
49
   QList<NumberWrapper*> vals = m_hNumbersByNames.values();
 
50
   //Used by indexes
48
51
   m_hNumbersByNames.clear();
49
52
   m_lSortedNames.clear();
50
53
   while (vals.size()) {
52
55
      vals.removeAt(0);
53
56
      delete w;
54
57
   }
 
58
 
 
59
   //Used by auto completion
55
60
   vals = m_hSortedNumbers.values();
56
61
   m_hSortedNumbers.clear();
57
62
   m_hDirectory.clear();
278
283
   return QVariant();
279
284
}
280
285
 
 
286
/**
 
287
 * This helper method make sure that number without an account get registered
 
288
 * correctly with their alternate URIs. In case there is an obvious duplication,
 
289
 * it will try to merge both numbers.
 
290
 */
 
291
void PhoneDirectoryModel::setAccount(PhoneNumber* number, Account* account ) {
 
292
   const URI& strippedUri = number->uri();
 
293
   const bool hasAtSign = strippedUri.hasHostname();
 
294
   number->setAccount(account);
 
295
 
 
296
   if (!hasAtSign) {
 
297
      NumberWrapper* wrap = m_hDirectory[strippedUri];
 
298
 
 
299
      //Let make sure none is created in the future for nothing
 
300
      if (!wrap) {
 
301
         //It wont be a duplicate as none exist for this URI
 
302
         const QString extendedUri = strippedUri+'@'+account->hostname();
 
303
         wrap = new NumberWrapper();
 
304
         m_hDirectory    [extendedUri] = wrap;
 
305
         m_hSortedNumbers[extendedUri] = wrap;
 
306
 
 
307
      }
 
308
      else {
 
309
         //After all this, it is possible the number is now a duplicate
 
310
         foreach(PhoneNumber* n, wrap->numbers) {
 
311
            if (n != number && n->account() && n->account() == number->account()) {
 
312
               number->merge(n);
 
313
            }
 
314
         }
 
315
      }
 
316
      wrap->numbers << number;
 
317
 
 
318
   }
 
319
}
 
320
 
 
321
/**
 
322
 * This version of getNumber() try to get a phone number with a contact from an URI and account
 
323
 * It will also try to attach an account to existing numbers. This is not 100% reliable, but
 
324
 * it is correct often enough to do it.
 
325
 */
 
326
PhoneNumber* PhoneDirectoryModel::getNumber(const QString& uri, Account* account, const QString& type)
 
327
{
 
328
   return getNumber(uri,nullptr,account,type);
 
329
}
 
330
 
 
331
///Add new information to existing numbers and try to merge
 
332
PhoneNumber* PhoneDirectoryModel::fillDetails(NumberWrapper* wrap, const URI& strippedUri, Account* account, Contact* contact, const QString& type)
 
333
{
 
334
   //TODO pick the best URI
 
335
   //TODO the account hostname change corner case
 
336
   //TODO search for account that has the same hostname as the URI
 
337
   if (wrap) {
 
338
      foreach(PhoneNumber* number, wrap->numbers) {
 
339
 
 
340
         //BEGIN Check if contact can be set
 
341
 
 
342
         //Check if the contact is compatible
 
343
         const bool hasCompatibleContact = contact && (
 
344
               (!number->contact())
 
345
            || (
 
346
                  (number->contact()->uid() == contact->uid())
 
347
               && number->contact() != contact
 
348
            )
 
349
         );
 
350
 
 
351
         //Check if the URI match
 
352
         const bool hasCompatibleURI =  hasCompatibleContact && (number->uri().hasHostname()?(
 
353
               /* Has hostname */
 
354
                   strippedUri == number->uri()
 
355
                   /* Something with an hostname can be used with IP2IP */ //TODO support DHT here
 
356
               || (account && account->id() == Account::ProtocolName::IP2IP)
 
357
            ) : ( /* Has no hostname */
 
358
                  number->account() && number->uri()+'@'+number->account()->hostname() == strippedUri
 
359
            ));
 
360
 
 
361
         //Check if the account is compatible
 
362
         const bool hasCompatibleAccount = hasCompatibleURI && ((!account)
 
363
            || (!number->account())
 
364
            /* Direct match, this is always valid */
 
365
            || (account == number->account())
 
366
            /* IP2IP is a special case */ //TODO support DHT here
 
367
            || (
 
368
                  account->id() == Account::ProtocolName::IP2IP
 
369
                  && strippedUri.hasHostname()
 
370
               ));
 
371
 
 
372
         //TODO the Display name could be used to influence the choice
 
373
         //It would need to ignore all possible translated values of unknown
 
374
         //and only be available when another information match
 
375
 
 
376
         //If everything match, set the contact
 
377
         if (hasCompatibleAccount)
 
378
            number->setContact(contact);
 
379
         //END Check if the contact can be set
 
380
 
 
381
 
 
382
         //BEGIN Check is account can be set
 
383
         // Try to match the account
 
384
         // Not perfect, but better than ignoring the high probabilities
 
385
         //TODO only do it is hostname match
 
386
         if ((!account) || account != number->account()) {
 
387
 
 
388
            if (account && (!contact) && !number->account())
 
389
               setAccount(number,account);
 
390
 
 
391
            //Set a type, this has low probabilities of being invalid
 
392
            if ((!number->hasType()) && (!type.isEmpty())) {
 
393
               number->setCategory(NumberCategoryModel::instance()->getCategory(type));
 
394
            }
 
395
 
 
396
            //We already have enough information to confirm the choice
 
397
            if (contact && number->contact() &&((contact->uid()) == number->contact()->uid()))
 
398
               return number;
 
399
         }
 
400
         //END Check is account can be set
 
401
      }
 
402
   }
 
403
   return nullptr;
 
404
}
 
405
 
 
406
///Return/create a number when no information is available
281
407
PhoneNumber* PhoneDirectoryModel::getNumber(const QString& uri, const QString& type)
282
408
{
283
 
   const QString strippedUri =  PhoneNumber::stripUri(uri);
 
409
   const URI strippedUri(uri);
284
410
   NumberWrapper* wrap = m_hDirectory[strippedUri];
285
411
   if (wrap) {
286
412
      PhoneNumber* nb = wrap->numbers[0];
287
 
      if ((!nb->m_hasType) && (!type.isEmpty())) {
 
413
      if ((!nb->hasType()) && (!type.isEmpty())) {
288
414
         nb->setCategory(NumberCategoryModel::instance()->getCategory(type));
289
415
      }
290
416
      return nb;
292
418
 
293
419
   //Too bad, lets create one
294
420
   PhoneNumber* number = new PhoneNumber(strippedUri,NumberCategoryModel::instance()->getCategory(type));
 
421
   number->setIndex(m_lNumbers.size());
 
422
   m_lNumbers << number;
295
423
   connect(number,SIGNAL(callAdded(Call*)),this,SLOT(slotCallAdded(Call*)));
296
424
   connect(number,SIGNAL(changed()),this,SLOT(slotChanged()));
297
 
   number->m_Index = m_lNumbers.size();
298
 
   m_lNumbers << number;
299
 
 
300
 
   const QString hn = number->hostname();
301
 
 
302
 
   //Check if we are lucky enough to have a single registered account with the same hostname
303
 
   /*if (!hn.isEmpty()) {
304
 
      
305
 
   }*/
 
425
 
 
426
   const QString hn = number->uri().hostname();
306
427
 
307
428
   emit layoutChanged();
308
429
   if (!wrap) {
314
435
   return number;
315
436
}
316
437
 
317
 
PhoneNumber* PhoneDirectoryModel::getNumber(const QString& uri, Account* account, const QString& type)
 
438
///Create a number when a more information is available duplicated ones
 
439
PhoneNumber* PhoneDirectoryModel::getNumber(const QString& uri, Contact* contact, Account* account, const QString& type)
318
440
{
319
 
   const QString strippedUri =  PhoneNumber::stripUri(uri);
 
441
   //Remove extra data such as "<sip:" from the main URI
 
442
   const URI strippedUri(uri);
 
443
 
320
444
   //See if the number is already loaded
321
 
   NumberWrapper* wrap = m_hDirectory[strippedUri];
322
 
   if ((!wrap) && account && uri.indexOf('@') == -1) {
 
445
   NumberWrapper* wrap  = m_hDirectory[strippedUri];
 
446
   NumberWrapper* wrap2 = nullptr;
 
447
   NumberWrapper* wrap3 = nullptr;
 
448
 
 
449
   //Check if the URI is complete or short
 
450
   const bool hasAtSign = strippedUri.hasHostname();
 
451
 
 
452
   //Try to see if there is a better candidate with a suffix (LAN only)
 
453
   if ( !hasAtSign && account ) {
323
454
      //Append the account hostname
324
 
      wrap = m_hDirectory[strippedUri+'@'+account->hostname()];
325
 
   }
326
 
   if (wrap) {
327
 
      foreach(PhoneNumber* number, wrap->numbers) {
328
 
         //Not perfect, but better than ignoring the high probabilities
329
 
         if (!number->account())
330
 
            number->setAccount(account);
331
 
         if ((!number->m_hasType) && (!type.isEmpty())) {
332
 
            number->setCategory(NumberCategoryModel::instance()->getCategory(type));
333
 
         }
334
 
         if ((!account) || number->account() == account)
335
 
            return number;
336
 
      }
337
 
   }
338
 
 
339
 
   //Create the number
340
 
   PhoneNumber* number = new PhoneNumber(strippedUri,NumberCategoryModel::instance()->getCategory(type));
341
 
   connect(number,SIGNAL(callAdded(Call*)),this,SLOT(slotCallAdded(Call*)));
342
 
   connect(number,SIGNAL(changed()),this,SLOT(slotChanged()));
343
 
   number->setAccount(account);
344
 
   number->m_Index = m_lNumbers.size();
345
 
   m_lNumbers << number;
346
 
   if (!wrap) {
347
 
      wrap = new NumberWrapper();
348
 
      m_hDirectory[strippedUri] = wrap;
349
 
      m_hSortedNumbers[strippedUri] = wrap;
350
 
   }
351
 
   wrap->numbers << number;
352
 
   emit layoutChanged();
353
 
   return number;
354
 
}
355
 
 
356
 
PhoneNumber* PhoneDirectoryModel::getNumber(const QString& uri, Contact* contact, Account* account, const QString& type)
357
 
{
358
 
   const QString strippedUri =  PhoneNumber::stripUri(uri);
359
 
   //See if the number is already loaded
360
 
   NumberWrapper* wrap = m_hDirectory[strippedUri];
361
 
   if (wrap) {
362
 
      //TODO find something better, it is prone to collisions
363
 
      foreach(PhoneNumber* number, wrap->numbers) {
364
 
         if (!number->contact()) {
365
 
            if (!number->account())
366
 
               number->setAccount(account);
367
 
            number->setContact(contact);
368
 
         }
369
 
      }
370
 
      foreach(PhoneNumber* number, wrap->numbers) {
371
 
         if ((!number->m_hasType) && (!type.isEmpty())) {
372
 
            number->setCategory(NumberCategoryModel::instance()->getCategory(type));
373
 
         }
374
 
         if (((!contact) || number->contact() == contact) && ((!account) || number->account() == account))
375
 
            return number;
376
 
      }
377
 
   }
378
 
 
379
 
   //Create the number
380
 
   PhoneNumber* number = new PhoneNumber(strippedUri,NumberCategoryModel::instance()->getCategory(type));
381
 
   connect(number,SIGNAL(callAdded(Call*)),this,SLOT(slotCallAdded(Call*)));
382
 
   connect(number,SIGNAL(changed()),this,SLOT(slotChanged()));
383
 
   number->setAccount(account);
384
 
   number->setContact(contact);
385
 
   number->m_Index = m_lNumbers.size();
386
 
   m_lNumbers << number;
387
 
   if (!wrap) {
388
 
      wrap = new NumberWrapper();
389
 
      m_hDirectory[strippedUri] = wrap;
390
 
      m_hSortedNumbers[strippedUri] = wrap;
391
 
   }
392
 
   wrap->numbers << number;
393
 
   emit layoutChanged();
 
455
      wrap2 = m_hDirectory[strippedUri+'@'+account->hostname()];
 
456
   }
 
457
 
 
458
   //Check
 
459
   PhoneNumber* confirmedCandidate = fillDetails(wrap,strippedUri,account,contact,type);
 
460
 
 
461
   //URIs can be represented in multiple way, check if a more verbose version
 
462
   //already exist
 
463
   PhoneNumber* confirmedCandidate2 = nullptr;
 
464
 
 
465
   //Try to use a PhoneNumber with a contact when possible, work only after the
 
466
   //contact are loaded
 
467
   if (confirmedCandidate && confirmedCandidate->contact())
 
468
      confirmedCandidate2 = fillDetails(wrap2,strippedUri,account,contact,type);
 
469
 
 
470
   PhoneNumber* confirmedCandidate3 = nullptr;
 
471
 
 
472
   //Else, try to see if the hostname correspond to the account and flush it
 
473
   //This have to be done after the parent if as the above give "better"
 
474
   //results. It cannot be merged with wrap2 as this check only work if the
 
475
   //candidate has an account.
 
476
   if (hasAtSign && account && strippedUri.hostname() == account->hostname()) {
 
477
     wrap3 = m_hDirectory[strippedUri.userinfo()];
 
478
     if (wrap3) {
 
479
         foreach(PhoneNumber* number, wrap3->numbers) {
 
480
            if (number->account() == account) {
 
481
               if (contact && ((!number->contact()) || (contact->uid() == number->contact()->uid())))
 
482
                  number->setContact(contact); //TODO Check all cases from fillDetails()
 
483
               //TODO add alternate URI
 
484
               confirmedCandidate3 = number;
 
485
               break;
 
486
            }
 
487
         }
 
488
     }
 
489
   }
 
490
 
 
491
   //If multiple PhoneNumber are confirmed, then they are the same, merge them
 
492
   if (confirmedCandidate3 && (confirmedCandidate || confirmedCandidate2)) {
 
493
      confirmedCandidate3->merge(confirmedCandidate?confirmedCandidate:confirmedCandidate2);
 
494
   }
 
495
   else if (confirmedCandidate && confirmedCandidate2) {
 
496
      if (confirmedCandidate->contact() && !confirmedCandidate2->contact())
 
497
         confirmedCandidate2->merge(confirmedCandidate);
 
498
      else if (confirmedCandidate2->contact() && !confirmedCandidate->contact())
 
499
         confirmedCandidate->merge(confirmedCandidate2);
 
500
   }
 
501
 
 
502
   //Empirical testing resulted in this as the best return order
 
503
   //The merge may have failed either in the "if" above or in the merging code
 
504
   if (confirmedCandidate2)
 
505
      return confirmedCandidate2;
 
506
   if (confirmedCandidate)
 
507
      return confirmedCandidate;
 
508
   if (confirmedCandidate3)
 
509
      return confirmedCandidate3;
 
510
 
 
511
   //No better candidates were found than the original assumption, use it
 
512
   if (wrap) {
 
513
      foreach(PhoneNumber* number, wrap->numbers) {
 
514
         if (((!account) || number->account() == account) && ((!contact) || ((*contact) == number->contact()) || (!number->contact()))) {
 
515
            //Assume this is valid until a smarter solution is implemented to merge both
 
516
            //For a short time, a placeholder contact and a contact can coexist, drop the placeholder
 
517
            if (contact && (!number->contact() || (contact->uid() == number->contact()->uid())))
 
518
               number->setContact(contact);
 
519
 
 
520
            return number;
 
521
         }
 
522
      }
 
523
   }
 
524
 
 
525
   //Create the number
 
526
   PhoneNumber* number = new PhoneNumber(strippedUri,NumberCategoryModel::instance()->getCategory(type));
 
527
   number->setAccount(account);
 
528
   number->setIndex( m_lNumbers.size());
 
529
   if (contact)
 
530
      number->setContact(contact);
 
531
   m_lNumbers << number;
 
532
   connect(number,SIGNAL(callAdded(Call*)),this,SLOT(slotCallAdded(Call*)));
 
533
   connect(number,SIGNAL(changed()),this,SLOT(slotChanged()));
 
534
   if (!wrap) {
 
535
      wrap = new NumberWrapper();
 
536
      m_hDirectory    [strippedUri] = wrap;
 
537
      m_hSortedNumbers[strippedUri] = wrap;
 
538
 
 
539
      //Also add its alternative URI, it should be safe to do
 
540
      if ( !hasAtSign && account && !account->hostname().isEmpty() ) {
 
541
         const QString extendedUri = strippedUri+'@'+account->hostname();
 
542
         //Also check if it hasn't been created by setAccount
 
543
         if ((!wrap2) && (!m_hDirectory[extendedUri])) {
 
544
            wrap2 = new NumberWrapper();
 
545
            m_hDirectory    [extendedUri] = wrap2;
 
546
            m_hSortedNumbers[extendedUri] = wrap2;
 
547
         }
 
548
         wrap2->numbers << number;
 
549
      }
 
550
 
 
551
   }
 
552
   wrap->numbers << number;
 
553
   emit layoutChanged();
 
554
 
394
555
   return number;
395
556
}
396
557
 
405
566
   if (fields.size() == 3) {
406
567
      const QString uri = fields[0];
407
568
      Account* account = AccountListModel::instance()->getAccountById(fields[1]);
408
 
      Contact* contact = Call::contactBackend()?Call::contactBackend()->getContactByUid(fields[2]):nullptr;
 
569
      Contact* contact = ContactModel::instance()->getContactByUid(fields[2].toUtf8());
409
570
      return getNumber(uri,contact,account);
410
571
   }
411
572
   else if (fields.size() == 1) {
434
595
            PhoneNumber* tmp = m_lPopularityIndex[currentIndex-1];
435
596
            m_lPopularityIndex[currentIndex-1] = number;
436
597
            m_lPopularityIndex[currentIndex  ] = tmp   ;
437
 
            tmp->m_PopularityIndex++;
 
598
            tmp->setPopularityIndex(tmp->popularityIndex()+1);
438
599
            currentIndex--;
439
600
         } while (currentIndex && m_lPopularityIndex[currentIndex-1]->callCount() < number->callCount());
440
 
         number->m_PopularityIndex = currentIndex;
 
601
         number->setPopularityIndex(currentIndex);
441
602
         emit layoutChanged();
442
603
      }
443
604
      //The top 10 is not complete, a call count of "1" is enough to make it
444
605
      else if (m_lPopularityIndex.size() < 10 && currentIndex == -1) {
445
606
         m_lPopularityIndex << number;
446
 
         number->m_PopularityIndex = m_lPopularityIndex.size()-1;
 
607
         number->setPopularityIndex(m_lPopularityIndex.size()-1);
447
608
         emit layoutChanged();
448
609
      }
449
610
      //The top 10 is full, but this number just made it to the top 10
450
611
      else if (currentIndex == -1 && m_lPopularityIndex.size() >= 10 && m_lPopularityIndex[9] != number && m_lPopularityIndex[9]->callCount() < number->callCount()) {
451
612
         PhoneNumber* tmp = m_lPopularityIndex[9];
452
 
         tmp->m_PopularityIndex    = -1;
 
613
         tmp->setPopularityIndex(-1);
453
614
         m_lPopularityIndex[9]     = number;
454
 
         number->m_PopularityIndex = 9;
 
615
         number->setPopularityIndex(9);
455
616
         emit tmp->changed();
456
617
         emit number->changed();
457
618
      }
467
628
{
468
629
   PhoneNumber* number = qobject_cast<PhoneNumber*>(sender());
469
630
   if (number) {
470
 
      const int idx = number->m_Index;
471
 
      emit dataChanged(index(idx,0),index(idx,5));
 
631
      const int idx = number->index();
 
632
#ifndef NDEBUG
 
633
      if (idx<0)
 
634
         qDebug() << "Invalid slotChanged() index!" << idx;
 
635
#endif
 
636
      emit dataChanged(index(idx,0),index(idx,static_cast<int>(Columns::UID)));
472
637
   }
473
638
}
474
639