blob: 0e019297670026053a551a017501f9b6c7ac98d1 [file] [log] [blame]
/*
* Copyright (C) 2015-2016 Savoir-faire Linux Inc.
* Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
*
* 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import "PersonsVC.h"
//Qt
#import <QSortFilterProxyModel>
#import <QtMacExtras/qmacfunctions.h>
#import <QPixmap>
//LRC
#import <person.h>
#import <personmodel.h>
#import <callmodel.h>
#import <contactmethod.h>
#import <categorizedcontactmodel.h>
#import <globalinstances.h>
#import "backends/AddressBookBackend.h"
#import "QNSTreeController.h"
#import "delegates/ImageManipulationDelegate.h"
#import "views/HoverTableRowView.h"
#import "views/ContextualTableCellView.h"
#import <AddressBook/AddressBook.h>
class ReachablePersonModel : public QSortFilterProxyModel
{
public:
ReachablePersonModel(QAbstractItemModel* parent) : QSortFilterProxyModel(parent)
{
setSourceModel(parent);
}
virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
return sourceModel()->index(source_row,0,source_parent).flags() & Qt::ItemIsEnabled;
}
};
@interface PersonsVC () {
QNSTreeController *treeController;
__unsafe_unretained IBOutlet RingOutlineView *personsView;
QSortFilterProxyModel *contactProxyModel;
}
@end
@implementation PersonsVC
// Tags for views
NSInteger const IMAGE_TAG = 100;
NSInteger const DISPLAYNAME_TAG = 200;
NSInteger const DETAILS_TAG = 300;
NSInteger const CALL_BUTTON_TAG = 400;
-(void) awakeFromNib
{
NSLog(@"INIT PersonsVC");
contactProxyModel = new ReachablePersonModel(&CategorizedContactModel::instance());
contactProxyModel->setSortRole(static_cast<int>(Qt::DisplayRole));
contactProxyModel->sort(0,Qt::AscendingOrder);
treeController = [[QNSTreeController alloc] initWithQModel:contactProxyModel];
[treeController setAvoidsEmptySelection:NO];
[treeController setChildrenKeyPath:@"children"];
[personsView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
[personsView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
[personsView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
[personsView setTarget:self];
[personsView setDoubleAction:@selector(callContact:)];
[personsView setContextMenuDelegate:self];
CategorizedContactModel::instance().setUnreachableHidden(YES);
}
- (void) dealloc
{
delete contactProxyModel;
}
- (IBAction)callContact:(id)sender
{
if([[treeController selectedNodes] count] > 0) {
auto item = [treeController selectedNodes][0];
QModelIndex qIdx = [treeController toQIdx:item];
ContactMethod* m = nil;
if (!qIdx.parent().isValid()) {
if ([personsView isItemExpanded:item]) {
[[personsView animator] collapseItem:item];
} else
[[personsView animator] expandItem:item];
return;
}
if(((NSTreeNode*)[treeController selectedNodes][0]).indexPath.length == 2) {
// Person
QVariant var = qIdx.data((int)Person::Role::Object);
if (var.isValid()) {
Person *c = var.value<Person*>();
if (c->phoneNumbers().size() > 0) {
m = c->phoneNumbers().first();
}
}
} else if (((NSTreeNode*)[treeController selectedNodes][0]).indexPath.length == 3) {
//ContactMethod
QVariant var = qIdx.data(static_cast<int>(ContactMethod::Role::Object));
if (var.isValid()) {
m = var.value<ContactMethod *>();
}
}
if(m){
Call* c = CallModel::instance().dialingCall();
c->setPeerContactMethod(m);
c << Call::Action::ACCEPT;
CallModel::instance().selectCall(c);
}
}
}
#pragma mark - NSOutlineViewDelegate methods
// -------------------------------------------------------------------------------
// shouldSelectItem:item
// -------------------------------------------------------------------------------
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
{
QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
if(!qIdx.isValid())
return NO;
return YES;
}
// -------------------------------------------------------------------------------
// shouldEditTableColumn:tableColumn:item
//
// Decide to allow the edit of the given outline view "item".
// -------------------------------------------------------------------------------
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
return NO;
}
/* View Based OutlineView: See the delegate method -tableView:viewForTableColumn:row: in NSTableView.
*/
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
NSTableCellView *result;
NSString* displayNameString = qIdx.data(Qt::DisplayRole).toString().toNSString();
if(!qIdx.parent().isValid()) {
result = [outlineView makeViewWithIdentifier:@"LetterCell" owner:outlineView];
[result setWantsLayer:YES];
[result setLayer:[CALayer layer]];
} else if(((NSTreeNode*)item).indexPath.length == 2) {
result = [outlineView makeViewWithIdentifier:@"PersonCell" owner:outlineView];
NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
Person* p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object));
if(displayNameString.length == 0 && p) {
displayNameString = qIdx.data((int)Person::Role::IdOfLastCMUsed).toString().toNSString();
}
[photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(qIdx.data(Qt::DecorationRole)))];
[((ContextualTableCellView*) result) setContextualsControls:[NSMutableArray arrayWithObject:[result viewWithTag:CALL_BUTTON_TAG]]];
[((ContextualTableCellView*) result) setShouldBlurParentView:NO];
NSTextField* details = [result viewWithTag:DETAILS_TAG];
if (p && p->phoneNumbers().size() > 0)
[details setStringValue:p->phoneNumbers().first()->uri().toNSString()];
} else {
result = [outlineView makeViewWithIdentifier:@"ContactMethodCell" owner:outlineView];
}
NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
[displayName setStringValue:displayNameString];
return result;
}
/* View Based OutlineView: See the delegate method -tableView:rowViewForRow: in NSTableView.
*/
- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
{
QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
HoverTableRowView* result = [outlineView makeViewWithIdentifier:@"HoverRowView" owner:nil];
if(!qIdx.parent().isValid()) {
[result setHighlightable:NO];
} else
[result setHighlightable:YES];
return result;
}
- (void)startCallForRow:(id)sender {
NSInteger row = [personsView rowForView:sender];
[personsView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
[self callContact:nil];
}
// -------------------------------------------------------------------------------
// outlineViewSelectionDidChange:notification
// -------------------------------------------------------------------------------
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
}
- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
{
QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
return (((NSTreeNode*)item).indexPath.length == 2) ? 60.0 : 20.0;
}
#pragma mark - ContextMenuDelegate
- (NSMenu*) contextualMenuForIndex:(NSTreeNode*) item
{
QModelIndex qIdx = [treeController toQIdx:item];
if (!qIdx.isValid()) {
return nil;
}
if (qIdx.parent().isValid()) {
Person* p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object));
if (p) {
NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@""];
NSMenuItem* removeContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Delete contact", @"Contextual menu action")
action:@selector(removeContactForRow:)
keyEquivalent:@""];
[removeContactItem setRepresentedObject:item];
[theMenu addItem:removeContactItem];
return theMenu;
}
}
return nil;
}
- (void) removeContactForRow:(id) sender
{
QModelIndex qIdx = [treeController toQIdx:[sender representedObject]];
if (!qIdx.isValid()) {
return;
}
if (!qIdx.parent().isValid()) {
return;
}
Person* p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object));
if(!p) {
return;
}
//check if contact is from MAC address book
ABPerson* adPerson = [[ABAddressBook sharedAddressBook] recordForUniqueId:[[NSString alloc] initWithUTF8String:p->uid().data()]];
if(adPerson) {
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:@"OK"];
[alert setMessageText:NSLocalizedString(@"Could not delete MAC contact", @"Contextual menu alert title")];
[alert setInformativeText:NSLocalizedString(@"To delete go to MAC Contacts App", @"Contextual menu alert remove contact")];
[alert setAlertStyle:NSAlertStyleWarning];
[alert runModal];
return;
}
NSString* name = qIdx.data(Qt::DisplayRole).toString().toNSString();
if(name.length == 0) {
name = qIdx.data((int)Person::Role::IdOfLastCMUsed).toString().toNSString();
}
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:@"OK"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:NSLocalizedString(@"Contact will be deleted", @"Contextual menu alert title")];
NSString* allertMsg = [NSString stringWithFormat:
NSLocalizedString(@"Are you sure you want to delete contact \"%@\"", @"Contextual menu alert remove contact {Name}"), name];
[alert setInformativeText:allertMsg];
[alert setAlertStyle:NSAlertStyleWarning];
NSInteger answer = [alert runModal];
if (answer == NSAlertFirstButtonReturn) {
p->remove();
}
}
@end