blob: 405148f887893fa681fd70696a6a0dd86f90fdc4 [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"
42#import "views/RingOutlineView.h"
Alexandre Lision4e280d62015-09-09 15:56:30 -040043#import "views/ContextualTableCellView.h"
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040044
Alexandre Lision61db3552015-10-22 19:12:52 -040045@interface SmartViewVC () <NSOutlineViewDelegate, NSPopoverDelegate, ContextMenuDelegate, ContactLinkedDelegate, KeyboardShortcutDelegate> {
Alexandre Lision0f66bd32016-01-18 11:30:45 -050046
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040047 QNSTreeController *treeController;
Alexandre Lision61db3552015-10-22 19:12:52 -040048 NSPopover* addToContactPopover;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040049
50 //UI elements
Alexandre Lision61db3552015-10-22 19:12:52 -040051 __unsafe_unretained IBOutlet RingOutlineView* smartView;
52 __unsafe_unretained IBOutlet NSSearchField* searchField;
53 __unsafe_unretained IBOutlet NSButton* showContactsButton;
54 __unsafe_unretained IBOutlet NSButton* showHistoryButton;
55 __unsafe_unretained IBOutlet NSTabView* tabbar;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040056}
57
58@end
59
60@implementation SmartViewVC
61
62// Tags for views
63NSInteger const IMAGE_TAG = 100;
64NSInteger const DISPLAYNAME_TAG = 200;
65NSInteger const DETAILS_TAG = 300;
66NSInteger const CALL_BUTTON_TAG = 400;
67NSInteger const TXT_BUTTON_TAG = 500;
68
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];
109
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500110 [showContactsButton setHighlighted:NO];
111 [showHistoryButton setHighlighted:NO];
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500112 [tabbar selectTabViewItemAtIndex:0];
113 [smartView scrollRowToVisible:proxyIdx.row()];
114 }
115 });
116
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400117 [self.view setWantsLayer:YES];
118 [self.view setLayer:[CALayer layer]];
119 [self.view.layer setBackgroundColor:[NSColor whiteColor].CGColor];
120
121 [searchField setWantsLayer:YES];
122 [searchField setLayer:[CALayer layer]];
123 [searchField.layer setBackgroundColor:[NSColor colorWithCalibratedRed:0.949 green:0.949 blue:0.949 alpha:0.9].CGColor];
124}
125
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500126-(void) selectRow:(id)sender
127{
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500128 if ([treeController selectedNodes].count == 0) {
129 RecentModel::instance().selectionModel()->clearCurrentIndex();
130 return;
131 }
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500132 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
133 auto proxyIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
134 RecentModel::instance().selectionModel()->setCurrentIndex(proxyIdx, QItemSelectionModel::ClearAndSelect);
135}
136
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400137- (void)placeCall:(id)sender
138{
139 QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
140 ContactMethod* m = nil;
141
142 // Double click on an ongoing call
143 if (qIdx.parent().isValid()) {
144 return;
145 }
146
147 if([[treeController selectedNodes] count] > 0) {
148 QVariant var = qIdx.data((int)Call::Role::ContactMethod);
149 m = qvariant_cast<ContactMethod*>(var);
150 if (!m) {
151 // test if it is a person
152 QVariant var = qIdx.data((int)Person::Role::Object);
153 if (var.isValid()) {
154 Person *c = var.value<Person*>();
155 if (c->phoneNumbers().size() > 0) {
156 m = c->phoneNumbers().first();
157 }
158 }
159 }
160 }
161
162 // Before calling check if we properly extracted a contact method and that
163 // there is NOT already an ongoing call for this index (e.g: no children for this node)
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400164 if(m && !RecentModel::instance().peopleProxy()->index(0, 0, qIdx).isValid()){
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500165 auto c = CallModel::instance().dialingCall();
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400166 c->setPeerContactMethod(m);
167 c << Call::Action::ACCEPT;
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500168 CallModel::instance().selectCall(c);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400169 }
170}
171
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500172- (IBAction)showHistory:(NSButton*)sender
173{
174 [showContactsButton setHighlighted:NO];
175 [showHistoryButton setHighlighted:![sender isHighlighted]];
176
177 if (![sender isHighlighted]) {
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400178 [tabbar selectTabViewItemAtIndex:0];
179 } else {
180 [tabbar selectTabViewItemAtIndex:1];
181 }
182}
183
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500184- (IBAction)showContacts:(NSButton*)sender
185{
186 [showContactsButton setHighlighted:![sender isHighlighted]];
187 [showHistoryButton setHighlighted:NO];
188
189 if (![sender isHighlighted]) {
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400190 [tabbar selectTabViewItemAtIndex:0];
191 } else {
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400192 [tabbar selectTabViewItemAtIndex:2];
193 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400194}
195
196#pragma mark - NSOutlineViewDelegate methods
197
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400198- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
199{
200 return YES;
201}
202
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400203- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
204{
205 return NO;
206}
207
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400208- (void)outlineViewSelectionDidChange:(NSNotification *)notification
209{
210 if ([treeController selectedNodes].count <= 0) {
Alexandre Lision89edc6a2015-11-09 11:30:47 -0500211 RecentModel::instance().selectionModel()->clearCurrentIndex();
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400212 return;
213 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400214}
215
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400216- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
217{
218 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
219 NSTableCellView *result;
220 if (!qIdx.parent().isValid()) {
221 result = [outlineView makeViewWithIdentifier:@"MainCell" owner:outlineView];
222 NSTextField* details = [result viewWithTag:DETAILS_TAG];
223
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500224 NSMutableArray* controls = [NSMutableArray arrayWithObject:[result viewWithTag:CALL_BUTTON_TAG]];
225 [((ContextualTableCellView*) result) setContextualsControls:controls];
Alexandre Lision4e280d62015-09-09 15:56:30 -0400226
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400227 if (auto call = RecentModel::instance().getActiveCall(RecentModel::instance().peopleProxy()->mapToSource(qIdx))) {
Alexandre Lisiond14bda32015-10-13 11:34:29 -0400228 [details setStringValue:call->roleData((int)Ring::Role::FormattedState).toString().toNSString()];
Alexandre Lision21666f32015-09-22 17:04:36 -0400229 [((ContextualTableCellView*) result) setActiveState:YES];
230 } else {
Alexandre Lisiond14bda32015-10-13 11:34:29 -0400231 [details setStringValue:qIdx.data((int)Ring::Role::FormattedLastUsed).toString().toNSString()];
Alexandre Lision21666f32015-09-22 17:04:36 -0400232 [((ContextualTableCellView*) result) setActiveState:NO];
233 }
234
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500235 NSTextField* unreadCount = [result viewWithTag:TXT_BUTTON_TAG];
236 int unread = qIdx.data((int)Ring::Role::UnreadTextMessageCount).toInt();
237 [unreadCount setHidden:(unread == 0)];
238 [unreadCount.layer setCornerRadius:5.0f];
239 [unreadCount setStringValue:qIdx.data((int)Ring::Role::UnreadTextMessageCount).toString().toNSString()];
240
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400241 } else {
242 result = [outlineView makeViewWithIdentifier:@"CallCell" owner:outlineView];
243 NSTextField* details = [result viewWithTag:DETAILS_TAG];
244
245 [details setStringValue:qIdx.data((int)Call::Role::HumanStateName).toString().toNSString()];
246 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400247
248 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500249 [displayName setStringValue:qIdx.data((int)Ring::Role::Name).toString().toNSString()];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400250 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
251 Person* p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object));
Alexandre Lisiond5229f32015-11-16 11:17:41 -0500252 QVariant photo = GlobalInstances::pixmapManipulator().contactPhoto(p, QSize(50,50));
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400253 [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
254 return result;
255}
256
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500257- (void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row
258{
259 [outlineView scrollRowToVisible:0];
260}
261
262- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
263{
264 return [outlineView makeViewWithIdentifier:@"HoverRowView" owner:nil];
265}
266
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400267- (IBAction)callClickedAtRow:(id)sender {
268 NSInteger row = [smartView rowForView:sender];
269 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
270 [self placeCall:nil];
271}
272
273- (IBAction)hangUpClickedAtRow:(id)sender {
274 NSInteger row = [smartView rowForView:sender];
275 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400276 CallModel::instance().getCall(CallModel::instance().selectionModel()->currentIndex()) << Call::Action::REFUSE;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400277}
278
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400279- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
280{
281 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
282 return (((NSTreeNode*)item).indexPath.length == 1) ? 60.0 : 45.0;
283}
284
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500285- (void) startConversationFromSearchField
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400286{
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500287 auto cm = PhoneDirectoryModel::instance().getNumber(QString::fromNSString([searchField stringValue]));
288 time_t currentTime;
289 ::time(&currentTime);
290 cm->setLastUsed(currentTime);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400291}
292
Alexandre Lision61db3552015-10-22 19:12:52 -0400293- (void) addToContact
294{
295 if ([treeController selectedNodes].count == 0)
296 return;
297
298 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
299 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
300 auto contactmethod = RecentModel::instance().getContactMethods(originIdx);
301 if (contactmethod.isEmpty())
302 return;
303
304 if (addToContactPopover != nullptr) {
305 [addToContactPopover performClose:self];
306 addToContactPopover = NULL;
307 } else if (contactmethod.first()) {
308 auto* editorVC = [[PersonLinkerVC alloc] initWithNibName:@"PersonLinker" bundle:nil];
309 [editorVC setMethodToLink:contactmethod.first()];
310 [editorVC setContactLinkedDelegate:self];
311 addToContactPopover = [[NSPopover alloc] init];
312 [addToContactPopover setContentSize:editorVC.view.frame.size];
313 [addToContactPopover setContentViewController:editorVC];
314 [addToContactPopover setAnimates:YES];
315 [addToContactPopover setBehavior:NSPopoverBehaviorTransient];
316 [addToContactPopover setDelegate:self];
317
318 [addToContactPopover showRelativeToRect:[smartView frameOfCellAtColumn:0 row:[smartView selectedRow]]
319 ofView:smartView preferredEdge:NSMaxXEdge];
320 }
321}
322
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400323- (void) copyNumberToPasteboard:(id) sender
324{
325 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
326 [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
327 [pasteBoard setString:[sender representedObject] forType:NSStringPboardType];
328}
329
330- (void) callNumber:(id) sender
331{
332 Call* c = CallModel::instance().dialingCall();
333 c->setDialNumber(QString::fromNSString([sender representedObject]));
334 c << Call::Action::ACCEPT;
335}
336
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400337#pragma NSTextFieldDelegate
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400338
339- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
340{
341 if (commandSelector == @selector(insertNewline:)) {
342 if([[searchField stringValue] isNotEqualTo:@""]) {
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500343 [self startConversationFromSearchField];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400344 return YES;
345 }
346 }
347
348 return NO;
349}
350
Alexandre Lision61db3552015-10-22 19:12:52 -0400351#pragma mark - NSPopOverDelegate
352
353- (void)popoverDidClose:(NSNotification *)notification
354{
355 if (addToContactPopover != nullptr) {
356 [addToContactPopover performClose:self];
357 addToContactPopover = NULL;
358 }
359}
360
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400361- (void)controlTextDidChange:(NSNotification *) notification
362{
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500363 RecentModel::instance().peopleProxy()->setFilterWildcard(QString::fromNSString([searchField stringValue]));
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400364}
365
Alexandre Lision61db3552015-10-22 19:12:52 -0400366#pragma mark - ContactLinkedDelegate
367
368- (void)contactLinked
369{
370 if (addToContactPopover != nullptr) {
371 [addToContactPopover performClose:self];
372 addToContactPopover = NULL;
373 }
374}
375
376#pragma mark - KeyboardShortcutDelegate
377
378- (void) onAddShortcut
379{
380 auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
381 auto originIdx = RecentModel::instance().peopleProxy()->mapToSource(qIdx);
382 auto contactmethods = RecentModel::instance().getContactMethods(originIdx);
383 if (contactmethods.isEmpty())
384 return;
385
386 auto contactmethod = contactmethods.first();
387 if (contactmethod && (!contactmethod->contact() || contactmethod->contact()->isPlaceHolder())) {
388 [self addToContact];
389 }
390}
391
392#pragma mark - ContextMenuDelegate
393
394- (NSMenu*) contextualMenuForIndex:(NSIndexPath*) path
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 nil;
401
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400402 NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@""];
403
404 if (contactmethods.size() == 1
405 && !contactmethods.first()->contact()
406 || contactmethods.first()->contact()->isPlaceHolder()) {
407
Alexandre Lision61db3552015-10-22 19:12:52 -0400408 [theMenu insertItemWithTitle:NSLocalizedString(@"Add to contacts", @"Contextual menu action")
409 action:@selector(addToContact)
410 keyEquivalent:@"a"
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400411 atIndex:theMenu.itemArray.count];
Alexandre Lision61db3552015-10-22 19:12:52 -0400412 }
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400413
414 NSMenu* copySubmenu = [[NSMenu alloc] init];
415 NSMenu* callSubmenu = [[NSMenu alloc] init];
416
417 for(auto cm : contactmethods) {
418 NSMenuItem* tmpCopyItem = [[NSMenuItem alloc] initWithTitle:cm->uri().toNSString()
419 action:@selector(copyNumberToPasteboard:)
420 keyEquivalent:@""];
421
422 [tmpCopyItem setRepresentedObject:cm->uri().toNSString()];
423 [copySubmenu addItem:tmpCopyItem];
424
425 NSMenuItem* tmpCallItem = [[NSMenuItem alloc] initWithTitle:cm->uri().toNSString()
426 action:@selector(callNumber:)
427 keyEquivalent:@""];
428 [tmpCallItem setRepresentedObject:cm->uri().toNSString()];
429 [callSubmenu addItem:tmpCallItem];
430 }
431
432 NSMenuItem* copyItems = [[NSMenuItem alloc] init];
433 [copyItems setTitle:NSLocalizedString(@"Copy number", @"Contextual menu action")];
434 [copyItems setSubmenu:copySubmenu];
435
436 NSMenuItem* callItems = [[NSMenuItem alloc] init];
437 [callItems setTitle:NSLocalizedString(@"Call number", @"Contextual menu action")];
438 [callItems setSubmenu:callSubmenu];
439
440 [theMenu insertItem:copyItems atIndex:theMenu.itemArray.count];
441 [theMenu insertItem:[NSMenuItem separatorItem] atIndex:theMenu.itemArray.count];
442 [theMenu insertItem:callItems atIndex:theMenu.itemArray.count];
443
444 return theMenu;
Alexandre Lision61db3552015-10-22 19:12:52 -0400445}
446
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400447@end