blob: 3e0ba7fc6046ada47c23a4babbce3f1a407345cf [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.
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
86 contactProxyModel = new OnlyPersonProxyModel(PersonModel::instance());
87 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 */
107 const auto& idx = NumberCategoryModel::instance()->index([categoryComboBox indexOfSelectedItem]);
108 if (idx.isValid()) {
109 auto category = NumberCategoryModel::instance()->getCategory(idx.data().toString());
110 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 */
154 const auto& idx = NumberCategoryModel::instance()->index([categoryComboBox indexOfSelectedItem]);
155 if (idx.isValid()) {
156 auto category = NumberCategoryModel::instance()->getCategory(idx.data().toString());
157 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);
170 PersonModel::instance()->addNewPerson(p);
171 [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];
204
205
206 if (!qIdx.isValid()) {
207 [photoView setImage:nil];
208 [displayName setStringValue:qIdx.data(Qt::DisplayRole).toString().toNSString()];
209 return result;
Alexandre Lision2db8f472015-07-22 15:05:46 -0400210 }
211
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400212 if (auto p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object))) {
213 QVariant photo = GlobalInstances::pixmapManipulator().contactPhoto(p, QSize(35,35));
214 [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
215 } else {
216 QVariant photo = GlobalInstances::pixmapManipulator().contactPhoto(nil, QSize(35,35));
217 [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
Alexandre Lision2db8f472015-07-22 15:05:46 -0400218 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400219
220 [displayName setStringValue:qIdx.data(Qt::DisplayRole).toString().toNSString()];
221
222 return result;
Alexandre Lision2db8f472015-07-22 15:05:46 -0400223}
224
225// -------------------------------------------------------------------------------
226// outlineViewSelectionDidChange:notification
227// -------------------------------------------------------------------------------
228
229- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
230{
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400231 return 60.0;
Alexandre Lision2db8f472015-07-22 15:05:46 -0400232}
233
234#pragma mark - NSTextFieldDelegate
235
236- (void)controlTextDidChange:(NSNotification *) notification
237{
238 if ([notification.object tag] == FIRSTNAME_TAG || [notification.object tag] == LASTNAME_TAG) {
239 NSTextView *textView = notification.userInfo[@"NSFieldEditor"];
240 BOOL enableCreate = textView.textStorage.string.length > 0;
241 [createNewContactButton setEnabled:enableCreate];
242 } else {
243 NSTextView *textView = notification.userInfo[@"NSFieldEditor"];
244 contactProxyModel->setFilterRegExp(QRegExp(QString::fromNSString(textView.textStorage.string), Qt::CaseInsensitive, QRegExp::FixedString));
245 [personsView scrollToBeginningOfDocument:nil];
246 }
247}
248
249#pragma mark - NSComboBoxDelegate
250
251- (void)comboBoxSelectionDidChange:(NSNotification*) notification
252{
253 [(NSComboBox *)[notification object] indexOfSelectedItem];
254}
255
256#pragma mark - NSComboBoxDatasource
257
258- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox
259{
260 return NumberCategoryModel::instance()->rowCount();
261}
262
263- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index
264{
265 return NumberCategoryModel::instance()->index(index).data().toString().toNSString();
266}
267
268@end