blob: e7065699163f6f4935b67bd77076390a83b9ebad [file] [log] [blame]
Alexandre Lision8521baa2015-03-13 11:08:00 -04001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Alexandre Lision8521baa2015-03-13 11:08:00 -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 Lision8521baa2015-03-13 11:08:00 -040018 */
Alexandre Lision4a7b95e2015-02-20 10:06:43 -050019#import "GeneralPrefsVC.h"
20
Alexandre Lision261f1b92016-04-04 12:35:34 -040021#import <Quartz/Quartz.h>
22
23//Qt
24#import <QSize>
25#import <QtMacExtras/qmacfunctions.h>
26#import <QPixmap>
27
28//LRC
Alexandre Lisione4041492015-03-20 18:20:43 -040029#import <categorizedhistorymodel.h>
Alexandre Lision261f1b92016-04-04 12:35:34 -040030#import <profilemodel.h>
31#import <profile.h>
32#import <person.h>
33#import <globalinstances.h>
Kateryna Kostiuk9aef8492017-06-22 16:58:36 -040034#import <media/recordingmodel.h>
Kateryna Kostiuk67735232018-05-10 15:05:32 -040035#import <api/datatransfermodel.h>
Alexandre Lisione4041492015-03-20 18:20:43 -040036
Alexandre Lision3d4143a2015-06-10 14:27:49 -040037#if ENABLE_SPARKLE
38#import <Sparkle/Sparkle.h>
39#endif
40
Alexandre Lisionc65310c2015-04-23 16:44:23 -040041#import "Constants.h"
Alexandre Lision261f1b92016-04-04 12:35:34 -040042#import "views/NSImage+Extensions.h"
43#import "delegates/ImageManipulationDelegate.h"
Kateryna Kostiuk4138db12018-06-08 15:52:18 -040044#import "utils.h"
Alexandre Lisionc65310c2015-04-23 16:44:23 -040045
Alexandre Lision261f1b92016-04-04 12:35:34 -040046@interface GeneralPrefsVC () {
47 __unsafe_unretained IBOutlet NSTextField* historyChangedLabel;
48 __unsafe_unretained IBOutlet NSButton* startUpButton;
49 __unsafe_unretained IBOutlet NSButton* toggleAutomaticUpdateCheck;
50 __unsafe_unretained IBOutlet NSPopUpButton* checkIntervalPopUp;
51 __unsafe_unretained IBOutlet NSView* sparkleContainer;
52 __unsafe_unretained IBOutlet NSTextField* historyTextField;
53 __unsafe_unretained IBOutlet NSStepper* historyStepper;
54 __unsafe_unretained IBOutlet NSButton* historySwitch;
55 __unsafe_unretained IBOutlet NSButton* photoView;
56 __unsafe_unretained IBOutlet NSTextField* profileNameField;
Kateryna Kostiuk87ae2bf2018-05-04 13:46:17 -040057 __unsafe_unretained IBOutlet NSImageView* addProfilePhotoImage;
Kateryna Kostiuk67735232018-05-10 15:05:32 -040058 __unsafe_unretained IBOutlet NSButton *downloadFolder;
Kateryna Kostiuk74fe20c2018-06-14 12:05:53 -040059 __unsafe_unretained IBOutlet NSTextField *downloadFolderLabel;
Alexandre Lision261f1b92016-04-04 12:35:34 -040060}
Alexandre Lision4a7b95e2015-02-20 10:06:43 -050061@end
62
Alexandre Lision4de68ce2015-04-24 18:22:49 -040063@implementation GeneralPrefsVC
Alexandre Lisione4041492015-03-20 18:20:43 -040064
Kateryna Kostiuk67735232018-05-10 15:05:32 -040065@synthesize dataTransferModel;
66
67
Kateryna Kostiukecaa3952018-07-13 16:00:34 -040068-(id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil dataTransferModel:(lrc::api::DataTransferModel*) dataTransferModel
Kateryna Kostiuk67735232018-05-10 15:05:32 -040069{
70 if (self = [self initWithNibName:nibNameOrNil bundle:nibBundleOrNil])
71 {
72 self.dataTransferModel = dataTransferModel;
73 }
74 return self;
75}
76
Alexandre Lisione4041492015-03-20 18:20:43 -040077- (void)loadView
78{
79 [super loadView];
Alexandre Lisionc65310c2015-04-23 16:44:23 -040080 [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:Preferences::HistoryLimit options:NSKeyValueObservingOptionNew context:NULL];
Alexandre Lision4de68ce2015-04-24 18:22:49 -040081
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +020082 [startUpButton setState:[self isLaunchAtStartup]];
83
Alexandre Lisionaa03df42016-01-26 09:39:25 -050084 int historyLimit = CategorizedHistoryModel::instance().historyLimit();
85 [historyTextField setStringValue:[NSString stringWithFormat:@"%d", historyLimit]];
86 [historyStepper setIntValue:historyLimit];
87
88 BOOL limited = CategorizedHistoryModel::instance().isHistoryLimited();
89 [historySwitch setState:limited];
90 [historyStepper setEnabled:limited];
91 [historyTextField setEnabled:limited];
Alexandre Lision3d4143a2015-06-10 14:27:49 -040092#if ENABLE_SPARKLE
93 [sparkleContainer setHidden:NO];
94 SUUpdater *updater = [SUUpdater sharedUpdater];
95 [toggleAutomaticUpdateCheck bind:@"value" toObject:updater withKeyPath:@"automaticallyChecksForUpdates" options:nil];
96
97 [checkIntervalPopUp bind:@"enabled" toObject:updater withKeyPath:@"automaticallyChecksForUpdates" options:nil];
98 [checkIntervalPopUp bind:@"selectedTag" toObject:updater withKeyPath:@"updateCheckInterval" options:nil];
99#else
100 [sparkleContainer setHidden:YES];
101#endif
102
Alexandre Lision261f1b92016-04-04 12:35:34 -0400103 [photoView setWantsLayer: YES];
104 photoView.layer.cornerRadius = photoView.frame.size.width / 2;
105 photoView.layer.masksToBounds = YES;
Kateryna Kostiuk87ae2bf2018-05-04 13:46:17 -0400106 [addProfilePhotoImage setWantsLayer: YES];
107 [addProfilePhotoImage setHidden:NO];
108 [photoView setBordered:YES];
Alexandre Lision261f1b92016-04-04 12:35:34 -0400109
110 if (auto pro = ProfileModel::instance().selectedProfile()) {
Alexandre Lision261f1b92016-04-04 12:35:34 -0400111 [profileNameField setStringValue:pro->person()->formattedName().toNSString()];
Kateryna Kostiuk87ae2bf2018-05-04 13:46:17 -0400112 if (pro->person() && pro->person()->photo().isValid()) {
113 auto photo = GlobalInstances::pixmapManipulator().contactPhoto(pro->person(), {140,140});
114 [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
115 [addProfilePhotoImage setHidden:YES];
116 [photoView setBordered:NO];
117 }
Alexandre Lision261f1b92016-04-04 12:35:34 -0400118 }
Kateryna Kostiuk74fe20c2018-06-14 12:05:53 -0400119 if (appSandboxed()) {
120 [downloadFolder setHidden:YES];
121 [downloadFolder setEnabled:NO];
122 [downloadFolderLabel setHidden: YES];
123 return;
124 }
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400125 if (dataTransferModel) {
126 downloadFolder.title = [@(dataTransferModel->downloadDirectory.c_str()) lastPathComponent];
127 }
Alexandre Lisione4041492015-03-20 18:20:43 -0400128}
129
Alexandre Lision81c97212015-06-17 15:51:53 -0400130- (void) dealloc
131{
132 [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:Preferences::HistoryLimit];
Alexandre Lision81c97212015-06-17 15:51:53 -0400133}
134
Alexandre Lisione4041492015-03-20 18:20:43 -0400135- (IBAction)clearHistory:(id)sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400136 CategorizedHistoryModel::instance().clearAllCollections();
Kateryna Kostiukf9a72c02018-08-01 14:28:49 -0400137 media::RecordingModel::instance().clearAllCollections();
Alexandre Lisione4041492015-03-20 18:20:43 -0400138 [historyChangedLabel setHidden:NO];
139}
140
Alexandre Lision261f1b92016-04-04 12:35:34 -0400141- (IBAction)toggleHistory:(NSButton*)sender {
Alexandre Lisionaa03df42016-01-26 09:39:25 -0500142 CategorizedHistoryModel::instance().setHistoryLimited([sender state]);
143 int historyLimit = CategorizedHistoryModel::instance().historyLimit();
144 [historyTextField setStringValue:[NSString stringWithFormat:@"%d", historyLimit]];
145 [historyStepper setIntValue:historyLimit];
146 [historyChangedLabel setHidden:NO];
147 [historyStepper setEnabled:[sender state]];
148 [historyTextField setEnabled:[sender state]];
149}
150
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400151- (IBAction)changeDownloadFolder:(id)sender {
152
153 NSOpenPanel *panel = [NSOpenPanel openPanel];
154 [panel setAllowsMultipleSelection:NO];
155 [panel setCanChooseDirectories:YES];
156 [panel setCanChooseFiles:NO];
Kateryna Kostiuk4138db12018-06-08 15:52:18 -0400157 panel.delegate = self;
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400158 if ([panel runModal] != NSFileHandlingPanelOKButton) return;
159 if ([[panel URLs] lastObject] == nil) return;
160 NSString * path = [[[[panel URLs] lastObject] path] stringByAppendingString:@"/"];
161 dataTransferModel->downloadDirectory = std::string([path UTF8String]);
162 downloadFolder.title = [@(dataTransferModel->downloadDirectory.c_str()) lastPathComponent];
163 [[NSUserDefaults standardUserDefaults] setObject:path forKey:Preferences::DownloadFolder];
164}
165
Alexandre Lisione4041492015-03-20 18:20:43 -0400166// KVO handler
167-(void)observeValueForKeyPath:(NSString *)aKeyPath ofObject:(id)anObject
168 change:(NSDictionary *)aChange context:(void *)aContext
169{
Alexandre Lisionaa03df42016-01-26 09:39:25 -0500170 if ([aKeyPath isEqualToString:Preferences::HistoryLimit]) {
171 CategorizedHistoryModel::instance().setHistoryLimit([[aChange objectForKey: NSKeyValueChangeNewKey] integerValue]);
Alexandre Lision4de68ce2015-04-24 18:22:49 -0400172 [historyChangedLabel setHidden:NO];
Alexandre Lision4de68ce2015-04-24 18:22:49 -0400173 }
Alexandre Lisione4041492015-03-20 18:20:43 -0400174}
Alexandre Lision4a7b95e2015-02-20 10:06:43 -0500175
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200176#pragma mark - Startup API
177
178// MIT license by Brian Dunagan
179- (BOOL)isLaunchAtStartup {
180 // See if the app is currently in LoginItems.
181 LSSharedFileListItemRef itemRef = [self itemRefInLoginItems];
182 // Store away that boolean.
183 BOOL isInList = itemRef != nil;
184 // Release the reference if it exists.
185 if (itemRef != nil) CFRelease(itemRef);
186
187 return isInList;
188}
189
190- (IBAction)toggleLaunchAtStartup:(id)sender {
191 // Toggle the state.
192 BOOL shouldBeToggled = ![self isLaunchAtStartup];
193 // Get the LoginItems list.
194 LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
195 if (loginItemsRef == nil) return;
196 if (shouldBeToggled) {
197 // Add the app to the LoginItems list.
Alexandre Lision81c97212015-06-17 15:51:53 -0400198 CFURLRef appUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200199 LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsRef, kLSSharedFileListItemLast, NULL, NULL, appUrl, NULL, NULL);
200 if (itemRef) CFRelease(itemRef);
201 }
202 else {
203 // Remove the app from the LoginItems list.
204 LSSharedFileListItemRef itemRef = [self itemRefInLoginItems];
205 LSSharedFileListItemRemove(loginItemsRef,itemRef);
206 if (itemRef != nil) CFRelease(itemRef);
207 }
208 CFRelease(loginItemsRef);
209}
210
211- (LSSharedFileListItemRef)itemRefInLoginItems {
212 LSSharedFileListItemRef itemRef = nil;
Alexandre Lision81c97212015-06-17 15:51:53 -0400213 CFURLRef itemUrl = nil;
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200214
215 // Get the app's URL.
Alexandre Lision81c97212015-06-17 15:51:53 -0400216 auto appUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200217 // Get the LoginItems list.
218 LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
219 if (loginItemsRef == nil) return nil;
220 // Iterate over the LoginItems.
Alexandre Lision81c97212015-06-17 15:51:53 -0400221 NSArray *loginItems = (__bridge_transfer NSArray *)LSSharedFileListCopySnapshot(loginItemsRef, nil);
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200222 for (int currentIndex = 0; currentIndex < [loginItems count]; currentIndex++) {
223 // Get the current LoginItem and resolve its URL.
Alexandre Lision81c97212015-06-17 15:51:53 -0400224 LSSharedFileListItemRef currentItemRef = (__bridge LSSharedFileListItemRef)[loginItems objectAtIndex:currentIndex];
225 if (LSSharedFileListItemResolve(currentItemRef, 0, &itemUrl, NULL) == noErr) {
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200226 // Compare the URLs for the current LoginItem and the app.
Alexandre Lision81c97212015-06-17 15:51:53 -0400227 if ([(__bridge NSURL *)itemUrl isEqual:appUrl]) {
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200228 // Save the LoginItem reference.
229 itemRef = currentItemRef;
230 }
231 }
232 }
233 // Retain the LoginItem reference.
234 if (itemRef != nil) CFRetain(itemRef);
235 // Release the LoginItems lists.
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200236 CFRelease(loginItemsRef);
237
238 return itemRef;
239}
240
Alexandre Lision261f1b92016-04-04 12:35:34 -0400241#pragma mark - Profile Photo edition
242
243- (IBAction) editPhoto:(id)sender {
244 auto pictureTaker = [IKPictureTaker pictureTaker];
245 [pictureTaker beginPictureTakerSheetForWindow:self.view.window
246 withDelegate:self
247 didEndSelector:@selector(pictureTakerDidEnd:returnCode:contextInfo:)
248 contextInfo:nil];
249}
250
251- (void) pictureTakerDidEnd:(IKPictureTaker *) picker
252 returnCode:(NSInteger) code
253 contextInfo:(void*) contextInfo
254{
255 if (auto outputImage = [picker outputImage]) {
256 [photoView setImage:outputImage];
Kateryna Kostiuk87ae2bf2018-05-04 13:46:17 -0400257 [addProfilePhotoImage setHidden:YES];
258 [photoView setBordered:NO];
259 if (auto pro = ProfileModel::instance().selectedProfile()) {
260 QPixmap p;
261 auto smallImg = [NSImage imageResize:outputImage newSize:{100,100}];
262 if (p.loadFromData(QByteArray::fromNSData([smallImg TIFFRepresentation]))) {
263 pro->person()->setPhoto(QVariant(p));
264 }
265 pro->save();
Alexandre Lision261f1b92016-04-04 12:35:34 -0400266 }
Kateryna Kostiuk87ae2bf2018-05-04 13:46:17 -0400267 } else if (!photoView.image){
268 [addProfilePhotoImage setHidden:NO];
269 [photoView setBordered:YES];
Alexandre Lision261f1b92016-04-04 12:35:34 -0400270 }
271}
272
273#pragma mark - NSTextFieldDelegate methods
274
275-(void)controlTextDidChange:(NSNotification *)notif
276{
277 if (auto pro = ProfileModel::instance().selectedProfile()) {
278 pro->person()->setFormattedName(profileNameField.stringValue.UTF8String);
279 pro->save();
280 }
281}
282
Kateryna Kostiuk4138db12018-06-08 15:52:18 -0400283#pragma mark - NSOpenSavePanelDelegate delegate methods
284
285- (BOOL) panel:(id)sender shouldEnableURL:(NSURL*)url {
Kateryna Kostiuk74fe20c2018-06-14 12:05:53 -0400286 return YES;
Kateryna Kostiuk4138db12018-06-08 15:52:18 -0400287}
288
Alexandre Lision4a7b95e2015-02-20 10:06:43 -0500289@end