HistoryVC.mm 11.6 KB
Newer Older
1
/*
2
 *  Copyright (C) 2015-2016 Savoir-faire Linux Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *  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.
 */
19
#import "HistoryVC.h"
20

Alexandre Lision's avatar
Alexandre Lision committed
21
//Qt
22
#import <QSortFilterProxyModel>
23 24
#import <QtMacExtras/qmacfunctions.h>
#import <QPixmap>
Alexandre Lision's avatar
Alexandre Lision committed
25 26 27

//LRC
#import <categorizedhistorymodel.h>
28 29
#import <callmodel.h>
#import <call.h>
30
#import <person.h>
31
#import <contactmethod.h>
32
#import <globalinstances.h>
33

34
#import "QNSTreeController.h"
35
#import "PersonLinkerVC.h"
36
#import "views/HoverTableRowView.h"
37
#import "delegates/ImageManipulationDelegate.h"
38

39
@interface HistoryVC() <NSPopoverDelegate, KeyboardShortcutDelegate, ContactLinkedDelegate> {
40

41 42 43 44 45
    QNSTreeController *treeController;
    IBOutlet RingOutlineView *historyView;
    QSortFilterProxyModel *historyProxyModel;
    NSPopover* addToContactPopover;
}
46

47
@end
48

49
@implementation HistoryVC
50

51
// Tags for Views
52
NSInteger const DIRECTION_TAG = 100;
53 54
NSInteger const DISPLAYNAME_TAG = 200;
NSInteger const DETAILS_TAG = 300;
55
NSInteger const PHOTO_TAG = 400;
56 57 58

- (void)awakeFromNib
{
59
    NSLog(@"INIT HVC");
60 61
    historyProxyModel = new QSortFilterProxyModel(&CategorizedHistoryModel::instance());
    historyProxyModel->setSourceModel(&CategorizedHistoryModel::instance());
62 63 64
    historyProxyModel->setSortRole(static_cast<int>(Call::Role::Date));
    historyProxyModel->sort(0,Qt::DescendingOrder);
    treeController = [[QNSTreeController alloc] initWithQModel:historyProxyModel];
65 66 67 68

    [treeController setAvoidsEmptySelection:NO];
    [treeController setChildrenKeyPath:@"children"];

69 70 71 72
    [historyView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
    [historyView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
    [historyView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
    [historyView setTarget:self];
73
    [historyView setDoubleAction:@selector(placeHistoryCall:)];
74 75
    [historyView setContextMenuDelegate:self];
    [historyView setShortcutsDelegate:self];
76

77
    QObject::connect(&CallModel::instance(),
78 79 80 81 82 83
                     &CategorizedHistoryModel::dataChanged,
                     [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
                         [historyView reloadDataForRowIndexes:
                          [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(topLeft.row(), bottomRight.row() + 1)]
                                                      columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, historyView.tableColumns.count)]];
                     });
84 85
}

86 87 88 89 90
- (void) dealloc
{
    delete historyProxyModel;
}

91
- (void)placeHistoryCall:(id)sender
92 93
{
    if([[treeController selectedNodes] count] > 0) {
94 95
        auto item = [treeController selectedNodes][0];
        QModelIndex qIdx = [treeController toQIdx:item];
96
        if (!qIdx.parent().isValid()) {
97 98 99 100
            if ([historyView isItemExpanded:item]) {
                [[historyView animator] collapseItem:item];
            } else
                [[historyView animator] expandItem:item];
101 102
            return;
        }
103
        QVariant var = historyProxyModel->data(qIdx, (int)Call::Role::ContactMethod);
104 105
        ContactMethod* m = qvariant_cast<ContactMethod*>(var);
        if(m){
106
            auto c = CallModel::instance().dialingCall();
107
            c->setPeerContactMethod(m);
108
            c << Call::Action::ACCEPT;
109
            CallModel::instance().selectCall(c);
110 111 112 113
        }
    }
}

114 115 116 117 118 119 120 121 122 123 124
#pragma mark - NSOutlineViewDelegate methods

// -------------------------------------------------------------------------------
//	shouldSelectItem:item
// -------------------------------------------------------------------------------
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
{
    return YES;
}

// -------------------------------------------------------------------------------
125 126 127
//	shouldEditTableColumn:tableColumn:item
//
//	Decide to allow the edit of the given outline view "item".
128
// -------------------------------------------------------------------------------
129
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
130
{
131
    return NO;
132 133
}

134
- (NSImage*) image:(NSImage*) img withTintedWithColor:(NSColor *)tint
135
{
136 137 138 139 140 141
    if (tint) {
        [img lockFocus];
        [tint set];
        NSRect imageRect = {NSZeroPoint, [img size]};
        NSRectFillUsingOperation(imageRect, NSCompositeSourceAtop);
        [img unlockFocus];
142
    }
143
    return img;
144 145
}

146 147 148
/* View Based OutlineView: See the delegate method -tableView:viewForTableColumn:row: in NSTableView.
 */
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
149
{
150 151
    QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];

152
    NSTableCellView* result;
153 154
    if(!qIdx.parent().isValid()) {
        result = [outlineView makeViewWithIdentifier:@"CategoryCell" owner:outlineView];
155

156 157
    } else {
        result = [outlineView makeViewWithIdentifier:@"HistoryCell" owner:outlineView];
158
        NSImageView* directionView = [result viewWithTag:DIRECTION_TAG];
159 160 161

        if (qvariant_cast<Call::Direction>(qIdx.data((int)Call::Role::Direction)) == Call::Direction::INCOMING) {
           if (qvariant_cast<Boolean>(qIdx.data((int) Call::Role::Missed))) {
162
               [directionView setImage:[self image:[NSImage imageNamed:@"ic_call_missed"] withTintedWithColor:[NSColor redColor]]];
163
            } else {
164
                [directionView setImage:[self image:[NSImage imageNamed:@"ic_call_received"]
165 166 167 168
                            withTintedWithColor:[NSColor colorWithCalibratedRed:116/255.0 green:179/255.0 blue:93/255.0 alpha:1.0]]];
            }
        } else {
            if (qvariant_cast<Boolean>(qIdx.data((int) Call::Role::Missed))) {
169
                [directionView setImage:[self image:[NSImage imageNamed:@"ic_call_missed"] withTintedWithColor:[NSColor redColor]]];
170
            } else {
171
                [directionView setImage:[self image:[NSImage imageNamed:@"ic_call_made"]
172 173 174 175
                            withTintedWithColor:[NSColor colorWithCalibratedRed:116/255.0 green:179/255.0 blue:93/255.0 alpha:1.0]]];
            }
        }

176 177 178 179 180 181
        auto call = qvariant_cast<Call*>(qIdx.data((int)Call::Role::Object));

        NSImageView* photoView = [result viewWithTag:PHOTO_TAG];
        QVariant photo = GlobalInstances::pixmapManipulator().callPhoto(call, QSize(50,50));
        [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];

182 183 184 185 186 187 188 189
        NSTextField* details = [result viewWithTag:DETAILS_TAG];
        [details setStringValue:qIdx.data((int)Call::Role::FormattedDate).toString().toNSString()];
    }

    NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
    [displayName setStringValue:qIdx.data(Qt::DisplayRole).toString().toNSString()];

    return result;
190 191
}

192
- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
193
{
194
    QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
195 196 197 198
    if(!qIdx.parent().isValid()) {
        return 35.0;
    } else {
        return 48.0;
199 200 201
    }
}

202 203 204
/* View Based OutlineView: See the delegate method -tableView:rowViewForRow: in NSTableView.
 */
- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
205
{
206 207 208 209 210 211 212 213 214
    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;
215 216
}

217 218 219 220 221 222 223 224 225 226 227 228 229
#pragma mark - ContextMenuDelegate

- (NSMenu*) contextualMenuForIndex:(NSIndexPath*) path
{
    if([[treeController selectedNodes] count] > 0) {
        QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
        const auto& var = qIdx.data(static_cast<int>(Call::Role::Object));
        if (qIdx.parent().isValid() && var.isValid()) {
            if (auto call = var.value<Call *>()) {
                auto contactmethod = call->peerContactMethod();
                if (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder()) {
                    NSMenu *theMenu = [[NSMenu alloc]
                                       initWithTitle:@""];
230
                    [theMenu insertItemWithTitle:NSLocalizedString(@"Add to contacts", @"Contextual menu action")
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
                                          action:@selector(addToContact)
                                   keyEquivalent:@"a"
                                         atIndex:0];
                    return theMenu;
                }
            }
        }
    }
    return nil;
}

- (void) addToContact
{
    ContactMethod* contactmethod = nullptr;
    if([[treeController selectedNodes] count] > 0) {
        QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
        const auto& var = qIdx.data(static_cast<int>(Call::Role::Object));
        if (qIdx.parent().isValid() && var.isValid()) {
            if (auto call = var.value<Call *>()) {
                contactmethod = call->peerContactMethod();
            }
        }
    }

255 256 257
    if (addToContactPopover != nullptr) {
        [addToContactPopover performClose:self];
        addToContactPopover = NULL;
258
    } else if (contactmethod) {
259
        auto* editorVC = [[PersonLinkerVC alloc] initWithNibName:@"PersonLinker" bundle:nil];
260 261
        [editorVC setMethodToLink:contactmethod];
        [editorVC setContactLinkedDelegate:self];
262 263 264 265 266 267 268 269
        addToContactPopover = [[NSPopover alloc] init];
        [addToContactPopover setContentSize:editorVC.view.frame.size];
        [addToContactPopover setContentViewController:editorVC];
        [addToContactPopover setAnimates:YES];
        [addToContactPopover setBehavior:NSPopoverBehaviorTransient];
        [addToContactPopover setDelegate:self];

        [addToContactPopover showRelativeToRect:[historyView frameOfOutlineCellAtRow:[historyView selectedRow]] ofView:historyView preferredEdge:NSMaxXEdge];
270 271 272 273 274 275 276
    }
}

#pragma mark - NSPopOverDelegate

- (void)popoverDidClose:(NSNotification *)notification
{
277 278 279
    if (addToContactPopover != nullptr) {
        [addToContactPopover performClose:self];
        addToContactPopover = NULL;
280 281 282 283 284 285 286
    }
}

#pragma mark - ContactLinkedDelegate

- (void)contactLinked
{
287 288 289
    if (addToContactPopover != nullptr) {
        [addToContactPopover performClose:self];
        addToContactPopover = NULL;
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
    }
}

#pragma mark - KeyboardShortcutDelegate

- (void) onAddShortcut
{
    if([[treeController selectedNodes] count] > 0) {
        QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
        const auto& var = qIdx.data(static_cast<int>(Call::Role::Object));
        if (qIdx.parent().isValid() && var.isValid()) {
            if (auto call = var.value<Call *>()) {
                auto contactmethod = call->peerContactMethod();
                if (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder()) {
                    [self addToContact];
                }
            }
        }
    }
}

311
@end