blob: f18a3bccc54169ec14e44f44292e5b64574d659f [file] [log] [blame]
Kateryna Kostiuk4db61092019-10-17 16:57:40 -04001/*
2 * Copyright (C) 2019 Savoir-faire Linux Inc.
3 * Author: Kateryna Kostiuk <kateryna.kostiuk@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 "ChooseContactVC.h"
21#import "views/RingTableView.h"
22#import "views/HoverTableRowView.h"
23#import "utils.h"
24#import "delegates/ImageManipulationDelegate.h"
25
26//LRC
27#import <globalinstances.h>
28#import <api/conversationmodel.h>
29#import <api/account.h>
30#import <api/newaccountmodel.h>
31
32//Qt
33#import <QtMacExtras/qmacfunctions.h>
34#import <QPixmap>
35
36@interface ChooseContactVC () {
37 __unsafe_unretained IBOutlet RingTableView* contactsView;
38 __unsafe_unretained IBOutlet RingTableView* callsView;
39 __unsafe_unretained IBOutlet NSSearchField* searchField;
40 __unsafe_unretained IBOutlet NSLayoutConstraint* contactsViewHeightConstraint;
41 __unsafe_unretained IBOutlet NSLayoutConstraint* calsViewHeightConstraint;
42 __unsafe_unretained IBOutlet NSStackView* callsContainer;
43 __unsafe_unretained IBOutlet NSStackView* contactsContainer;
44 __unsafe_unretained IBOutlet NSTextField* contactsLabel;
45}
46
47@end
48
49@implementation ChooseContactVC
50
51lrc::api::ConversationModel* convModel;
52std::string currentConversation;
53
54std::map<lrc::api::ConferenceableItem, lrc::api::ConferenceableValue> values;
55
56// Tags for views
57NSInteger const IMAGE_TAG = 100;
58NSInteger const DISPLAYNAME_TAG = 200;
59NSInteger const RING_ID_LABEL = 300;
60NSInteger const PRESENCE_TAG = 400;
61
62NSInteger const ROW_HEIGHT = 60;
63NSInteger const MINIMUM_TABLE_SIZE = 0;
64NSInteger const NORMAL_TABLE_SIZE = 120;
65NSInteger const MAXIMUM_TABLE_SIZE = 240;
66
67- (void)controlTextDidChange:(NSNotification *) notification
68{
69 values = convModel->getConferenceableConversations(currentConversation, [searchField.stringValue UTF8String]);
70 [self reloadView];
71}
72
73- (void) reloadView
74{
75 [callsView reloadData];
76 [contactsView reloadData];
77 auto callsSize = [callsView numberOfRows] * ROW_HEIGHT;
78 auto contactsSize = [contactsView numberOfRows] * ROW_HEIGHT;
79 if (callsSize >= NORMAL_TABLE_SIZE) {
80 if (contactsSize >= NORMAL_TABLE_SIZE) {
81 contactsViewHeightConstraint.constant = NORMAL_TABLE_SIZE;
82 calsViewHeightConstraint.constant = NORMAL_TABLE_SIZE;
83 } else {
84 contactsViewHeightConstraint.constant = contactsSize;
85 calsViewHeightConstraint.constant = MAXIMUM_TABLE_SIZE - contactsSize;
86 }
87 } else if (callsSize == MINIMUM_TABLE_SIZE) {
88 // when call stack view is hidden add 35 to avoid controller size changes
89 // 17 - call label height + 8*2 stack view margins
90 contactsViewHeightConstraint.constant = MAXIMUM_TABLE_SIZE + 35;
91 calsViewHeightConstraint.constant = callsSize;
92 } else {
93 contactsViewHeightConstraint.constant = MAXIMUM_TABLE_SIZE - callsSize;
94 calsViewHeightConstraint.constant = callsSize;
95 }
96
97 [callsContainer setHidden: callsSize == 0];
98 [contactsLabel setHidden: contactsSize == 0];
99}
100
101-(void)viewDidLoad {
102 [super viewDidLoad];
103 [self reloadView];
104}
105
106- (void)setConversationModel:(lrc::api::ConversationModel *)conversationModel
107 andCurrentConversation:(std::string)conversation
108{
109 convModel = conversationModel;
110 if (convModel == nil) {
111 return;
112 }
113 currentConversation = conversation;
114 values = convModel->getConferenceableConversations(currentConversation, "");
115 [self reloadView];
116}
117
118#pragma mark - NSTableViewDelegate methods
119
120- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
121{
122 return YES;
123}
124
125- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
126{
127 return NO;
128}
129
130- (void)tableViewSelectionDidChange:(NSNotification *)notification
131{
132 NSInteger row = [notification.object selectedRow];
133 NSTableView *table = notification.object;
134
135 NSInteger rows = [table numberOfRows];
136 for (int i = 0; i< rows; i++) {
137 HoverTableRowView* cellRowView = [table rowViewAtRow:i makeIfNecessary: NO];
138 [cellRowView drawSelection: (i == row)];
139 }
140 if (row == -1 || convModel == nil)
141 return;
142
143 lrc::api::AccountConversation conversation = table == callsView ? values.find(lrc::api::ConferenceableItem::CALL)->second[row].front() : values.find(lrc::api::ConferenceableItem::CONTACT)->second[row].front();
144 auto accountID = conversation.accountId;
145 auto convID = conversation.convId;
146 auto convMod = convModel->owner.accountModel->getAccountInfo(accountID).conversationModel.get();
147 auto conversationInfo = getConversationFromUid(convID, *convMod);
148 auto convQueue = convModel->owner.accountModel->getAccountInfo(accountID).conversationModel.get()->allFilteredConversations();
149 if (conversationInfo == convQueue.end()) {
150 return;
151 }
152
153 if (table == callsView) {
154 auto callID = conversationInfo->confId.empty() ? conversationInfo->callId : conversationInfo->confId;
155 [self.delegate joinCall: callID];
156 } else if (table == contactsView) {
157 auto uid = conversationInfo->participants.front();
158 [self.delegate callToContact:uid convUID:convModel->filteredConversation(row).uid];
159 }
160}
161
162- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
163{
164 HoverTableRowView *howerRow = [tableView makeViewWithIdentifier:@"HoverRowView" owner:nil];
165 [howerRow setBlurType:7];
166 return howerRow;
167}
168
169- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
170{
171 if (convModel == nil)
172 return nil;
173
174 std::vector<lrc::api::AccountConversation> conversations;
175 if (tableView == callsView && convModel != nullptr) {
176 conversations = values.find(lrc::api::ConferenceableItem::CALL)->second[row];
177 } else if (tableView == contactsView && convModel != nullptr) {
178 conversations = values.find(lrc::api::ConferenceableItem::CONTACT)->second[row];
179 }
180 if (conversations.size() < 1) {
181 return nil;
182 }
183
184 NSTableCellView* result = [tableView makeViewWithIdentifier:@"MainCell" owner:tableView];
185
186 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
187 NSTextField* displayRingID = [result viewWithTag:RING_ID_LABEL];
188 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
189 NSView* presenceView = [result viewWithTag:PRESENCE_TAG];
190
191 [displayName setStringValue:@""];
192 [displayRingID setStringValue:@""];
193 [photoView setHidden: YES];
194 [presenceView setHidden:YES];
195
196 // setup conference cell
197 if(conversations.size() > 1) {
198 NSString* displayNameString = @"";
199 for (auto conversation : conversations) {
200 auto accountID = conversation.accountId;
201 auto convID = conversation.convId;
202 auto *convMod = convModel->owner.accountModel->getAccountInfo(accountID).conversationModel.get();
203 auto *conversationInfo = &(*getConversationFromUid(convID, *convMod));
204 if (displayNameString.length > 0) {
205 displayNameString = [displayNameString stringByAppendingString:@", "];
206 }
207 displayNameString = [displayNameString stringByAppendingString: bestNameForConversation(*conversationInfo, *convMod)];
208 }
209 [displayName setStringValue:displayNameString];
210 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
211 NSArray* constraints = [NSLayoutConstraint
212 constraintsWithVisualFormat:@"H:[photoView(0)]"
213 options:0
214 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
215 [NSLayoutConstraint activateConstraints:constraints];
216 return result;
217 }
218
219 lrc::api::AccountConversation conversation = conversations.front();
220 auto accountID = conversation.accountId;
221 auto convID = conversation.convId;
222 auto *convMod = convModel->owner.accountModel->getAccountInfo(accountID).conversationModel.get();
223 auto *conversationInfo = &(*getConversationFromUid(convID, *convMod));
224 if (conversationInfo->participants.empty()) {
225 return nil;
226 }
227 NSString* displayNameString = bestNameForConversation(*conversationInfo, *convMod);
228 NSString* displayIDString = bestIDForConversation(*conversationInfo, *convMod);
229 if(displayNameString.length == 0 || [displayNameString isEqualToString:displayIDString]) {
230 [displayName setStringValue:displayIDString];
231 [displayRingID setHidden:YES];
232 } else {
233 [displayName setStringValue:displayNameString];
234 [displayRingID setStringValue:displayIDString];
235 [displayRingID setHidden:NO];
236 }
237 auto& imageManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
238 NSImage* image = QtMac::toNSImage(qvariant_cast<QPixmap>(imageManip.conversationPhoto(*conversationInfo, convMod->owner)));
239 if(image) {
240 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
241 NSArray* constraints = [NSLayoutConstraint
242 constraintsWithVisualFormat:@"H:[photoView(54)]"
243 options:0
244 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
245 [NSLayoutConstraint activateConstraints:constraints];
246 } else {
247 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
248 NSArray* constraints = [NSLayoutConstraint
249 constraintsWithVisualFormat:@"H:[photoView(0)]"
250 options:0
251 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
252 [NSLayoutConstraint activateConstraints:constraints];
253 }
254 [photoView setHidden: NO];
255 [photoView setImage: image];
256 try {
257 auto contact = convModel->owner.contactModel->getContact(conversationInfo->participants[0]);
258 if (contact.isPresent) {
259 [presenceView setHidden:NO];
260 }
261 } catch (std::out_of_range& e) {
262 NSLog(@"viewForTableColumn: getContact - out of range");
263 }
264 return result;
265}
266
267- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
268{
269 return 60.0;
270}
271
272#pragma mark - NSTableDataSource methods
273
274- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
275{
276 if(!convModel) {
277 return 0;
278 }
279 if (tableView == contactsView) {
280 return values.count(lrc::api::ConferenceableItem::CONTACT) ? values.find(lrc::api::ConferenceableItem::CONTACT)->second.size() : 0;
281 } else if (tableView == callsView) {
282 return values.count(lrc::api::ConferenceableItem::CALL) ? values.find(lrc::api::ConferenceableItem::CALL)->second.size() : 0;
283 }
284 return 0;
285}
286
287@end