blob: a17de8ea251bd2ed897eb10a1cee2d34909d9109 [file] [log] [blame]
Alexandre Lision2db8f472015-07-22 15:05:46 -04001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Alexandre Lision2db8f472015-07-22 15:05:46 -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.
Alexandre Lision2db8f472015-07-22 15:05:46 -040018 */
19
20#import "PersonLinkerVC.h"
21
22//Qt
23#import <QtMacExtras/qmacfunctions.h>
24#import <QPixmap>
25
26//LRC
27#import <person.h>
28#import <personmodel.h>
29#import <contactmethod.h>
30#import <numbercategorymodel.h>
Alexandre Lision7a166e42015-09-02 15:04:43 -040031#import <globalinstances.h>
Alexandre Lision2db8f472015-07-22 15:05:46 -040032
33#import "QNSTreeController.h"
34#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision2db8f472015-07-22 15:05:46 -040035
36class OnlyPersonProxyModel : public QSortFilterProxyModel
37{
38public:
39 OnlyPersonProxyModel(QAbstractItemModel* parent) : QSortFilterProxyModel(parent)
40 {
41 setSourceModel(parent);
42 }
43 virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
44 {
45 bool match = filterRegExp().indexIn(sourceModel()->index(source_row,0,source_parent).data(Qt::DisplayRole).toString()) != -1;
46 //qDebug() << "FILTERING" << sourceModel()->index(source_row,0,source_parent) << "match:" << match;
47 return match && !sourceModel()->index(source_row,0,source_parent).parent().isValid();
48 }
49};
50
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040051@interface PersonLinkerVC () <NSTextFieldDelegate, NSComboBoxDelegate, NSComboBoxDataSource> {
Alexandre Lision2db8f472015-07-22 15:05:46 -040052
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040053 __unsafe_unretained IBOutlet NSTextField *contactMethodLabel;
54 __unsafe_unretained IBOutlet NSOutlineView *personsView;
55 __unsafe_unretained IBOutlet NSTextField *firstNameField;
56 __unsafe_unretained IBOutlet NSTextField *lastNameField;
57 __unsafe_unretained IBOutlet NSButton *createNewContactButton;
58 __unsafe_unretained IBOutlet NSComboBox *categoryComboBox;
59 __unsafe_unretained IBOutlet NSView *linkToExistingSubview;
Alexandre Lision2db8f472015-07-22 15:05:46 -040060
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040061 QSortFilterProxyModel* contactProxyModel;
62 QNSTreeController* treeController;
63 IBOutlet NSView *createContactSubview;
64}
Alexandre Lision2db8f472015-07-22 15:05:46 -040065
66@end
67
68@implementation PersonLinkerVC
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040069
70// Tags for views
71NSInteger const FIRSTNAME_TAG = 1;
72NSInteger const LASTNAME_TAG = 2;
73NSInteger const IMAGE_TAG = 100;
74NSInteger const DISPLAYNAME_TAG = 200;
75NSInteger const DETAILS_TAG = 300;
Alexandre Lision2db8f472015-07-22 15:05:46 -040076
77-(void) awakeFromNib
78{
79 NSLog(@"INIT PersonLinkerVC");
80
81 [firstNameField setTag:FIRSTNAME_TAG];
82 [lastNameField setTag:LASTNAME_TAG];
83
84 [categoryComboBox selectItemAtIndex:0];
85
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040086 contactProxyModel = new OnlyPersonProxyModel(&PersonModel::instance());
Alexandre Lision2db8f472015-07-22 15:05:46 -040087 contactProxyModel->setSortRole(static_cast<int>(Qt::DisplayRole));
88 contactProxyModel->sort(0,Qt::AscendingOrder);
89 contactProxyModel->setFilterRole(Qt::DisplayRole);
90 treeController = [[QNSTreeController alloc] initWithQModel:contactProxyModel];
91
92 [treeController setAvoidsEmptySelection:NO];
93 [treeController setChildrenKeyPath:@"children"];
94
95 [personsView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
96 [personsView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
97 [personsView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
98 [personsView setTarget:self];
99 [personsView setDoubleAction:@selector(addToContact:)];
100
101 [contactMethodLabel setStringValue:self.methodToLink->uri().toNSString()];
102}
103
104- (IBAction)addToContact:(id)sender
105{
106 /* get the selected number category */
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400107 const auto& idx = NumberCategoryModel::instance().index([categoryComboBox indexOfSelectedItem]);
Alexandre Lision2db8f472015-07-22 15:05:46 -0400108 if (idx.isValid()) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400109 auto category = NumberCategoryModel::instance().getCategory(idx.data().toString());
Alexandre Lision2db8f472015-07-22 15:05:46 -0400110 self.methodToLink->setCategory(category);
111 }
112
113 if([[treeController selectedNodes] count] > 0) {
114 QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
115 ContactMethod* m = nil;
116 if(((NSTreeNode*)[treeController selectedNodes][0]).indexPath.length == 1) {
117 // Person
118 QVariant var = qIdx.data((int)Person::Role::Object);
119 if (var.isValid()) {
120 Person *p = var.value<Person*>();
121 Person::ContactMethods cms = p->phoneNumbers();
122 cms.append(self.methodToLink);
123 p->setContactMethods(cms);
124 self.methodToLink->setPerson(p);
125 p->save();
126 [self.contactLinkedDelegate contactLinked];
127 }
128 }
129 }
130}
131
132- (void) dealloc
133{
134 // No ARC for c++ pointers
135 delete contactProxyModel;
136}
137
138- (IBAction)presentNewContactForm:(id)sender {
139 [createContactSubview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
140 //[createContactSubview setBounds:linkToExistingSubview.bounds];
141 [createContactSubview setFrame:linkToExistingSubview.frame];
142 [linkToExistingSubview setHidden:YES];
143 [self.view addSubview:createContactSubview];
144
145 [[[NSApplication sharedApplication] mainWindow] makeFirstResponder:firstNameField];
146 [firstNameField setNextKeyView:lastNameField];
147 [lastNameField setNextKeyView:createNewContactButton];
148 [createNewContactButton setNextKeyView:firstNameField];
149}
150
151- (IBAction)createContact:(id)sender
152{
153 /* get the selected number category */
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400154 const auto& idx = NumberCategoryModel::instance().index([categoryComboBox indexOfSelectedItem]);
Alexandre Lision2db8f472015-07-22 15:05:46 -0400155 if (idx.isValid()) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400156 auto category = NumberCategoryModel::instance().getCategory(idx.data().toString());
Alexandre Lision2db8f472015-07-22 15:05:46 -0400157 self.methodToLink->setCategory(category);
158 }
159
160 /* create a new person */
161 Person *p = new Person();
162 p->setFirstName(QString::fromNSString(firstNameField.stringValue));
163 p->setFamilyName(QString::fromNSString(lastNameField.stringValue));
164 p->setFormattedName(QString::fromNSString([[NSString alloc] initWithFormat:@"%@ %@", firstNameField.stringValue, lastNameField.stringValue]));
165 /* associate the new person with the contact method */
166 Person::ContactMethods numbers;
167 numbers << self.methodToLink;
168 p->setContactMethods(numbers);
169 self.methodToLink->setPerson(p);
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400170 PersonModel::instance().addNewPerson(p);
Alexandre Lision2db8f472015-07-22 15:05:46 -0400171 [self.contactLinkedDelegate contactLinked];
172}
173
174#pragma mark - NSOutlineViewDelegate methods
175
176// -------------------------------------------------------------------------------
177// shouldSelectItem:item
178// -------------------------------------------------------------------------------
179- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
180{
181 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400182 return qIdx.isValid();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400183}
184
185// -------------------------------------------------------------------------------
186// shouldEditTableColumn:tableColumn:item
187//
188// Decide to allow the edit of the given outline view "item".
189// -------------------------------------------------------------------------------
190- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
191{
192 return NO;
193}
194
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400195/* View Based OutlineView: See the delegate method -tableView:viewForTableColumn:row: in NSTableView.
196 */
197- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
Alexandre Lision2db8f472015-07-22 15:05:46 -0400198{
199 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400200
201 NSTableCellView *result = [outlineView makeViewWithIdentifier:@"MainCell" owner:outlineView];
202 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
203 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
Alexandre Lision196545b2016-05-13 17:05:13 -0400204 [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(qIdx.data(Qt::DecorationRole)))];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400205 [displayName setStringValue:qIdx.data(Qt::DisplayRole).toString().toNSString()];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400206 return result;
Alexandre Lision2db8f472015-07-22 15:05:46 -0400207}
208
209// -------------------------------------------------------------------------------
210// outlineViewSelectionDidChange:notification
211// -------------------------------------------------------------------------------
212
213- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
214{
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400215 return 60.0;
Alexandre Lision2db8f472015-07-22 15:05:46 -0400216}
217
218#pragma mark - NSTextFieldDelegate
219
220- (void)controlTextDidChange:(NSNotification *) notification
221{
222 if ([notification.object tag] == FIRSTNAME_TAG || [notification.object tag] == LASTNAME_TAG) {
223 NSTextView *textView = notification.userInfo[@"NSFieldEditor"];
224 BOOL enableCreate = textView.textStorage.string.length > 0;
225 [createNewContactButton setEnabled:enableCreate];
226 } else {
227 NSTextView *textView = notification.userInfo[@"NSFieldEditor"];
228 contactProxyModel->setFilterRegExp(QRegExp(QString::fromNSString(textView.textStorage.string), Qt::CaseInsensitive, QRegExp::FixedString));
229 [personsView scrollToBeginningOfDocument:nil];
230 }
231}
232
233#pragma mark - NSComboBoxDelegate
234
235- (void)comboBoxSelectionDidChange:(NSNotification*) notification
236{
237 [(NSComboBox *)[notification object] indexOfSelectedItem];
238}
239
240#pragma mark - NSComboBoxDatasource
241
242- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox
243{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400244 return NumberCategoryModel::instance().rowCount();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400245}
246
247- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index
248{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400249 return NumberCategoryModel::instance().index(index).data().toString().toNSString();
Alexandre Lision2db8f472015-07-22 15:05:46 -0400250}
251
252@end