blob: b1693ccfd47cc8f5f05168292d6a54ea0121082f [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>
Alexandre Lisione4041492015-03-20 18:20:43 -040034
Alexandre Lision3d4143a2015-06-10 14:27:49 -040035#if ENABLE_SPARKLE
36#import <Sparkle/Sparkle.h>
37#endif
38
Alexandre Lisionc65310c2015-04-23 16:44:23 -040039#import "Constants.h"
Alexandre Lision261f1b92016-04-04 12:35:34 -040040#import "views/NSImage+Extensions.h"
41#import "delegates/ImageManipulationDelegate.h"
Alexandre Lisionc65310c2015-04-23 16:44:23 -040042
Alexandre Lision261f1b92016-04-04 12:35:34 -040043@interface GeneralPrefsVC () {
44 __unsafe_unretained IBOutlet NSTextField* historyChangedLabel;
45 __unsafe_unretained IBOutlet NSButton* startUpButton;
46 __unsafe_unretained IBOutlet NSButton* toggleAutomaticUpdateCheck;
47 __unsafe_unretained IBOutlet NSPopUpButton* checkIntervalPopUp;
48 __unsafe_unretained IBOutlet NSView* sparkleContainer;
49 __unsafe_unretained IBOutlet NSTextField* historyTextField;
50 __unsafe_unretained IBOutlet NSStepper* historyStepper;
51 __unsafe_unretained IBOutlet NSButton* historySwitch;
52 __unsafe_unretained IBOutlet NSButton* photoView;
53 __unsafe_unretained IBOutlet NSTextField* profileNameField;
54}
Alexandre Lision4a7b95e2015-02-20 10:06:43 -050055@end
56
Alexandre Lision4de68ce2015-04-24 18:22:49 -040057@implementation GeneralPrefsVC
Alexandre Lisione4041492015-03-20 18:20:43 -040058
59- (void)loadView
60{
61 [super loadView];
Alexandre Lisionc65310c2015-04-23 16:44:23 -040062 [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:Preferences::HistoryLimit options:NSKeyValueObservingOptionNew context:NULL];
Alexandre Lision4de68ce2015-04-24 18:22:49 -040063
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +020064 [startUpButton setState:[self isLaunchAtStartup]];
65
Alexandre Lisionaa03df42016-01-26 09:39:25 -050066 int historyLimit = CategorizedHistoryModel::instance().historyLimit();
67 [historyTextField setStringValue:[NSString stringWithFormat:@"%d", historyLimit]];
68 [historyStepper setIntValue:historyLimit];
69
70 BOOL limited = CategorizedHistoryModel::instance().isHistoryLimited();
71 [historySwitch setState:limited];
72 [historyStepper setEnabled:limited];
73 [historyTextField setEnabled:limited];
Alexandre Lision3d4143a2015-06-10 14:27:49 -040074#if ENABLE_SPARKLE
75 [sparkleContainer setHidden:NO];
76 SUUpdater *updater = [SUUpdater sharedUpdater];
77 [toggleAutomaticUpdateCheck bind:@"value" toObject:updater withKeyPath:@"automaticallyChecksForUpdates" options:nil];
78
79 [checkIntervalPopUp bind:@"enabled" toObject:updater withKeyPath:@"automaticallyChecksForUpdates" options:nil];
80 [checkIntervalPopUp bind:@"selectedTag" toObject:updater withKeyPath:@"updateCheckInterval" options:nil];
81#else
82 [sparkleContainer setHidden:YES];
83#endif
84
Alexandre Lision261f1b92016-04-04 12:35:34 -040085 [photoView setWantsLayer: YES];
86 photoView.layer.cornerRadius = photoView.frame.size.width / 2;
87 photoView.layer.masksToBounds = YES;
88
89 if (auto pro = ProfileModel::instance().selectedProfile()) {
90 auto photo = GlobalInstances::pixmapManipulator().contactPhoto(pro->person(), {140,140});
91 [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
92 [profileNameField setStringValue:pro->person()->formattedName().toNSString()];
93 }
94
Alexandre Lisione4041492015-03-20 18:20:43 -040095}
96
Alexandre Lision81c97212015-06-17 15:51:53 -040097- (void) dealloc
98{
99 [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:Preferences::HistoryLimit];
Alexandre Lision81c97212015-06-17 15:51:53 -0400100}
101
Alexandre Lisione4041492015-03-20 18:20:43 -0400102- (IBAction)clearHistory:(id)sender {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400103 CategorizedHistoryModel::instance().clearAllCollections();
Alexandre Lisione4041492015-03-20 18:20:43 -0400104 [historyChangedLabel setHidden:NO];
105}
106
Alexandre Lision261f1b92016-04-04 12:35:34 -0400107- (IBAction)toggleHistory:(NSButton*)sender {
Alexandre Lisionaa03df42016-01-26 09:39:25 -0500108 CategorizedHistoryModel::instance().setHistoryLimited([sender state]);
109 int historyLimit = CategorizedHistoryModel::instance().historyLimit();
110 [historyTextField setStringValue:[NSString stringWithFormat:@"%d", historyLimit]];
111 [historyStepper setIntValue:historyLimit];
112 [historyChangedLabel setHidden:NO];
113 [historyStepper setEnabled:[sender state]];
114 [historyTextField setEnabled:[sender state]];
115}
116
Alexandre Lisione4041492015-03-20 18:20:43 -0400117// KVO handler
118-(void)observeValueForKeyPath:(NSString *)aKeyPath ofObject:(id)anObject
119 change:(NSDictionary *)aChange context:(void *)aContext
120{
Alexandre Lisionaa03df42016-01-26 09:39:25 -0500121 if ([aKeyPath isEqualToString:Preferences::HistoryLimit]) {
122 CategorizedHistoryModel::instance().setHistoryLimit([[aChange objectForKey: NSKeyValueChangeNewKey] integerValue]);
Alexandre Lision4de68ce2015-04-24 18:22:49 -0400123 [historyChangedLabel setHidden:NO];
Alexandre Lision4de68ce2015-04-24 18:22:49 -0400124 }
Alexandre Lisione4041492015-03-20 18:20:43 -0400125}
Alexandre Lision4a7b95e2015-02-20 10:06:43 -0500126
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200127#pragma mark - Startup API
128
129// MIT license by Brian Dunagan
130- (BOOL)isLaunchAtStartup {
131 // See if the app is currently in LoginItems.
132 LSSharedFileListItemRef itemRef = [self itemRefInLoginItems];
133 // Store away that boolean.
134 BOOL isInList = itemRef != nil;
135 // Release the reference if it exists.
136 if (itemRef != nil) CFRelease(itemRef);
137
138 return isInList;
139}
140
141- (IBAction)toggleLaunchAtStartup:(id)sender {
142 // Toggle the state.
143 BOOL shouldBeToggled = ![self isLaunchAtStartup];
144 // Get the LoginItems list.
145 LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
146 if (loginItemsRef == nil) return;
147 if (shouldBeToggled) {
148 // Add the app to the LoginItems list.
Alexandre Lision81c97212015-06-17 15:51:53 -0400149 CFURLRef appUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200150 LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsRef, kLSSharedFileListItemLast, NULL, NULL, appUrl, NULL, NULL);
151 if (itemRef) CFRelease(itemRef);
152 }
153 else {
154 // Remove the app from the LoginItems list.
155 LSSharedFileListItemRef itemRef = [self itemRefInLoginItems];
156 LSSharedFileListItemRemove(loginItemsRef,itemRef);
157 if (itemRef != nil) CFRelease(itemRef);
158 }
159 CFRelease(loginItemsRef);
160}
161
162- (LSSharedFileListItemRef)itemRefInLoginItems {
163 LSSharedFileListItemRef itemRef = nil;
Alexandre Lision81c97212015-06-17 15:51:53 -0400164 CFURLRef itemUrl = nil;
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200165
166 // Get the app's URL.
Alexandre Lision81c97212015-06-17 15:51:53 -0400167 auto appUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200168 // Get the LoginItems list.
169 LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
170 if (loginItemsRef == nil) return nil;
171 // Iterate over the LoginItems.
Alexandre Lision81c97212015-06-17 15:51:53 -0400172 NSArray *loginItems = (__bridge_transfer NSArray *)LSSharedFileListCopySnapshot(loginItemsRef, nil);
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200173 for (int currentIndex = 0; currentIndex < [loginItems count]; currentIndex++) {
174 // Get the current LoginItem and resolve its URL.
Alexandre Lision81c97212015-06-17 15:51:53 -0400175 LSSharedFileListItemRef currentItemRef = (__bridge LSSharedFileListItemRef)[loginItems objectAtIndex:currentIndex];
176 if (LSSharedFileListItemResolve(currentItemRef, 0, &itemUrl, NULL) == noErr) {
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200177 // Compare the URLs for the current LoginItem and the app.
Alexandre Lision81c97212015-06-17 15:51:53 -0400178 if ([(__bridge NSURL *)itemUrl isEqual:appUrl]) {
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200179 // Save the LoginItem reference.
180 itemRef = currentItemRef;
181 }
182 }
183 }
184 // Retain the LoginItem reference.
185 if (itemRef != nil) CFRetain(itemRef);
186 // Release the LoginItems lists.
Alexandre Lision1ad5a2f2015-05-27 14:21:45 +0200187 CFRelease(loginItemsRef);
188
189 return itemRef;
190}
191
Alexandre Lision261f1b92016-04-04 12:35:34 -0400192#pragma mark - Profile Photo edition
193
194- (IBAction) editPhoto:(id)sender {
195 auto pictureTaker = [IKPictureTaker pictureTaker];
196 [pictureTaker beginPictureTakerSheetForWindow:self.view.window
197 withDelegate:self
198 didEndSelector:@selector(pictureTakerDidEnd:returnCode:contextInfo:)
199 contextInfo:nil];
200}
201
202- (void) pictureTakerDidEnd:(IKPictureTaker *) picker
203 returnCode:(NSInteger) code
204 contextInfo:(void*) contextInfo
205{
206 if (auto outputImage = [picker outputImage]) {
207 [photoView setImage:outputImage];
208 } else
209 [photoView setImage:[NSImage imageNamed:@"default_user_icon"]];
210 if (auto pro = ProfileModel::instance().selectedProfile()) {
211 QPixmap p;
212 auto smallImg = [NSImage imageResize:[photoView image] newSize:{100,100}];
213 if (p.loadFromData(QByteArray::fromNSData([smallImg TIFFRepresentation]))) {
214 pro->person()->setPhoto(QVariant(p));
215 }
216 pro->save();
217 }
218}
219
220#pragma mark - NSTextFieldDelegate methods
221
222-(void)controlTextDidChange:(NSNotification *)notif
223{
224 if (auto pro = ProfileModel::instance().selectedProfile()) {
225 pro->person()->setFormattedName(profileNameField.stringValue.UTF8String);
226 pro->save();
227 }
228}
229
Alexandre Lision4a7b95e2015-02-20 10:06:43 -0500230@end