blob: 291a4d4d840abbd36a269b08ccf18aa55f6a1ae8 [file] [log] [blame]
Alexandre Lision2db8f472015-07-22 15:05:46 -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 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
24 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#import "PersonLinkerVC.h"
32
33//Qt
34#import <QtMacExtras/qmacfunctions.h>
35#import <QPixmap>
36
37//LRC
38#import <person.h>
39#import <personmodel.h>
40#import <contactmethod.h>
41#import <numbercategorymodel.h>
42
43#import "QNSTreeController.h"
44#import "delegates/ImageManipulationDelegate.h"
45#import "views/PersonCell.h"
46
47#define FIRSTNAME_TAG 1
48#define LASTNAME_TAG 2
49
50#define COLUMNID_NAME @"NameColumn"
51
52class OnlyPersonProxyModel : public QSortFilterProxyModel
53{
54public:
55 OnlyPersonProxyModel(QAbstractItemModel* parent) : QSortFilterProxyModel(parent)
56 {
57 setSourceModel(parent);
58 }
59 virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
60 {
61 bool match = filterRegExp().indexIn(sourceModel()->index(source_row,0,source_parent).data(Qt::DisplayRole).toString()) != -1;
62 //qDebug() << "FILTERING" << sourceModel()->index(source_row,0,source_parent) << "match:" << match;
63 return match && !sourceModel()->index(source_row,0,source_parent).parent().isValid();
64 }
65};
66
67@interface PersonLinkerVC () <NSTextFieldDelegate, NSComboBoxDelegate, NSComboBoxDataSource>
68
69@property QSortFilterProxyModel* contactProxyModel;
70@property QNSTreeController* treeController;
71
72
73@property (unsafe_unretained) IBOutlet NSTextField *contactMethodLabel;
74@property (unsafe_unretained) IBOutlet NSOutlineView *personsView;
75@property (unsafe_unretained) IBOutlet NSTextField *firstNameField;
76@property (unsafe_unretained) IBOutlet NSTextField *lastNameField;
77@property (unsafe_unretained) IBOutlet NSButton *createNewContactButton;
78@property (unsafe_unretained) IBOutlet NSComboBox *categoryComboBox;
79@property (strong) IBOutlet NSView *createContactSubview;
80@property (unsafe_unretained) IBOutlet NSView *linkToExistingSubview;
81
82
83@end
84
85@implementation PersonLinkerVC
86@synthesize treeController;
87@synthesize personsView;
88@synthesize contactProxyModel;
89@synthesize contactMethodLabel;
90@synthesize categoryComboBox, firstNameField, lastNameField;
91@synthesize createContactSubview, linkToExistingSubview, createNewContactButton;
92
93-(void) awakeFromNib
94{
95 NSLog(@"INIT PersonLinkerVC");
96
97 [firstNameField setTag:FIRSTNAME_TAG];
98 [lastNameField setTag:LASTNAME_TAG];
99
100 [categoryComboBox selectItemAtIndex:0];
101
102 contactProxyModel = new OnlyPersonProxyModel(PersonModel::instance());
103 contactProxyModel->setSortRole(static_cast<int>(Qt::DisplayRole));
104 contactProxyModel->sort(0,Qt::AscendingOrder);
105 contactProxyModel->setFilterRole(Qt::DisplayRole);
106 treeController = [[QNSTreeController alloc] initWithQModel:contactProxyModel];
107
108 [treeController setAvoidsEmptySelection:NO];
109 [treeController setChildrenKeyPath:@"children"];
110
111 [personsView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
112 [personsView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
113 [personsView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
114 [personsView setTarget:self];
115 [personsView setDoubleAction:@selector(addToContact:)];
116
117 [contactMethodLabel setStringValue:self.methodToLink->uri().toNSString()];
118}
119
120- (IBAction)addToContact:(id)sender
121{
122 /* get the selected number category */
123 const auto& idx = NumberCategoryModel::instance()->index([categoryComboBox indexOfSelectedItem]);
124 if (idx.isValid()) {
125 auto category = NumberCategoryModel::instance()->getCategory(idx.data().toString());
126 self.methodToLink->setCategory(category);
127 }
128
129 if([[treeController selectedNodes] count] > 0) {
130 QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
131 ContactMethod* m = nil;
132 if(((NSTreeNode*)[treeController selectedNodes][0]).indexPath.length == 1) {
133 // Person
134 QVariant var = qIdx.data((int)Person::Role::Object);
135 if (var.isValid()) {
136 Person *p = var.value<Person*>();
137 Person::ContactMethods cms = p->phoneNumbers();
138 cms.append(self.methodToLink);
139 p->setContactMethods(cms);
140 self.methodToLink->setPerson(p);
141 p->save();
142 [self.contactLinkedDelegate contactLinked];
143 }
144 }
145 }
146}
147
148- (void) dealloc
149{
150 // No ARC for c++ pointers
151 delete contactProxyModel;
152}
153
154- (IBAction)presentNewContactForm:(id)sender {
155 [createContactSubview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
156 //[createContactSubview setBounds:linkToExistingSubview.bounds];
157 [createContactSubview setFrame:linkToExistingSubview.frame];
158 [linkToExistingSubview setHidden:YES];
159 [self.view addSubview:createContactSubview];
160
161 [[[NSApplication sharedApplication] mainWindow] makeFirstResponder:firstNameField];
162 [firstNameField setNextKeyView:lastNameField];
163 [lastNameField setNextKeyView:createNewContactButton];
164 [createNewContactButton setNextKeyView:firstNameField];
165}
166
167- (IBAction)createContact:(id)sender
168{
169 /* get the selected number category */
170 const auto& idx = NumberCategoryModel::instance()->index([categoryComboBox indexOfSelectedItem]);
171 if (idx.isValid()) {
172 auto category = NumberCategoryModel::instance()->getCategory(idx.data().toString());
173 self.methodToLink->setCategory(category);
174 }
175
176 /* create a new person */
177 Person *p = new Person();
178 p->setFirstName(QString::fromNSString(firstNameField.stringValue));
179 p->setFamilyName(QString::fromNSString(lastNameField.stringValue));
180 p->setFormattedName(QString::fromNSString([[NSString alloc] initWithFormat:@"%@ %@", firstNameField.stringValue, lastNameField.stringValue]));
181 /* associate the new person with the contact method */
182 Person::ContactMethods numbers;
183 numbers << self.methodToLink;
184 p->setContactMethods(numbers);
185 self.methodToLink->setPerson(p);
186 PersonModel::instance()->addNewPerson(p);
187 [self.contactLinkedDelegate contactLinked];
188}
189
190#pragma mark - NSOutlineViewDelegate methods
191
192// -------------------------------------------------------------------------------
193// shouldSelectItem:item
194// -------------------------------------------------------------------------------
195- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
196{
197 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
198 if(!qIdx.isValid())
199 return NO;
200
201 if(qIdx.parent().isValid()) {
202 return NO;
203 } else {
204 return YES;
205 }
206}
207
208// -------------------------------------------------------------------------------
209// dataCellForTableColumn:tableColumn:item
210// -------------------------------------------------------------------------------
211- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item
212{
213 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
214 PersonCell *returnCell = [tableColumn dataCell];
215 return returnCell;
216}
217
218// -------------------------------------------------------------------------------
219// shouldEditTableColumn:tableColumn:item
220//
221// Decide to allow the edit of the given outline view "item".
222// -------------------------------------------------------------------------------
223- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
224{
225 return NO;
226}
227
228// -------------------------------------------------------------------------------
229// outlineView:willDisplayCell:forTableColumn:item
230// -------------------------------------------------------------------------------
231- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
232{
233 QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
234 if(!qIdx.isValid()) {
235 [((PersonCell *)cell) setPersonImage:nil];
236 return;
237 }
238
239 if ([[tableColumn identifier] isEqualToString:COLUMNID_NAME])
240 {
241 PersonCell *pCell = (PersonCell *)cell;
242 [pCell setPersonImage:nil];
243 if(!qIdx.parent().isValid()) {
244 pCell.title = qIdx.data(Qt::DisplayRole).toString().toNSString();
245 Person* p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object));
246 QVariant photo = ImageManipulationDelegate::instance()->contactPhoto(p, QSize(35,35));
247 [pCell setPersonImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
248 } else {
249 pCell.title = qIdx.data(Qt::DisplayRole).toString().toNSString();
250
251 }
252 }
253}
254
255// -------------------------------------------------------------------------------
256// outlineViewSelectionDidChange:notification
257// -------------------------------------------------------------------------------
258
259- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
260{
261 return 45.0;
262}
263
264#pragma mark - NSTextFieldDelegate
265
266- (void)controlTextDidChange:(NSNotification *) notification
267{
268 if ([notification.object tag] == FIRSTNAME_TAG || [notification.object tag] == LASTNAME_TAG) {
269 NSTextView *textView = notification.userInfo[@"NSFieldEditor"];
270 BOOL enableCreate = textView.textStorage.string.length > 0;
271 [createNewContactButton setEnabled:enableCreate];
272 } else {
273 NSTextView *textView = notification.userInfo[@"NSFieldEditor"];
274 contactProxyModel->setFilterRegExp(QRegExp(QString::fromNSString(textView.textStorage.string), Qt::CaseInsensitive, QRegExp::FixedString));
275 [personsView scrollToBeginningOfDocument:nil];
276 }
277}
278
279#pragma mark - NSComboBoxDelegate
280
281- (void)comboBoxSelectionDidChange:(NSNotification*) notification
282{
283 [(NSComboBox *)[notification object] indexOfSelectedItem];
284}
285
286#pragma mark - NSComboBoxDatasource
287
288- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox
289{
290 return NumberCategoryModel::instance()->rowCount();
291}
292
293- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index
294{
295 return NumberCategoryModel::instance()->index(index).data().toString().toNSString();
296}
297
298@end