blob: 51693e7eb3447b0cfc5abdb2fc889b73fa973f71 [file] [log] [blame]
Alexandre Lision4dfcafc2015-08-20 12:43:23 -04001/*
2 * Copyright (C) 2015 Savoir-faire Linux Inc.
3 * 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>
32#import <person.h>
33#import <contactmethod.h>
34#import <globalinstances.h>
35
36#import "QNSTreeController.h"
37#import "delegates/ImageManipulationDelegate.h"
38
39@interface SmartViewVC () <NSOutlineViewDelegate> {
40 BOOL isShowingContacts;
41 QNSTreeController *treeController;
42
43 //UI elements
44 __unsafe_unretained IBOutlet NSOutlineView *smartView;
45 __unsafe_unretained IBOutlet NSSearchField *searchField;
46 __unsafe_unretained IBOutlet NSButton *showContactsButton;
47 __unsafe_unretained IBOutlet NSButton *showHistoryButton;
48 __unsafe_unretained IBOutlet NSTabView *tabbar;
49}
50
51@end
52
53@implementation SmartViewVC
54
55// Tags for views
56NSInteger const IMAGE_TAG = 100;
57NSInteger const DISPLAYNAME_TAG = 200;
58NSInteger const DETAILS_TAG = 300;
59NSInteger const CALL_BUTTON_TAG = 400;
60NSInteger const TXT_BUTTON_TAG = 500;
61
62- (void)awakeFromNib
63{
64 NSLog(@"INIT SmartView VC");
65
66 isShowingContacts = false;
67 treeController = [[QNSTreeController alloc] initWithQModel:RecentModel::instance()];
68
69 [treeController setAvoidsEmptySelection:NO];
70 [treeController setChildrenKeyPath:@"children"];
71
72 [smartView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
73 [smartView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
74 [smartView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
75 [smartView setTarget:self];
76 [smartView setDoubleAction:@selector(placeCall:)];
77
78 [self.view setWantsLayer:YES];
79 [self.view setLayer:[CALayer layer]];
80 [self.view.layer setBackgroundColor:[NSColor whiteColor].CGColor];
81
82 [searchField setWantsLayer:YES];
83 [searchField setLayer:[CALayer layer]];
84 [searchField.layer setBackgroundColor:[NSColor colorWithCalibratedRed:0.949 green:0.949 blue:0.949 alpha:0.9].CGColor];
85}
86
87- (void)placeCall:(id)sender
88{
89 QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
90 ContactMethod* m = nil;
91
92 // Double click on an ongoing call
93 if (qIdx.parent().isValid()) {
94 return;
95 }
96
97 if([[treeController selectedNodes] count] > 0) {
98 QVariant var = qIdx.data((int)Call::Role::ContactMethod);
99 m = qvariant_cast<ContactMethod*>(var);
100 if (!m) {
101 // test if it is a person
102 QVariant var = qIdx.data((int)Person::Role::Object);
103 if (var.isValid()) {
104 Person *c = var.value<Person*>();
105 if (c->phoneNumbers().size() > 0) {
106 m = c->phoneNumbers().first();
107 }
108 }
109 }
110 }
111
112 // Before calling check if we properly extracted a contact method and that
113 // there is NOT already an ongoing call for this index (e.g: no children for this node)
114 if(m && !RecentModel::instance()->index(0, 0, qIdx).isValid()){
115 Call* c = CallModel::instance()->dialingCall();
116 c->setPeerContactMethod(m);
117 c << Call::Action::ACCEPT;
118
119 [smartView selectRowIndexes:[[NSIndexSet alloc] initWithIndex:1] byExtendingSelection:NO];
120 }
121}
122
123- (IBAction)showHistory:(NSButton*)sender {
124 if (isShowingContacts) {
125 [showContactsButton setState:NO];
126 isShowingContacts = NO;
127 [tabbar selectTabViewItemAtIndex:1];
128 } else if ([sender state] == NSOffState) {
129 [tabbar selectTabViewItemAtIndex:0];
130 } else {
131 [tabbar selectTabViewItemAtIndex:1];
132 }
133}
134
135- (IBAction)showContacts:(NSButton*)sender {
136 if (isShowingContacts) {
137 [showContactsButton setState:NO];
138 [tabbar selectTabViewItemAtIndex:0];
139 } else {
140 [showHistoryButton setState:![sender state]];
141 [tabbar selectTabViewItemAtIndex:2];
142 }
143
144 isShowingContacts = [sender state];
145}
146
147#pragma mark - NSOutlineViewDelegate methods
148
149// -------------------------------------------------------------------------------
150// shouldSelectItem:item
151// -------------------------------------------------------------------------------
152- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
153{
154 return YES;
155}
156
157// -------------------------------------------------------------------------------
158// shouldEditTableColumn:tableColumn:item
159//
160// Decide to allow the edit of the given outline view "item".
161// -------------------------------------------------------------------------------
162- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
163{
164 return NO;
165}
166
167
168// -------------------------------------------------------------------------------
169// outlineViewSelectionDidChange:notification
170// -------------------------------------------------------------------------------
171- (void)outlineViewSelectionDidChange:(NSNotification *)notification
172{
173 if ([treeController selectedNodes].count <= 0) {
174 CallModel::instance()->selectionModel()->clearCurrentIndex();
175 return;
176 }
177
178 QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
179
180 // ask the tree controller for the current selection
181 if(auto selected = RecentModel::instance()->getActiveCall(qIdx.parent())) {
182 CallModel::instance()->selectCall(selected);
183 } else if (auto selected = RecentModel::instance()->getActiveCall(qIdx)){
184 CallModel::instance()->selectCall(selected);
185 } else {
186 CallModel::instance()->selectionModel()->clearCurrentIndex();
187 }
188}
189
190/* View Based OutlineView: See the delegate method -tableView:viewForTableColumn:row: in NSTableView.
191 */
192- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
193{
194 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
195 NSTableCellView *result;
196 if (!qIdx.parent().isValid()) {
197 result = [outlineView makeViewWithIdentifier:@"MainCell" owner:outlineView];
198 NSTextField* details = [result viewWithTag:DETAILS_TAG];
199
200 [details setStringValue:qIdx.data((int)Person::Role::FormattedLastUsed).toString().toNSString()];
201 } else {
202 result = [outlineView makeViewWithIdentifier:@"CallCell" owner:outlineView];
203 NSTextField* details = [result viewWithTag:DETAILS_TAG];
204
205 [details setStringValue:qIdx.data((int)Call::Role::HumanStateName).toString().toNSString()];
206 }
207 BOOL ongoing = RecentModel::instance()->hasActiveCall(qIdx);
208
209 [[result viewWithTag:CALL_BUTTON_TAG] setHidden:ongoing];
210
211
212 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
213 [displayName setStringValue:qIdx.data(Qt::DisplayRole).toString().toNSString()];
214 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
215 Person* p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object));
216 QVariant photo = GlobalInstances::pixmapManipulator().contactPhoto(p, QSize(35,35));
217 [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
218 return result;
219}
220
221- (IBAction)callClickedAtRow:(id)sender {
222 NSInteger row = [smartView rowForView:sender];
223 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
224 [self placeCall:nil];
225}
226
227- (IBAction)hangUpClickedAtRow:(id)sender {
228 NSInteger row = [smartView rowForView:sender];
229 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
230 CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::REFUSE;
231}
232
233/* View Based OutlineView: See the delegate method -tableView:rowViewForRow: in NSTableView.
234
235- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
236{
237
238}
239 */
240
241/* View Based OutlineView: This delegate method can be used to know when a new 'rowView' has been added to the table. At this point, you can choose to add in extra views, or modify any properties on 'rowView'.
242 */
243- (void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row
244{
245
246}
247
248/* View Based OutlineView: This delegate method can be used to know when 'rowView' has been removed from the table. The removed 'rowView' may be reused by the table so any additionally inserted views should be removed at this point. A 'row' parameter is included. 'row' will be '-1' for rows that are being deleted from the table and no longer have a valid row, otherwise it will be the valid row that is being removed due to it being moved off screen.
249 */
250- (void)outlineView:(NSOutlineView *)outlineView didRemoveRowView:(NSTableRowView *)rowView forRow:(NSInteger)row
251{
252
253}
254
255- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
256{
257 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
258 return (((NSTreeNode*)item).indexPath.length == 1) ? 60.0 : 45.0;
259}
260
261- (void) placeCallFromSearchField
262{
263 Call* c = CallModel::instance()->dialingCall();
264 // check for a valid ring hash
265 NSCharacterSet *hexSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789abcdefABCDEF"];
266 BOOL valid = [[[searchField stringValue] stringByTrimmingCharactersInSet:hexSet] isEqualToString:@""];
267
268 if(valid && searchField.stringValue.length == 40) {
269 c->setDialNumber(QString::fromNSString([NSString stringWithFormat:@"ring:%@",[searchField stringValue]]));
270 } else {
271 c->setDialNumber(QString::fromNSString([searchField stringValue]));
272 }
273
274 c << Call::Action::ACCEPT;
275}
276
277
278#pragma NSTextField Delegate
279
280- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
281{
282 if (commandSelector == @selector(insertNewline:)) {
283 if([[searchField stringValue] isNotEqualTo:@""]) {
284 [self placeCallFromSearchField];
285 return YES;
286 }
287 }
288
289 return NO;
290}
291
292@end