blob: 10c8db5ed54d2d0b09ede8e3f3e3dbf1f15ba48c [file] [log] [blame]
Alexandre Lision4dfcafc2015-08-20 12:43:23 -04001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Alexandre Lision4dfcafc2015-08-20 12:43:23 -04003 * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#import "SmartViewVC.h"
21
22//Qt
23#import <QtMacExtras/qmacfunctions.h>
24#import <QPixmap>
25#import <QIdentityProxyModel>
26#import <QItemSelectionModel>
27
28//LRC
29#import <recentmodel.h>
30#import <callmodel.h>
31#import <call.h>
Alexandre Lisiond14bda32015-10-13 11:34:29 -040032#import <itemdataroles.h>
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040033#import <person.h>
34#import <contactmethod.h>
35#import <globalinstances.h>
Alexandre Lision0f66bd32016-01-18 11:30:45 -050036#import <phonedirectorymodel.h>
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040037
38#import "QNSTreeController.h"
39#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision4e280d62015-09-09 15:56:30 -040040#import "views/HoverTableRowView.h"
Alexandre Lision61db3552015-10-22 19:12:52 -040041#import "PersonLinkerVC.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050042#import "views/IconButton.h"
Alexandre Lision61db3552015-10-22 19:12:52 -040043#import "views/RingOutlineView.h"
Alexandre Lision4e280d62015-09-09 15:56:30 -040044#import "views/ContextualTableCellView.h"
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040045
Alexandre Lision61db3552015-10-22 19:12:52 -040046@interface SmartViewVC () <NSOutlineViewDelegate, NSPopoverDelegate, ContextMenuDelegate, ContactLinkedDelegate, KeyboardShortcutDelegate> {
Alexandre Lision0f66bd32016-01-18 11:30:45 -050047
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040048 QNSTreeController *treeController;
Alexandre Lision61db3552015-10-22 19:12:52 -040049 NSPopover* addToContactPopover;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040050
51 //UI elements
Alexandre Lision61db3552015-10-22 19:12:52 -040052 __unsafe_unretained IBOutlet RingOutlineView* smartView;
53 __unsafe_unretained IBOutlet NSSearchField* searchField;
Alexandre Lision61db3552015-10-22 19:12:52 -040054 __unsafe_unretained IBOutlet NSTabView* tabbar;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040055}
56
57@end
58
59@implementation SmartViewVC
60
61// Tags for views
Alexandre Lision57914fa2016-02-10 14:52:24 -050062NSInteger const IMAGE_TAG = 100;
63NSInteger const DISPLAYNAME_TAG = 200;
64NSInteger const DETAILS_TAG = 300;
65NSInteger const CALL_BUTTON_TAG = 400;
66NSInteger const TXT_BUTTON_TAG = 500;
67NSInteger const CANCEL_BUTTON_TAG = 600;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040068
69- (void)awakeFromNib
70{
71 NSLog(@"INIT SmartView VC");
72
Alexandre Lision0f66bd32016-01-18 11:30:45 -050073 RecentModel::instance().peopleProxy()->setFilterRole((int)Ring::Role::Name);
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040074 treeController = [[QNSTreeController alloc] initWithQModel:RecentModel::instance().peopleProxy()];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040075 [treeController setAvoidsEmptySelection:NO];
76 [treeController setChildrenKeyPath:@"children"];
77
78 [smartView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
79 [smartView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
80 [smartView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
81 [smartView setTarget:self];
Alexandre Lision89edc6a2015-11-09 11:30:47 -050082 [smartView setAction:@selector(selectRow:)];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040083 [smartView setDoubleAction:@selector(placeCall:)];
84
Alexandre Lision61db3552015-10-22 19:12:52 -040085 [smartView setContextMenuDelegate:self];
86 [smartView setShortcutsDelegate:self];
87
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040088 QObject::connect(RecentModel::instance().peopleProxy(),
Alexandre Lisionee098462015-10-22 17:22:50 -040089 &QAbstractItemModel::dataChanged,
90 [self](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
91 for(int row = topLeft.row() ; row <= bottomRight.row() ; ++row)
92 {
93 [smartView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:row]
94 columnIndexes:[NSIndexSet indexSetWithIndex:0]];
95 }
96 });
97
Alexandre Lision89edc6a2015-11-09 11:30:47 -050098 QObject::connect(RecentModel::instance().selectionModel(),
99 &QItemSelectionModel::currentChanged,
100 [=](const QModelIndex &current, const QModelIndex &previous) {
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500101 if(!current.isValid()) {
102 [smartView deselectAll:nil];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500103 return;
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500104 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500105
106 auto proxyIdx = RecentModel::instance().peopleProxy()->mapFromSource(current);
107 if (proxyIdx.isValid()) {
108 [treeController setSelectionQModelIndex:proxyIdx];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500109 [tabbar selectTabViewItemAtIndex:0];
110 [smartView scrollRowToVisible:proxyIdx.row()];
111 }
112 });
113
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400114 [self.view setWantsLayer:YES];
115 [self.view setLayer:[CALayer layer]];
116 [self.view.layer setBackgroundColor:[NSColor whiteColor].CGColor];
117
118 [searchField setWantsLayer:YES];
119 [searchField setLayer:[CALayer layer]];
120 [searchField.layer setBackgroundColor:[NSColor colorWithCalibratedRed:0.949 green:0.949 blue:0.949 alpha:0.9].CGColor];
121}
122
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500123-(void) selectRow:(id)sender
124{
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500125 if ([treeController selectedNodes].count == 0) {
126 RecentModel::instance().selectionModel()->clearCurrentIndex();
127 return;
128 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500129 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
130 auto proxyIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
131 RecentModel::instance().selectionModel()->setCurrentIndex(proxyIdx, QItemSelectionModel::ClearAndSelect);
132}
133
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400134- (void)placeCall:(id)sender
135{
136 QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
137 ContactMethod* m = nil;
138
139 // Double click on an ongoing call
140 if (qIdx.parent().isValid()) {
141 return;
142 }
143
144 if([[treeController selectedNodes] count] > 0) {
145 QVariant var = qIdx.data((int)Call::Role::ContactMethod);
146 m = qvariant_cast<ContactMethod*>(var);
147 if (!m) {
148 // test if it is a person
149 QVariant var = qIdx.data((int)Person::Role::Object);
150 if (var.isValid()) {
151 Person *c = var.value<Person*>();
152 if (c->phoneNumbers().size() > 0) {
153 m = c->phoneNumbers().first();
154 }
155 }
156 }
157 }
158
159 // Before calling check if we properly extracted a contact method and that
160 // there is NOT already an ongoing call for this index (e.g: no children for this node)
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400161 if(m && !RecentModel::instance().peopleProxy()->index(0, 0, qIdx).isValid()){
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500162 auto c = CallModel::instance().dialingCall();
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400163 c->setPeerContactMethod(m);
164 c << Call::Action::ACCEPT;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500165 CallModel::instance().selectCall(c);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400166 }
167}
168
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500169- (IBAction)showHistory:(NSButton*)sender
170{
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500171 [tabbar selectTabViewItemAtIndex:1];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400172}
173
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500174- (IBAction)showContacts:(NSButton*)sender
175{
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500176 [tabbar selectTabViewItemAtIndex:2];
177}
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500178
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500179- (IBAction)showSmartlist:(NSButton*)sender
180{
181 [tabbar selectTabViewItemAtIndex:0];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400182}
183
184#pragma mark - NSOutlineViewDelegate methods
185
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400186- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
187{
188 return YES;
189}
190
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400191- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
192{
193 return NO;
194}
195
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400196- (void)outlineViewSelectionDidChange:(NSNotification *)notification
197{
198 if ([treeController selectedNodes].count <= 0) {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500199 RecentModel::instance().selectionModel()->clearCurrentIndex();
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400200 return;
201 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400202}
203
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400204- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
205{
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400206 QModelIndex proxyIdx = [treeController toQIdx:((NSTreeNode*)item)];
207 QModelIndex qIdx = RecentModel::instance().peopleProxy()->mapToSource(proxyIdx);
208
209 NSTableCellView* result;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400210 if (!qIdx.parent().isValid()) {
211 result = [outlineView makeViewWithIdentifier:@"MainCell" owner:outlineView];
212 NSTextField* details = [result viewWithTag:DETAILS_TAG];
213
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500214 NSMutableArray* controls = [NSMutableArray arrayWithObject:[result viewWithTag:CALL_BUTTON_TAG]];
215 [((ContextualTableCellView*) result) setContextualsControls:controls];
Alexandre Lision4e280d62015-09-09 15:56:30 -0400216
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400217 if (auto call = RecentModel::instance().getActiveCall(qIdx)) {
Alexandre Lisiond14bda32015-10-13 11:34:29 -0400218 [details setStringValue:call->roleData((int)Ring::Role::FormattedState).toString().toNSString()];
Alexandre Lision21666f32015-09-22 17:04:36 -0400219 [((ContextualTableCellView*) result) setActiveState:YES];
220 } else {
Alexandre Lisiond14bda32015-10-13 11:34:29 -0400221 [details setStringValue:qIdx.data((int)Ring::Role::FormattedLastUsed).toString().toNSString()];
Alexandre Lision21666f32015-09-22 17:04:36 -0400222 [((ContextualTableCellView*) result) setActiveState:NO];
223 }
224
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500225 NSTextField* unreadCount = [result viewWithTag:TXT_BUTTON_TAG];
226 int unread = qIdx.data((int)Ring::Role::UnreadTextMessageCount).toInt();
227 [unreadCount setHidden:(unread == 0)];
228 [unreadCount.layer setCornerRadius:5.0f];
229 [unreadCount setStringValue:qIdx.data((int)Ring::Role::UnreadTextMessageCount).toString().toNSString()];
230
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400231 } else {
232 result = [outlineView makeViewWithIdentifier:@"CallCell" owner:outlineView];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500233 NSMutableArray* controls = [NSMutableArray arrayWithObject:[result viewWithTag:CANCEL_BUTTON_TAG]];
234 [((ContextualTableCellView*) result) setContextualsControls:controls];
235 [((ContextualTableCellView*) result) setActiveState:NO];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400236 NSTextField* details = [result viewWithTag:DETAILS_TAG];
237
238 [details setStringValue:qIdx.data((int)Call::Role::HumanStateName).toString().toNSString()];
239 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400240
241 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500242 [displayName setStringValue:qIdx.data((int)Ring::Role::Name).toString().toNSString()];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400243 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400244
245 [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(qIdx.data(Qt::DecorationRole)))];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400246 return result;
247}
248
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500249- (void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row
250{
251 [outlineView scrollRowToVisible:0];
252}
253
254- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
255{
256 return [outlineView makeViewWithIdentifier:@"HoverRowView" owner:nil];
257}
258
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400259- (IBAction)callClickedAtRow:(id)sender {
260 NSInteger row = [smartView rowForView:sender];
261 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
262 [self placeCall:nil];
263}
264
265- (IBAction)hangUpClickedAtRow:(id)sender {
266 NSInteger row = [smartView rowForView:sender];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500267 id callNode = [smartView itemAtRow:row];
268 auto callIdx = [treeController toQIdx:((NSTreeNode*)callNode)];
269
270 if (callIdx.isValid()) {
271 auto call = RecentModel::instance().getActiveCall(RecentModel::instance().peopleProxy()->mapToSource(callIdx));
272 call << Call::Action::REFUSE;
273 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400274}
275
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400276- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
277{
278 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500279 return (((NSTreeNode*)item).indexPath.length == 1) ? 60.0 : 50.0;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400280}
281
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500282- (IBAction)placeCallFromSearchField:(id)sender
283{
284 if ([searchField stringValue].length == 0) {
285 return;
286 }
287 auto cm = PhoneDirectoryModel::instance().getNumber(QString::fromNSString([searchField stringValue]));
288 auto c = CallModel::instance().dialingCall();
289 [searchField setStringValue:@""];
290 RecentModel::instance().peopleProxy()->setFilterWildcard(QString::fromNSString([searchField stringValue]));
291 c->setPeerContactMethod(cm);
292 c << Call::Action::ACCEPT;
293 CallModel::instance().selectCall(c);
294}
295
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500296- (void) startConversationFromSearchField
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400297{
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500298 auto cm = PhoneDirectoryModel::instance().getNumber(QString::fromNSString([searchField stringValue]));
299 time_t currentTime;
300 ::time(&currentTime);
301 cm->setLastUsed(currentTime);
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500302 [searchField setStringValue:@""];
303 RecentModel::instance().peopleProxy()->setFilterWildcard(QString::fromNSString([searchField stringValue]));
304 auto proxyIdx = RecentModel::instance().peopleProxy()->mapToSource(RecentModel::instance().peopleProxy()->index(0, 0));
305 RecentModel::instance().selectionModel()->setCurrentIndex(proxyIdx, QItemSelectionModel::ClearAndSelect);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400306}
307
Alexandre Lision61db3552015-10-22 19:12:52 -0400308- (void) addToContact
309{
310 if ([treeController selectedNodes].count == 0)
311 return;
312
313 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
314 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
315 auto contactmethod = RecentModel::instance().getContactMethods(originIdx);
316 if (contactmethod.isEmpty())
317 return;
318
319 if (addToContactPopover != nullptr) {
320 [addToContactPopover performClose:self];
321 addToContactPopover = NULL;
322 } else if (contactmethod.first()) {
323 auto* editorVC = [[PersonLinkerVC alloc] initWithNibName:@"PersonLinker" bundle:nil];
324 [editorVC setMethodToLink:contactmethod.first()];
325 [editorVC setContactLinkedDelegate:self];
326 addToContactPopover = [[NSPopover alloc] init];
327 [addToContactPopover setContentSize:editorVC.view.frame.size];
328 [addToContactPopover setContentViewController:editorVC];
329 [addToContactPopover setAnimates:YES];
330 [addToContactPopover setBehavior:NSPopoverBehaviorTransient];
331 [addToContactPopover setDelegate:self];
332
333 [addToContactPopover showRelativeToRect:[smartView frameOfCellAtColumn:0 row:[smartView selectedRow]]
334 ofView:smartView preferredEdge:NSMaxXEdge];
335 }
336}
337
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400338- (void) copyNumberToPasteboard:(id) sender
339{
340 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
341 [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
342 [pasteBoard setString:[sender representedObject] forType:NSStringPboardType];
343}
344
345- (void) callNumber:(id) sender
346{
347 Call* c = CallModel::instance().dialingCall();
348 c->setDialNumber(QString::fromNSString([sender representedObject]));
349 c << Call::Action::ACCEPT;
350}
351
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400352#pragma NSTextFieldDelegate
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400353
354- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
355{
356 if (commandSelector == @selector(insertNewline:)) {
357 if([[searchField stringValue] isNotEqualTo:@""]) {
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500358 [self startConversationFromSearchField];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400359 return YES;
360 }
361 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400362 return NO;
363}
364
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500365- (void)controlTextDidChange:(NSNotification *) notification
366{
367 BOOL empty = [[searchField stringValue] isEqualTo:@""];
368 RecentModel::instance().peopleProxy()->setFilterWildcard(QString::fromNSString([searchField stringValue]));
369}
370
Alexandre Lision61db3552015-10-22 19:12:52 -0400371#pragma mark - NSPopOverDelegate
372
373- (void)popoverDidClose:(NSNotification *)notification
374{
375 if (addToContactPopover != nullptr) {
376 [addToContactPopover performClose:self];
377 addToContactPopover = NULL;
378 }
379}
380
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400381
Alexandre Lision61db3552015-10-22 19:12:52 -0400382#pragma mark - ContactLinkedDelegate
383
384- (void)contactLinked
385{
386 if (addToContactPopover != nullptr) {
387 [addToContactPopover performClose:self];
388 addToContactPopover = NULL;
389 }
390}
391
392#pragma mark - KeyboardShortcutDelegate
393
394- (void) onAddShortcut
395{
396 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
397 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
398 auto contactmethods = RecentModel::instance().getContactMethods(originIdx);
399 if (contactmethods.isEmpty())
400 return;
401
402 auto contactmethod = contactmethods.first();
403 if (contactmethod && (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder())) {
404 [self addToContact];
405 }
406}
407
408#pragma mark - ContextMenuDelegate
409
Alexandre Lision12946a72016-03-08 13:39:34 -0500410- (NSMenu*) contextualMenuForIndex:(NSTreeNode*) item
Alexandre Lision61db3552015-10-22 19:12:52 -0400411{
Alexandre Lision12946a72016-03-08 13:39:34 -0500412 auto qIdx = [treeController toQIdx:item];
413
414 if (!qIdx.isValid()) {
415 return nil;
416 }
417
Alexandre Lision61db3552015-10-22 19:12:52 -0400418 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
419 auto contactmethods = RecentModel::instance().getContactMethods(originIdx);
420 if (contactmethods.isEmpty())
421 return nil;
422
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400423 NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@""];
424
425 if (contactmethods.size() == 1
426 && !contactmethods.first()->contact()
427 || contactmethods.first()->contact()->isPlaceHolder()) {
428
Alexandre Lision61db3552015-10-22 19:12:52 -0400429 [theMenu insertItemWithTitle:NSLocalizedString(@"Add to contacts", @"Contextual menu action")
430 action:@selector(addToContact)
431 keyEquivalent:@"a"
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400432 atIndex:theMenu.itemArray.count];
Alexandre Lision61db3552015-10-22 19:12:52 -0400433 }
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400434
435 NSMenu* copySubmenu = [[NSMenu alloc] init];
436 NSMenu* callSubmenu = [[NSMenu alloc] init];
437
438 for(auto cm : contactmethods) {
439 NSMenuItem* tmpCopyItem = [[NSMenuItem alloc] initWithTitle:cm->uri().toNSString()
440 action:@selector(copyNumberToPasteboard:)
441 keyEquivalent:@""];
442
443 [tmpCopyItem setRepresentedObject:cm->uri().toNSString()];
444 [copySubmenu addItem:tmpCopyItem];
445
446 NSMenuItem* tmpCallItem = [[NSMenuItem alloc] initWithTitle:cm->uri().toNSString()
447 action:@selector(callNumber:)
448 keyEquivalent:@""];
449 [tmpCallItem setRepresentedObject:cm->uri().toNSString()];
450 [callSubmenu addItem:tmpCallItem];
451 }
452
453 NSMenuItem* copyItems = [[NSMenuItem alloc] init];
454 [copyItems setTitle:NSLocalizedString(@"Copy number", @"Contextual menu action")];
455 [copyItems setSubmenu:copySubmenu];
456
457 NSMenuItem* callItems = [[NSMenuItem alloc] init];
458 [callItems setTitle:NSLocalizedString(@"Call number", @"Contextual menu action")];
459 [callItems setSubmenu:callSubmenu];
460
461 [theMenu insertItem:copyItems atIndex:theMenu.itemArray.count];
462 [theMenu insertItem:[NSMenuItem separatorItem] atIndex:theMenu.itemArray.count];
463 [theMenu insertItem:callItems atIndex:theMenu.itemArray.count];
464
465 return theMenu;
Alexandre Lision61db3552015-10-22 19:12:52 -0400466}
467
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400468@end