project: use better structure
Create src/ and ui/ sub-folders instead of flat structure
Remove cantarell folder and licence duplicate
Refs #69161
Change-Id: Ifa136b0e26533f4e9d178479fc958a2563917894
diff --git a/src/AccAdvancedVC.h b/src/AccAdvancedVC.h
new file mode 100644
index 0000000..52216be
--- /dev/null
+++ b/src/AccAdvancedVC.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import <Cocoa/Cocoa.h>
+
+#import <account.h>
+
+@interface AccAdvancedVC : NSViewController <NSTextFieldDelegate> {
+
+}
+
+- (void)loadAccount:(Account *)account;
+
+@end
diff --git a/src/AccAdvancedVC.mm b/src/AccAdvancedVC.mm
new file mode 100644
index 0000000..d4bfeca
--- /dev/null
+++ b/src/AccAdvancedVC.mm
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#define REGISTRATION_TAG 0
+#define LOCALPORT_TAG 1
+#define STUNURL_TAG 2
+#define PUBLICADDR_TAG 3
+#define PUBLICPORT_TAG 4
+#define MINAUDIO_TAG 5
+#define MAXAUDIO_TAG 6
+#define MINVIDEO_TAG 7
+#define MAXVIDEO_TAG 8
+
+#import "AccAdvancedVC.h"
+
+@interface AccAdvancedVC ()
+
+@property Account* privateAccount;
+@property (assign) IBOutlet NSTextField *registrationField;
+@property (assign) IBOutlet NSTextField *localPortField;
+@property (assign) IBOutlet NSButton *isUsingSTUN;
+
+@property (assign) IBOutlet NSTextField *STUNserverURLField;
+@property (assign) IBOutlet NSTextField *minAudioRTPRange;
+@property (assign) IBOutlet NSTextField *maxAudioRTPRange;
+@property (assign) IBOutlet NSTextField *minVideoRTPRange;
+@property (assign) IBOutlet NSTextField *maxVideoRTPRange;
+
+
+@property (assign) IBOutlet NSStepper *registrationStepper;
+@property (assign) IBOutlet NSStepper *localPortStepper;
+@property (assign) IBOutlet NSStepper *minAudioPortStepper;
+@property (assign) IBOutlet NSStepper *maxAudioPortStepper;
+@property (assign) IBOutlet NSStepper *minVideoPortStepper;
+@property (assign) IBOutlet NSStepper *maxVideoPortStepper;
+
+@property (assign) IBOutlet NSMatrix *publishAddrAndPortRadioGroup;
+@property (assign) IBOutlet NSTextField *publishedAddrField;
+@property (assign) IBOutlet NSTextField *publishedPortField;
+
+@end
+
+@implementation AccAdvancedVC
+@synthesize privateAccount;
+@synthesize registrationField;
+@synthesize localPortField;
+@synthesize isUsingSTUN;
+@synthesize STUNserverURLField;
+@synthesize minAudioRTPRange;
+@synthesize maxAudioRTPRange;
+@synthesize minVideoRTPRange;
+@synthesize maxVideoRTPRange;
+@synthesize registrationStepper;
+@synthesize localPortStepper;
+@synthesize minAudioPortStepper;
+@synthesize maxAudioPortStepper;
+@synthesize minVideoPortStepper;
+@synthesize maxVideoPortStepper;
+@synthesize publishAddrAndPortRadioGroup;
+@synthesize publishedAddrField;
+@synthesize publishedPortField;
+
+- (void)awakeFromNib
+{
+ NSLog(@"INIT Advanced VC");
+ [registrationStepper setTag:REGISTRATION_TAG];
+ [localPortStepper setTag:LOCALPORT_TAG];
+ [minAudioPortStepper setTag:MINAUDIO_TAG];
+ [maxAudioPortStepper setTag:MAXAUDIO_TAG];
+ [minVideoPortStepper setTag:MINVIDEO_TAG];
+ [maxVideoPortStepper setTag:MAXVIDEO_TAG];
+
+ [registrationField setTag:REGISTRATION_TAG];
+ [localPortField setTag:LOCALPORT_TAG];
+ [minAudioRTPRange setTag:MINAUDIO_TAG];
+ [maxAudioRTPRange setTag:MAXAUDIO_TAG];
+ [minVideoRTPRange setTag:MINVIDEO_TAG];
+ [maxVideoRTPRange setTag:MAXVIDEO_TAG];
+
+ [STUNserverURLField setTag:STUNURL_TAG];
+ [publishedPortField setTag:PUBLICPORT_TAG];
+ [publishedAddrField setTag:PUBLICADDR_TAG];
+
+}
+
+- (void)loadAccount:(Account *)account
+{
+ privateAccount = account;
+ [self updateControlsWithTag:REGISTRATION_TAG];
+ [self updateControlsWithTag:LOCALPORT_TAG];
+ [self updateControlsWithTag:MINAUDIO_TAG];
+ [self updateControlsWithTag:MAXAUDIO_TAG];
+ [self updateControlsWithTag:MINVIDEO_TAG];
+ [self updateControlsWithTag:MAXVIDEO_TAG];
+
+ [STUNserverURLField setStringValue:privateAccount->sipStunServer().toNSString()];
+ [isUsingSTUN setState:privateAccount->isSipStunEnabled()?NSOnState:NSOffState];
+ [STUNserverURLField setEnabled:privateAccount->isSipStunEnabled()];
+
+ if(privateAccount->isPublishedSameAsLocal())
+ [publishAddrAndPortRadioGroup selectCellAtRow:0 column:0];
+ else {
+ [publishAddrAndPortRadioGroup selectCellAtRow:1 column:0];
+ }
+
+ [publishedAddrField setStringValue:privateAccount->publishedAddress().toNSString()];
+ [publishedPortField setIntValue:privateAccount->publishedPort()];
+ [publishedAddrField setEnabled:!privateAccount->isPublishedSameAsLocal()];
+ [publishedPortField setEnabled:!privateAccount->isPublishedSameAsLocal()];
+}
+
+#pragma mark - NSTextFieldDelegate methods
+
+- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor
+{
+ NSLog(@"textShouldBeginEditing");
+ return YES;
+}
+
+- (void)control:(NSControl *)control didFailToValidatePartialString:(NSString *)string errorDescription:(NSString *)error
+{
+ NSLog(@"didFailToValidatePartialString");
+}
+
+-(void)controlTextDidBeginEditing:(NSNotification *)obj
+{
+
+}
+
+-(void)controlTextDidChange:(NSNotification *)notif
+{
+ NSTextField *textField = [notif object];
+ NSRange test = [[textField currentEditor] selectedRange];
+
+ [self valueDidChange:textField];
+ //FIXME: saving account lose focus because in NSTreeController we remove and reinsert row so View selction change
+ [textField.window makeFirstResponder:textField];
+ [[textField currentEditor] setSelectedRange:test];
+}
+
+- (IBAction) valueDidChange: (id) sender
+{
+ switch ([sender tag]) {
+ case REGISTRATION_TAG:
+ privateAccount->setRegistrationExpire([sender integerValue]);
+ break;
+ case LOCALPORT_TAG:
+ privateAccount->setLocalPort([sender integerValue]);
+ break;
+ case STUNURL_TAG:
+ privateAccount->setSipStunServer([[sender stringValue] UTF8String]);
+ break;
+ case PUBLICADDR_TAG:
+ privateAccount->setPublishedAddress([[sender stringValue] UTF8String]);
+ break;
+ case PUBLICPORT_TAG:
+ privateAccount->setPublishedPort([sender integerValue]);
+ break;
+ case MINAUDIO_TAG:
+ privateAccount->setAudioPortMin([sender integerValue]);
+ break;
+ case MAXAUDIO_TAG:
+ privateAccount->setAudioPortMax([sender integerValue]);
+ break;
+ case MINVIDEO_TAG:
+ privateAccount->setVideoPortMin([sender integerValue]);
+ break;
+ case MAXVIDEO_TAG:
+ privateAccount->setVideoPortMax([sender integerValue]);
+ break;
+ default:
+ break;
+ }
+ [self updateControlsWithTag:[sender tag]];
+}
+
+- (IBAction)toggleSTUN:(NSButton *)sender
+{
+ privateAccount->setSipStunEnabled([sender state]==NSOnState);
+ [STUNserverURLField setEnabled:privateAccount->isSipStunEnabled()];
+}
+
+- (IBAction)didSwitchPublishedAddress:(NSMatrix *)matrix
+{
+ NSInteger row = [matrix selectedRow];
+ if(row == 0) {
+ privateAccount->setPublishedSameAsLocal(YES);
+ } else {
+ privateAccount->setPublishedSameAsLocal(NO);
+ }
+ [publishedAddrField setEnabled:!privateAccount->isPublishedSameAsLocal()];
+ [publishedPortField setEnabled:!privateAccount->isPublishedSameAsLocal()];
+
+}
+
+- (void) updateControlsWithTag:(NSInteger) tag
+{
+ switch (tag) {
+ case REGISTRATION_TAG:
+ [registrationStepper setIntegerValue:privateAccount->registrationExpire()];
+ [registrationField setIntegerValue:privateAccount->registrationExpire()];
+ break;
+ case LOCALPORT_TAG:
+ [localPortStepper setIntegerValue:privateAccount->localPort()];
+ [localPortField setIntegerValue:privateAccount->localPort()];
+ break;
+ case MINAUDIO_TAG:
+ [minAudioPortStepper setIntegerValue:privateAccount->audioPortMin()];
+ [minAudioRTPRange setIntegerValue:privateAccount->audioPortMin()];
+ break;
+ case MAXAUDIO_TAG:
+ [maxAudioPortStepper setIntegerValue:privateAccount->audioPortMax()];
+ [maxAudioRTPRange setIntegerValue:privateAccount->audioPortMax()];
+ break;
+ case MINVIDEO_TAG:
+ [minVideoPortStepper setIntegerValue:privateAccount->videoPortMin()];
+ [minVideoRTPRange setIntegerValue:privateAccount->videoPortMin()];
+ break;
+ case MAXVIDEO_TAG:
+ [maxVideoPortStepper setIntegerValue:privateAccount->videoPortMax()];
+ [maxVideoRTPRange setIntegerValue:privateAccount->videoPortMax()];
+ break;
+ default:
+ break;
+ }
+}
+
+@end
diff --git a/src/AccAudioVC.h b/src/AccAudioVC.h
new file mode 100644
index 0000000..38f8283
--- /dev/null
+++ b/src/AccAudioVC.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef ACCAUDIOVC_H
+#define ACCAUDIOVC_H
+
+#import <Cocoa/Cocoa.h>
+
+#import <account.h>
+
+#import "QNSTreeController.h"
+
+@interface AccAudioVC : NSViewController <NSOutlineViewDelegate> {
+
+}
+
+- (void)loadAccount:(Account *)account;
+
+@end
+
+#endif // ACCAUDIOVC_H
\ No newline at end of file
diff --git a/src/AccAudioVC.mm b/src/AccAudioVC.mm
new file mode 100644
index 0000000..704c8cc
--- /dev/null
+++ b/src/AccAudioVC.mm
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#define COLUMNID_STATE @"AudioStateColumn"
+#define COLUMNID_CODECS @"AudioCodecsColumn"
+#define COLUMNID_FREQ @"AudioFrequencyColumn"
+#define COLUMNID_BITRATE @"AudioBitrateColumn"
+
+#import "AccAudioVC.h"
+
+#import <QSortFilterProxyModel>
+#import <audio/codecmodel.h>
+#import <accountmodel.h>
+
+@interface AccAudioVC ()
+
+@property Account* privateAccount;
+@property QNSTreeController *treeController;
+@property (assign) IBOutlet NSOutlineView *codecsView;
+
+@end
+
+@implementation AccAudioVC
+@synthesize treeController;
+@synthesize codecsView;
+@synthesize privateAccount;
+
+- (void)awakeFromNib
+{
+ NSLog(@"INIT Audio VC");
+}
+
+- (void)loadAccount:(Account *)account
+{
+ privateAccount = account;
+ treeController = [[QNSTreeController alloc] initWithQModel:privateAccount->codecModel()->audioCodecs()];
+
+ [treeController setAvoidsEmptySelection:NO];
+ [treeController setChildrenKeyPath:@"children"];
+
+ [codecsView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
+ [codecsView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
+ [codecsView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
+}
+
+- (IBAction)moveUp:(id)sender {
+ if([[treeController selectedNodes] count] > 0) {
+ QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
+ if(!qIdx.isValid())
+ return;
+
+ QMimeData* mime = privateAccount->codecModel()->audioCodecs()->mimeData(QModelIndexList() << qIdx);
+ privateAccount->codecModel()->audioCodecs()->dropMimeData(mime, Qt::MoveAction, qIdx.row() - 1, 0, QModelIndex());
+ }
+}
+
+- (IBAction)moveDown:(id)sender {
+ if([[treeController selectedNodes] count] > 0) {
+ QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
+ if(!qIdx.isValid())
+ return;
+
+ QMimeData* mime = privateAccount->codecModel()->audioCodecs()->mimeData(QModelIndexList() << qIdx);
+ privateAccount->codecModel()->audioCodecs()->dropMimeData(mime, Qt::MoveAction, qIdx.row() + 1, 0, QModelIndex());
+ }
+}
+
+- (IBAction)toggleCodec:(NSOutlineView*)sender {
+ NSInteger row = [sender clickedRow];
+ NSTableColumn *col = [sender tableColumnWithIdentifier:COLUMNID_STATE];
+ NSButtonCell *cell = [col dataCellForRow:row];
+ QModelIndex qIdx = privateAccount->codecModel()->audioCodecs()->index(row, 0, QModelIndex());
+ privateAccount->codecModel()->audioCodecs()->setData(qIdx, cell.state == NSOnState ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole);
+}
+
+#pragma mark - NSOutlineViewDelegate methods
+
+// -------------------------------------------------------------------------------
+// shouldSelectItem:item
+// -------------------------------------------------------------------------------
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
+{
+ return YES;
+}
+
+// -------------------------------------------------------------------------------
+// dataCellForTableColumn:tableColumn:item
+// -------------------------------------------------------------------------------
+- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ NSCell *returnCell = [tableColumn dataCell];
+
+ if(item == nil)
+ return returnCell;
+
+ return returnCell;
+}
+
+// -------------------------------------------------------------------------------
+// textShouldEndEditing:fieldEditor
+// -------------------------------------------------------------------------------
+- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
+{
+ if ([[fieldEditor string] length] == 0)
+ {
+ // don't allow empty node names
+ return NO;
+ }
+ else
+ {
+ return YES;
+ }
+}
+
+// -------------------------------------------------------------------------------
+// shouldEditTableColumn:tableColumn:item
+//
+// Decide to allow the edit of the given outline view "item".
+// -------------------------------------------------------------------------------
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ return NO;
+}
+
+// -------------------------------------------------------------------------------
+// outlineView:willDisplayCell:forTableColumn:item
+// -------------------------------------------------------------------------------
+- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
+ if(!qIdx.isValid())
+ return;
+ if([[tableColumn identifier] isEqualToString:COLUMNID_STATE]) {
+ [cell setState:privateAccount->codecModel()->audioCodecs()->data(qIdx, Qt::CheckStateRole).value<BOOL>()?NSOnState:NSOffState];
+ } else if ([[tableColumn identifier] isEqualToString:COLUMNID_CODECS])
+ {
+ cell.title = privateAccount->codecModel()->audioCodecs()->data(qIdx, CodecModel::Role::NAME).toString().toNSString();
+ } else if ([[tableColumn identifier] isEqualToString:COLUMNID_FREQ])
+ {
+ cell.title = privateAccount->codecModel()->audioCodecs()->data(qIdx, CodecModel::Role::SAMPLERATE).toString().toNSString();
+ } else if ([[tableColumn identifier] isEqualToString:COLUMNID_BITRATE])
+ {
+ cell.title = privateAccount->codecModel()->audioCodecs()->data(qIdx, CodecModel::Role::BITRATE).toString().toNSString();
+ }
+}
+
+// -------------------------------------------------------------------------------
+// outlineViewSelectionDidChange:notification
+// -------------------------------------------------------------------------------
+- (void)outlineViewSelectionDidChange:(NSNotification *)notification
+{
+
+}
+
+@end
diff --git a/src/AccGeneralVC.h b/src/AccGeneralVC.h
new file mode 100644
index 0000000..139fd1b
--- /dev/null
+++ b/src/AccGeneralVC.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef ACCGENERALVC_H
+#define ACCGENERALVC_H
+
+#import <Cocoa/Cocoa.h>
+
+#import <account.h>
+
+@interface AccGeneralVC : NSViewController <NSTextFieldDelegate> {
+
+}
+
+- (IBAction)toggleUpnp:(NSButton *)sender;
+- (IBAction)toggleAutoAnswer:(NSButton *)sender;
+- (IBAction)toggleCustomAgent:(NSButton *)sender;
+
+- (void)loadAccount:(Account *)account;
+
+@end
+
+#endif // ACCGENERALVC_H
\ No newline at end of file
diff --git a/src/AccGeneralVC.mm b/src/AccGeneralVC.mm
new file mode 100644
index 0000000..cfb92ea
--- /dev/null
+++ b/src/AccGeneralVC.mm
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#define ALIAS_TAG 0
+#define HOSTNAME_TAG 1
+#define USERNAME_TAG 2
+#define PASSWORD_TAG 3
+#define USERAGENT_TAG 4
+
+
+#import "AccGeneralVC.h"
+
+#import <accountmodel.h>
+#import <protocolmodel.h>
+#import <qitemselectionmodel.h>
+
+@interface AccGeneralVC ()
+
+@property Account* privateAccount;
+
+@property (assign) IBOutlet NSView *boxingAccount;
+@property (assign) IBOutlet NSView *boxingParameters;
+@property (assign) IBOutlet NSView *boxingCommon;
+
+@property (assign) IBOutlet NSTextField *aliasTextField;
+@property (assign) IBOutlet NSTextField *typeLabel;
+
+@property (assign) IBOutlet NSTextField *serverHostTextField;
+@property (assign) IBOutlet NSTextField *usernameTextField;
+@property (assign) IBOutlet NSSecureTextField *passwordTextField;
+
+@property (assign) IBOutlet NSButton *upnpButton;
+@property (assign) IBOutlet NSButton *autoAnswerButton;
+@property (assign) IBOutlet NSButton *userAgentButton;
+
+@property (assign) IBOutlet NSTextField *userAgentTextField;
+
+@end
+
+@implementation AccGeneralVC
+@synthesize typeLabel;
+@synthesize boxingAccount;
+@synthesize boxingParameters;
+@synthesize boxingCommon;
+@synthesize aliasTextField;
+@synthesize serverHostTextField;
+@synthesize usernameTextField;
+@synthesize passwordTextField;
+@synthesize upnpButton;
+@synthesize autoAnswerButton;
+@synthesize userAgentButton;
+@synthesize userAgentTextField;
+@synthesize privateAccount;
+
+- (void)awakeFromNib
+{
+ NSLog(@"INIT General VC");
+ [aliasTextField setTag:ALIAS_TAG];
+ [serverHostTextField setTag:HOSTNAME_TAG];
+ [usernameTextField setTag:USERNAME_TAG];
+ [passwordTextField setTag:PASSWORD_TAG];
+ [userAgentTextField setTag:USERAGENT_TAG];
+}
+
+- (IBAction)toggleUpnp:(NSButton *)sender {
+ privateAccount->setUpnpEnabled([sender state] == NSOnState);
+}
+
+- (IBAction)toggleAutoAnswer:(NSButton *)sender {
+ privateAccount->setAutoAnswer([sender state] == NSOnState);
+}
+
+- (IBAction)toggleCustomAgent:(NSButton *)sender {
+ [self.userAgentTextField setEnabled:[sender state] == NSOnState];
+ privateAccount->setHasCustomUserAgent([sender state] == NSOnState);
+}
+
+- (void)loadAccount:(Account *)account
+{
+
+ privateAccount = account;
+
+ if([account->alias().toNSString() isEqualToString:@"IP2IP"]) {
+ [boxingAccount.subviews setValue:@YES forKeyPath:@"hidden"];
+ [boxingParameters.subviews setValue:@YES forKeyPath:@"hidden"];
+
+ NSLog(@"IP@IP");
+ // Put visible items at top of the frame
+ [boxingCommon setFrameOrigin:NSMakePoint(boxingAccount.frame.origin.x,
+ boxingAccount.frame.origin.y - 40)];
+ [boxingCommon setNeedsDisplay:YES];
+
+ } else {
+ [boxingAccount.subviews setValue:@NO forKeyPath:@"hidden"];
+ [boxingParameters.subviews setValue:@NO forKeyPath:@"hidden"];
+
+ [self.aliasTextField setStringValue:account->alias().toNSString()];
+ [self.serverHostTextField setStringValue:account->hostname().toNSString()];
+ [self.usernameTextField setStringValue:account->username().toNSString()];
+ [self.passwordTextField setStringValue:account->password().toNSString()];
+ }
+
+ switch (account->protocol()) {
+ case Account::Protocol::SIP:
+ [self.typeLabel setStringValue:@"SIP"];
+ break;
+ case Account::Protocol::IAX:
+ [self.typeLabel setStringValue:@"IAX"];
+ break;
+ case Account::Protocol::RING:
+ [self.typeLabel setStringValue:@"RING"];
+ break;
+
+ default:
+ break;
+ }
+
+ [upnpButton setState:privateAccount->isUpnpEnabled()];
+ [userAgentButton setState:privateAccount->hasCustomUserAgent()];
+ [userAgentTextField setEnabled:privateAccount->hasCustomUserAgent()];
+ [self.autoAnswerButton setState:privateAccount->isAutoAnswer()];
+ [self.userAgentTextField setStringValue:account->userAgent().toNSString()];
+}
+
+#pragma mark - NSTextFieldDelegate methods
+
+- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor
+{
+ return YES;
+}
+
+-(void)controlTextDidChange:(NSNotification *)notif
+{
+ NSTextField *textField = [notif object];
+
+ switch ([textField tag]) {
+ case ALIAS_TAG:
+ privateAccount->setAlias([[textField stringValue] UTF8String]);
+ break;
+ case HOSTNAME_TAG:
+ privateAccount->setHostname([[textField stringValue] UTF8String]);
+ break;
+ case USERNAME_TAG:
+ privateAccount->setUsername([[textField stringValue] UTF8String]);
+ break;
+ case PASSWORD_TAG:
+ privateAccount->setPassword([[textField stringValue] UTF8String]);
+ break;
+ case USERAGENT_TAG:
+ privateAccount->setUserAgent([[textField stringValue] UTF8String]);
+ break;
+ default:
+ break;
+ }
+}
+@end
diff --git a/src/AccRingVC.h b/src/AccRingVC.h
new file mode 100644
index 0000000..df21709
--- /dev/null
+++ b/src/AccRingVC.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef ACCRINGVC_H
+#define ACCRINGVC_H
+
+#import <Cocoa/Cocoa.h>
+
+#import <account.h>
+
+@interface AccRingVC : NSViewController <NSTextFieldDelegate> {
+
+ NSTextField *hashField;
+}
+
+- (void)loadAccount:(Account *)account;
+
+@end
+
+#endif // ACCRINGVC_H
diff --git a/src/AccRingVC.mm b/src/AccRingVC.mm
new file mode 100644
index 0000000..88a5f6a
--- /dev/null
+++ b/src/AccRingVC.mm
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#define ALIAS_TAG 0
+#define HOSTNAME_TAG 1
+#define USERNAME_TAG 2
+#define PASSWORD_TAG 3
+#define USERAGENT_TAG 4
+
+#import "AccRingVC.h"
+
+@interface AccRingVC ()
+
+@property Account* privateAccount;
+
+@property (assign) IBOutlet NSTextField *aliasTextField;
+@property (assign) IBOutlet NSTextField *typeLabel;
+@property (assign) IBOutlet NSTextField *bootstrapField;
+@property (assign) IBOutlet NSTextField *hashField;
+
+@property (assign) IBOutlet NSButton *upnpButton;
+@property (assign) IBOutlet NSButton *autoAnswerButton;
+@property (assign) IBOutlet NSButton *userAgentButton;
+@property (assign) IBOutlet NSTextField *userAgentTextField;
+
+@end
+
+@implementation AccRingVC
+@synthesize privateAccount;
+@synthesize typeLabel;
+@synthesize bootstrapField;
+@synthesize hashField;
+@synthesize aliasTextField;
+@synthesize upnpButton;
+@synthesize autoAnswerButton;
+@synthesize userAgentButton;
+@synthesize userAgentTextField;
+
+- (void)awakeFromNib
+{
+ NSLog(@"INIT Ring VC");
+ [aliasTextField setTag:ALIAS_TAG];
+ [userAgentTextField setTag:USERAGENT_TAG];
+ [bootstrapField setTag:HOSTNAME_TAG];
+}
+
+- (void)loadAccount:(Account *)account
+{
+ if(privateAccount == account)
+ return;
+
+ privateAccount = account;
+
+ [self.aliasTextField setStringValue:account->alias().toNSString()];
+
+ switch (account->protocol()) {
+ case Account::Protocol::SIP:
+ [typeLabel setStringValue:@"SIP"];
+ break;
+ case Account::Protocol::IAX:
+ [typeLabel setStringValue:@"IAX"];
+ break;
+ case Account::Protocol::RING:
+ [typeLabel setStringValue:@"RING"];
+ break;
+
+ default:
+ break;
+ }
+
+ [upnpButton setState:privateAccount->isUpnpEnabled()];
+ [userAgentButton setState:privateAccount->hasCustomUserAgent()];
+ [userAgentTextField setEnabled:privateAccount->hasCustomUserAgent()];
+
+ [autoAnswerButton setState:privateAccount->isAutoAnswer()];
+ [userAgentTextField setStringValue:account->userAgent().toNSString()];
+
+ [bootstrapField setStringValue:account->hostname().toNSString()];
+
+ if([privateAccount->username().toNSString() isEqualToString:@""])
+ [hashField setStringValue:@"Reopen account to see your hash"];
+ else
+ [hashField setStringValue:privateAccount->username().toNSString()];
+
+}
+
+- (IBAction)toggleUpnp:(NSButton *)sender {
+ privateAccount->setUpnpEnabled([sender state] == NSOnState);
+}
+
+- (IBAction)toggleAutoAnswer:(NSButton *)sender {
+ privateAccount->setAutoAnswer([sender state] == NSOnState);
+}
+
+- (IBAction)toggleCustomAgent:(NSButton *)sender {
+ [self.userAgentTextField setEnabled:[sender state] == NSOnState];
+ privateAccount->setHasCustomUserAgent([sender state] == NSOnState);
+}
+
+#pragma mark - NSTextFieldDelegate methods
+
+- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor
+{
+ return YES;
+}
+
+-(void)controlTextDidChange:(NSNotification *)notif
+{
+ NSTextField *textField = [notif object];
+
+ switch ([textField tag]) {
+ case ALIAS_TAG:
+ privateAccount->setAlias([[textField stringValue] UTF8String]);
+ break;
+ case HOSTNAME_TAG:
+ privateAccount->setHostname([[textField stringValue] UTF8String]);
+ break;
+ case PASSWORD_TAG:
+ privateAccount->setPassword([[textField stringValue] UTF8String]);
+ break;
+ case USERAGENT_TAG:
+ privateAccount->setUserAgent([[textField stringValue] UTF8String]);
+ break;
+ default:
+ break;
+ }
+}
+
+@end
diff --git a/src/AccSecurityVC.h b/src/AccSecurityVC.h
new file mode 100644
index 0000000..952a8de
--- /dev/null
+++ b/src/AccSecurityVC.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef ACCSECURITYVC_H
+#define ACCSECURITYVC_H
+
+#import <Cocoa/Cocoa.h>
+
+#import <account.h>
+
+@interface AccSecurityVC : NSViewController<NSPathControlDelegate, NSOpenSavePanelDelegate> {
+
+}
+
+- (void)loadAccount:(Account *)account;
+
+@end
+
+#endif // ACCSECURITYVC_H
\ No newline at end of file
diff --git a/src/AccSecurityVC.mm b/src/AccSecurityVC.mm
new file mode 100644
index 0000000..6302cbe
--- /dev/null
+++ b/src/AccSecurityVC.mm
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "AccSecurityVC.h"
+
+@interface AccSecurityVC ()
+
+@property Account* privateAccount;
+
+@end
+
+@implementation AccSecurityVC
+@synthesize privateAccount;
+
+- (void)awakeFromNib
+{
+ NSLog(@"INIT Security VC");
+}
+
+- (void)loadAccount:(Account *)account
+{
+ privateAccount = account;
+}
+
+#pragma mark - NSPathControl delegate methods
+
+/*
+ Delegate method of NSPathControl to determine how the NSOpenPanel will look/behave.
+ */
+- (void)pathControl:(NSPathControl *)pathControl willDisplayOpenPanel:(NSOpenPanel *)openPanel
+{
+ NSLog(@"willDisplayOpenPanel");
+ [openPanel setAllowsMultipleSelection:NO];
+ [openPanel setCanChooseDirectories:NO];
+ [openPanel setCanChooseFiles:YES];
+ [openPanel setResolvesAliases:YES];
+ [openPanel setTitle:NSLocalizedString(@"Choose a file", @"Open panel title")];
+ [openPanel setPrompt:NSLocalizedString(@"Choose", @"Open panel prompt for 'Choose a file'")];
+ [openPanel setDelegate:self];
+}
+
+- (void)pathControl:(NSPathControl *)pathControl willPopUpMenu:(NSMenu *)menu
+{
+
+}
+
+#pragma mark - NSOpenSavePanelDelegate delegate methods
+
+- (void)panel:(id)sender willExpand:(BOOL)expanding
+{
+ //NSLog(@"willExpand");
+}
+
+- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag
+{
+ //NSLog(@"userEnteredFilename");
+}
+
+- (void)panelSelectionDidChange:(id)sender
+{
+ //NSLog(@"panelSelectionDidChange");
+}
+
+- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError
+{
+ NSLog(@"validateURL");
+ return YES;
+
+}
+
+- (void)panel:(id)sender didChangeToDirectoryURL:(NSURL *)url
+{
+ //NSLog(@"didChangeToDirectoryURL");
+}
+
+@end
diff --git a/src/AccVideoVC.h b/src/AccVideoVC.h
new file mode 100644
index 0000000..b7fa537
--- /dev/null
+++ b/src/AccVideoVC.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef ACCVIDEOVC_H
+#define ACCVIDEOVC_H
+
+#import <Cocoa/Cocoa.h>
+
+#import <account.h>
+
+@interface AccVideoVC : NSViewController <NSOutlineViewDelegate> {
+
+}
+
+- (void)loadAccount:(Account *)account;
+
+@end
+
+#endif // ACCVIDEOVC_H
\ No newline at end of file
diff --git a/src/AccVideoVC.mm b/src/AccVideoVC.mm
new file mode 100644
index 0000000..7d3ebe5
--- /dev/null
+++ b/src/AccVideoVC.mm
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#define COLUMNID_STATE @"VideoStateColumn"
+#define COLUMNID_CODECS @"VideoCodecsColumn"
+#define COLUMNID_FREQ @"VideoFrequencyColumn"
+#define COLUMNID_BITRATE @"VideoBitrateColumn"
+
+#import "AccVideoVC.h"
+
+#include <QtCore/QSortFilterProxyModel>
+#import <audio/codecmodel.h>
+#import <accountmodel.h>
+
+#import "QNSTreeController.h"
+
+@interface AccVideoVC ()
+
+@property Account* privateAccount;
+@property QNSTreeController *treeController;
+@property (assign) IBOutlet NSOutlineView *codecsView;
+@property (assign) IBOutlet NSView *videoPanelContainer;
+@property (assign) IBOutlet NSButton *toggleVideoButton;
+
+@end
+
+@implementation AccVideoVC
+@synthesize treeController;
+@synthesize codecsView;
+@synthesize privateAccount;
+@synthesize videoPanelContainer;
+@synthesize toggleVideoButton;
+
+- (void)awakeFromNib
+{
+ NSLog(@"INIT Video VC");
+}
+
+- (void)loadAccount:(Account *)account
+{
+ privateAccount = account;
+
+ treeController = [[QNSTreeController alloc] initWithQModel:privateAccount->codecModel()->videoCodecs()];
+ [treeController setAvoidsEmptySelection:NO];
+ [treeController setChildrenKeyPath:@"children"];
+
+ [codecsView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
+ [codecsView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
+ [codecsView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
+ [videoPanelContainer setHidden:!privateAccount->isVideoEnabled()];
+ [toggleVideoButton setState:privateAccount->isVideoEnabled()?NSOnState:NSOffState];
+}
+
+- (IBAction)toggleVideoEnabled:(id)sender {
+ privateAccount->setVideoEnabled([sender state] == NSOnState);
+ [videoPanelContainer setHidden:!privateAccount->isVideoEnabled()];
+}
+
+- (IBAction)toggleCodec:(NSOutlineView*)sender {
+ NSInteger row = [sender clickedRow];
+ NSTableColumn *col = [sender tableColumnWithIdentifier:COLUMNID_STATE];
+ NSButtonCell *cell = [col dataCellForRow:row];
+ privateAccount->codecModel()->videoCodecs()->setData(privateAccount->codecModel()->videoCodecs()->index(row, 0, QModelIndex()),
+ cell.state == NSOnState ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole);
+}
+
+- (IBAction)moveUp:(id)sender {
+
+ if([[treeController selectedNodes] count] > 0) {
+ QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
+ if(!qIdx.isValid())
+ return;
+
+ QMimeData* mime = privateAccount->codecModel()->mimeData(QModelIndexList() << qIdx);
+ privateAccount->codecModel()->dropMimeData(mime, Qt::MoveAction, qIdx.row() - 1, 0, QModelIndex());
+ }
+}
+
+- (IBAction)moveDown:(id)sender {
+ if([[treeController selectedNodes] count] > 0) {
+ QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
+ if(!qIdx.isValid())
+ return;
+
+ QMimeData* mime = privateAccount->codecModel()->mimeData(QModelIndexList() << qIdx);
+ privateAccount->codecModel()->dropMimeData(mime, Qt::MoveAction, qIdx.row() + 1, 0, QModelIndex());
+ }
+}
+
+#pragma mark - NSOutlineViewDelegate methods
+
+// -------------------------------------------------------------------------------
+// shouldSelectItem:item
+// -------------------------------------------------------------------------------
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
+{
+ return YES;
+}
+
+// -------------------------------------------------------------------------------
+// dataCellForTableColumn:tableColumn:item
+// -------------------------------------------------------------------------------
+- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ NSCell *returnCell = [tableColumn dataCell];
+
+ if(item == nil)
+ return returnCell;
+ return returnCell;
+}
+
+// -------------------------------------------------------------------------------
+// textShouldEndEditing:fieldEditor
+// -------------------------------------------------------------------------------
+- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
+{
+ if ([[fieldEditor string] length] == 0)
+ {
+ // don't allow empty node names
+ return NO;
+ }
+ else
+ {
+ return YES;
+ }
+}
+
+// -------------------------------------------------------------------------------
+// shouldEditTableColumn:tableColumn:item
+//
+// Decide to allow the edit of the given outline view "item".
+// -------------------------------------------------------------------------------
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ return NO;
+}
+
+// -------------------------------------------------------------------------------
+// outlineView:willDisplayCell:forTableColumn:item
+// -------------------------------------------------------------------------------
+- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
+ if(!qIdx.isValid())
+ return;
+
+ if([[tableColumn identifier] isEqualToString:COLUMNID_STATE]) {
+ [cell setState:privateAccount->codecModel()->videoCodecs()->data(qIdx, Qt::CheckStateRole).value<BOOL>()?NSOnState:NSOffState];
+ } else if ([[tableColumn identifier] isEqualToString:COLUMNID_CODECS])
+ {
+ cell.title = privateAccount->codecModel()->videoCodecs()->data(qIdx, CodecModel::Role::NAME).toString().toNSString();
+ [cell setState:privateAccount->codecModel()->videoCodecs()->data(qIdx, Qt::CheckStateRole).value<BOOL>()?NSOnState:NSOffState];
+ } else if ([[tableColumn identifier] isEqualToString:COLUMNID_FREQ])
+ {
+ cell.title = privateAccount->codecModel()->videoCodecs()->data(qIdx, CodecModel::Role::SAMPLERATE).toString().toNSString();
+ } else if ([[tableColumn identifier] isEqualToString:COLUMNID_BITRATE])
+ {
+ cell.title = privateAccount->codecModel()->videoCodecs()->data(qIdx, CodecModel::Role::BITRATE).toString().toNSString();
+ }
+}
+
+// -------------------------------------------------------------------------------
+// outlineViewSelectionDidChange:notification
+// -------------------------------------------------------------------------------
+- (void)outlineViewSelectionDidChange:(NSNotification *)notification
+{
+ // ask the tree controller for the current selection
+}
+
+@end
diff --git a/src/AccountsVC.h b/src/AccountsVC.h
new file mode 100644
index 0000000..f79fc3e
--- /dev/null
+++ b/src/AccountsVC.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef ACCOUNTSVC_H
+#define ACCOUNTSVC_H
+
+#import <Cocoa/Cocoa.h>
+
+@interface AccountsVC : NSViewController <NSOutlineViewDelegate, NSMenuDelegate> {
+
+}
+
+@end
+
+#endif // ACCOUNTSVC_H
diff --git a/src/AccountsVC.mm b/src/AccountsVC.mm
new file mode 100644
index 0000000..3bbd086
--- /dev/null
+++ b/src/AccountsVC.mm
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#define COLUMNID_ENABLE @"EnableColumn"
+#define COLUMNID_NAME @"NameColumn"
+#define COLUMNID_STATE @"StateColumn"
+
+#import "AccountsVC.h"
+
+// LibRingClient
+#import <QSortFilterProxyModel>
+#import <accountmodel.h>
+#import <protocolmodel.h>
+#import <QItemSelectionModel>
+#import <account.h>
+
+#import "QNSTreeController.h"
+#import "AccGeneralVC.h"
+#import "AccAudioVC.h"
+#import "AccVideoVC.h"
+#import "AccAdvancedVC.h"
+#import "AccSecurityVC.h"
+#import "AccRingVC.h"
+
+// We disabled IAX protocol for now, so don't show it to the user
+class ActiveProtocolModel : public QSortFilterProxyModel
+{
+public:
+ ActiveProtocolModel(QAbstractItemModel* parent) : QSortFilterProxyModel(parent)
+ {
+ setSourceModel(parent);
+ }
+ virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
+ {
+ return sourceModel()->index(source_row,0,source_parent).flags() & Qt::ItemIsEnabled;
+ }
+};
+
+@interface AccountsVC ()
+@property (assign) IBOutlet NSPopUpButton *protocolList;
+
+@property (assign) IBOutlet NSTabView *configPanels;
+@property (retain) IBOutlet NSTabViewItem *generalTabItem;
+@property (retain) IBOutlet NSTabViewItem *audioTabItem;
+@property (retain) IBOutlet NSTabViewItem *videoTabItem;
+@property (retain) IBOutlet NSTabViewItem *advancedTabItem;
+@property (retain) IBOutlet NSTabViewItem *securityTabItem;
+@property (retain) IBOutlet NSTabViewItem *ringTabItem;
+
+@property QNSTreeController *treeController;
+@property ActiveProtocolModel* proxyProtocolModel;
+@property (assign) IBOutlet NSOutlineView *accountsListView;
+@property (assign) IBOutlet NSTabView *accountDetailsView;
+
+@property AccRingVC* ringVC;
+@property AccGeneralVC* generalVC;
+@property AccAudioVC* audioVC;
+@property AccVideoVC* videoVC;
+@property AccAdvancedVC* advancedVC;
+@property AccSecurityVC* securityVC;
+
+@end
+
+@implementation AccountsVC
+@synthesize protocolList;
+@synthesize configPanels;
+@synthesize generalTabItem;
+@synthesize audioTabItem;
+@synthesize videoTabItem;
+@synthesize advancedTabItem;
+@synthesize securityTabItem;
+@synthesize ringTabItem;
+@synthesize accountsListView;
+@synthesize accountDetailsView;
+@synthesize treeController;
+@synthesize proxyProtocolModel;
+
+- (void)awakeFromNib
+{
+ treeController = [[QNSTreeController alloc] initWithQModel:AccountModel::instance()];
+ [treeController setAvoidsEmptySelection:NO];
+ [treeController setAlwaysUsesMultipleValuesMarker:YES];
+ [treeController setChildrenKeyPath:@"children"];
+
+ [accountsListView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
+ [accountsListView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
+ [accountsListView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
+
+
+ QObject::connect(AccountModel::instance(),
+ &QAbstractItemModel::dataChanged,
+ [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
+ NSLog(@"data changed %d, %d", topLeft.row(), bottomRight.row());
+
+ [accountsListView reloadDataForRowIndexes:
+ [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(topLeft.row(), bottomRight.row() + 1)]
+ columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, accountsListView.tableColumns.count)]];
+
+ });
+
+ self.proxyProtocolModel = new ActiveProtocolModel(AccountModel::instance()->protocolModel());
+ QModelIndex qProtocolIdx = AccountModel::instance()->protocolModel()->selectionModel()->currentIndex();
+ [self.protocolList addItemWithTitle:
+ AccountModel::instance()->protocolModel()->data(qProtocolIdx, Qt::DisplayRole).toString().toNSString()];
+
+ self.generalVC = [[AccGeneralVC alloc] initWithNibName:@"AccGeneral" bundle:nil];
+ [[self.generalVC view] setFrame:[self.generalTabItem.view frame]];
+ [[self.generalVC view] setBounds:[self.generalTabItem.view bounds]];
+ [self.generalTabItem setView:self.generalVC.view];
+
+ self.audioVC = [[AccAudioVC alloc] initWithNibName:@"AccAudio" bundle:nil];
+ [[self.audioVC view] setFrame:[self.audioTabItem.view frame]];
+ [[self.audioVC view] setBounds:[self.audioTabItem.view bounds]];
+ [self.audioTabItem setView:self.audioVC.view];
+
+ self.videoVC = [[AccVideoVC alloc] initWithNibName:@"AccVideo" bundle:nil];
+ [[self.videoVC view] setFrame:[self.videoTabItem.view frame]];
+ [[self.videoVC view] setBounds:[self.videoTabItem.view bounds]];
+ [self.videoTabItem setView:self.videoVC.view];
+
+ self.advancedVC = [[AccAdvancedVC alloc] initWithNibName:@"AccAdvanced" bundle:nil];
+ [[self.advancedVC view] setFrame:[self.advancedTabItem.view frame]];
+ [[self.advancedVC view] setBounds:[self.advancedTabItem.view bounds]];
+ [self.advancedTabItem setView:self.advancedVC.view];
+
+ self.securityVC = [[AccSecurityVC alloc] initWithNibName:@"AccSecurity" bundle:nil];
+ [[self.securityVC view] setFrame:[self.securityTabItem.view frame]];
+ [[self.securityVC view] setBounds:[self.securityTabItem.view bounds]];
+ [self.securityTabItem setView:self.securityVC.view];
+
+ self.ringVC = [[AccRingVC alloc] initWithNibName:@"AccRing" bundle:nil];
+ [[self.ringVC view] setFrame:[self.ringTabItem.view frame]];
+ [[self.ringVC view] setBounds:[self.ringTabItem.view bounds]];
+ [self.ringTabItem setView:self.ringVC.view];
+}
+
+- (IBAction)moveUp:(id)sender {
+ AccountModel::instance()->moveUp();
+}
+
+- (IBAction)moveDown:(id)sender {
+ AccountModel::instance()->moveDown();
+}
+
+- (IBAction)removeAccount:(id)sender {
+
+ if(treeController.selectedNodes.count > 0) {
+ QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
+ AccountModel::instance()->remove(qIdx);
+ AccountModel::instance()->save();
+ }
+}
+- (IBAction)addAccount:(id)sender {
+ QModelIndex qIdx = AccountModel::instance()->protocolModel()->selectionModel()->currentIndex();
+
+ NSString* newAccName = [[NSString alloc] initWithFormat:@"%@ account",
+ AccountModel::instance()->protocolModel()->data(qIdx, Qt::DisplayRole).toString().toNSString(), nil];
+
+ Account* newAcc =AccountModel::instance()->add([newAccName UTF8String], qIdx);
+ AccountModel::instance()->save();
+}
+
+- (IBAction)protocolSelectedChanged:(id)sender {
+
+ int index = [sender indexOfSelectedItem];
+ QModelIndex proxyIdx = proxyProtocolModel->index(index, 0);
+ AccountModel::instance()->protocolModel()->selectionModel()->setCurrentIndex(
+ proxyProtocolModel->mapToSource(proxyIdx), QItemSelectionModel::ClearAndSelect);
+
+}
+
+- (void) setupSIPPanelsForAccount:(Account*) acc
+{
+ NSTabViewItem* selected = [configPanels selectedTabViewItem];
+
+ // Start by removing all tabs
+ for(NSTabViewItem* item in configPanels.tabViewItems) {
+ [configPanels removeTabViewItem:item];
+ }
+
+ [configPanels insertTabViewItem:generalTabItem atIndex:0];
+ [configPanels insertTabViewItem:audioTabItem atIndex:1];
+ [configPanels insertTabViewItem:videoTabItem atIndex:2];
+ //[configPanels insertTabViewItem:advancedTabItem atIndex:3];
+ //[configPanels insertTabViewItem:securityTabItem atIndex:4];
+
+ [self.generalVC loadAccount:acc];
+ [self.audioVC loadAccount:acc];
+ [self.videoVC loadAccount:acc];
+ [self.advancedVC loadAccount:acc];
+ [self.securityVC loadAccount:acc];
+}
+
+- (void) setupIAXPanelsForAccount:(Account*) acc
+{
+ NSTabViewItem* selected = [configPanels selectedTabViewItem];
+
+ // Start by removing all tabs
+ for(NSTabViewItem* item in configPanels.tabViewItems) {
+ [configPanels removeTabViewItem:item];
+ }
+
+ [configPanels insertTabViewItem:generalTabItem atIndex:0];
+ [configPanels insertTabViewItem:audioTabItem atIndex:1];
+ [configPanels insertTabViewItem:videoTabItem atIndex:2];
+
+ [self.generalVC loadAccount:acc];
+ [self.audioVC loadAccount:acc];
+ [self.videoVC loadAccount:acc];
+}
+
+- (void) setupRINGPanelsForAccount:(Account*) acc
+{
+ NSTabViewItem* selected = [configPanels selectedTabViewItem];
+
+ // Start by removing all tabs
+ for(NSTabViewItem* item in configPanels.tabViewItems) {
+ [configPanels removeTabViewItem:item];
+ }
+
+ [configPanels insertTabViewItem:ringTabItem atIndex:0];
+ [configPanels insertTabViewItem:audioTabItem atIndex:1];
+ [configPanels insertTabViewItem:videoTabItem atIndex:2];
+ //[configPanels insertTabViewItem:advancedTabItem atIndex:3];
+ //[configPanels insertTabViewItem:securityTabItem atIndex:4];
+
+ [self.ringVC loadAccount:acc];
+ [self.audioVC loadAccount:acc];
+ [self.videoVC loadAccount:acc];
+ [self.advancedVC loadAccount:acc];
+ [self.securityVC loadAccount:acc];
+}
+
+- (IBAction)toggleAccount:(NSOutlineView*)sender {
+
+ if([sender clickedColumn] < 0)
+ return;
+
+ NSTableColumn* col = [sender.tableColumns objectAtIndex:[sender clickedColumn]];
+ if([col.identifier isEqualToString:COLUMNID_ENABLE]) {
+ NSInteger row = [sender clickedRow];
+ QModelIndex accIdx = AccountModel::instance()->index(row);
+ Account* toToggle = AccountModel::instance()->getAccountByModelIndex(accIdx);
+ NSButtonCell *cell = [col dataCellForRow:row];
+ toToggle->setEnabled(cell.state == NSOnState ? NO : YES);
+ }
+}
+
+#pragma mark - NSOutlineViewDelegate methods
+
+// -------------------------------------------------------------------------------
+// shouldSelectItem:item
+// -------------------------------------------------------------------------------
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
+{
+ return YES;
+}
+
+// -------------------------------------------------------------------------------
+// dataCellForTableColumn:tableColumn:item
+// -------------------------------------------------------------------------------
+- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ NSCell *returnCell = [tableColumn dataCell];
+ return returnCell;
+}
+
+// -------------------------------------------------------------------------------
+// textShouldEndEditing:fieldEditor
+// -------------------------------------------------------------------------------
+- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
+{
+ if ([[fieldEditor string] length] == 0)
+ {
+ // don't allow empty node names
+ return NO;
+ }
+ else
+ {
+ return YES;
+ }
+}
+
+// -------------------------------------------------------------------------------
+// shouldEditTableColumn:tableColumn:item
+//
+// Decide to allow the edit of the given outline view "item".
+// -------------------------------------------------------------------------------
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ return NO;
+}
+
+// -------------------------------------------------------------------------------
+// outlineView:willDisplayCell:forTableColumn:item
+// -------------------------------------------------------------------------------
+- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
+ if(!qIdx.isValid())
+ return;
+
+ if ([[tableColumn identifier] isEqualToString:COLUMNID_NAME])
+ {
+ cell.title = AccountModel::instance()->data(qIdx, Qt::DisplayRole).toString().toNSString();
+ } else if([[tableColumn identifier] isEqualToString:COLUMNID_STATE]) {
+ Account::RegistrationState state = qvariant_cast<Account::RegistrationState>(AccountModel::instance()->data(qIdx, (int)Account::Role::RegistrationState));
+ switch (state) {
+ case Account::RegistrationState::READY:
+ [cell setTitle:@"Ready"];
+ break;
+ case Account::RegistrationState::TRYING:
+ [cell setTitle:@"Trying..."];
+ break;
+ case Account::RegistrationState::UNREGISTERED:
+ [cell setTitle:@"Unregistered"];
+ break;
+ case Account::RegistrationState::ERROR:
+ [cell setTitle:@"Error"];
+ break;
+ default:
+ break;
+ }
+ } else if([[tableColumn identifier] isEqualToString:COLUMNID_ENABLE]) {
+ [cell setState:AccountModel::instance()->data(qIdx, Qt::CheckStateRole).value<BOOL>()];
+ }
+}
+
+// -------------------------------------------------------------------------------
+// outlineViewSelectionDidChange:notification
+// -------------------------------------------------------------------------------
+- (void)outlineViewSelectionDidChange:(NSNotification *)notification
+{
+ // ask the tree controller for the current selection
+ if([[treeController selectedNodes] count] > 0) {
+ QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
+ //Update details view
+ AccountModel::instance()->selectionModel()->setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
+ Account* acc = AccountModel::instance()->getAccountByModelIndex(qIdx);
+
+ switch (acc->protocol()) {
+ case Account::Protocol::SIP:
+ NSLog(@"SIP");
+ [self setupSIPPanelsForAccount:acc];
+ break;
+ case Account::Protocol::IAX:
+ NSLog(@"IAX");
+ [self setupIAXPanelsForAccount:acc];
+ break;
+ case Account::Protocol::RING:
+ [self setupRINGPanelsForAccount:acc];
+ NSLog(@"DRING");
+ break;
+ default:
+ break;
+ }
+
+
+ [self.accountDetailsView setHidden:NO];
+ } else {
+ [self.accountDetailsView setHidden:YES];
+ AccountModel::instance()->selectionModel()->clearCurrentIndex();
+ }
+}
+
+#pragma mark - NSMenuDelegate methods
+
+- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel
+{
+ QModelIndex proxyIdx = proxyProtocolModel->index(index, 0);
+ QModelIndex qIdx = AccountModel::instance()->protocolModel()->index(proxyProtocolModel->mapToSource(proxyIdx).row());
+ [item setTitle:AccountModel::instance()->protocolModel()->data(qIdx, Qt::DisplayRole).toString().toNSString()];
+
+ return YES;
+}
+
+- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
+{
+ return proxyProtocolModel->rowCount();
+}
+
+
+
+@end
diff --git a/src/AppDelegate.h b/src/AppDelegate.h
new file mode 100644
index 0000000..a0b6053
--- /dev/null
+++ b/src/AppDelegate.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef APPDELEGATE_H
+#define APPDELEGATE_H
+
+#import <AppKit/NSApplication.h> // NSApplicationDelegate
+
+#import "RingWindowController.h"
+#import "PreferencesWindowController.h"
+
+@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate>
+
+- (void) showWizard;
+- (void) showMainWindow;
+
+@end
+
+#endif // APPDELEGATE_H
diff --git a/src/AppDelegate.mm b/src/AppDelegate.mm
new file mode 100644
index 0000000..b73e0e5
--- /dev/null
+++ b/src/AppDelegate.mm
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "AppDelegate.h"
+
+#import <callmodel.h>
+
+#import <accountmodel.h>
+#import <protocolmodel.h>
+#import <QItemSelectionModel>
+#import <account.h>
+
+#import "RingWizardWC.h"
+
+@interface AppDelegate()
+
+@property RingWindowController* ringWindowController;
+@property RingWizardWC* wizard;
+
+@end
+
+@implementation AppDelegate
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints"];
+
+
+ [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
+
+ if([self checkForRingAccount]) {
+ [self showMainWindow];
+ } else {
+ [self showWizard];
+ }
+ [self connect];
+}
+
+- (void) connect
+{
+ QObject::connect(CallModel::instance(),
+ &CallModel::incomingCall,
+ [=](Call* call) {
+ BOOL shouldComeToForeground = [[NSUserDefaults standardUserDefaults] boolForKey:@"window_behaviour"];
+ BOOL shouldNotify = [[NSUserDefaults standardUserDefaults] boolForKey:@"enable_notifications"];
+ if(shouldComeToForeground)
+ [NSApp activateIgnoringOtherApps:YES];
+
+ if(shouldNotify) {
+ [self showIncomingNotification:call];
+ }
+ });
+}
+
+- (void) showIncomingNotification:(Call*) call{
+ NSUserNotification *notification = [[NSUserNotification alloc] init];
+ notification.title = @"Incoming call", call->peerName();
+ //notification.informativeText = @"A notification";
+ notification.soundName = NSUserNotificationDefaultSoundName;
+
+ [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
+}
+
+/**
+ * click in MainMenu "Setup Ring"
+ */
+- (IBAction)showWizard:(id)sender {
+ [self showWizard];
+}
+
+- (void) showWizard
+{
+ NSLog(@"Showing wizard");
+ if(self.wizard == nil) {
+ self.wizard = [[RingWizardWC alloc] initWithWindowNibName:@"RingWizard"];
+ }
+ [self.wizard.window orderFront:self];
+}
+
+- (void) showMainWindow
+{
+ if(self.ringWindowController == nil)
+ self.ringWindowController = [[RingWindowController alloc] initWithWindowNibName:@"RingWindow"];
+
+ [self.ringWindowController.window makeKeyAndOrderFront:self];
+}
+
+- (BOOL) checkForRingAccount
+{
+ for (int i = 0 ; i < AccountModel::instance()->rowCount() ; ++i) {
+ QModelIndex idx = AccountModel::instance()->index(i);
+ Account* acc = AccountModel::instance()->getAccountByModelIndex(idx);
+ if(acc->protocol() == Account::Protocol::RING) {
+ return YES;
+ }
+ }
+ return FALSE;
+}
+
+- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
+{
+ if([self checkForRingAccount]) {
+ [self showMainWindow];
+ } else {
+ [self showWizard];
+ }
+ return YES;
+}
+
+@end
diff --git a/src/AudioPrefsVC.h b/src/AudioPrefsVC.h
new file mode 100644
index 0000000..024d955
--- /dev/null
+++ b/src/AudioPrefsVC.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef AUDIOPREFSVC_H
+#define AUDIOPREFSVC_H
+
+#import <Cocoa/Cocoa.h>
+
+@interface AudioPrefsVC : NSViewController <NSMenuDelegate, NSPathControlDelegate, NSOpenSavePanelDelegate> {
+
+}
+
+@end
+
+#endif // AUDIOPREFSVC_H
diff --git a/src/AudioPrefsVC.mm b/src/AudioPrefsVC.mm
new file mode 100644
index 0000000..16700a6
--- /dev/null
+++ b/src/AudioPrefsVC.mm
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "AudioPrefsVC.h"
+
+#import <audio/settings.h>
+#import <QUrl>
+#import <audio/inputdevicemodel.h>
+#import <audio/outputdevicemodel.h>
+#import <qitemselectionmodel.h>
+
+@interface AudioPrefsVC ()
+
+@property (assign) IBOutlet NSPathControl *recordingsPathControl;
+@property (assign) IBOutlet NSPopUpButton *outputDeviceList;
+@property (assign) IBOutlet NSPopUpButton *inputDeviceList;
+@property (assign) IBOutlet NSButton *alwaysRecordingButton;
+@property (assign) IBOutlet NSButton *muteDTMFButton;
+
+@end
+
+@implementation AudioPrefsVC
+@synthesize recordingsPathControl;
+@synthesize outputDeviceList;
+@synthesize inputDeviceList;
+@synthesize alwaysRecordingButton;
+@synthesize muteDTMFButton;
+
+- (void)loadView
+{
+ [super loadView];
+
+ QModelIndex qInputIdx = Audio::Settings::instance()->inputDeviceModel()->selectionModel()->currentIndex();
+ QModelIndex qOutputIdx = Audio::Settings::instance()->outputDeviceModel()->selectionModel()->currentIndex();
+
+ [self.outputDeviceList addItemWithTitle:
+ Audio::Settings::instance()->outputDeviceModel()->data(qOutputIdx, Qt::DisplayRole).toString().toNSString()];
+
+ [self.inputDeviceList addItemWithTitle:
+ Audio::Settings::instance()->inputDeviceModel()->data(qInputIdx, Qt::DisplayRole).toString().toNSString()];
+ [self.alwaysRecordingButton setState:
+ Audio::Settings::instance()->isAlwaysRecording()?NSOnState:NSOffState];
+
+ [self.muteDTMFButton setState:
+ Audio::Settings::instance()->areDTMFMuted()?NSOnState:NSOffState];
+
+ if([[Audio::Settings::instance()->recordPath().toNSURL() absoluteString] isEqualToString:@""]) {
+ NSArray * pathComponentArray = [self pathComponentArray];
+ [recordingsPathControl setPathComponentCells:pathComponentArray];
+ }
+}
+
+- (IBAction)toggleMuteDTMF:(NSButton *)sender
+{
+ Audio::Settings::instance()->setDTMFMuted([sender state] == NSOnState);
+}
+
+- (IBAction)toggleAlwaysRecording:(NSButton *)sender
+{
+ Audio::Settings::instance()->setAlwaysRecording([sender state] == NSOnState);
+}
+
+- (IBAction)pathControlSingleClick:(id)sender {
+ // Select that chosen component of the path.
+ [self.recordingsPathControl setURL:[[self.recordingsPathControl clickedPathComponentCell] URL]];
+ Audio::Settings::instance()->setRecordPath(QUrl::fromNSURL(self.recordingsPathControl.URL));
+}
+
+- (IBAction)chooseOutput:(id)sender {
+ int index = [sender indexOfSelectedItem];
+ QModelIndex qIdx = Audio::Settings::instance()->outputDeviceModel()->index(index, 0);
+ Audio::Settings::instance()->outputDeviceModel()->selectionModel()->setCurrentIndex(
+ qIdx, QItemSelectionModel::ClearAndSelect);
+}
+
+- (IBAction)chooseInput:(id)sender {
+ int index = [sender indexOfSelectedItem];
+ QModelIndex qIdx = Audio::Settings::instance()->inputDeviceModel()->index(index, 0);
+ Audio::Settings::instance()->inputDeviceModel()->selectionModel()->setCurrentIndex(
+ qIdx, QItemSelectionModel::ClearAndSelect);
+}
+
+#pragma mark - NSPathControl delegate methods
+
+/*
+ Assemble a set of custom cells to display into an array to pass to the path control.
+ */
+- (NSArray *)pathComponentArray
+{
+ NSMutableArray *pathComponentArray = [[NSMutableArray alloc] init];
+
+ NSFileManager *fileManager = [[NSFileManager alloc] init];
+
+ NSURL* desktopURL = [fileManager URLForDirectory:NSDesktopDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
+ NSURL* documentsURL = [fileManager URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
+ NSURL* userURL = [fileManager URLForDirectory:NSUserDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
+
+ NSPathComponentCell *componentCell;
+
+ // Use utility method to obtain a NSPathComponentCell based on icon, title and URL.
+ componentCell = [self componentCellForType:kGenericFolderIcon withTitle:@"Desktop" URL:desktopURL];
+ [pathComponentArray addObject:componentCell];
+
+ componentCell = [self componentCellForType:kGenericFolderIcon withTitle:@"Documents" URL:documentsURL];
+ [pathComponentArray addObject:componentCell];
+
+ componentCell = [self componentCellForType:kUserFolderIcon withTitle:NSUserName() URL:userURL];
+ [pathComponentArray addObject:componentCell];
+
+ return pathComponentArray;
+}
+
+/*
+ This method is used by pathComponentArray to create a NSPathComponent cell based on icon, title and URL information.
+ Each path component needs an icon, URL and title.
+ */
+- (NSPathComponentCell *)componentCellForType:(OSType)withIconType withTitle:(NSString *)title URL:(NSURL *)url
+{
+ NSPathComponentCell *componentCell = [[NSPathComponentCell alloc] init];
+
+ NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(withIconType)];
+ [componentCell setImage:iconImage];
+ [componentCell setURL:url];
+ [componentCell setTitle:title];
+
+ return componentCell;
+}
+
+/*
+ Delegate method of NSPathControl to determine how the NSOpenPanel will look/behave.
+ */
+- (void)pathControl:(NSPathControl *)pathControl willDisplayOpenPanel:(NSOpenPanel *)openPanel
+{
+ NSLog(@"willDisplayOpenPanel");
+ [openPanel setAllowsMultipleSelection:NO];
+ [openPanel setCanChooseDirectories:YES];
+ [openPanel setCanChooseFiles:NO];
+ [openPanel setResolvesAliases:YES];
+ [openPanel setTitle:NSLocalizedString(@"Choose a file", @"Open panel title")];
+ [openPanel setPrompt:NSLocalizedString(@"Choose", @"Open panel prompt for 'Choose a directory'")];
+ [openPanel setDelegate:self];
+}
+
+- (void)pathControl:(NSPathControl *)pathControl willPopUpMenu:(NSMenu *)menu
+{
+
+}
+
+#pragma mark - NSOpenSavePanelDelegate delegate methods
+
+- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError
+{
+ [recordingsPathControl setURL:url];
+ return YES;
+}
+
+#pragma mark - NSMenuDelegate methods
+
+- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel
+{
+ QModelIndex qIdx;
+
+ if([menu.title isEqualToString:@"inputlist"])
+ {
+ qIdx = Audio::Settings::instance()->inputDeviceModel()->index(index);
+ [item setTitle:Audio::Settings::instance()->inputDeviceModel()->data(qIdx, Qt::DisplayRole).toString().toNSString()];
+ } else
+ {
+ qIdx = Audio::Settings::instance()->outputDeviceModel()->index(index);
+ [item setTitle:Audio::Settings::instance()->outputDeviceModel()->data(qIdx, Qt::DisplayRole).toString().toNSString()];
+ }
+
+ return YES;
+}
+
+- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
+{
+ if([menu.title isEqualToString:@"inputlist"])
+ return Audio::Settings::instance()->inputDeviceModel()->rowCount();
+ else
+ return Audio::Settings::instance()->outputDeviceModel()->rowCount();
+}
+
+@end
diff --git a/src/ConversationsViewController.h b/src/ConversationsViewController.h
new file mode 100644
index 0000000..09d4e3b
--- /dev/null
+++ b/src/ConversationsViewController.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef CONVERSATIONSVC_H
+#define CONVERSATIONSVC_H
+
+#import <Cocoa/Cocoa.h>
+#import "QNSTreeController.h"
+
+@interface ConversationsViewController : NSViewController <NSOutlineViewDelegate> {
+
+}
+
+@end
+
+#endif // CONVERSATIONSVC_H
diff --git a/src/ConversationsViewController.mm b/src/ConversationsViewController.mm
new file mode 100644
index 0000000..4b7ce22
--- /dev/null
+++ b/src/ConversationsViewController.mm
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "ConversationsViewController.h"
+
+#import <callmodel.h>
+#import <QtCore/qitemselectionmodel.h>
+
+#import "CurrentCallVC.h"
+
+#define COLUMNID_CONVERSATIONS @"ConversationsColumn" // the single column name in our outline view
+
+@interface ConversationsViewController ()
+
+@property CurrentCallVC* currentVC;
+@property (assign) IBOutlet NSView *currentCallView;
+@property QNSTreeController *treeController;
+@property (assign) IBOutlet NSOutlineView *conversationsView;
+
+@end
+
+@implementation ConversationsViewController
+@synthesize conversationsView;
+@synthesize treeController;
+@synthesize currentVC;
+@synthesize currentCallView;
+
+- (void)awakeFromNib
+{
+ NSLog(@"INIT Conversations VC");
+
+ treeController = [[QNSTreeController alloc] initWithQModel:CallModel::instance()];
+
+ [treeController setAvoidsEmptySelection:NO];
+ [treeController setChildrenKeyPath:@"children"];
+
+ [self.conversationsView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
+ [self.conversationsView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
+ [self.conversationsView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
+
+ NSInteger idx = [conversationsView columnWithIdentifier:COLUMNID_CONVERSATIONS];
+ [[[[self.conversationsView tableColumns] objectAtIndex:idx] headerCell] setStringValue:@"Conversations"];
+
+ QObject::connect(CallModel::instance(),
+ &QAbstractItemModel::dataChanged,
+ [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
+ [conversationsView reloadDataForRowIndexes:
+ [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(topLeft.row(), bottomRight.row() + 1)]
+ columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, conversationsView.tableColumns.count)]];
+
+ });
+
+ currentVC = [[CurrentCallVC alloc] initWithNibName:@"CurrentCall" bundle:nil];
+ [currentCallView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ [[currentVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+
+ [currentCallView addSubview:[self.currentVC view]];
+ [currentVC initFrame];
+}
+
+#pragma mark - NSOutlineViewDelegate methods
+
+// -------------------------------------------------------------------------------
+// shouldSelectItem:item
+// -------------------------------------------------------------------------------
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
+{
+ return YES;
+}
+
+// -------------------------------------------------------------------------------
+// dataCellForTableColumn:tableColumn:item
+// -------------------------------------------------------------------------------
+- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ NSCell *returnCell = [tableColumn dataCell];
+ if(item == nil)
+ return returnCell;
+ return returnCell;
+}
+
+// -------------------------------------------------------------------------------
+// textShouldEndEditing:fieldEditor
+// -------------------------------------------------------------------------------
+- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
+{
+ if ([[fieldEditor string] length] == 0)
+ {
+ // don't allow empty node names
+ return NO;
+ }
+ else
+ {
+ return YES;
+ }
+}
+
+// -------------------------------------------------------------------------------
+// shouldEditTableColumn:tableColumn:item
+//
+// Decide to allow the edit of the given outline view "item".
+// -------------------------------------------------------------------------------
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ return NO;
+}
+
+// -------------------------------------------------------------------------------
+// outlineView:willDisplayCell:forTableColumn:item
+// -------------------------------------------------------------------------------
+- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ if ([[tableColumn identifier] isEqualToString:COLUMNID_CONVERSATIONS])
+ {
+ QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
+ if(qIdx.isValid())
+ cell.title = CallModel::instance()->data(qIdx, Qt::DisplayRole).toString().toNSString();
+ }
+}
+
+// -------------------------------------------------------------------------------
+// outlineViewSelectionDidChange:notification
+// -------------------------------------------------------------------------------
+- (void)outlineViewSelectionDidChange:(NSNotification *)notification
+{
+ // ask the tree controller for the current selection
+ if([[treeController selectedNodes] count] > 0) {
+ QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
+ //Update details view by changing selection
+ CallModel::instance()->selectionModel()->setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
+ } else {
+ CallModel::instance()->selectionModel()->clearCurrentIndex();
+ }
+}
+
+
+@end
diff --git a/src/CurrentCallVC.h b/src/CurrentCallVC.h
new file mode 100644
index 0000000..e24cf01
--- /dev/null
+++ b/src/CurrentCallVC.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef CURRENTCALLVC_H
+#define CURRENTCALLVC_H
+
+#import <Cocoa/Cocoa.h>
+
+class Call;
+
+@interface CurrentCallVC : NSViewController {
+
+}
+
+- (void) initFrame;
+
+@end
+
+#endif // CURRENTCALLVC_H
\ No newline at end of file
diff --git a/src/CurrentCallVC.mm b/src/CurrentCallVC.mm
new file mode 100644
index 0000000..ba70063
--- /dev/null
+++ b/src/CurrentCallVC.mm
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "CurrentCallVC.h"
+
+#import <QuartzCore/QuartzCore.h>
+
+#import <call.h>
+#import <callmodel.h>
+#import <useractionmodel.h>
+#import <contactmethod.h>
+#import <qabstractitemmodel.h>
+#import <QItemSelectionModel>
+#import <QItemSelection>
+
+#import <video/previewmanager.h>
+#import <video/renderer.h>
+
+/** FrameReceiver class - delegate for AVCaptureSession
+ */
+@interface RendererConnectionsHolder : NSObject
+
+@property QMetaObject::Connection frameUpdated;
+@property QMetaObject::Connection started;
+@property QMetaObject::Connection stopped;
+
+@end
+
+@implementation RendererConnectionsHolder
+
+@end
+
+@interface CurrentCallVC ()
+
+@property (assign) IBOutlet NSTextField *personLabel;
+@property (assign) IBOutlet NSTextField *stateLabel;
+@property (assign) IBOutlet NSButton *holdOnOffButton;
+@property (assign) IBOutlet NSButton *hangUpButton;
+@property (assign) IBOutlet NSButton *recordOnOffButton;
+@property (assign) IBOutlet NSButton *pickUpButton;
+@property (assign) IBOutlet NSTextField *timeSpentLabel;
+@property (assign) IBOutlet NSView *controlsPanel;
+
+@property QHash<int, NSButton*> actionHash;
+
+// Video
+@property (assign) IBOutlet NSView *videoView;
+@property CALayer* videoLayer;
+@property (assign) IBOutlet NSView *previewView;
+@property CALayer* previewLayer;
+
+@property RendererConnectionsHolder* previewHolder;
+@property RendererConnectionsHolder* videoHolder;
+@property QMetaObject::Connection videoStarted;
+
+@end
+
+@implementation CurrentCallVC
+@synthesize personLabel;
+@synthesize actionHash;
+@synthesize stateLabel;
+@synthesize holdOnOffButton;
+@synthesize hangUpButton;
+@synthesize recordOnOffButton;
+@synthesize pickUpButton;
+@synthesize timeSpentLabel;
+@synthesize controlsPanel;
+@synthesize videoView;
+@synthesize videoLayer;
+@synthesize previewLayer;
+@synthesize previewView;
+
+@synthesize previewHolder;
+@synthesize videoHolder;
+
+- (void) updateAllActions
+{
+ for(int i = 0 ; i <= CallModel::instance()->userActionModel()->rowCount() ; i++) {
+ [self updateActionAtIndex:i];
+ }
+}
+
+- (void) updateActionAtIndex:(int) row
+{
+ const QModelIndex& idx = CallModel::instance()->userActionModel()->index(row,0);
+ UserActionModel::Action action = qvariant_cast<UserActionModel::Action>(idx.data(UserActionModel::Role::ACTION));
+ NSButton* a = actionHash[(int) action];
+ if (a != nil) {
+ [a setEnabled:(idx.flags() & Qt::ItemIsEnabled)];
+ [a setState:(idx.data(Qt::CheckStateRole) == Qt::Checked) ? NSOnState : NSOffState];
+
+ if(action == UserActionModel::Action::HOLD) {
+ [a setTitle:(a.state == NSOnState ? @"Hold off" : @"Hold")];
+ }
+ if(action == UserActionModel::Action::RECORD) {
+ [a setTitle:(a.state == NSOnState ? @"Record off" : @"Record")];
+ }
+ }
+}
+
+-(void) updateCall
+{
+ QModelIndex callIdx = CallModel::instance()->selectionModel()->currentIndex();
+ [personLabel setStringValue:CallModel::instance()->data(callIdx, Qt::DisplayRole).toString().toNSString()];
+ [timeSpentLabel setStringValue:CallModel::instance()->data(callIdx, (int)Call::Role::Length).toString().toNSString()];
+
+ Call::State state = CallModel::instance()->data(callIdx, (int)Call::Role::State).value<Call::State>();
+
+ switch (state) {
+ case Call::State::INITIALIZATION:
+ [stateLabel setStringValue:@"Initializing"];
+ break;
+ case Call::State::RINGING:
+ [stateLabel setStringValue:@"Ringing"];
+ break;
+ case Call::State::CURRENT:
+ [stateLabel setStringValue:@"Current"];
+ break;
+ case Call::State::HOLD:
+ [stateLabel setStringValue:@"On Hold"];
+ break;
+ case Call::State::BUSY:
+ [stateLabel setStringValue:@"Busy"];
+ break;
+ case Call::State::OVER:
+ [stateLabel setStringValue:@"Finished"];
+ break;
+ case Call::State::FAILURE:
+ [stateLabel setStringValue:@"Failure"];
+ break;
+ default:
+ break;
+ }
+
+}
+
+- (void)awakeFromNib
+{
+ NSLog(@"INIT CurrentCall VC");
+ [self.view setWantsLayer:YES];
+ [self.view setLayer:[CALayer layer]];
+
+ [controlsPanel setWantsLayer:YES];
+ [controlsPanel setLayer:[CALayer layer]];
+ [controlsPanel.layer setZPosition:2.0];
+ [controlsPanel.layer setBackgroundColor:[NSColor whiteColor].CGColor];
+
+ actionHash[ (int)UserActionModel::Action::ACCEPT] = pickUpButton;
+ actionHash[ (int)UserActionModel::Action::HOLD ] = holdOnOffButton;
+ actionHash[ (int)UserActionModel::Action::RECORD] = recordOnOffButton;
+ actionHash[ (int)UserActionModel::Action::HANGUP] = hangUpButton;
+
+ videoLayer = [CALayer layer];
+ [videoView setWantsLayer:YES];
+ [videoView setLayer:videoLayer];
+ [videoView.layer setBackgroundColor:[NSColor blackColor].CGColor];
+ [videoView.layer setFrame:videoView.frame];
+ [videoView.layer setContentsGravity:kCAGravityResizeAspect];
+
+ previewLayer = [CALayer layer];
+ [previewView setWantsLayer:YES];
+ [previewView setLayer:previewLayer];
+ [previewLayer setBackgroundColor:[NSColor blackColor].CGColor];
+ [previewLayer setContentsGravity:kCAGravityResizeAspectFill];
+ [previewLayer setFrame:previewView.frame];
+
+ [controlsPanel setWantsLayer:YES];
+ [controlsPanel setLayer:[CALayer layer]];
+ [controlsPanel.layer setBackgroundColor:[NSColor clearColor].CGColor];
+ [controlsPanel.layer setFrame:controlsPanel.frame];
+
+ previewHolder = [[RendererConnectionsHolder alloc] init];
+ videoHolder = [[RendererConnectionsHolder alloc] init];
+
+ [self connect];
+}
+
+- (void) connect
+{
+ QObject::connect(CallModel::instance()->selectionModel(),
+ &QItemSelectionModel::currentChanged,
+ [=](const QModelIndex ¤t, const QModelIndex &previous) {
+ if(!current.isValid()) {
+ [self animateOut];
+ return;
+ }
+ [self updateCall];
+ [self updateAllActions];
+ [self animateOut];
+ });
+
+ QObject::connect(CallModel::instance(),
+ &QAbstractItemModel::dataChanged,
+ [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
+ [self updateCall];
+ });
+
+ QObject::connect(CallModel::instance()->userActionModel(),
+ &QAbstractItemModel::dataChanged,
+ [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
+ const int first(topLeft.row()),last(bottomRight.row());
+ for(int i = first; i <= last;i++) {
+ [self updateActionAtIndex:i];
+ }
+ });
+
+ QObject::connect(CallModel::instance(),
+ &CallModel::callStateChanged,
+ [self](Call* c, Call::State state) {
+ [self updateCall];
+ });
+}
+
+-(void) connectVideoSignals
+{
+ QModelIndex idx = CallModel::instance()->selectionModel()->currentIndex();
+ Call* call = CallModel::instance()->getCall(idx);
+ QObject::connect(call,
+ &Call::videoStarted,
+ [=](Video::Renderer* renderer) {
+ NSLog(@"Video started!");
+ QObject::disconnect(self.videoStarted);
+ [self connectVideoRenderer:renderer];
+ });
+
+ if(call->videoRenderer())
+ {
+ [self connectVideoRenderer:call->videoRenderer()];
+ }
+
+ [self connectPreviewRenderer];
+
+}
+
+-(void) connectPreviewRenderer
+{
+ QObject::disconnect(previewHolder.frameUpdated);
+ QObject::disconnect(previewHolder.stopped);
+ QObject::disconnect(previewHolder.started);
+ previewHolder.started = QObject::connect(Video::PreviewManager::instance(),
+ &Video::PreviewManager::previewStarted,
+ [=](Video::Renderer* renderer) {
+ QObject::disconnect(previewHolder.frameUpdated);
+ previewHolder.frameUpdated = QObject::connect(renderer,
+ &Video::Renderer::frameUpdated,
+ [=]() {
+ [self renderer:Video::PreviewManager::instance()->previewRenderer()
+ renderFrameForView:previewView];
+ });
+ });
+
+ previewHolder.stopped = QObject::connect(Video::PreviewManager::instance(),
+ &Video::PreviewManager::previewStopped,
+ [=](Video::Renderer* renderer) {
+ QObject::disconnect(previewHolder.frameUpdated);
+ [previewView.layer setContents:nil];
+ });
+
+ previewHolder.frameUpdated = QObject::connect(Video::PreviewManager::instance()->previewRenderer(),
+ &Video::Renderer::frameUpdated,
+ [=]() {
+ [self renderer:Video::PreviewManager::instance()->previewRenderer()
+ renderFrameForView:previewView];
+ });
+}
+
+-(void) connectVideoRenderer: (Video::Renderer*)renderer
+{
+ QObject::disconnect(videoHolder.frameUpdated);
+ QObject::disconnect(videoHolder.started);
+ QObject::disconnect(videoHolder.stopped);
+ videoHolder.frameUpdated = QObject::connect(renderer,
+ &Video::Renderer::frameUpdated,
+ [=]() {
+ [self renderer:renderer renderFrameForView:videoView];
+ });
+
+ videoHolder.started = QObject::connect(renderer,
+ &Video::Renderer::started,
+ [=]() {
+ QObject::disconnect(videoHolder.frameUpdated);
+ videoHolder.frameUpdated = QObject::connect(renderer,
+ &Video::Renderer::frameUpdated,
+ [=]() {
+ [self renderer:renderer renderFrameForView:videoView];
+ });
+ });
+
+ videoHolder.stopped = QObject::connect(renderer,
+ &Video::Renderer::stopped,
+ [=]() {
+ QObject::disconnect(videoHolder.frameUpdated);
+ [videoView.layer setContents:nil];
+ });
+}
+
+-(void) renderer: (Video::Renderer*)renderer renderFrameForView:(NSView*) view
+{
+ const QByteArray& data = renderer->currentFrame();
+ QSize res = renderer->size();
+
+ auto buf = reinterpret_cast<const unsigned char*>(data.data());
+
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGContextRef newContext = CGBitmapContextCreate((void *)buf,
+ res.width(),
+ res.height(),
+ 8,
+ 4*res.width(),
+ colorSpace,
+ kCGImageAlphaPremultipliedLast);
+
+
+ CGImageRef newImage = CGBitmapContextCreateImage(newContext);
+
+ /*We release some components*/
+ CGContextRelease(newContext);
+ CGColorSpaceRelease(colorSpace);
+
+ [CATransaction begin];
+ view.layer.contents = (__bridge id)newImage;
+ [CATransaction commit];
+
+ CFRelease(newImage);
+}
+
+- (void) initFrame
+{
+ [self.view setFrame:self.view.superview.bounds];
+ [self.view setHidden:YES];
+ self.view.layer.position = self.view.frame.origin;
+}
+
+# pragma private IN/OUT animations
+
+-(void) animateIn
+{
+ NSLog(@"animateIn");
+ CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
+ [self.view setHidden:NO];
+
+ [CATransaction begin];
+ CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
+ [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
+ [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
+ [animation setDuration:0.2f];
+ [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]];
+ [CATransaction setCompletionBlock:^{
+ [self connectVideoSignals];
+ }];
+ [self.view.layer addAnimation:animation forKey:animation.keyPath];
+
+ [CATransaction commit];
+}
+
+-(void) cleanUp
+{
+ QObject::disconnect(videoHolder.frameUpdated);
+ QObject::disconnect(videoHolder.started);
+ QObject::disconnect(videoHolder.stopped);
+ QObject::disconnect(previewHolder.frameUpdated);
+ QObject::disconnect(previewHolder.stopped);
+ QObject::disconnect(previewHolder.started);
+ [videoView.layer setContents:nil];
+ [previewView.layer setContents:nil];
+}
+
+-(void) animateOut
+{
+ NSLog(@"animateOut");
+ if(self.view.frame.origin.x < 0) {
+ NSLog(@"Already hidden");
+ if (CallModel::instance()->selectionModel()->currentIndex().isValid()) {
+ [self animateIn];
+ }
+ return;
+ }
+
+ CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
+ [CATransaction begin];
+ CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
+ [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
+ [animation setToValue:[NSValue valueWithPoint:frame.origin]];
+ [animation setDuration:0.2f];
+ [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]];
+
+ [CATransaction setCompletionBlock:^{
+ [self.view setHidden:YES];
+ // first make sure everything is disconnected
+ [self cleanUp];
+ if (CallModel::instance()->selectionModel()->currentIndex().isValid()) {
+ [self animateIn];
+ }
+ }];
+ [self.view.layer addAnimation:animation forKey:animation.keyPath];
+ [CATransaction commit];
+}
+
+/**
+ * Debug purpose
+ */
+-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name
+{
+ NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
+}
+
+
+#pragma button methods
+- (IBAction)hangUp:(id)sender {
+ CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::REFUSE;
+}
+
+- (IBAction)accept:(id)sender {
+ CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::ACCEPT;
+}
+
+- (IBAction)toggleRecording:(id)sender {
+ CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::RECORD;
+}
+
+- (IBAction)toggleHold:(id)sender {
+ CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::HOLD;
+}
+
+@end
diff --git a/src/GeneralPrefsVC.h b/src/GeneralPrefsVC.h
new file mode 100644
index 0000000..ef059ae
--- /dev/null
+++ b/src/GeneralPrefsVC.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef GENERALPREFSVC_H
+#define GENERALPREFSVC_H
+
+#import <Cocoa/Cocoa.h>
+
+@interface GeneralPrefsVC : NSViewController {
+
+ NSTextField *historyChangedLabel;
+}
+
+@end
+
+#endif // GENERALPREFSVC_H
\ No newline at end of file
diff --git a/src/GeneralPrefsVC.mm b/src/GeneralPrefsVC.mm
new file mode 100644
index 0000000..ea6d972
--- /dev/null
+++ b/src/GeneralPrefsVC.mm
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "GeneralPrefsVC.h"
+
+#import <categorizedhistorymodel.h>
+
+@interface GeneralPrefsVC ()
+@property (assign) IBOutlet NSTextField *historyChangedLabel;
+
+@end
+
+@implementation GeneralPrefsVC {
+
+}
+@synthesize historyChangedLabel;
+
+- (void)loadView
+{
+ [super loadView];
+ [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"history_limit" options:NSKeyValueObservingOptionNew context:NULL];
+}
+
+- (IBAction)clearHistory:(id)sender {
+ CategorizedHistoryModel::instance()->clearAllCollections();
+ [historyChangedLabel setHidden:NO];
+}
+
+// KVO handler
+-(void)observeValueForKeyPath:(NSString *)aKeyPath ofObject:(id)anObject
+ change:(NSDictionary *)aChange context:(void *)aContext
+{
+ NSLog(@"VALUE CHANGED");
+ [historyChangedLabel setHidden:NO];
+}
+
+@end
diff --git a/src/HistoryViewController.h b/src/HistoryViewController.h
new file mode 100644
index 0000000..4c4dbea
--- /dev/null
+++ b/src/HistoryViewController.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef HISTORYVIEWCONTROLLER_H
+#define HISTORYVIEWCONTROLLER_H
+
+#import <Cocoa/Cocoa.h>
+
+@interface HistoryViewController : NSViewController <NSOutlineViewDelegate> {
+
+}
+
+@end
+
+#endif // HISTORYVIEWCONTROLLER_H
diff --git a/src/HistoryViewController.mm b/src/HistoryViewController.mm
new file mode 100644
index 0000000..5ca5dad
--- /dev/null
+++ b/src/HistoryViewController.mm
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "HistoryViewController.h"
+
+#import <categorizedhistorymodel.h>
+#import <QSortFilterProxyModel>
+#import <callmodel.h>
+#import <call.h>
+#import <contactmethod.h>
+
+#import "backends/MinimalHistoryBackend.h"
+#import "QNSTreeController.h"
+
+#define COLUMNID_DAY @"DayColumn" // the single column name in our outline view
+#define COLUMNID_CONTACTMETHOD @"ContactMethodColumn" // the single column name in our outline view
+#define COLUMNID_DATE @"DateColumn" // the single column name in our outline view
+
+@interface HistoryViewController()
+
+@property NSTreeController *treeController;
+@property (assign) IBOutlet NSOutlineView *historyView;
+@property QSortFilterProxyModel *historyProxyModel;
+@end
+
+@implementation HistoryViewController
+@synthesize treeController;
+@synthesize historyView;
+@synthesize historyProxyModel;
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ if (self = [super initWithCoder:aDecoder]) {
+ NSLog(@"INIT HVC");
+
+ }
+ return self;
+}
+
+- (void)awakeFromNib
+{
+ historyProxyModel = new QSortFilterProxyModel(CategorizedHistoryModel::instance());
+ historyProxyModel->setSourceModel(CategorizedHistoryModel::instance());
+ historyProxyModel->setSortRole(static_cast<int>(Call::Role::Date));
+ historyProxyModel->sort(0,Qt::DescendingOrder);
+ treeController = [[QNSTreeController alloc] initWithQModel:historyProxyModel];
+
+ [treeController setAvoidsEmptySelection:NO];
+ [treeController setChildrenKeyPath:@"children"];
+
+ [historyView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
+ [historyView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
+ [historyView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
+ [historyView setTarget:self];
+ [historyView setDoubleAction:@selector(placeCall:)];
+
+ CategorizedHistoryModel::instance()->addCollection<MinimalHistoryBackend>(LoadOptions::FORCE_ENABLED);
+}
+
+- (void)placeCall:(id)sender
+{
+ if([[treeController selectedNodes] count] > 0) {
+ Call* c = CallModel::instance()->dialingCall();
+ QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
+ QVariant var = historyProxyModel->data(qIdx, (int)Call::Role::ContactMethod);
+ ContactMethod* m = qvariant_cast<ContactMethod*>(var);
+ if(m){
+ Call* c = CallModel::instance()->dialingCall();
+ c->setDialNumber(m);
+ c << Call::Action::ACCEPT;
+ }
+ }
+}
+
+#pragma mark - NSOutlineViewDelegate methods
+
+// -------------------------------------------------------------------------------
+// shouldSelectItem:item
+// -------------------------------------------------------------------------------
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
+{
+ return YES;
+}
+
+// -------------------------------------------------------------------------------
+// dataCellForTableColumn:tableColumn:item
+// -------------------------------------------------------------------------------
+- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ NSCell *returnCell = [tableColumn dataCell];
+ if(item == nil)
+ return returnCell;
+ return returnCell;
+}
+
+// -------------------------------------------------------------------------------
+// textShouldEndEditing:fieldEditor
+// -------------------------------------------------------------------------------
+- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
+{
+ if ([[fieldEditor string] length] == 0)
+ {
+ // don't allow empty node names
+ return NO;
+ }
+ else
+ {
+ return YES;
+ }
+}
+
+// -------------------------------------------------------------------------------
+// shouldEditTableColumn:tableColumn:item
+//
+// Decide to allow the edit of the given outline view "item".
+// -------------------------------------------------------------------------------
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ return NO;
+}
+
+// -------------------------------------------------------------------------------
+// outlineView:willDisplayCell:forTableColumn:item
+// -------------------------------------------------------------------------------
+- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
+ if(!qIdx.isValid())
+ return;
+
+ if ([[tableColumn identifier] isEqualToString:COLUMNID_DAY])
+ {
+ cell.title = historyProxyModel->data(qIdx, Qt::DisplayRole).toString().toNSString();
+ } else if ([[tableColumn identifier] isEqualToString:COLUMNID_CONTACTMETHOD])
+ {
+ cell.title = historyProxyModel->data(qIdx, (int)Call::Role::Number).toString().toNSString();
+ } else if ([[tableColumn identifier] isEqualToString:COLUMNID_DATE])
+ {
+ cell.title = historyProxyModel->data(qIdx, (int)Call::Role::FormattedDate).toString().toNSString();
+ }
+}
+
+// -------------------------------------------------------------------------------
+// outlineViewSelectionDidChange:notification
+// -------------------------------------------------------------------------------
+- (void)outlineViewSelectionDidChange:(NSNotification *)notification
+{
+ // ask the tree controller for the current selection
+ //NSLog(@"outlineViewSelectionDidChange!!");
+}
+
+@end
diff --git a/src/PreferencesViewController.h b/src/PreferencesViewController.h
new file mode 100644
index 0000000..ac0400c
--- /dev/null
+++ b/src/PreferencesViewController.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef PREFERENCESVIEWCONTROLLER_H
+#define PREFERENCESVIEWCONTROLLER_H
+
+#import <Cocoa/Cocoa.h>
+
+@interface PreferencesViewController : NSViewController <NSToolbarDelegate>
+
+- (void) close;
+@property (nonatomic, assign) NSViewController *currentVC;
+@property (nonatomic, assign) NSViewController *accountsPrefsVC;
+@property (nonatomic, assign) NSViewController *generalPrefsVC;
+@property (nonatomic, assign) NSViewController *audioPrefsVC;
+@property (nonatomic, assign) NSViewController *videoPrefsVC;
+
+- (void)displayGeneral:(NSToolbarItem *)sender;
+- (void)displayAudio:(NSToolbarItem *)sender;
+- (void)displayAncrage:(NSToolbarItem *)sender;
+- (void)displayVideo:(NSToolbarItem *)sender;
+- (void)displayAccounts:(NSToolbarItem *)sender;
+
+@end
+
+#endif // PREFERENCESVIEWCONTROLLER_H
\ No newline at end of file
diff --git a/src/PreferencesViewController.mm b/src/PreferencesViewController.mm
new file mode 100644
index 0000000..5ccde7a
--- /dev/null
+++ b/src/PreferencesViewController.mm
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "PreferencesViewController.h"
+
+#import <QuartzCore/QuartzCore.h>
+
+#import <accountmodel.h>
+#import <audio/codecmodel.h>
+
+#import "AccountsVC.h"
+#import "GeneralPrefsVC.h"
+#import "AudioPrefsVC.h"
+#import "VideoPrefsVC.h"
+
+@interface PreferencesViewController ()
+
+@property NSButton* toggleAdvancedSettings;
+
+@end
+
+@implementation PreferencesViewController
+@synthesize toggleAdvancedSettings;
+
+static NSString* const kProfilePrefsIdentifier = @"ProfilesPrefsIdentifier";
+static NSString* const kGeneralPrefsIdentifier = @"GeneralPrefsIdentifier";
+static NSString* const kAudioPrefsIdentifer = @"AudioPrefsIdentifer";
+static NSString* const kAncragePrefsIdentifer = @"AncragePrefsIdentifer";
+static NSString* const kVideoPrefsIdentifer = @"VideoPrefsIdentifer";
+static NSString* const kDonePrefsIdentifer = @"DonePrefsIdentifer";
+static NSString* const kPowerSettingsIdentifer = @"PowerSettingsIdentifer";
+
+-(void)loadView
+{
+ [super loadView];
+
+ [self displayGeneral:nil];
+
+ [self.view setWantsLayer:YES];
+ self.view.layer.backgroundColor = [NSColor windowBackgroundColor].CGColor;
+
+ // Set the layer redraw policy. This would be better done in
+ // the initialization method of a NSView subclass instead of here.
+ self.view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
+
+ [self.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+
+ CGRect frame = CGRectOffset(self.view.frame, 0, -self.view.frame.size.height);
+
+ CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
+ animation.fromValue = [NSValue valueWithPoint:frame.origin];
+ animation.toValue = [NSValue valueWithPoint:self.view.frame.origin];
+ animation.duration = 0.3f;
+ [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]];
+ [self.view.layer addAnimation:animation forKey:animation.keyPath];
+}
+
+- (void) close
+{
+ // first save codecs for each account
+ for (int i = 0 ; i < AccountModel::instance()->rowCount(); ++i) {
+ QModelIndex qIdx = AccountModel::instance()->index(i);
+ AccountModel::instance()->getAccountByModelIndex(qIdx)->codecModel()->save();
+ }
+
+ // then save accounts
+ AccountModel::instance()->save();
+
+ CGRect frame = CGRectOffset(self.view.frame, 0, -self.view.frame.size.height);
+
+ [CATransaction begin];
+ CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
+ animation.fromValue = [NSValue valueWithPoint:self.view.frame.origin];
+ animation.toValue = [NSValue valueWithPoint:frame.origin];
+ animation.duration = 0.3f;
+ [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]];
+
+ [CATransaction setCompletionBlock:^{
+ [self.view removeFromSuperview];
+ }];
+
+
+ [self.view.layer addAnimation:animation forKey:animation.keyPath];
+ [CATransaction commit];
+}
+
+- (void)displayGeneral:(NSToolbarItem *)sender {
+ if (self.currentVC != nil) {
+ [self.currentVC.view removeFromSuperview];
+ }
+ self.generalPrefsVC = [[GeneralPrefsVC alloc] initWithNibName:@"GeneralPrefs" bundle:nil];
+ [self.view addSubview:self.generalPrefsVC.view];
+ [self.generalPrefsVC.view setFrame:[self.view bounds]];
+ self.currentVC = self.generalPrefsVC;
+}
+
+- (void)displayAudio:(NSToolbarItem *)sender {
+ if (self.currentVC != nil) {
+ [self.currentVC.view removeFromSuperview];
+ }
+ self.audioPrefsVC = [[AudioPrefsVC alloc] initWithNibName:@"AudioPrefs" bundle:nil];
+ [self.view addSubview:self.audioPrefsVC.view];
+ [self.audioPrefsVC.view setFrame:[self.view bounds]];
+ self.currentVC = self.audioPrefsVC;
+}
+
+- (void)displayAncrage:(NSToolbarItem *)sender {
+
+}
+
+- (void)displayVideo:(NSToolbarItem *)sender {
+ if (self.currentVC != nil) {
+ [self.currentVC.view removeFromSuperview];
+ }
+ self.videoPrefsVC = [[VideoPrefsVC alloc] initWithNibName:@"VideoPrefs" bundle:nil];
+ [self.view addSubview:self.videoPrefsVC.view];
+ [self.videoPrefsVC.view setFrame:[self.view bounds]];
+ self.currentVC = self.videoPrefsVC;
+}
+
+- (void) displayAccounts:(NSToolbarItem *) sender {
+ if (self.currentVC != nil) {
+ [self.currentVC.view removeFromSuperview];
+ }
+ self.accountsPrefsVC = [[AccountsVC alloc] initWithNibName:@"Accounts" bundle:nil];
+ [self.view addSubview:self.accountsPrefsVC.view];
+ [self.accountsPrefsVC.view setFrame:[self.view bounds]];
+ self.currentVC = self.accountsPrefsVC;
+}
+
+
+#pragma NSToolbar Delegate
+
+-(NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
+{
+ NSToolbarItem* item = nil;
+
+ if ([itemIdentifier isEqualToString: kProfilePrefsIdentifier]) {
+
+ item = [[NSToolbarItem alloc] initWithItemIdentifier: kProfilePrefsIdentifier];
+ [item setImage: [NSImage imageNamed: @"NSUserAccounts"]];
+ [item setLabel: @"Accounts"];
+ [item setAction:@selector(displayAccounts:)];
+ }
+
+ if ([itemIdentifier isEqualToString: kGeneralPrefsIdentifier]) {
+ item = [[NSToolbarItem alloc] initWithItemIdentifier: kGeneralPrefsIdentifier];
+ [item setImage: [NSImage imageNamed: @"general"]];
+ [item setLabel: @"General"];
+ [item setAction:@selector(displayGeneral:)];
+ }
+
+ if ([itemIdentifier isEqualToString: kAudioPrefsIdentifer]) {
+ item = [[NSToolbarItem alloc] initWithItemIdentifier: kAudioPrefsIdentifer];
+ [item setImage: [NSImage imageNamed: @"audio"]];
+ [item setLabel: @"Audio"];
+ [item setAction:@selector(displayAudio:)];
+ }
+
+ if ([itemIdentifier isEqualToString: kPowerSettingsIdentifer]) {
+ item = [[NSToolbarItem alloc] initWithItemIdentifier: kPowerSettingsIdentifer];
+ toggleAdvancedSettings = [[NSButton alloc] initWithFrame:NSMakeRect(0,0,20,20)];
+ [toggleAdvancedSettings setButtonType:NSSwitchButton];
+ [toggleAdvancedSettings setTitle:@""];
+ [toggleAdvancedSettings setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"show_advanced"]];
+ [item setLabel:@"Show Advanced"];
+ [item setView:toggleAdvancedSettings];
+ [item setAction:@selector(togglePowerSettings:)];
+ }
+
+ if ([itemIdentifier isEqualToString: kDonePrefsIdentifer]) {
+ item = [[NSToolbarItem alloc] initWithItemIdentifier: kDonePrefsIdentifer];
+ [item setImage: [NSImage imageNamed: @"ic_action_cancel"]];
+ [item setLabel: @"Done"];
+ [item setAction:@selector(closePreferences:)];
+ }
+
+ if ([itemIdentifier isEqualToString: kVideoPrefsIdentifer]) {
+ item = [[NSToolbarItem alloc] initWithItemIdentifier: kVideoPrefsIdentifer];
+ [item setImage: [NSImage imageNamed: @"video"]];
+ [item setLabel: @"Video"];
+ [item setAction:@selector(displayVideo:)];
+ }
+
+ return item;
+}
+
+-(NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
+{
+
+ NSMutableArray* items = [NSMutableArray arrayWithObjects:
+ kPowerSettingsIdentifer,
+ NSToolbarFlexibleSpaceItemIdentifier,
+ kGeneralPrefsIdentifier,
+ kAudioPrefsIdentifer,
+ kVideoPrefsIdentifer,
+ // kAncragePrefsIdentifer,
+ NSToolbarFlexibleSpaceItemIdentifier,
+ kDonePrefsIdentifer,
+ nil];
+
+ if([[NSUserDefaults standardUserDefaults] boolForKey:@"show_advanced"]) {
+ [items insertObject:NSToolbarSpaceItemIdentifier atIndex:5];
+ [items insertObject:kProfilePrefsIdentifier atIndex:2];
+ } else
+ [items insertObject:NSToolbarSpaceItemIdentifier atIndex:5];
+
+ return items;
+}
+
+-(NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
+{
+ NSMutableArray* items = [NSMutableArray arrayWithObjects:
+ kPowerSettingsIdentifer,
+ kGeneralPrefsIdentifier,
+ kAudioPrefsIdentifer,
+ kVideoPrefsIdentifer,
+ nil];
+
+ if([[NSUserDefaults standardUserDefaults] boolForKey:@"show_advanced"])
+ [items insertObject:kProfilePrefsIdentifier atIndex:1];
+
+
+ return items;
+}
+
+-(NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
+{
+ return nil;
+}
+
+
+
+
+
+@end
diff --git a/src/PreferencesWindowController.h b/src/PreferencesWindowController.h
new file mode 100644
index 0000000..13eb9aa
--- /dev/null
+++ b/src/PreferencesWindowController.h
@@ -0,0 +1,13 @@
+//
+// PreferenceWindowController.h
+// Ring
+//
+// Created by Alexandre Lision on 2015-02-03.
+//
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface PreferencesWindowController : NSWindowController
+
+@end
diff --git a/src/PreferencesWindowController.mm b/src/PreferencesWindowController.mm
new file mode 100644
index 0000000..97c73d9
--- /dev/null
+++ b/src/PreferencesWindowController.mm
@@ -0,0 +1,23 @@
+//
+// PreferenceWindowController.m
+// Ring
+//
+// Created by Alexandre Lision on 2015-02-03.
+//
+//
+
+#import "PreferencesWindowController.h"
+
+@interface PreferencesWindowController ()
+
+@end
+
+@implementation PreferencesWindowController
+
+- (void)windowDidLoad {
+ [super windowDidLoad];
+
+ // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
+}
+
+@end
diff --git a/src/QNSTreeController.h b/src/QNSTreeController.h
new file mode 100644
index 0000000..735fe56
--- /dev/null
+++ b/src/QNSTreeController.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef QNSTREECONTROLLER_H
+#define QNSTREECONTROLLER_H
+
+#import <Cocoa/Cocoa.h>
+#import <qabstractitemmodel.h>
+
+@interface QNSTreeController : NSTreeController {
+
+QAbstractItemModel *privateQModel;
+NSMutableArray* topNodes;
+
+}
+
+- (void*)connect;
+- (id) initWithQModel:(QAbstractItemModel*) model;
+- (QModelIndex) toQIdx:(NSTreeNode*) node;
+
+@end
+
+#endif // QNSTREECONTROLLER_H
\ No newline at end of file
diff --git a/src/QNSTreeController.mm b/src/QNSTreeController.mm
new file mode 100644
index 0000000..75533c2
--- /dev/null
+++ b/src/QNSTreeController.mm
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "QNSTreeController.h"
+
+@interface Node : NSObject {
+ NSMutableArray *children;
+}
+@end
+
+@implementation Node
+- (id) init
+{
+ if (self = [super init]) {
+ children = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
+
+- (void) addChild:(Node*) child
+{
+ [children addObject:child];
+}
+
+@end
+
+
+@implementation QNSTreeController
+
+- (id) initWithQModel:(QAbstractItemModel*) model
+{
+ [super init];
+ self->privateQModel = model;
+
+ topNodes = [[NSMutableArray alloc] init];
+ [self connect];
+
+ [self populate];
+
+ return [self initWithContent:topNodes];
+}
+
+-(void) populate
+{
+ for (int i =0 ; i < self->privateQModel->rowCount() ; ++i){
+ [topNodes insertObject:[[Node alloc] init] atIndex:i];
+ }
+}
+
+- (BOOL)isEditable
+{
+ return self->privateQModel->flags(self->privateQModel->index(0, 0)) | Qt::ItemIsEditable;
+}
+
+- (QModelIndex) toQIdx:(NSTreeNode*) node
+{
+ NSIndexPath* idx = node.indexPath;
+ NSUInteger myArray[[idx length]];
+ [idx getIndexes:myArray];
+ QModelIndex toReturn;
+ if(idx.length == 2)
+ toReturn = self->privateQModel->index(myArray[1], 0, self->privateQModel->index(myArray[0], 0));
+ else
+ toReturn = self->privateQModel->index(myArray[0], 0);
+ return toReturn;
+}
+
+- (void)connect
+{
+ QObject::connect(self->privateQModel,
+ &QAbstractItemModel::rowsInserted,
+ [=](const QModelIndex & parent, int first, int last) {
+ for( int row = first; row <= last; row++) {
+ if(!parent.isValid()) {
+ //Inserting topnode
+ Node* n = [[Node alloc] init];
+ [self insertObject:n atArrangedObjectIndexPath:[[NSIndexPath alloc] initWithIndex:row]];
+ } else {
+ Node* child = [[Node alloc] init];
+ NSUInteger indexes[] = { (NSUInteger)parent.row(), (NSUInteger)row};
+ [self insertObject:child atArrangedObjectIndexPath:[[NSIndexPath alloc] initWithIndexes:indexes length:2]];
+ }
+ }
+ }
+ );
+
+ QObject::connect(self->privateQModel,
+ &QAbstractItemModel::rowsAboutToBeMoved,
+ [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationRow) {
+ NSLog(@"rows about to be moved, start: %d, end: %d, moved to: %d", sourceStart, sourceEnd, destinationRow);
+ /* first remove the row from old location
+ * then insert them at the new location on the "rowsMoved signal */
+ for( int row = sourceStart; row <= sourceEnd; row++) {
+ //TODO
+ }
+ }
+ );
+
+ QObject::connect(self->privateQModel,
+ &QAbstractItemModel::rowsMoved,
+ [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationRow) {
+ //NSLog(@"rows moved, start: %d, end: %d, moved to: %d", sourceStart, sourceEnd, destinationRow);
+ /* these rows should have been removed in the "rowsAboutToBeMoved" handler
+ * now insert them in the new location */
+ for( int row = sourceStart; row <= sourceEnd; row++) {
+ //TODO
+ }
+ }
+ );
+
+ QObject::connect(self->privateQModel,
+ &QAbstractItemModel::rowsAboutToBeRemoved,
+ [=](const QModelIndex & parent, int first, int last) {
+ NSLog(@"rows about to be removed");
+ }
+ );
+
+ QObject::connect(self->privateQModel,
+ &QAbstractItemModel::rowsRemoved,
+ [=](const QModelIndex & parent, int first, int last) {
+ //NSLog(@"rows removed");
+ for( int row = first; row <= last; row++) {
+ if(parent.isValid())
+ {
+ //Removing leaf
+ NSUInteger indexes[] = { (NSUInteger)parent.row(), (NSUInteger)row};
+ [self removeObjectAtArrangedObjectIndexPath:[[NSIndexPath alloc] initWithIndexes:indexes length:2]];
+ } else
+ {
+ [self removeObjectAtArrangedObjectIndexPath:[[NSIndexPath alloc] initWithIndex:row]];
+ }
+ }
+ }
+ );
+
+ QObject::connect(self->privateQModel,
+ &QAbstractItemModel::layoutChanged,
+ [=]() {
+ //NSLog(@"layout changed");
+ }
+ );
+
+ QObject::connect(self->privateQModel,
+ &QAbstractItemModel::dataChanged,
+ [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
+ //NSLog(@"data changed");
+ for(int row = topLeft.row() ; row <= bottomRight.row() ; ++row)
+ {
+ QModelIndex tmpIdx = self->privateQModel->index(row, 0);
+ if(tmpIdx.row() >= [self.arrangedObjects count]) {
+ Node* n = [[Node alloc] init];
+ if(tmpIdx.isValid())
+ [self insertObject:n atArrangedObjectIndexPath:[[NSIndexPath alloc] initWithIndex:row]];
+ }
+ }
+ });
+}
+
+@end
diff --git a/src/RingWindowController.h b/src/RingWindowController.h
new file mode 100644
index 0000000..77c93c4
--- /dev/null
+++ b/src/RingWindowController.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef RINGWINDOWCONTROLLER_H
+#define RINGWINDOWCONTROLLER_H
+
+#import <Cocoa/Cocoa.h>
+#import "HistoryViewController.h"
+#import "PreferencesViewController.h"
+
+@interface RingWindowController : NSWindowController <NSToolbarDelegate, NSTextFieldDelegate>{
+ IBOutlet NSView *currentView;
+}
+@property (nonatomic, assign) NSViewController *myCurrentViewController;
+@property PreferencesViewController* preferencesViewController;
+
+- (IBAction)openPreferences:(id)sender;
+- (IBAction)closePreferences:(NSToolbarItem *)sender;
+
+@end
+
+#endif // RINGWINDOWCONTROLLER_H
diff --git a/src/RingWindowController.mm b/src/RingWindowController.mm
new file mode 100644
index 0000000..f46bf66
--- /dev/null
+++ b/src/RingWindowController.mm
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "RingWindowController.h"
+
+#import <accountmodel.h>
+#import <callmodel.h>
+#import <account.h>
+#import <call.h>
+
+#import "AppDelegate.h"
+
+@interface RingWindowController ()
+
+@property NSSearchField* callField;
+
+@end
+
+@implementation RingWindowController
+@synthesize callField;
+static NSString* const kSearchViewIdentifier = @"SearchViewIdentifier";
+static NSString* const kPreferencesIdentifier = @"PreferencesIdentifier";
+static NSString* const kCallButtonIdentifer = @"CallButtonIdentifier";
+
+- (void)windowDidLoad {
+ [super windowDidLoad];
+ [self.window setReleasedWhenClosed:FALSE];
+ [self displayMainToolBar];
+}
+
+- (IBAction)openPreferences:(id)sender
+{
+ if(self.preferencesViewController != nil) {
+ [self closePreferences:nil];
+ return;
+ }
+ NSToolbar* tb = [[NSToolbar alloc] initWithIdentifier: @"PreferencesToolbar"];
+
+ self.preferencesViewController = [[PreferencesViewController alloc] initWithNibName:@"PreferencesScreen" bundle:nil];
+ self.myCurrentViewController = self.preferencesViewController;
+
+ NSLayoutConstraint* test = [NSLayoutConstraint constraintWithItem:self.preferencesViewController.view
+ attribute:NSLayoutAttributeWidth
+ relatedBy:NSLayoutRelationEqual
+ toItem:currentView
+ attribute:NSLayoutAttributeWidth
+ multiplier:1.0f
+ constant:0.0f];
+
+ NSLayoutConstraint* test2 = [NSLayoutConstraint constraintWithItem:self.preferencesViewController.view
+ attribute:NSLayoutAttributeHeight
+ relatedBy:NSLayoutRelationEqual
+ toItem:currentView
+ attribute:NSLayoutAttributeHeight
+ multiplier:1.0f
+ constant:0.0f];
+
+ NSLayoutConstraint* test3 = [NSLayoutConstraint constraintWithItem:self.preferencesViewController.view
+ attribute:NSLayoutAttributeCenterX
+ relatedBy:NSLayoutRelationEqual
+ toItem:currentView
+ attribute:NSLayoutAttributeCenterX
+ multiplier:1.0f
+ constant:0.0f];
+
+
+ [currentView addSubview:[self.preferencesViewController view]];
+
+ [tb setDelegate: self.preferencesViewController];
+ [self.window setToolbar: tb];
+
+ [self.window.toolbar setSelectedItemIdentifier:@"GeneralPrefsIdentifier"];
+
+ [currentView addConstraint:test];
+ [currentView addConstraint:test2];
+ [currentView addConstraint:test3];
+ // make sure we automatically resize the controller's view to the current window size
+ [[self.myCurrentViewController view] setFrame:[currentView bounds]];
+
+ // set the view controller's represented object to the number of subviews in that controller
+ // (our NSTextField's value binding will reflect this value)
+ [self.myCurrentViewController setRepresentedObject:[NSNumber numberWithUnsignedInteger:[[[self.myCurrentViewController view] subviews] count]]];
+
+}
+
+- (IBAction) closePreferences:(NSToolbarItem *)sender {
+ if(self.myCurrentViewController != nil)
+ {
+ [self.preferencesViewController close];
+ [self displayMainToolBar];
+ self.preferencesViewController = nil;
+ }
+}
+
+-(void) displayMainToolBar
+{
+ NSToolbar* tb = [[NSToolbar alloc] initWithIdentifier: @"MainToolbar"];
+ [tb setDisplayMode:NSToolbarDisplayModeIconAndLabel];
+ [tb setDelegate: self];
+ [self.window setToolbar: tb];
+}
+
+// FIXME: This is sick, NSWindowController is catching my selectors
+- (void)displayGeneral:(NSToolbarItem *)sender {
+ [self.preferencesViewController displayGeneral:sender];
+}
+
+- (void)displayAudio:(NSToolbarItem *)sender {
+ [self.preferencesViewController displayAudio:sender];
+}
+
+- (void)displayAncrage:(NSToolbarItem *)sender {
+ [self.preferencesViewController displayAncrage:sender];
+}
+
+- (void)displayVideo:(NSToolbarItem *)sender {
+ [self.preferencesViewController displayVideo:sender];
+}
+
+- (void)displayAccounts:(NSToolbarItem *)sender {
+ [self.preferencesViewController displayAccounts:sender];
+}
+
+- (void)togglePowerSettings:(id)sender
+{
+ BOOL advanced = [[NSUserDefaults standardUserDefaults] boolForKey:@"show_advanced"];
+ [[NSUserDefaults standardUserDefaults] setBool:!advanced forKey:@"show_advanced"];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+
+ NSToolbar* tb = [[NSToolbar alloc] initWithIdentifier: @"PreferencesToolbar"];
+ [tb setDelegate: self.preferencesViewController];
+ [self.preferencesViewController displayGeneral:nil];
+ [self.window setToolbar:tb];
+}
+
+#pragma NSToolbar Delegate
+
+-(NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
+{
+ NSToolbarItem* item = nil;
+
+ if ([itemIdentifier isEqualToString: kSearchViewIdentifier]) {
+ item = [[NSToolbarItem alloc] initWithItemIdentifier: kSearchViewIdentifier];
+ callField = [[NSSearchField alloc] initWithFrame:NSMakeRect(0,0,400,21)];
+ [[callField cell] setSearchButtonCell:nil];
+ [callField setToolTip:@"Call"];
+ [callField setAlignment:NSCenterTextAlignment];
+ [callField setDelegate:self];
+ [item setView:callField];
+ }
+
+ if ([itemIdentifier isEqualToString: kCallButtonIdentifer]) {
+ item = [[NSToolbarItem alloc] initWithItemIdentifier: kCallButtonIdentifer];
+
+ NSButton *callButton = [[NSButton alloc] initWithFrame:NSMakeRect(0,0,80,30)];
+
+ [callButton setButtonType:NSMomentaryLightButton]; //Set what type button You want
+ [callButton setBezelStyle:NSRoundedBezelStyle]; //Set what style You want]
+ [callButton setBordered:YES];
+ [callButton setTitle:@"Call"];
+ [item setView:callButton];
+ [item setAction:@selector(placeCall:)];
+ }
+
+ if ([itemIdentifier isEqualToString: kPreferencesIdentifier]) {
+ item = [[NSToolbarItem alloc] initWithItemIdentifier: kPreferencesIdentifier];
+ [item setImage: [NSImage imageNamed: @"NSAdvanced"]];
+ [item setLabel: @"Settings"];
+ [item setAction:@selector(openPreferences:)];
+ }
+
+ return item;
+}
+
+- (IBAction)placeCall:(id)sender
+{
+ Call* c = CallModel::instance()->dialingCall();
+
+ // check for a valid ring hash
+ NSCharacterSet *hexSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789abcdefABCDEF"];
+ BOOL valid = [[[callField stringValue] stringByTrimmingCharactersInSet:hexSet] isEqualToString:@""];
+
+ if(valid && callField.stringValue.length == 40) {
+ c->setDialNumber(QString::fromNSString([NSString stringWithFormat:@"ring:%@",[callField stringValue]]));
+ } else {
+ c->setDialNumber(QString::fromNSString([callField stringValue]));
+ }
+
+ c << Call::Action::ACCEPT;
+}
+
+-(NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
+{
+ return [NSArray arrayWithObjects:
+ NSToolbarSpaceItemIdentifier,
+ NSToolbarFlexibleSpaceItemIdentifier,
+ NSToolbarSpaceItemIdentifier,
+ NSToolbarSpaceItemIdentifier,
+ kSearchViewIdentifier,
+ kCallButtonIdentifer,
+ NSToolbarFlexibleSpaceItemIdentifier,
+ kPreferencesIdentifier,
+ nil];
+}
+
+-(NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
+{
+ return [NSArray arrayWithObjects:
+ kSearchViewIdentifier,
+ kCallButtonIdentifer,
+ kPreferencesIdentifier,
+ nil];
+}
+
+-(NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
+{
+ return nil;
+}
+
+#pragma NSTextField Delegate
+
+- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
+{
+ if (commandSelector == @selector(insertNewline:)) {
+ if([[callField stringValue] isNotEqualTo:@""]) {
+ [self placeCall:nil];
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+
+@end
diff --git a/src/RingWizardWC.h b/src/RingWizardWC.h
new file mode 100644
index 0000000..e3ca858
--- /dev/null
+++ b/src/RingWizardWC.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface RingWizardWC : NSWindowController <NSWindowDelegate>{
+
+ NSButton *goToAppButton;
+}
+
+@end
diff --git a/src/RingWizardWC.mm b/src/RingWizardWC.mm
new file mode 100644
index 0000000..57ed1d9
--- /dev/null
+++ b/src/RingWizardWC.mm
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "RingWizardWC.h"
+
+#import <accountmodel.h>
+#import <protocolmodel.h>
+#import <QItemSelectionModel>
+#import <account.h>
+
+#import "AppDelegate.h"
+
+
+@interface RingWizardWC ()
+@property (assign) IBOutlet NSButton *goToAppButton;
+@property (assign) IBOutlet NSTextField *nickname;
+@property (assign) IBOutlet NSProgressIndicator *progressBar;
+@property (assign) IBOutlet NSTextField *indicationLabel;
+@property (assign) IBOutlet NSButton *createButton;
+@end
+
+@implementation RingWizardWC
+@synthesize goToAppButton;
+@synthesize nickname;
+@synthesize progressBar;
+@synthesize indicationLabel;
+@synthesize createButton;
+
+- (void)windowDidLoad {
+ [super windowDidLoad];
+
+ [self.window makeKeyAndOrderFront:nil];
+ [self.window setLevel:NSStatusWindowLevel];
+ [self.window makeMainWindow];
+ [self checkForRingAccount];
+}
+
+- (void) checkForRingAccount
+{
+ for (int i = 0 ; i < AccountModel::instance()->rowCount() ; ++i) {
+ QModelIndex idx = AccountModel::instance()->index(i);
+ Account* acc = AccountModel::instance()->getAccountByModelIndex(idx);
+ if(acc->protocol() == Account::Protocol::RING) {
+ [indicationLabel setStringValue:@"Ring is already ready to work"];
+ [self displayHash:acc->username().toNSString()];
+ }
+ }
+}
+
+- (void) displayHash:(NSString* ) hash
+{
+ [nickname setFrameSize:NSMakeSize(400, nickname.frame.size.height)];
+ [nickname setStringValue:hash];
+ [nickname setEditable:NO];
+ [nickname setHidden:NO];
+
+ [goToAppButton setHidden:NO];
+
+ NSSharingService* emailSharingService = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail];
+
+ [createButton setTitle:@"Share by mail"];
+ //[createButton setImage:emailSharingService.image];
+ [createButton setAlternateImage:emailSharingService.alternateImage];
+ [createButton setAction:@selector(shareByEmail)];
+}
+
+- (IBAction)createRingAccount:(id)sender {
+
+ [nickname setHidden:YES];
+ [progressBar setHidden:NO];
+ [createButton setEnabled:NO];
+ [indicationLabel setStringValue:@"Just a moment..."];
+
+ QModelIndex qIdx = AccountModel::instance()->protocolModel()->selectionModel()->currentIndex();
+
+ [self setCallback];
+ [self performSelector:@selector(saveAccount) withObject:nil afterDelay:1];
+
+}
+
+- (void) saveAccount
+{
+ NSString* newAccName = @"My Ring";
+ Account* newAcc = AccountModel::instance()->add([newAccName UTF8String], Account::Protocol::RING);
+ newAcc->setAlias([[nickname stringValue] UTF8String]);
+ newAcc << Account::EditAction::SAVE;
+}
+
+- (void) setCallback
+{
+ QObject::connect(AccountModel::instance(),
+ &AccountModel::accountStateChanged,
+ [=](Account *account, const Account::RegistrationState state) {
+ NSLog(@"Account created!");
+ [progressBar setHidden:YES];
+ [createButton setEnabled:YES];
+ [indicationLabel setStringValue:@"This is your number, share it with your friends!"];
+ [self displayHash:account->username().toNSString()];
+ });
+}
+
+- (IBAction)goToApp:(id)sender {
+ [self.window close];
+ AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
+ [appDelegate showMainWindow];
+}
+
+- (void) shareByEmail
+{
+ /*
+ Create the array of items to share.
+ Start with just the content of the text view. If there's an image, add that too.
+ */
+ NSMutableArray *shareItems = [[NSMutableArray alloc] initWithObjects:[nickname stringValue], nil];
+ NSSharingService* emailSharingService = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail];
+
+ /*
+ Perform the service using the array of items.
+ */
+ [emailSharingService performWithItems:shareItems];
+}
+
+
+# pragma NSWindowDelegate methods
+
+- (BOOL)windowShouldClose:(id)sender
+{
+ NSLog(@"windowShouldClose");
+ return YES;
+}
+
+- (void)windowDidBecomeKey:(NSNotification *)notification
+{
+ NSLog(@"windowDidBecomeKey");
+}
+
+- (void)windowDidResignKey:(NSNotification *)notification
+{
+ NSLog(@"windowDidResignKey");
+}
+
+- (void)windowDidBecomeMain:(NSNotification *)notification
+{
+ NSLog(@"windowDidBecomeMain");
+}
+
+- (void)windowDidResignMain:(NSNotification *)notification
+{
+ NSLog(@"windowDidResignMain");
+ [self.window close];
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+ //NSLog(@"windowWillClose");
+ AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
+ [appDelegate showMainWindow];
+}
+
+@end
diff --git a/src/VideoPrefsVC.h b/src/VideoPrefsVC.h
new file mode 100644
index 0000000..8a85304
--- /dev/null
+++ b/src/VideoPrefsVC.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#ifndef RING_VIDEOPREFSVC_H
+#define RING_VIDEOPREFSVC_H
+
+#import <Cocoa/Cocoa.h>
+
+@interface VideoPrefsVC : NSViewController <NSMenuDelegate> {
+
+}
+
+@end
+
+#endif // RING_VIDEOPREFSVC_H
diff --git a/src/VideoPrefsVC.mm b/src/VideoPrefsVC.mm
new file mode 100644
index 0000000..79a9353
--- /dev/null
+++ b/src/VideoPrefsVC.mm
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import "VideoPrefsVC.h"
+
+#import <QuartzCore/QuartzCore.h>
+
+#import <QItemSelectionModel>
+#import <QAbstractProxyModel>
+
+#import <video/configurationproxy.h>
+#import <video/sourcemodel.h>
+#import <video/previewmanager.h>
+#import <video/renderer.h>
+#import <video/device.h>
+#import <video/devicemodel.h>
+
+@interface VideoPrefsVC ()
+
+@property (assign) IBOutlet NSView *previewView;
+@property (assign) IBOutlet NSPopUpButton *videoDevicesList;
+@property (assign) IBOutlet NSPopUpButton *sizesList;
+@property (assign) IBOutlet NSPopUpButton *ratesList;
+
+@end
+
+@implementation VideoPrefsVC
+@synthesize previewView;
+@synthesize videoDevicesList;
+@synthesize sizesList;
+@synthesize ratesList;
+
+QMetaObject::Connection frameUpdated;
+QMetaObject::Connection previewStarted;
+QMetaObject::Connection previewStopped;
+
+- (void)loadView
+{
+ [super loadView];
+
+ Video::ConfigurationProxy::deviceModel()->rowCount();
+ Video::ConfigurationProxy::resolutionModel()->rowCount();
+ Video::ConfigurationProxy::rateModel()->rowCount();
+
+ QModelIndex qDeviceIdx = Video::ConfigurationProxy::deviceSelectionModel()->currentIndex();
+ qDeviceIdx = Video::ConfigurationProxy::deviceSelectionModel()->currentIndex();
+
+ [videoDevicesList addItemWithTitle:Video::ConfigurationProxy::deviceModel()->data(qDeviceIdx, Qt::DisplayRole).toString().toNSString()];
+
+ QModelIndex qSizeIdx = Video::ConfigurationProxy::resolutionSelectionModel()->currentIndex();
+ [sizesList addItemWithTitle:Video::ConfigurationProxy::resolutionModel()->data(qSizeIdx, Qt::DisplayRole).toString().toNSString()];
+
+ if(qobject_cast<QAbstractProxyModel*>(Video::ConfigurationProxy::resolutionModel())) {
+ QObject::connect(qobject_cast<QAbstractProxyModel*>(Video::ConfigurationProxy::resolutionModel()),
+ &QAbstractProxyModel::modelReset,
+ [=]() {
+ NSLog(@"resolution Source model changed!!!");
+ });
+
+ }
+
+ QModelIndex qRate = Video::ConfigurationProxy::rateSelectionModel()->currentIndex();
+ [ratesList addItemWithTitle:Video::ConfigurationProxy::rateModel()->data(qDeviceIdx, Qt::DisplayRole).toString().toNSString()];
+
+ if(qobject_cast<QAbstractProxyModel*>(Video::ConfigurationProxy::rateModel())) {
+ QObject::connect(qobject_cast<QAbstractProxyModel*>(Video::ConfigurationProxy::rateModel()),
+ &QAbstractProxyModel::modelReset,
+ [=]() {
+ NSLog(@"rates Source model changed!!!");
+ });
+
+ }
+
+
+ [previewView setWantsLayer:YES];
+ [previewView setLayer:[CALayer layer]];
+ [previewView.layer setBackgroundColor:[NSColor blackColor].CGColor];
+ [previewView.layer setContentsGravity:kCAGravityResizeAspect];
+ [previewView.layer setFrame:previewView.frame];
+ [previewView.layer setBounds:previewView.frame];
+
+ [self connectPreviewSignals];
+}
+
+- (IBAction)chooseDevice:(id)sender {
+ int index = [sender indexOfSelectedItem];
+ QModelIndex qIdx = Video::ConfigurationProxy::deviceModel()->index(index, 0);
+ Video::ConfigurationProxy::deviceSelectionModel()->setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
+}
+
+- (IBAction)chooseSize:(id)sender {
+ int index = [sender indexOfSelectedItem];
+ QModelIndex qIdx = Video::ConfigurationProxy::resolutionModel()->index(index, 0);
+ Video::ConfigurationProxy::resolutionSelectionModel()->setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
+}
+
+- (IBAction)chooseRate:(id)sender {
+ int index = [sender indexOfSelectedItem];
+ QModelIndex qIdx = Video::ConfigurationProxy::rateModel()->index(index, 0);
+ Video::ConfigurationProxy::rateSelectionModel()->setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
+}
+
+- (void) connectPreviewSignals
+{
+ QObject::disconnect(frameUpdated);
+ QObject::disconnect(previewStopped);
+ QObject::disconnect(previewStarted);
+ previewStarted = QObject::connect(Video::PreviewManager::instance(),
+ &Video::PreviewManager::previewStarted,
+ [=](Video::Renderer* renderer) {
+ NSLog(@"Preview started");
+ QObject::disconnect(frameUpdated);
+ frameUpdated = QObject::connect(renderer,
+ &Video::Renderer::frameUpdated,
+ [=]() {
+ [self renderer:Video::PreviewManager::instance()->previewRenderer() renderFrameForView:previewView];
+ });
+ });
+
+ previewStopped = QObject::connect(Video::PreviewManager::instance(),
+ &Video::PreviewManager::previewStopped,
+ [=](Video::Renderer* renderer) {
+ NSLog(@"Preview stopped");
+ QObject::disconnect(frameUpdated);
+ [previewView.layer setContents:nil];
+ });
+
+ frameUpdated = QObject::connect(Video::PreviewManager::instance()->previewRenderer(),
+ &Video::Renderer::frameUpdated,
+ [=]() {
+ [self renderer:Video::PreviewManager::instance()->previewRenderer()
+ renderFrameForView:previewView];
+ });
+}
+
+-(void) renderer: (Video::Renderer*)renderer renderFrameForView:(NSView*) view
+{
+ const QByteArray& data = renderer->currentFrame();
+ QSize res = renderer->size();
+
+ auto buf = reinterpret_cast<const unsigned char*>(data.data());
+
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGContextRef newContext = CGBitmapContextCreate((void *)buf,
+ res.width(),
+ res.height(),
+ 8,
+ 4*res.width(),
+ colorSpace,
+ kCGImageAlphaPremultipliedLast);
+
+
+ CGImageRef newImage = CGBitmapContextCreateImage(newContext);
+
+ /*We release some components*/
+ CGContextRelease(newContext);
+ CGColorSpaceRelease(colorSpace);
+
+ [CATransaction begin];
+ view.layer.contents = (__bridge id)newImage;
+ [CATransaction commit];
+
+ CFRelease(newImage);
+}
+
+- (IBAction)togglePreview:(id)sender {
+ if([sender state] == NSOnState)
+ Video::PreviewManager::instance()->startPreview();
+ else
+ Video::PreviewManager::instance()->stopPreview();
+}
+
+- (void)viewWillDisappear
+{
+ Video::PreviewManager::instance()->stopPreview();
+}
+
+#pragma mark - NSMenuDelegate methods
+
+- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel
+{
+ QModelIndex qIdx;
+ if([menu.title isEqualToString:@"devices"]) {
+
+ qIdx = Video::ConfigurationProxy::deviceModel()->index(index, 0);
+ [item setTitle:Video::ConfigurationProxy::deviceModel()->data(qIdx, Qt::DisplayRole).toString().toNSString()];
+
+ } else if([menu.title isEqualToString:@"sizes"]) {
+
+ qIdx = Video::ConfigurationProxy::resolutionModel()->index(index, 0);
+ [item setTitle:Video::ConfigurationProxy::resolutionModel()->data(qIdx, Qt::DisplayRole).toString().toNSString()];
+
+ } else if([menu.title isEqualToString:@"rates"]) {
+
+ qIdx = Video::ConfigurationProxy::rateModel()->index(index, 0);
+ [item setTitle:Video::ConfigurationProxy::rateModel()->data(qIdx, Qt::DisplayRole).toString().toNSString()];
+
+ }
+ return YES;
+}
+
+- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
+{
+ if([menu.title isEqualToString:@"devices"]) {
+ return Video::ConfigurationProxy::deviceModel()->rowCount();
+ } else if([menu.title isEqualToString:@"sizes"]) {
+ return Video::ConfigurationProxy::resolutionModel()->rowCount();
+ } else if([menu.title isEqualToString:@"rates"]) {
+ return Video::ConfigurationProxy::rateModel()->rowCount();
+ }
+}
+
+@end
diff --git a/src/backends/MinimalHistoryBackend.h b/src/backends/MinimalHistoryBackend.h
new file mode 100644
index 0000000..50eef51
--- /dev/null
+++ b/src/backends/MinimalHistoryBackend.h
@@ -0,0 +1,50 @@
+/************************************************************************************
+ * Copyright (C) 2014-2015 by Savoir-Faire Linux *
+ * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this library; if not, write to the Free Software *
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***********************************************************************************/
+#ifndef MINIMALHISTORYBACKEND_H
+#define MINIMALHISTORYBACKEND_H
+
+#import <collectioninterface.h>
+
+class Call;
+
+template<typename T> class CollectionMediator;
+
+class LIB_EXPORT MinimalHistoryBackend : public CollectionInterface
+{
+public:
+ explicit MinimalHistoryBackend(CollectionMediator<Call>* mediator);
+ virtual ~MinimalHistoryBackend();
+
+ virtual bool load() override;
+ virtual bool reload() override;
+ virtual bool clear() override;
+ virtual QString name () const override;
+ virtual QString category () const override;
+ virtual QVariant icon () const override;
+ virtual bool isEnabled() const override;
+ virtual QByteArray id () const override;
+ virtual SupportedFeatures supportedFeatures() const override;
+ int daysSince(time_t timestamp);
+
+
+private:
+ CollectionMediator<Call>* m_pMediator;
+};
+
+#endif
diff --git a/src/backends/MinimalHistoryBackend.mm b/src/backends/MinimalHistoryBackend.mm
new file mode 100644
index 0000000..2f126cd
--- /dev/null
+++ b/src/backends/MinimalHistoryBackend.mm
@@ -0,0 +1,290 @@
+/************************************************************************************
+ * Copyright (C) 2014-2015 by Savoir-Faire Linux *
+ * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this library; if not, write to the Free Software *
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***********************************************************************************/
+#import "minimalhistorybackend.h"
+
+#import <Cocoa/Cocoa.h>
+
+//Qt
+#import <QtCore/QFile>
+#import <QtCore/QDir>
+#import <QtCore/qlist.h>
+#import <QtCore/QHash>
+#import <QtWidgets/QApplication>
+#import <QtCore/QStandardPaths>
+#import <collectioneditor.h>
+
+//Ring
+#import <call.h>
+#import <account.h>
+#import <person.h>
+#import <contactmethod.h>
+#import <categorizedhistorymodel.h>
+
+class MinimalHistoryEditor : public CollectionEditor<Call>
+{
+public:
+ MinimalHistoryEditor(CollectionMediator<Call>* m, MinimalHistoryBackend* parent);
+ virtual bool save ( const Call* item ) override;
+ virtual bool remove ( const Call* item ) override;
+ virtual bool batchRemove(const QList<Call*> contacts) override;
+ virtual bool edit ( Call* item ) override;
+ virtual bool addNew ( const Call* item ) override;
+ virtual bool addExisting( const Call* item ) override;
+
+private:
+ virtual QVector<Call*> items() const override;
+
+ //Helpers
+ void saveCall(QTextStream& stream, const Call* call);
+ bool regenFile(const Call* toIgnore);
+
+ //Attributes
+ QVector<Call*> m_lItems;
+ MinimalHistoryBackend* m_pCollection;
+};
+
+MinimalHistoryEditor::MinimalHistoryEditor(CollectionMediator<Call>* m, MinimalHistoryBackend* parent) :
+CollectionEditor<Call>(m),m_pCollection(parent)
+{
+
+}
+
+MinimalHistoryBackend::MinimalHistoryBackend(CollectionMediator<Call>* mediator) :
+CollectionInterface(new MinimalHistoryEditor(mediator,this)),m_pMediator(mediator)
+{
+
+}
+
+MinimalHistoryBackend::~MinimalHistoryBackend()
+{
+
+}
+
+void MinimalHistoryEditor::saveCall(QTextStream& stream, const Call* call)
+{
+ const QString direction = (call->direction()==Call::Direction::INCOMING)?
+ Call::HistoryStateName::INCOMING : Call::HistoryStateName::OUTGOING;
+
+ const Account* a = call->account();
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::CALLID ).arg(call->historyId() );
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::TIMESTAMP_START ).arg(call->startTimeStamp() );
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::TIMESTAMP_STOP ).arg(call->stopTimeStamp() );
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::ACCOUNT_ID ).arg(a?QString(a->id()):"" );
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::DISPLAY_NAME ).arg(call->peerName() );
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::PEER_NUMBER ).arg(call->peerContactMethod()->uri() );
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::DIRECTION ).arg(direction );
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::MISSED ).arg(call->isMissed() );
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::RECORDING_PATH ).arg(call->recordingPath() );
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::CONTACT_USED ).arg(false );//TODO
+ if (call->peerContactMethod()->contact()) {
+ stream << QString("%1=%2\n").arg(Call::HistoryMapFields::CONTACT_UID ).arg(
+ QString(call->peerContactMethod()->contact()->uid())
+ );
+ }
+ stream << "\n";
+ stream.flush();
+}
+
+bool MinimalHistoryEditor::regenFile(const Call* toIgnore)
+{
+ QDir dir(QString('/'));
+ dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + QString());
+
+ QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') +"history.ini");
+ if ( file.open(QIODevice::WriteOnly | QIODevice::Text) ) {
+ QTextStream stream(&file);
+ for (const Call* c : CategorizedHistoryModel::instance()->getHistoryCalls()) {
+ if (c != toIgnore)
+ saveCall(stream, c);
+ }
+ file.close();
+ return true;
+ }
+ return false;
+}
+
+bool MinimalHistoryEditor::save(const Call* call)
+{
+ if (call->collection()->editor<Call>() != this)
+ return addNew(call);
+
+ return regenFile(nullptr);
+}
+
+bool MinimalHistoryEditor::remove(const Call* item)
+{
+ mediator()->removeItem(item);
+ return regenFile(item);
+}
+
+bool MinimalHistoryEditor::batchRemove(const QList<Call*> calls) {
+ QFile::remove(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + "history.ini");
+ return YES;
+}
+
+bool MinimalHistoryEditor::edit( Call* item)
+{
+ Q_UNUSED(item)
+ return false;
+}
+
+bool MinimalHistoryEditor::addNew(const Call* call)
+{
+ QDir dir(QString('/'));
+ dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + QString());
+
+ if ((call->collection() && call->collection()->editor<Call>() == this) || call->historyId().isEmpty()) return false;
+ //TODO support \r and \n\r end of line
+ QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/')+"history.ini");
+
+ if ( file.open(QIODevice::Append | QIODevice::Text) ) {
+ QTextStream streamFileOut(&file);
+ saveCall(streamFileOut, call);
+ file.close();
+
+ const_cast<Call*>(call)->setCollection(m_pCollection);
+ addExisting(call);
+ return true;
+ }
+ else
+ qWarning() << "Unable to save history";
+ return false;
+}
+
+bool MinimalHistoryEditor::addExisting(const Call* item)
+{
+ m_lItems << const_cast<Call*>(item);
+ mediator()->addItem(item);
+ return true;
+}
+
+QVector<Call*> MinimalHistoryEditor::items() const
+{
+ return m_lItems;
+}
+
+QString MinimalHistoryBackend::name () const
+{
+ return QObject::tr("Minimal history backend");
+}
+
+QString MinimalHistoryBackend::category () const
+{
+ return QObject::tr("History");
+}
+
+QVariant MinimalHistoryBackend::icon() const
+{
+ return QVariant();
+}
+
+bool MinimalHistoryBackend::isEnabled() const
+{
+ return true;
+}
+
+bool MinimalHistoryBackend::load()
+{
+ // get history limit from our preferences set
+ NSInteger historyLimit = [[NSUserDefaults standardUserDefaults] integerForKey:@"history_limit"];
+
+ QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') +"history.ini");
+ if ( file.open(QIODevice::ReadOnly | QIODevice::Text) ) {
+ QMap<QString,QString> hc;
+ while (!file.atEnd()) {
+ QByteArray line = file.readLine().trimmed();
+
+ //The item is complete
+ if ((line.isEmpty() || !line.size()) && hc.size()) {
+ Call* pastCall = Call::buildHistoryCall(hc);
+ if (pastCall->peerName().isEmpty()) {
+ pastCall->setPeerName(QObject::tr("Unknown"));
+ }
+
+ if(daysSince(pastCall->startTimeStamp()) < historyLimit) {
+ pastCall->setRecordingPath(hc[ Call::HistoryMapFields::RECORDING_PATH ]);
+ pastCall->setCollection(this);
+
+ editor<Call>()->addExisting(pastCall);
+ }
+ hc.clear();
+ }
+ // Add to the current set
+ else {
+ const int idx = line.indexOf("=");
+ if (idx >= 0)
+ hc[line.left(idx)] = line.right(line.size()-idx-1);
+ }
+ }
+ return true;
+ }
+ else
+ qWarning() << "History doesn't exist or is not readable";
+ return false;
+}
+
+int MinimalHistoryBackend::daysSince(time_t timestamp)
+{
+ NSDate *fromDate;
+ NSDate *toDate;
+
+ NSDate* fromDateTime = [NSDate dateWithTimeIntervalSince1970:timestamp];
+
+ NSCalendar *calendar = [NSCalendar currentCalendar];
+
+ [calendar rangeOfUnit:NSCalendarUnitDay startDate:&fromDate
+ interval:NULL forDate:fromDateTime];
+ [calendar rangeOfUnit:NSCalendarUnitDay startDate:&toDate
+ interval:NULL forDate:[NSDate date]];
+
+ NSDateComponents *difference = [calendar components:NSCalendarUnitDay
+ fromDate:fromDate toDate:toDate options:0];
+
+ return [difference day];
+}
+
+bool MinimalHistoryBackend::reload()
+{
+ return false;
+}
+
+CollectionInterface::SupportedFeatures MinimalHistoryBackend::supportedFeatures() const
+{
+ return (CollectionInterface::SupportedFeatures) (
+ CollectionInterface::SupportedFeatures::NONE |
+ CollectionInterface::SupportedFeatures::LOAD |
+ CollectionInterface::SupportedFeatures::CLEAR |
+ CollectionInterface::SupportedFeatures::REMOVE|
+ CollectionInterface::SupportedFeatures::ADD );
+}
+
+bool MinimalHistoryBackend::clear()
+{
+ editor<Call>()->batchRemove(items<Call>().toList());
+ QList<Call*> calls = items<Call>().toList();
+ for(int i = 0 ; i < calls.count() ; ++i) {
+ CategorizedHistoryModel::instance()->deleteItem(calls[i]);
+ }
+ return true;
+}
+
+QByteArray MinimalHistoryBackend::id() const
+{
+ return "mhb";
+}
diff --git a/src/main.mm b/src/main.mm
new file mode 100644
index 0000000..c1d8676
--- /dev/null
+++ b/src/main.mm
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+#import <AppKit/NSApplication.h> // NSApplicationMain
+#import <qapplication.h>
+#import <QDebug>
+#import <QDir>
+
+int main(int argc, const char *argv[]) {
+
+ QDir dir(QString::fromUtf8(argv[0]));
+ dir.cdUp();
+ dir.cdUp();
+ dir.cd("Plugins");
+ QCoreApplication::addLibraryPath(dir.absolutePath());
+ qDebug() << "" << QCoreApplication::libraryPaths();
+ //Qt event loop will override native event loop
+ QApplication* app = new QApplication(argc, const_cast<char**>(argv));
+ app->setAttribute(Qt::AA_MacPluginApplication);
+
+
+ return NSApplicationMain(argc, argv);
+}