Commit 4de68ce8 authored by Alexandre Lision's avatar Alexandre Lision Committed by Guillaume Roguez

security: add options for SIP accounts

This commits adds ability to enable TLS and SRTP
in SIP accounts preferences.

Refs #73027
Change-Id: Idad3d1cd3c325fa370855af79b50b2da19a87832
parent 8131541e
......@@ -56,6 +56,8 @@ SET(ringclient_CONTROLLERS
src/AccAdvancedVC.h
src/AccSecurityVC.mm
src/AccSecurityVC.h
src/CertificateWC.mm
src/CertificateWC.h
src/AudioPrefsVC.mm
src/AudioPrefsVC.h
src/AccountsVC.mm
......@@ -103,7 +105,8 @@ SET(ringclient_XIBS
AudioPrefs
VideoPrefs
PreferencesScreen
RingWizard)
RingWizard
CertificateWindow)
# Icons
# This part tells CMake where to find and install the file itself
......
......@@ -137,22 +137,6 @@
#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];
......
......@@ -54,6 +54,8 @@
@property (assign) IBOutlet NSTextField *serverHostTextField;
@property (assign) IBOutlet NSTextField *usernameTextField;
@property (assign) IBOutlet NSSecureTextField *passwordTextField;
@property (assign) IBOutlet NSTextField *clearTextField;
@property (assign) IBOutlet NSButton *tryRegisterButton;
@property (assign) IBOutlet NSButton *upnpButton;
@property (assign) IBOutlet NSButton *autoAnswerButton;
......@@ -72,6 +74,7 @@
@synthesize serverHostTextField;
@synthesize usernameTextField;
@synthesize passwordTextField;
@synthesize clearTextField;
@synthesize upnpButton;
@synthesize autoAnswerButton;
@synthesize userAgentButton;
......@@ -124,6 +127,7 @@
[self.serverHostTextField setStringValue:account->hostname().toNSString()];
[self.usernameTextField setStringValue:account->username().toNSString()];
[self.passwordTextField setStringValue:account->password().toNSString()];
[self.clearTextField setStringValue:account->password().toNSString()];
}
switch (account->protocol()) {
......@@ -148,6 +152,36 @@
[self.userAgentTextField setStringValue:account->userAgent().toNSString()];
}
- (IBAction)tryRegistration:(id)sender {
self.privateAccount << Account::EditAction::SAVE;
}
- (IBAction)showPassword:(NSButton *)sender {
if (sender.state == NSOnState) {
clearTextField = [[NSTextField alloc] initWithFrame:passwordTextField.frame];
[clearTextField setTag:passwordTextField.tag];
[clearTextField setDelegate:self];
[clearTextField setBounds:passwordTextField.bounds];
[clearTextField setStringValue:passwordTextField.stringValue];
[clearTextField becomeFirstResponder];
[boxingParameters addSubview:clearTextField];
[passwordTextField setHidden:YES];
} else {
[passwordTextField setStringValue:clearTextField.stringValue];
[passwordTextField setHidden:NO];
[clearTextField removeFromSuperview];
clearTextField = nil;
}
}
/**
* 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 mark - NSTextFieldDelegate methods
- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor
......
......@@ -34,7 +34,7 @@
#import <account.h>
@interface AccSecurityVC : NSViewController<NSPathControlDelegate, NSOpenSavePanelDelegate> {
@interface AccSecurityVC : NSViewController<NSMenuDelegate, NSPathControlDelegate, NSOpenSavePanelDelegate> {
}
......
......@@ -29,26 +29,305 @@
*/
#import "AccSecurityVC.h"
#import <QUrl>
#import <certificate.h>
#import <tlsmethodmodel.h>
#import <qitemselectionmodel.h>
#import <ciphermodel.h>
#import "QNSTreeController.h"
#import "CertificateWC.h"
// Tags for views
#define PVK_PASSWORD_TAG 0
#define OUTGOING_TLS_SRV_NAME 1
#define TLS_NEGOTIATION_TAG 2
#define COLUMNID_NAME @"CipherNameColumn"
#define COLUMNID_STATE @"CipherStateColumn"
@interface AccSecurityVC ()
@property Account* privateAccount;
@property NSTreeController *treeController;
@property (unsafe_unretained) IBOutlet NSOutlineView *cipherListView;
@property (unsafe_unretained) IBOutlet NSButton *useTLS;
@property (unsafe_unretained) IBOutlet NSView *tlsContainer;
@property (unsafe_unretained) IBOutlet NSSecureTextField *pvkPasswordField;
@property (unsafe_unretained) IBOutlet NSTextField *outgoingTlsServerName;
@property (unsafe_unretained) IBOutlet NSTextField *tlsNegotiationTimeout;
@property (unsafe_unretained) IBOutlet NSStepper *tlsNegotiationTimeoutStepper;
@property CertificateWC* certificateWC;
@property (unsafe_unretained) IBOutlet NSPathControl *caListPathControl;
@property (unsafe_unretained) IBOutlet NSPathControl *certificatePathControl;
@property (unsafe_unretained) IBOutlet NSPathControl *pvkPathControl;
@property (unsafe_unretained) IBOutlet NSPopUpButton *tlsMethodList;
@property (unsafe_unretained) IBOutlet NSButton *srtpRTPFallback;
@property (unsafe_unretained) IBOutlet NSButton *useSRTP;
@property (unsafe_unretained) IBOutlet NSButton *verifyCertAsClientButton;
@property (unsafe_unretained) IBOutlet NSButton *verifyCertAsServerButton;
@property (unsafe_unretained) IBOutlet NSButton *requireCertButton;
@end
@implementation AccSecurityVC
@synthesize privateAccount;
@synthesize treeController;
@synthesize cipherListView;
@synthesize certificateWC;
@synthesize tlsContainer;
@synthesize useTLS;
@synthesize useSRTP;
@synthesize srtpRTPFallback;
@synthesize pvkPasswordField;
@synthesize tlsNegotiationTimeout;
@synthesize tlsNegotiationTimeoutStepper;
@synthesize outgoingTlsServerName;
@synthesize caListPathControl;
@synthesize certificatePathControl;
@synthesize pvkPathControl;
@synthesize verifyCertAsClientButton;
@synthesize verifyCertAsServerButton;
@synthesize requireCertButton;
- (void)awakeFromNib
{
NSLog(@"INIT Security VC");
[pvkPasswordField setTag:PVK_PASSWORD_TAG];
[outgoingTlsServerName setTag:OUTGOING_TLS_SRV_NAME];
[tlsNegotiationTimeoutStepper setTag:TLS_NEGOTIATION_TAG];
[tlsNegotiationTimeout setTag:TLS_NEGOTIATION_TAG];
}
- (void)loadAccount:(Account *)account
{
privateAccount = account;
[self updateControlsWithTag:PVK_PASSWORD_TAG];
[self updateControlsWithTag:OUTGOING_TLS_SRV_NAME];
[self updateControlsWithTag:TLS_NEGOTIATION_TAG];
QModelIndex qTlsMethodIdx = privateAccount->tlsMethodModel()->selectionModel()->currentIndex();
[self.tlsMethodList removeAllItems];
[self.tlsMethodList addItemWithTitle:qTlsMethodIdx.data(Qt::DisplayRole).toString().toNSString()];
treeController = [[QNSTreeController alloc] initWithQModel:privateAccount->cipherModel()];
[treeController setAvoidsEmptySelection:NO];
[treeController setAlwaysUsesMultipleValuesMarker:YES];
[treeController setChildrenKeyPath:@"children"];
[cipherListView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil];
[cipherListView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil];
[cipherListView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil];
[useTLS setState:privateAccount->isTlsEnabled()];
[tlsContainer setHidden:!privateAccount->isTlsEnabled()];
[useSRTP setState:privateAccount->isSrtpEnabled()];
[srtpRTPFallback setState:privateAccount->isSrtpRtpFallback()];
[srtpRTPFallback setEnabled:useSRTP.state];
NSArray * pathComponentArray = [self pathComponentArray];
if(privateAccount->tlsCaListCertificate() != nil) {
NSLog(@"CA ==> %@", privateAccount->tlsCaListCertificate()->path().toNSURL());
[caListPathControl setURL:privateAccount->tlsCaListCertificate()->path().toNSURL()];
} else {
[caListPathControl setURL:nil];
}
if(privateAccount->tlsCertificate() != nil) {
NSLog(@" CERT ==> %@", privateAccount->tlsCertificate()->path().toNSURL());
[certificatePathControl setURL:privateAccount->tlsCertificate()->path().toNSURL()];
} else {
[certificatePathControl setURL:nil];
}
if(privateAccount->tlsPrivateKeyCertificate() != nil) {
NSLog(@" PVK ==> %@", privateAccount->tlsPrivateKeyCertificate()->path().toNSURL());
[pvkPathControl setURL:privateAccount->tlsPrivateKeyCertificate()->path().toNSURL()];
} else {
[pvkPathControl setURL:nil];
}
[verifyCertAsServerButton setState:privateAccount->isTlsVerifyServer()];
[verifyCertAsClientButton setState:privateAccount->isTlsVerifyClient()];
[requireCertButton setState:privateAccount->isTlsRequireClientCertificate()];
}
/*
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;
}
- (IBAction)chooseTlsMethod:(id)sender {
int index = [sender indexOfSelectedItem];
QModelIndex qIdx = privateAccount->tlsMethodModel()->index(index, 0);
privateAccount->tlsMethodModel()->selectionModel()->setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
}
- (IBAction)toggleUseTLS:(id)sender {
privateAccount->setTlsEnabled([sender state]);
[tlsContainer setHidden:![sender state]];
}
- (IBAction)toggleUseSRTP:(id)sender {
privateAccount->setSrtpEnabled([sender state]);
[srtpRTPFallback setEnabled:[sender state]];
}
- (IBAction)toggleRTPFallback:(id)sender {
privateAccount->setSrtpRtpFallback([sender state]);
}
- (IBAction)toggleVerifyCertAsClient:(id)sender {
privateAccount->setTlsVerifyClient([sender state]);
}
- (IBAction)toggleVerifyCertServer:(id)sender {
privateAccount->setTlsVerifyServer([sender state]);
}
- (IBAction)toggleRequireCert:(id)sender {
privateAccount->setTlsRequireClientCertificate([sender state]);
}
- (IBAction)toggleCipher:(id)sender {
NSInteger row = [sender clickedRow];
NSTableColumn *col = [sender tableColumnWithIdentifier:COLUMNID_STATE];
NSButtonCell *cell = [col dataCellForRow:row];
privateAccount->cipherModel()->setData(privateAccount->cipherModel()->index(row, 0, QModelIndex()),
cell.state == NSOnState ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole);
}
- (void) updateControlsWithTag:(NSInteger) tag
{
switch (tag) {
case PVK_PASSWORD_TAG:
[pvkPasswordField setStringValue:privateAccount->tlsPassword().toNSString()];
break;
case OUTGOING_TLS_SRV_NAME:
[outgoingTlsServerName setStringValue:privateAccount->tlsServerName().toNSString()];
break;
case TLS_NEGOTIATION_TAG:
[tlsNegotiationTimeout setIntegerValue:privateAccount->tlsNegotiationTimeoutSec()];
[tlsNegotiationTimeoutStepper setIntegerValue:privateAccount->tlsNegotiationTimeoutSec()];
break;
default:
break;
}
}
#pragma mark - NSTextFieldDelegate methods
-(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 PVK_PASSWORD_TAG:
privateAccount->setTlsPassword([[sender stringValue] UTF8String]);
break;
case OUTGOING_TLS_SRV_NAME:
privateAccount->setTlsServerName([[sender stringValue] UTF8String]);
break;
case TLS_NEGOTIATION_TAG:
privateAccount->setTlsNegotiationTimeoutSec([sender integerValue]);
break;
default:
break;
}
[self updateControlsWithTag:[sender tag]];
}
#pragma mark - NSPathControl delegate methods
- (IBAction)caListPathControlSingleClick:(id)sender {
NSURL* fileURL = [[sender clickedPathComponentCell] URL];
NSLog(@"==> %@", fileURL);
[self.caListPathControl setURL:fileURL];
privateAccount->setTlsCaListCertificate(QUrl::fromNSURL(fileURL).toString());
}
- (IBAction)certificatePathControlSingleClick:(id)sender {
// Select that chosen component of the path.
NSURL* fileURL = [[sender clickedPathComponentCell] URL];
NSLog(@"==> %@", fileURL);
[self.certificatePathControl setURL:fileURL];
privateAccount->setTlsCertificate(QUrl::fromNSURL(fileURL).toString());
}
- (IBAction)pvkFilePathControlSingleClick:(id)sender {
NSURL* fileURL = [[sender clickedPathComponentCell] URL];
NSLog(@"==> %@", fileURL);
[self.pvkPathControl setURL:fileURL];
privateAccount->setTlsPrivateKeyCertificate(QUrl::fromNSURL(fileURL).toString());
// qDebug() << "TEST" << privateAccount->tlsPrivateKeyCertificate()->hasPrivateKey();
}
- (IBAction)showCA:(id)sender
{
certificateWC = [[CertificateWC alloc] initWithWindowNibName:@"CertificateWindow"];
[certificateWC setCertificate:privateAccount->tlsCaListCertificate()];
[self.view.window beginSheet:certificateWC.window completionHandler:nil];
}
- (IBAction)showEndpointCertificate:(id)sender
{
certificateWC = [[CertificateWC alloc] initWithWindowNibName:@"CertificateWindow"];
[certificateWC setCertificate:privateAccount->tlsCertificate()];
[self.view.window beginSheet:certificateWC.window completionHandler:nil];}
/*
Delegate method of NSPathControl to determine how the NSOpenPanel will look/behave.
......@@ -60,7 +339,16 @@
[openPanel setCanChooseDirectories:NO];
[openPanel setCanChooseFiles:YES];
[openPanel setResolvesAliases:YES];
[openPanel setTitle:NSLocalizedString(@"Choose a file", @"Open panel title")];
if(pathControl == self.caListPathControl) {
[openPanel setTitle:NSLocalizedString(@"Choose a CA list", @"Open panel title")];
} else if (pathControl == self.certificatePathControl) {
[openPanel setTitle:NSLocalizedString(@"Choose a certificate", @"Open panel title")];
} else {
[openPanel setTitle:NSLocalizedString(@"Choose a private key file", @"Open panel title")];
}
[openPanel setPrompt:NSLocalizedString(@"Choose", @"Open panel prompt for 'Choose a file'")];
[openPanel setDelegate:self];
}
......@@ -91,12 +379,97 @@
{
NSLog(@"validateURL");
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:@"tlsmethodlist"])
{
qIdx = privateAccount->tlsMethodModel()->index(index);
[item setTitle:qIdx.data(Qt::DisplayRole).toString().toNSString()];
}
return YES;
}
- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
{
if([menu.title isEqualToString:@"tlsmethodlist"])
return privateAccount->tlsMethodModel()->rowCount();
}
#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;
}
- (void)panel:(id)sender didChangeToDirectoryURL:(NSURL *)url
// -------------------------------------------------------------------------------
// textShouldEndEditing:fieldEditor
// -------------------------------------------------------------------------------
- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
{
//NSLog(@"didChangeToDirectoryURL");
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 = qIdx.data(Qt::DisplayRole).toString().toNSString();
}
}
// -------------------------------------------------------------------------------
// outlineViewSelectionDidChange:notification
// -------------------------------------------------------------------------------
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
// ask the tree controller for the current selection
if([[treeController selectedNodes] count] > 0) {
}
}
@end
......@@ -113,12 +113,9 @@ public:
[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)]];
......@@ -208,8 +205,8 @@ public:
[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];
[configPanels insertTabViewItem:advancedTabItem atIndex:3];
[configPanels insertTabViewItem:securityTabItem atIndex:4];
[self.generalVC loadAccount:acc];
[self.audioVC loadAccount:acc];
......@@ -270,6 +267,7 @@ public:
Account* toToggle = AccountModel::instance()->getAccountByModelIndex(accIdx);
NSButtonCell *cell = [col dataCellForRow:row];
toToggle->setEnabled(cell.state == NSOnState ? NO : YES);
toToggle << Account::EditAction::SAVE;
}
}
......@@ -288,7 +286,17 @@ public:
// -------------------------------------------------------------------------------
- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
NSCell *returnCell = [tableColumn dataCell];
NSCell *returnCell;
QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)];
// Prevent user from enabling/disabling IP2IP account
if ([[tableColumn identifier] isEqualToString:COLUMNID_ENABLE] &&
AccountModel::instance()->ip2ip()->index() == qIdx) {
returnCell = [[NSCell alloc] init];
} else {
returnCell = [tableColumn dataCell];
}
return returnCell;
}
......@@ -331,25 +339,30 @@ public:
{
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));
NSTextFieldCell* stateCell = cell;
Account::RegistrationState state = qvariant_cast<Account::RegistrationState>(qIdx.data((int)Account::Role::RegistrationState));
switch (state) {
case Account::RegistrationState::READY:
[cell setTitle:@"Ready"];
[stateCell setTextColor:[NSColor colorWithCalibratedRed:116/255.0 green:179/255.0 blue:93/255.0 alpha:1.0]];
[stateCell setTitle:@"Ready"];
break;
case Account::RegistrationState::TRYING:
[cell setTitle:@"Trying..."];
[stateCell setTextColor:[NSColor redColor]];
[stateCell setTitle:@"Trying..."];
break;
case Account::RegistrationState::UNREGISTERED:
[cell setTitle:@"Unregistered"];
[stateCell setTextColor:[NSColor blackColor]];
[stateCell setTitle:@"Unregistered"];
break;
case Account::RegistrationState::ERROR:
[cell setTitle:@"Error"];
[stateCell setTextColor:[NSColor redColor]];
[stateCell setTitle:@"Error"];
break;
default:
break;
}
} else if([[tableColumn identifier] isEqualToString:COLUMNID_ENABLE]) {
[cell setState:AccountModel::instance()->data(qIdx, Qt::CheckStateRole).value<BOOL>()];
[cell setState:qIdx.data(Qt::CheckStateRole).value<BOOL>()];
}
}
......@@ -396,7 +409,7 @@ public:
{
QModelIndex proxyIdx = proxyProtocolModel->index(index, 0);
QModelIndex qIdx = AccountModel::instance()->protocolModel()->index(proxyProtocolModel->mapToSource(proxyIdx).row());