blob: f0698574828aa1ae40b880fdb0934fad37c42bfa [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{
206 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
207 NSTableCellView *result;
208 if (!qIdx.parent().isValid()) {
209 result = [outlineView makeViewWithIdentifier:@"MainCell" owner:outlineView];
210 NSTextField* details = [result viewWithTag:DETAILS_TAG];
211
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500212 NSMutableArray* controls = [NSMutableArray arrayWithObject:[result viewWithTag:CALL_BUTTON_TAG]];
213 [((ContextualTableCellView*) result) setContextualsControls:controls];
Alexandre Lision4e280d62015-09-09 15:56:30 -0400214
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400215 if (auto call = RecentModel::instance().getActiveCall(RecentModel::instance().peopleProxy()->mapToSource(qIdx))) {
Alexandre Lisiond14bda32015-10-13 11:34:29 -0400216 [details setStringValue:call->roleData((int)Ring::Role::FormattedState).toString().toNSString()];
Alexandre Lision21666f32015-09-22 17:04:36 -0400217 [((ContextualTableCellView*) result) setActiveState:YES];
218 } else {
Alexandre Lisiond14bda32015-10-13 11:34:29 -0400219 [details setStringValue:qIdx.data((int)Ring::Role::FormattedLastUsed).toString().toNSString()];
Alexandre Lision21666f32015-09-22 17:04:36 -0400220 [((ContextualTableCellView*) result) setActiveState:NO];
221 }
222
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500223 NSTextField* unreadCount = [result viewWithTag:TXT_BUTTON_TAG];
224 int unread = qIdx.data((int)Ring::Role::UnreadTextMessageCount).toInt();
225 [unreadCount setHidden:(unread == 0)];
226 [unreadCount.layer setCornerRadius:5.0f];
227 [unreadCount setStringValue:qIdx.data((int)Ring::Role::UnreadTextMessageCount).toString().toNSString()];
228
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400229 } else {
230 result = [outlineView makeViewWithIdentifier:@"CallCell" owner:outlineView];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500231 NSMutableArray* controls = [NSMutableArray arrayWithObject:[result viewWithTag:CANCEL_BUTTON_TAG]];
232 [((ContextualTableCellView*) result) setContextualsControls:controls];
233 [((ContextualTableCellView*) result) setActiveState:NO];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400234 NSTextField* details = [result viewWithTag:DETAILS_TAG];
235
236 [details setStringValue:qIdx.data((int)Call::Role::HumanStateName).toString().toNSString()];
237 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400238
239 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500240 [displayName setStringValue:qIdx.data((int)Ring::Role::Name).toString().toNSString()];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400241 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
242 Person* p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object));
Alexandre Lisiond5229f32015-11-16 11:17:41 -0500243 QVariant photo = GlobalInstances::pixmapManipulator().contactPhoto(p, QSize(50,50));
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400244 [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
245 return result;
246}
247
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500248- (void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row
249{
250 [outlineView scrollRowToVisible:0];
251}
252
253- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
254{
255 return [outlineView makeViewWithIdentifier:@"HoverRowView" owner:nil];
256}
257
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400258- (IBAction)callClickedAtRow:(id)sender {
259 NSInteger row = [smartView rowForView:sender];
260 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
261 [self placeCall:nil];
262}
263
264- (IBAction)hangUpClickedAtRow:(id)sender {
265 NSInteger row = [smartView rowForView:sender];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500266 id callNode = [smartView itemAtRow:row];
267 auto callIdx = [treeController toQIdx:((NSTreeNode*)callNode)];
268
269 if (callIdx.isValid()) {
270 auto call = RecentModel::instance().getActiveCall(RecentModel::instance().peopleProxy()->mapToSource(callIdx));
271 call << Call::Action::REFUSE;
272 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400273}
274
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400275- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
276{
277 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500278 return (((NSTreeNode*)item).indexPath.length == 1) ? 60.0 : 50.0;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400279}
280
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500281- (IBAction)placeCallFromSearchField:(id)sender
282{
283 if ([searchField stringValue].length == 0) {
284 return;
285 }
286 auto cm = PhoneDirectoryModel::instance().getNumber(QString::fromNSString([searchField stringValue]));
287 auto c = CallModel::instance().dialingCall();
288 [searchField setStringValue:@""];
289 RecentModel::instance().peopleProxy()->setFilterWildcard(QString::fromNSString([searchField stringValue]));
290 c->setPeerContactMethod(cm);
291 c << Call::Action::ACCEPT;
292 CallModel::instance().selectCall(c);
293}
294
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500295- (void) startConversationFromSearchField
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400296{
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500297 auto cm = PhoneDirectoryModel::instance().getNumber(QString::fromNSString([searchField stringValue]));
298 time_t currentTime;
299 ::time(&currentTime);
300 cm->setLastUsed(currentTime);
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500301 [searchField setStringValue:@""];
302 RecentModel::instance().peopleProxy()->setFilterWildcard(QString::fromNSString([searchField stringValue]));
303 auto proxyIdx = RecentModel::instance().peopleProxy()->mapToSource(RecentModel::instance().peopleProxy()->index(0, 0));
304 RecentModel::instance().selectionModel()->setCurrentIndex(proxyIdx, QItemSelectionModel::ClearAndSelect);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400305}
306
Alexandre Lision61db3552015-10-22 19:12:52 -0400307- (void) addToContact
308{
309 if ([treeController selectedNodes].count == 0)
310 return;
311
312 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
313 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
314 auto contactmethod = RecentModel::instance().getContactMethods(originIdx);
315 if (contactmethod.isEmpty())
316 return;
317
318 if (addToContactPopover != nullptr) {
319 [addToContactPopover performClose:self];
320 addToContactPopover = NULL;
321 } else if (contactmethod.first()) {
322 auto* editorVC = [[PersonLinkerVC alloc] initWithNibName:@"PersonLinker" bundle:nil];
323 [editorVC setMethodToLink:contactmethod.first()];
324 [editorVC setContactLinkedDelegate:self];
325 addToContactPopover = [[NSPopover alloc] init];
326 [addToContactPopover setContentSize:editorVC.view.frame.size];
327 [addToContactPopover setContentViewController:editorVC];
328 [addToContactPopover setAnimates:YES];
329 [addToContactPopover setBehavior:NSPopoverBehaviorTransient];
330 [addToContactPopover setDelegate:self];
331
332 [addToContactPopover showRelativeToRect:[smartView frameOfCellAtColumn:0 row:[smartView selectedRow]]
333 ofView:smartView preferredEdge:NSMaxXEdge];
334 }
335}
336
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400337- (void) copyNumberToPasteboard:(id) sender
338{
339 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
340 [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
341 [pasteBoard setString:[sender representedObject] forType:NSStringPboardType];
342}
343
344- (void) callNumber:(id) sender
345{
346 Call* c = CallModel::instance().dialingCall();
347 c->setDialNumber(QString::fromNSString([sender representedObject]));
348 c << Call::Action::ACCEPT;
349}
350
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400351#pragma NSTextFieldDelegate
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400352
353- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
354{
355 if (commandSelector == @selector(insertNewline:)) {
356 if([[searchField stringValue] isNotEqualTo:@""]) {
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500357 [self startConversationFromSearchField];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400358 return YES;
359 }
360 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400361 return NO;
362}
363
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500364- (void)controlTextDidChange:(NSNotification *) notification
365{
366 BOOL empty = [[searchField stringValue] isEqualTo:@""];
367 RecentModel::instance().peopleProxy()->setFilterWildcard(QString::fromNSString([searchField stringValue]));
368}
369
Alexandre Lision61db3552015-10-22 19:12:52 -0400370#pragma mark - NSPopOverDelegate
371
372- (void)popoverDidClose:(NSNotification *)notification
373{
374 if (addToContactPopover != nullptr) {
375 [addToContactPopover performClose:self];
376 addToContactPopover = NULL;
377 }
378}
379
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400380
Alexandre Lision61db3552015-10-22 19:12:52 -0400381#pragma mark - ContactLinkedDelegate
382
383- (void)contactLinked
384{
385 if (addToContactPopover != nullptr) {
386 [addToContactPopover performClose:self];
387 addToContactPopover = NULL;
388 }
389}
390
391#pragma mark - KeyboardShortcutDelegate
392
393- (void) onAddShortcut
394{
395 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
396 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
397 auto contactmethods = RecentModel::instance().getContactMethods(originIdx);
398 if (contactmethods.isEmpty())
399 return;
400
401 auto contactmethod = contactmethods.first();
402 if (contactmethod && (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder())) {
403 [self addToContact];
404 }
405}
406
407#pragma mark - ContextMenuDelegate
408
Alexandre Lision12946a72016-03-08 13:39:34 -0500409- (NSMenu*) contextualMenuForIndex:(NSTreeNode*) item
Alexandre Lision61db3552015-10-22 19:12:52 -0400410{
Alexandre Lision12946a72016-03-08 13:39:34 -0500411 auto qIdx = [treeController toQIdx:item];
412
413 if (!qIdx.isValid()) {
414 return nil;
415 }
416
Alexandre Lision61db3552015-10-22 19:12:52 -0400417 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
418 auto contactmethods = RecentModel::instance().getContactMethods(originIdx);
419 if (contactmethods.isEmpty())
420 return nil;
421
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400422 NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@""];
423
424 if (contactmethods.size() == 1
425 && !contactmethods.first()->contact()
426 || contactmethods.first()->contact()->isPlaceHolder()) {
427
Alexandre Lision61db3552015-10-22 19:12:52 -0400428 [theMenu insertItemWithTitle:NSLocalizedString(@"Add to contacts", @"Contextual menu action")
429 action:@selector(addToContact)
430 keyEquivalent:@"a"
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400431 atIndex:theMenu.itemArray.count];
Alexandre Lision61db3552015-10-22 19:12:52 -0400432 }
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400433
434 NSMenu* copySubmenu = [[NSMenu alloc] init];
435 NSMenu* callSubmenu = [[NSMenu alloc] init];
436
437 for(auto cm : contactmethods) {
438 NSMenuItem* tmpCopyItem = [[NSMenuItem alloc] initWithTitle:cm->uri().toNSString()
439 action:@selector(copyNumberToPasteboard:)
440 keyEquivalent:@""];
441
442 [tmpCopyItem setRepresentedObject:cm->uri().toNSString()];
443 [copySubmenu addItem:tmpCopyItem];
444
445 NSMenuItem* tmpCallItem = [[NSMenuItem alloc] initWithTitle:cm->uri().toNSString()
446 action:@selector(callNumber:)
447 keyEquivalent:@""];
448 [tmpCallItem setRepresentedObject:cm->uri().toNSString()];
449 [callSubmenu addItem:tmpCallItem];
450 }
451
452 NSMenuItem* copyItems = [[NSMenuItem alloc] init];
453 [copyItems setTitle:NSLocalizedString(@"Copy number", @"Contextual menu action")];
454 [copyItems setSubmenu:copySubmenu];
455
456 NSMenuItem* callItems = [[NSMenuItem alloc] init];
457 [callItems setTitle:NSLocalizedString(@"Call number", @"Contextual menu action")];
458 [callItems setSubmenu:callSubmenu];
459
460 [theMenu insertItem:copyItems atIndex:theMenu.itemArray.count];
461 [theMenu insertItem:[NSMenuItem separatorItem] atIndex:theMenu.itemArray.count];
462 [theMenu insertItem:callItems atIndex:theMenu.itemArray.count];
463
464 return theMenu;
Alexandre Lision61db3552015-10-22 19:12:52 -0400465}
466
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400467@end