blob: 6aad5223e637709595b54caff3c6fdf8dd58d488 [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
62NSInteger 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;
67
68- (void)awakeFromNib
69{
70 NSLog(@"INIT SmartView VC");
71
Alexandre Lision0f66bd32016-01-18 11:30:45 -050072 RecentModel::instance().peopleProxy()->setFilterRole((int)Ring::Role::Name);
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040073 treeController = [[QNSTreeController alloc] initWithQModel:RecentModel::instance().peopleProxy()];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040074 [treeController setAvoidsEmptySelection:NO];
75 [treeController setChildrenKeyPath:@"children"];
76
77 [smartView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
78 [smartView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
79 [smartView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
80 [smartView setTarget:self];
Alexandre Lision89edc6a2015-11-09 11:30:47 -050081 [smartView setAction:@selector(selectRow:)];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040082 [smartView setDoubleAction:@selector(placeCall:)];
83
Alexandre Lision61db3552015-10-22 19:12:52 -040084 [smartView setContextMenuDelegate:self];
85 [smartView setShortcutsDelegate:self];
86
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040087 QObject::connect(RecentModel::instance().peopleProxy(),
Alexandre Lisionee098462015-10-22 17:22:50 -040088 &QAbstractItemModel::dataChanged,
89 [self](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
90 for(int row = topLeft.row() ; row <= bottomRight.row() ; ++row)
91 {
92 [smartView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:row]
93 columnIndexes:[NSIndexSet indexSetWithIndex:0]];
94 }
95 });
96
Alexandre Lision89edc6a2015-11-09 11:30:47 -050097 QObject::connect(RecentModel::instance().selectionModel(),
98 &QItemSelectionModel::currentChanged,
99 [=](const QModelIndex &current, const QModelIndex &previous) {
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500100 if(!current.isValid()) {
101 [smartView deselectAll:nil];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500102 return;
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500103 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500104
105 auto proxyIdx = RecentModel::instance().peopleProxy()->mapFromSource(current);
106 if (proxyIdx.isValid()) {
107 [treeController setSelectionQModelIndex:proxyIdx];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500108 [tabbar selectTabViewItemAtIndex:0];
109 [smartView scrollRowToVisible:proxyIdx.row()];
110 }
111 });
112
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400113 [self.view setWantsLayer:YES];
114 [self.view setLayer:[CALayer layer]];
115 [self.view.layer setBackgroundColor:[NSColor whiteColor].CGColor];
116
117 [searchField setWantsLayer:YES];
118 [searchField setLayer:[CALayer layer]];
119 [searchField.layer setBackgroundColor:[NSColor colorWithCalibratedRed:0.949 green:0.949 blue:0.949 alpha:0.9].CGColor];
120}
121
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500122-(void) selectRow:(id)sender
123{
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500124 if ([treeController selectedNodes].count == 0) {
125 RecentModel::instance().selectionModel()->clearCurrentIndex();
126 return;
127 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500128 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
129 auto proxyIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
130 RecentModel::instance().selectionModel()->setCurrentIndex(proxyIdx, QItemSelectionModel::ClearAndSelect);
131}
132
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400133- (void)placeCall:(id)sender
134{
135 QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
136 ContactMethod* m = nil;
137
138 // Double click on an ongoing call
139 if (qIdx.parent().isValid()) {
140 return;
141 }
142
143 if([[treeController selectedNodes] count] > 0) {
144 QVariant var = qIdx.data((int)Call::Role::ContactMethod);
145 m = qvariant_cast<ContactMethod*>(var);
146 if (!m) {
147 // test if it is a person
148 QVariant var = qIdx.data((int)Person::Role::Object);
149 if (var.isValid()) {
150 Person *c = var.value<Person*>();
151 if (c->phoneNumbers().size() > 0) {
152 m = c->phoneNumbers().first();
153 }
154 }
155 }
156 }
157
158 // Before calling check if we properly extracted a contact method and that
159 // there is NOT already an ongoing call for this index (e.g: no children for this node)
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400160 if(m && !RecentModel::instance().peopleProxy()->index(0, 0, qIdx).isValid()){
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500161 auto c = CallModel::instance().dialingCall();
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400162 c->setPeerContactMethod(m);
163 c << Call::Action::ACCEPT;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500164 CallModel::instance().selectCall(c);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400165 }
166}
167
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500168- (IBAction)showHistory:(NSButton*)sender
169{
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500170 [tabbar selectTabViewItemAtIndex:1];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400171}
172
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500173- (IBAction)showContacts:(NSButton*)sender
174{
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500175 [tabbar selectTabViewItemAtIndex:2];
176}
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500177
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500178- (IBAction)showSmartlist:(NSButton*)sender
179{
180 [tabbar selectTabViewItemAtIndex:0];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400181}
182
183#pragma mark - NSOutlineViewDelegate methods
184
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400185- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
186{
187 return YES;
188}
189
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400190- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
191{
192 return NO;
193}
194
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400195- (void)outlineViewSelectionDidChange:(NSNotification *)notification
196{
197 if ([treeController selectedNodes].count <= 0) {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500198 RecentModel::instance().selectionModel()->clearCurrentIndex();
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400199 return;
200 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400201}
202
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400203- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
204{
205 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
206 NSTableCellView *result;
207 if (!qIdx.parent().isValid()) {
208 result = [outlineView makeViewWithIdentifier:@"MainCell" owner:outlineView];
209 NSTextField* details = [result viewWithTag:DETAILS_TAG];
210
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500211 NSMutableArray* controls = [NSMutableArray arrayWithObject:[result viewWithTag:CALL_BUTTON_TAG]];
212 [((ContextualTableCellView*) result) setContextualsControls:controls];
Alexandre Lision4e280d62015-09-09 15:56:30 -0400213
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400214 if (auto call = RecentModel::instance().getActiveCall(RecentModel::instance().peopleProxy()->mapToSource(qIdx))) {
Alexandre Lisiond14bda32015-10-13 11:34:29 -0400215 [details setStringValue:call->roleData((int)Ring::Role::FormattedState).toString().toNSString()];
Alexandre Lision21666f32015-09-22 17:04:36 -0400216 [((ContextualTableCellView*) result) setActiveState:YES];
217 } else {
Alexandre Lisiond14bda32015-10-13 11:34:29 -0400218 [details setStringValue:qIdx.data((int)Ring::Role::FormattedLastUsed).toString().toNSString()];
Alexandre Lision21666f32015-09-22 17:04:36 -0400219 [((ContextualTableCellView*) result) setActiveState:NO];
220 }
221
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500222 NSTextField* unreadCount = [result viewWithTag:TXT_BUTTON_TAG];
223 int unread = qIdx.data((int)Ring::Role::UnreadTextMessageCount).toInt();
224 [unreadCount setHidden:(unread == 0)];
225 [unreadCount.layer setCornerRadius:5.0f];
226 [unreadCount setStringValue:qIdx.data((int)Ring::Role::UnreadTextMessageCount).toString().toNSString()];
227
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400228 } else {
229 result = [outlineView makeViewWithIdentifier:@"CallCell" owner:outlineView];
230 NSTextField* details = [result viewWithTag:DETAILS_TAG];
231
232 [details setStringValue:qIdx.data((int)Call::Role::HumanStateName).toString().toNSString()];
233 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400234
235 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500236 [displayName setStringValue:qIdx.data((int)Ring::Role::Name).toString().toNSString()];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400237 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
238 Person* p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object));
Alexandre Lisiond5229f32015-11-16 11:17:41 -0500239 QVariant photo = GlobalInstances::pixmapManipulator().contactPhoto(p, QSize(50,50));
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400240 [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
241 return result;
242}
243
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500244- (void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row
245{
246 [outlineView scrollRowToVisible:0];
247}
248
249- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
250{
251 return [outlineView makeViewWithIdentifier:@"HoverRowView" owner:nil];
252}
253
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400254- (IBAction)callClickedAtRow:(id)sender {
255 NSInteger row = [smartView rowForView:sender];
256 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
257 [self placeCall:nil];
258}
259
260- (IBAction)hangUpClickedAtRow:(id)sender {
261 NSInteger row = [smartView rowForView:sender];
262 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400263 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex()) << Call::Action::REFUSE;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400264}
265
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400266- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
267{
268 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
269 return (((NSTreeNode*)item).indexPath.length == 1) ? 60.0 : 45.0;
270}
271
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500272- (IBAction)placeCallFromSearchField:(id)sender
273{
274 if ([searchField stringValue].length == 0) {
275 return;
276 }
277 auto cm = PhoneDirectoryModel::instance().getNumber(QString::fromNSString([searchField stringValue]));
278 auto c = CallModel::instance().dialingCall();
279 [searchField setStringValue:@""];
280 RecentModel::instance().peopleProxy()->setFilterWildcard(QString::fromNSString([searchField stringValue]));
281 c->setPeerContactMethod(cm);
282 c << Call::Action::ACCEPT;
283 CallModel::instance().selectCall(c);
284}
285
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500286- (void) startConversationFromSearchField
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400287{
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500288 auto cm = PhoneDirectoryModel::instance().getNumber(QString::fromNSString([searchField stringValue]));
289 time_t currentTime;
290 ::time(&currentTime);
291 cm->setLastUsed(currentTime);
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500292 [searchField setStringValue:@""];
293 RecentModel::instance().peopleProxy()->setFilterWildcard(QString::fromNSString([searchField stringValue]));
294 auto proxyIdx = RecentModel::instance().peopleProxy()->mapToSource(RecentModel::instance().peopleProxy()->index(0, 0));
295 RecentModel::instance().selectionModel()->setCurrentIndex(proxyIdx, QItemSelectionModel::ClearAndSelect);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400296}
297
Alexandre Lision61db3552015-10-22 19:12:52 -0400298- (void) addToContact
299{
300 if ([treeController selectedNodes].count == 0)
301 return;
302
303 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
304 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
305 auto contactmethod = RecentModel::instance().getContactMethods(originIdx);
306 if (contactmethod.isEmpty())
307 return;
308
309 if (addToContactPopover != nullptr) {
310 [addToContactPopover performClose:self];
311 addToContactPopover = NULL;
312 } else if (contactmethod.first()) {
313 auto* editorVC = [[PersonLinkerVC alloc] initWithNibName:@"PersonLinker" bundle:nil];
314 [editorVC setMethodToLink:contactmethod.first()];
315 [editorVC setContactLinkedDelegate:self];
316 addToContactPopover = [[NSPopover alloc] init];
317 [addToContactPopover setContentSize:editorVC.view.frame.size];
318 [addToContactPopover setContentViewController:editorVC];
319 [addToContactPopover setAnimates:YES];
320 [addToContactPopover setBehavior:NSPopoverBehaviorTransient];
321 [addToContactPopover setDelegate:self];
322
323 [addToContactPopover showRelativeToRect:[smartView frameOfCellAtColumn:0 row:[smartView selectedRow]]
324 ofView:smartView preferredEdge:NSMaxXEdge];
325 }
326}
327
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400328- (void) copyNumberToPasteboard:(id) sender
329{
330 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
331 [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
332 [pasteBoard setString:[sender representedObject] forType:NSStringPboardType];
333}
334
335- (void) callNumber:(id) sender
336{
337 Call* c = CallModel::instance().dialingCall();
338 c->setDialNumber(QString::fromNSString([sender representedObject]));
339 c << Call::Action::ACCEPT;
340}
341
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400342#pragma NSTextFieldDelegate
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400343
344- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
345{
346 if (commandSelector == @selector(insertNewline:)) {
347 if([[searchField stringValue] isNotEqualTo:@""]) {
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500348 [self startConversationFromSearchField];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400349 return YES;
350 }
351 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400352 return NO;
353}
354
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500355- (void)controlTextDidChange:(NSNotification *) notification
356{
357 BOOL empty = [[searchField stringValue] isEqualTo:@""];
358 RecentModel::instance().peopleProxy()->setFilterWildcard(QString::fromNSString([searchField stringValue]));
359}
360
Alexandre Lision61db3552015-10-22 19:12:52 -0400361#pragma mark - NSPopOverDelegate
362
363- (void)popoverDidClose:(NSNotification *)notification
364{
365 if (addToContactPopover != nullptr) {
366 [addToContactPopover performClose:self];
367 addToContactPopover = NULL;
368 }
369}
370
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400371
Alexandre Lision61db3552015-10-22 19:12:52 -0400372#pragma mark - ContactLinkedDelegate
373
374- (void)contactLinked
375{
376 if (addToContactPopover != nullptr) {
377 [addToContactPopover performClose:self];
378 addToContactPopover = NULL;
379 }
380}
381
382#pragma mark - KeyboardShortcutDelegate
383
384- (void) onAddShortcut
385{
386 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
387 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
388 auto contactmethods = RecentModel::instance().getContactMethods(originIdx);
389 if (contactmethods.isEmpty())
390 return;
391
392 auto contactmethod = contactmethods.first();
393 if (contactmethod && (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder())) {
394 [self addToContact];
395 }
396}
397
398#pragma mark - ContextMenuDelegate
399
Alexandre Lision12946a72016-03-08 13:39:34 -0500400- (NSMenu*) contextualMenuForIndex:(NSTreeNode*) item
Alexandre Lision61db3552015-10-22 19:12:52 -0400401{
Alexandre Lision12946a72016-03-08 13:39:34 -0500402 auto qIdx = [treeController toQIdx:item];
403
404 if (!qIdx.isValid()) {
405 return nil;
406 }
407
Alexandre Lision61db3552015-10-22 19:12:52 -0400408 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
409 auto contactmethods = RecentModel::instance().getContactMethods(originIdx);
410 if (contactmethods.isEmpty())
411 return nil;
412
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400413 NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@""];
414
415 if (contactmethods.size() == 1
416 && !contactmethods.first()->contact()
417 || contactmethods.first()->contact()->isPlaceHolder()) {
418
Alexandre Lision61db3552015-10-22 19:12:52 -0400419 [theMenu insertItemWithTitle:NSLocalizedString(@"Add to contacts", @"Contextual menu action")
420 action:@selector(addToContact)
421 keyEquivalent:@"a"
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400422 atIndex:theMenu.itemArray.count];
Alexandre Lision61db3552015-10-22 19:12:52 -0400423 }
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400424
425 NSMenu* copySubmenu = [[NSMenu alloc] init];
426 NSMenu* callSubmenu = [[NSMenu alloc] init];
427
428 for(auto cm : contactmethods) {
429 NSMenuItem* tmpCopyItem = [[NSMenuItem alloc] initWithTitle:cm->uri().toNSString()
430 action:@selector(copyNumberToPasteboard:)
431 keyEquivalent:@""];
432
433 [tmpCopyItem setRepresentedObject:cm->uri().toNSString()];
434 [copySubmenu addItem:tmpCopyItem];
435
436 NSMenuItem* tmpCallItem = [[NSMenuItem alloc] initWithTitle:cm->uri().toNSString()
437 action:@selector(callNumber:)
438 keyEquivalent:@""];
439 [tmpCallItem setRepresentedObject:cm->uri().toNSString()];
440 [callSubmenu addItem:tmpCallItem];
441 }
442
443 NSMenuItem* copyItems = [[NSMenuItem alloc] init];
444 [copyItems setTitle:NSLocalizedString(@"Copy number", @"Contextual menu action")];
445 [copyItems setSubmenu:copySubmenu];
446
447 NSMenuItem* callItems = [[NSMenuItem alloc] init];
448 [callItems setTitle:NSLocalizedString(@"Call number", @"Contextual menu action")];
449 [callItems setSubmenu:callSubmenu];
450
451 [theMenu insertItem:copyItems atIndex:theMenu.itemArray.count];
452 [theMenu insertItem:[NSMenuItem separatorItem] atIndex:theMenu.itemArray.count];
453 [theMenu insertItem:callItems atIndex:theMenu.itemArray.count];
454
455 return theMenu;
Alexandre Lision61db3552015-10-22 19:12:52 -0400456}
457
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400458@end