blob: 5739e0f39b4481beccf20a82893151f620570750 [file] [log] [blame]
Alexandre Lision0f66bd32016-01-18 11:30:45 -05001/*
2 * Copyright (C) 2016 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 "ConversationVC.h"
21
22#import <QItemSelectionModel>
23#import <qstring.h>
24#import <QPixmap>
25#import <QtMacExtras/qmacfunctions.h>
26
27#import <media/media.h>
28#import <recentmodel.h>
29#import <person.h>
30#import <contactmethod.h>
31#import <media/text.h>
32#import <media/textrecording.h>
33#import <callmodel.h>
34#import <globalinstances.h>
35
36#import "views/IconButton.h"
37#import "views/IMTableCellView.h"
38#import "views/NSColor+RingTheme.h"
39#import "QNSTreeController.h"
Alexandre Lision83180df2016-01-18 11:32:20 -050040#import "INDSequentialTextSelectionManager.h"
Alexandre Lision0f66bd32016-01-18 11:30:45 -050041#import "delegates/ImageManipulationDelegate.h"
Kateryna Kostiuk0ba1baf2017-05-12 16:51:27 -040042#import "SendContactRequestWC.h"
43#import "PhoneDirectoryModel.h"
44#import "account.h"
45#import "AvailableAccountModel.h"
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -040046#import "MessagesVC.h"
Kateryna Kostiuk0ba1baf2017-05-12 16:51:27 -040047
Alexandre Lision0f66bd32016-01-18 11:30:45 -050048
49#import <QuartzCore/QuartzCore.h>
50
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -040051@interface ConversationVC () <NSOutlineViewDelegate, MessagesVCDelegate> {
Alexandre Lision0f66bd32016-01-18 11:30:45 -050052
Alexandre Lision0f66bd32016-01-18 11:30:45 -050053 __unsafe_unretained IBOutlet NSTextField* messageField;
54 QVector<ContactMethod*> contactMethods;
55 NSMutableString* textSelection;
56
Alexandre Lision05ca83c2016-11-24 19:31:08 -050057 QMetaObject::Connection contactMethodChanged;
58 ContactMethod* selectedContactMethod;
Kateryna Kostiuk0ba1baf2017-05-12 16:51:27 -040059 SendContactRequestWC* sendRequestWC;
Alexandre Lision0f66bd32016-01-18 11:30:45 -050060
61 __unsafe_unretained IBOutlet NSView* sendPanel;
62 __unsafe_unretained IBOutlet NSTextField* conversationTitle;
63 __unsafe_unretained IBOutlet NSTextField* emptyConversationPlaceHolder;
64 __unsafe_unretained IBOutlet IconButton* sendButton;
Alexandre Lision0f66bd32016-01-18 11:30:45 -050065 __unsafe_unretained IBOutlet NSPopUpButton* contactMethodsPopupButton;
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -040066 IBOutlet MessagesVC* messagesViewVC;
Alexandre Lision0f66bd32016-01-18 11:30:45 -050067}
68
Alexandre Lision83180df2016-01-18 11:32:20 -050069
Alexandre Lision0f66bd32016-01-18 11:30:45 -050070@end
71
72@implementation ConversationVC
73
Alexandre Lision4baba4c2016-02-11 13:00:57 -050074- (void)loadView {
75 [super loadView];
Alexandre Lision0f66bd32016-01-18 11:30:45 -050076 // Do view setup here.
77 [self.view setWantsLayer:YES];
78 [self.view setLayer:[CALayer layer]];
79 [self.view.layer setBackgroundColor:[NSColor ringGreyHighlight].CGColor];
80 [self.view.layer setCornerRadius:5.0f];
81
82 [sendPanel setWantsLayer:YES];
83 [sendPanel setLayer:[CALayer layer]];
84
85 [self setupChat];
86
87}
88
89- (void) initFrame
90{
91 [self.view setFrame:self.view.superview.bounds];
92 [self.view setHidden:YES];
93 self.view.layer.position = self.view.frame.origin;
94}
95
96- (void) setupChat
97{
98 QObject::connect(RecentModel::instance().selectionModel(),
99 &QItemSelectionModel::currentChanged,
100 [=](const QModelIndex &current, const QModelIndex &previous) {
101
102 contactMethods = RecentModel::instance().getContactMethods(current);
103 if (contactMethods.isEmpty()) {
104 return ;
105 }
106
107 [contactMethodsPopupButton removeAllItems];
108 for (auto cm : contactMethods) {
109 [contactMethodsPopupButton addItemWithTitle:cm->uri().toNSString()];
110 }
111
112 [contactMethodsPopupButton setEnabled:(contactMethods.length() > 1)];
113
114 [emptyConversationPlaceHolder setHidden:NO];
115 // Select first cm
116 [contactMethodsPopupButton selectItemAtIndex:0];
117 [self itemChanged:contactMethodsPopupButton];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500118 });
119}
120
121- (IBAction)sendMessage:(id)sender
122{
123 /* make sure there is text to send */
124 NSString* text = self.message;
125 if (text && text.length > 0) {
126 QMap<QString, QString> messages;
127 messages["text/plain"] = QString::fromNSString(text);
128 contactMethods.at([contactMethodsPopupButton indexOfSelectedItem])->sendOfflineTextMessage(messages);
129 self.message = @"";
130 }
131}
132
133- (IBAction)placeCall:(id)sender
134{
135 if(auto cm = contactMethods.at([contactMethodsPopupButton indexOfSelectedItem])) {
136 auto c = CallModel::instance().dialingCall();
137 c->setPeerContactMethod(cm);
138 c << Call::Action::ACCEPT;
139 CallModel::instance().selectCall(c);
140 }
141}
142
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500143- (IBAction)backPressed:(id)sender {
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500144 RecentModel::instance().selectionModel()->clearCurrentIndex();
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -0400145 messagesViewVC.delegate = nil;
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500146}
147
Kateryna Kostiuk0ba1baf2017-05-12 16:51:27 -0400148- (IBAction)openSendContactRequestWindow:(id)sender
149{
150 if(auto cm = contactMethods.at([contactMethodsPopupButton indexOfSelectedItem])) {
151 sendRequestWC = [[SendContactRequestWC alloc] initWithWindowNibName:@"SendContactRequest"];
152 sendRequestWC.contactMethod = cm;
153 [sendRequestWC.window makeKeyAndOrderFront:sendRequestWC.window];
154 }
155}
156
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500157# pragma mark private IN/OUT animations
158
159-(void) animateIn
160{
161 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
162 [self.view setHidden:NO];
163
164 [CATransaction begin];
165 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
166 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
167 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
168 [animation setDuration:0.2f];
169 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
170 [self.view.layer addAnimation:animation forKey:animation.keyPath];
171
172 [CATransaction commit];
173}
174
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500175-(void) animateOut
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500176{
177 if(self.view.frame.origin.x < 0) {
178 return;
179 }
180
181 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
182 [CATransaction begin];
183 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
184 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
185 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
186 [animation setDuration:0.2f];
187 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
188
189 [CATransaction setCompletionBlock:^{
190 [self.view setHidden:YES];
191 }];
192 [self.view.layer addAnimation:animation forKey:animation.keyPath];
193
194 [self.view.layer setPosition:frame.origin];
195 [CATransaction commit];
196}
197
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500198
199#pragma mark - NSTextFieldDelegate
200
201- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
202{
203 if (commandSelector == @selector(insertNewline:) && self.message.length > 0) {
204 [self sendMessage:nil];
205 return YES;
206 }
207 return NO;
208}
209
210#pragma mark - NSPopUpButton item selection
211
212- (IBAction)itemChanged:(id)sender {
213 NSInteger index = [(NSPopUpButton *)sender indexOfSelectedItem];
214
Alexandre Lision05ca83c2016-11-24 19:31:08 -0500215 selectedContactMethod = contactMethods.at(index);
216 [conversationTitle setStringValue:selectedContactMethod->primaryName().toNSString()];
217 QObject::disconnect(contactMethodChanged);
218 contactMethodChanged = QObject::connect(selectedContactMethod,
219 &ContactMethod::changed,
220 [self] {
221 [conversationTitle setStringValue:selectedContactMethod->primaryName().toNSString()];
222 });
223
224 if (auto txtRecording = selectedContactMethod->textRecording()) {
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -0400225 messagesViewVC.delegate = self;
226 [messagesViewVC setUpViewWithModel:txtRecording->instantMessagingModel()];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500227 }
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500228}
229
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -0400230#pragma mark - MessagesVC delegate
231
232-(void) newMessageAdded {
233
234 if (auto txtRecording = contactMethods.at([contactMethodsPopupButton indexOfSelectedItem])->textRecording()) {
235 [emptyConversationPlaceHolder setHidden:txtRecording->instantMessagingModel()->rowCount() > 0];
236 txtRecording->setAllRead();
237 }
238}
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500239
240@end