Commit 49c00700 authored by Aline Gondim Santos's avatar Aline Gondim Santos Committed by Kateryna Kostiuk

plugins: add preferences to settings view

Change-Id: I8296c7b145b76fe6025255bedf326c091530b1c1
parent cfaf2907
......@@ -188,6 +188,10 @@ SET(ringclient_CONTROLLERS
src/ConnectToAccManagerVC.h
src/AccountBackupVC.mm
src/AccountBackupVC.h
src/PluginItemDelegateVC.h
src/PluginItemDelegateVC.mm
src/PreferenceTabDelegateVC.h
src/PreferenceTabDelegateVC.mm
)
SET(ringclient_VIEWS
......@@ -248,6 +252,12 @@ SET(ringclient_VIEWS
src/views/FileToSendCollectionItem.mm
src/views/DraggingDestinationView.h
src/views/DraggingDestinationView.mm
src/views/PluginCell.h
src/views/PluginCell.mm
src/views/PluginPreferenceTab.h
src/views/PluginPreferenceTab.mm
src/views/NoResponderTableView.h
src/views/NoResponderTableView.mm
)
SET(ringclient_OTHERS
......@@ -309,6 +319,9 @@ SET(ringclient_XIBS
ConnectToAccManagerVC
AccountBackupVC
FileToSendCollectionItem
PluginItemDelegate
PluginPreferenceTab
PreferenceTabDelegateVC
)
# Icons
......@@ -370,6 +383,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_camera.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_audio_msg.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_group.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_plugin.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_plugin_fill.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_reset.png
${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_picture.png
${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_moderator.png
${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_moderator_audio_muted.png
......
/*
* Copyright (C) 2021 Savoir-faire Linux Inc.
* Author: Aline Gondim Santos <aline.gondimsantos@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.
*/
#import <Cocoa/Cocoa.h>
#include <qstring.h>
#import "LrcModelsProtocol.h"
struct CallBackInfos {
NSInteger row = -1;
QString name = "" ;
};
struct PluginItemDelegateCallBacks {
std::function<void(CallBackInfos)> uninstall;
};
@interface PluginItemDelegateVC : NSViewController<LrcModelsProtocol, NSTableViewDataSource, NSTableViewDelegate> {}
-(void) setup:(const QString&)pluginName row:(NSInteger)row callbacks:(PluginItemDelegateCallBacks)callbacks;
@end
/*
* Copyright (C) 2021 Savoir-faire Linux Inc.
* Author: Aline Gondim Santos <aline.gondimsantos@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.
*/
#import "PluginItemDelegateVC.h"
#import "AppDelegate.h"
#import <api/pluginmodel.h>
#import "views/PluginPreferenceTab.h"
@interface PluginItemDelegateVC ()
@property (unsafe_unretained) IBOutlet NSSwitch *loadButton;
@property (unsafe_unretained) IBOutlet NSTextField *pluginNameLabel;
@property (unsafe_unretained) IBOutlet NSImageView *pluginIcon;
@property (unsafe_unretained) IBOutlet NSButton *disclosureButton;
@property (unsafe_unretained) IBOutlet NSStackView *hidableView;
@property (unsafe_unretained) IBOutlet NSTabView *tabView;
@property (unsafe_unretained) IBOutlet NSStackView *tabViewContainer;
@property (unsafe_unretained) IBOutlet NSTableView *preferencesListView;
@property (unsafe_unretained) IBOutlet NSButton *reset;
@property (unsafe_unretained) IBOutlet NSButton *uninstall;
@property VectorMapStringString fullPreferences;
@property NSInteger outerrow;
@property QString currentPluginName;
@property PluginItemDelegateCallBacks callbacks;
@end
@implementation PluginItemDelegateVC
@synthesize pluginModel;
@synthesize hidableView, tabView, disclosureButton, loadButton, pluginNameLabel, pluginIcon, callbacks, fullPreferences;
NSInteger PREFERENCE_NAME_TAG = 100;
NSInteger PREFERENCE_VALUE_TAG = 200;
-(void) setup:(const QString&)pluginName row:(NSInteger)row callbacks:(PluginItemDelegateCallBacks) callbacks{
self.currentPluginName = pluginName;
self.outerrow = row;
self.callbacks = callbacks;
auto details = self.pluginModel->getPluginDetails(self.currentPluginName);
if (details.iconPath.endsWith(".svg")) {
details.iconPath.replace(".svg", ".png");
}
NSString* pathIcon = details.iconPath.toNSString();
NSImage *image = [[NSImage alloc] initWithContentsOfFile:pathIcon];
[pluginIcon setImage: image];
[pluginNameLabel setStringValue: details.name.toNSString()];
[loadButton setState: details.loaded];
[self.disclosureButton setState:NSControlStateValueOff];
[self.hidableView setHidden:YES];
auto fullPrefs = pluginModel->getPluginPreferences(self.currentPluginName);
QSet<QString> categories;
for (const auto& item : fullPrefs) {
auto currentCategory = item["category"];
if (!currentCategory.isEmpty())
categories.insert(currentCategory);
}
if (categories.size() <= 1) {
self.fullPreferences = fullPrefs;
[self.tabViewContainer setHidden:YES];
} else {
[self.tabViewContainer setHidden:NO];
if ([self.tabView numberOfTabViewItems] == 0) {
for (const auto item : categories) {
PluginPreferenceTab* newTab = [[PluginPreferenceTab alloc] init];
[newTab setLabel: item.toNSString()];
[newTab awakeFromNib];
[newTab.viewController setup: self.currentPluginName category:item];
[self.tabView addTabViewItem:newTab];
}
}
for (auto it = fullPrefs.begin(); it != fullPrefs.end();) {
if ((*it)["category"].isEmpty())
it++;
else
fullPrefs.erase(it);
}
self.fullPreferences = fullPrefs;
}
if (@available(macOS 11.0, *)) {
self.preferencesListView.style = NSTableViewStylePlain;
}
[self.preferencesListView reloadData];
}
#pragma mark - actions
- (IBAction)loadPlugin:(id)sender
{
if (loadButton.state == NSControlStateValueOff) {
self.pluginModel->unloadPlugin(self.currentPluginName);
} else
self.pluginModel->loadPlugin(self.currentPluginName);
self.pluginModel->chatHandlerStatusUpdated(false);
auto details = self.pluginModel->getPluginDetails(self.currentPluginName);
[loadButton setState:details.loaded];
}
- (IBAction)uninstallPlugin:(id)sender
{
CallBackInfos currentPluginInfos;
currentPluginInfos.row = self.outerrow;
currentPluginInfos.name = self.currentPluginName;
self.callbacks.uninstall(currentPluginInfos);
}
- (IBAction)resetPlugin:(id)sender
{
self.pluginModel->resetPluginPreferencesValues(self.currentPluginName);
[self.preferencesListView reloadData];
auto tabs = [self.tabView tabViewItems];
for (auto tab : tabs) {
auto test = reinterpret_cast<PluginPreferenceTab*>(tab);
[test.viewController update];
}
}
-(IBAction)setPreference:(id)sender {
NSInteger row = [self.preferencesListView rowForView:sender];
if(row < 0) {
return;
}
auto valuesPrefs = pluginModel->getPluginPreferencesValues(self.currentPluginName);
QString name = self.fullPreferences[row]["title"];
QString key = self.fullPreferences[row]["key"];
QString type = self.fullPreferences[row]["type"];
if (type == "Switch") {
auto* rowView = [self.preferencesListView rowViewAtRow:row makeIfNecessary:NO];
NSButton* button = [rowView viewWithTag:PREFERENCE_VALUE_TAG];
QString value = [button state] ? "1" : "0";
pluginModel->setPluginPreference(self.currentPluginName, key, value);
} else if (type == "EditText") {
auto* rowView = [self.preferencesListView rowViewAtRow:row makeIfNecessary:NO];
NSTextField* text = [rowView viewWithTag:PREFERENCE_VALUE_TAG];
QString value = QString::fromNSString([text stringValue]);
pluginModel->setPluginPreference(self.currentPluginName, key, value);
} else if (type == "List") {
auto* rowView = [self.preferencesListView rowViewAtRow:row makeIfNecessary:NO];
NSPopUpButton* button = [rowView viewWithTag:PREFERENCE_VALUE_TAG];
auto entries = self.fullPreferences[row]["entries"].split(",");
auto entryValues = self.fullPreferences[row]["entryValues"].split(",");
QString value = QString::fromNSString([[button selectedItem] title]);
auto newEntry = entries.indexOf(value);
pluginModel->setPluginPreference(self.currentPluginName, key, entryValues[newEntry]);
} else if (type == "Path") {
auto* rowView = [self.preferencesListView rowViewAtRow:row makeIfNecessary:NO];
NSButton* button = [rowView viewWithTag:PREFERENCE_VALUE_TAG];
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setAllowsMultipleSelection:NO];
[panel setCanChooseDirectories:NO];
[panel setCanChooseFiles:YES];
auto mimeTypes = self.fullPreferences[row]["mimeType"].split(",");
NSMutableArray* allowedTypes = [[NSMutableArray alloc] init];
for (const auto& mime : mimeTypes) {
QString ext = mime.split("/").last();
if (ext == "*") {
allowedTypes = nil;
break;
}
[allowedTypes addObject:ext.toNSString()];
}
[panel setAllowedFileTypes: allowedTypes];
if ([panel runModal] != NSFileHandlingPanelOKButton) return;
if ([[panel URLs] lastObject] == nil) return;
NSString * path = [[[panel URLs] lastObject] path];
pluginModel->setPluginPreference(self.currentPluginName, key, QString::fromNSString(path));
[button setTitle: [path lastPathComponent]];
}
}
- (IBAction)showPreferences:(id)sender {
[self.hidableView setHidden: !self.hidableView.isHidden ];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
auto valuesPrefs = pluginModel->getPluginPreferencesValues(self.currentPluginName);
QString name = self.fullPreferences[row]["title"];
QString key = self.fullPreferences[row]["key"];
QString type = self.fullPreferences[row]["type"];
QString value = valuesPrefs[key];
NSTableCellView* preferenceCell = [tableView makeViewWithIdentifier:@"TableCellPreferenceSwitchItem" owner:self];
NSTextField* preferenceLabel = [preferenceCell viewWithTag: PREFERENCE_NAME_TAG];
NSSwitch* preferenceToggledButton = [preferenceCell viewWithTag: PREFERENCE_VALUE_TAG];
[preferenceLabel setStringValue: name.toNSString()];
[preferenceToggledButton setHidden:YES];
if (type == "Switch") {
NSSwitch* preferenceToggledButton = [preferenceCell viewWithTag: PREFERENCE_VALUE_TAG];
[preferenceToggledButton setHidden:NO];
[preferenceToggledButton setState:value == "1"];
[preferenceToggledButton setAction:@selector(setPreference:)];
} else if (type == "EditText") {
preferenceCell = [tableView makeViewWithIdentifier:@"TableCellPreferenceEditTextItem" owner:self];
NSTextField* preferenceLabel = [preferenceCell viewWithTag: PREFERENCE_NAME_TAG];
NSTextField* preferenceText = [preferenceCell viewWithTag: PREFERENCE_VALUE_TAG];
[preferenceLabel setStringValue: name.toNSString()];
[preferenceText setHidden:NO];
[preferenceText setEditable:YES];
[preferenceText setStringValue:value.toNSString()];
[preferenceText setAction:@selector(setPreference:)];
} else if (type == "List") {
preferenceCell = [tableView makeViewWithIdentifier:@"TableCellPreferenceListItem" owner:self];
NSTextField* preferenceLabel = [preferenceCell viewWithTag: PREFERENCE_NAME_TAG];
NSPopUpButton* listButton = [preferenceCell viewWithTag: PREFERENCE_VALUE_TAG];
auto entries = self.fullPreferences[row]["entries"].split(",");
auto entryValues = self.fullPreferences[row]["entryValues"].split(",");
auto currentEntry = entryValues.indexOf(value);
[preferenceLabel setStringValue: name.toNSString()];
[listButton setHidden:NO];
[listButton removeAllItems];
for (const auto& value : entries) {
[listButton addItemWithTitle:value.toNSString()];
}
[listButton selectItemWithTitle:entries[currentEntry].toNSString()];
[listButton setAction:@selector(setPreference:)];
} else if (type == "Path") {
preferenceCell = [tableView makeViewWithIdentifier:@"TableCellPreferencePathItem" owner:self];
NSTextField* preferenceLabel = [preferenceCell viewWithTag: PREFERENCE_NAME_TAG];
NSButton* pathButton = [preferenceCell viewWithTag: PREFERENCE_VALUE_TAG];
[preferenceLabel setStringValue: name.toNSString()];
[pathButton setHidden:NO];
[pathButton setTitle: [value.toNSString() lastPathComponent]];
}
return preferenceCell;
}
#pragma mark - NSTableViewDataSource methods
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
if (self.currentPluginName.isEmpty())
return 0;
return self.fullPreferences.size();
}
@end
......@@ -17,7 +17,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import <Cocoa/Cocoa.h>
#import "LrcModelsProtocol.h"
......
......@@ -16,28 +16,27 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import "PluginPrefsVC.h"
#import "AppDelegate.h"
#import <api/pluginmodel.h>
#import "views/PluginCell.h"
#import "PluginItemDelegateVC.h"
@interface PluginPrefsVC ()
@property (assign) IBOutlet NSButton *enablePluginsButton;
@property (unsafe_unretained) IBOutlet NSTableView *installedPluginsView;
@property (unsafe_unretained) IBOutlet NSStackView *hidableView;
@end
@implementation PluginPrefsVC
PluginItemDelegateVC *viewController;
@synthesize pluginModel;
@synthesize installedPluginsView;
NS_ENUM(NSInteger, tablesViews1) {
PLUGIN_NAME_TAG = 1,
PLUGIN_LOADED_TAG,
PLUGIN_UNINSTALL_TAG,
PLUGIN_ICON_TAG
};
@synthesize installedPluginsView, hidableView;
-(id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil pluginModel:(lrc::api::PluginModel*) pluginModel
{
......@@ -53,6 +52,10 @@ NS_ENUM(NSInteger, tablesViews1) {
[super awakeFromNib];
self.installedPluginsView.delegate = self;
self.installedPluginsView.dataSource = self;
if (!self.pluginModel->getPluginsEnabled())
[hidableView setHidden:YES];
else
[hidableView setHidden:NO];
}
- (void)update {
......@@ -68,9 +71,13 @@ NS_ENUM(NSInteger, tablesViews1) {
#pragma mark - actions
- (IBAction)toggleEnablePluginsButton:(NSButton *)sender {
bool enabled = [sender state]==NSOnState;
bool enabled = [sender state] == NSOnState;
self.pluginModel->setPluginsEnabled(enabled);
self.pluginModel->chatHandlerStatusUpdated(false);
if (!enabled)
[hidableView setHidden:YES];
else
[hidableView setHidden:NO];
[self update];
}
......@@ -80,6 +87,9 @@ NS_ENUM(NSInteger, tablesViews1) {
[panel setAllowsMultipleSelection:NO];
[panel setCanChooseDirectories:NO];
[panel setCanChooseFiles:YES];
NSMutableArray* allowedTypes = [[NSMutableArray alloc] init];
[allowedTypes addObject:@"jpl"];
[panel setAllowedFileTypes: allowedTypes];
if ([panel runModal] != NSFileHandlingPanelOKButton) return;
if ([[panel URLs] lastObject] == nil) return;
NSString * path = [[[panel URLs] lastObject] path];
......@@ -88,81 +98,42 @@ NS_ENUM(NSInteger, tablesViews1) {
[self update];
}
- (IBAction)loadPlugin:(id)sender
{
NSInteger row = [installedPluginsView rowForView:sender];
if(row < 0) {
return;
}
auto installedPlugins = self.pluginModel->getInstalledPlugins();
if ((installedPlugins.size()-1) < row) {
return;
}
auto plugin = installedPlugins[row];
auto details = self.pluginModel->getPluginDetails(plugin);
if (details.loaded) {
self.pluginModel->unloadPlugin(plugin);
} else
self.pluginModel->loadPlugin(plugin);
self.pluginModel->chatHandlerStatusUpdated(false);
[self update];
}
- (IBAction)uninstallPlugin:(id)sender
{
NSInteger row = [installedPluginsView rowForView:sender];
if(row < 0) {
return;
}
auto installedPlugins = self.pluginModel->getInstalledPlugins();
if ((installedPlugins.size()-1) < row) {
return;
}
auto plugin = installedPlugins[row];
self.pluginModel->uninstallPlugin(plugin);
[self update];
}
#pragma mark - signals
#pragma mark - dispaly
#pragma mark - NSOpenSavePanelDelegate delegate methods
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSTableCellView* installedPluginCell = [tableView makeViewWithIdentifier:@"TableCellInstalledPluginItem" owner:self];
NSTextField* pluginNameLabel = [installedPluginCell viewWithTag: PLUGIN_NAME_TAG];
NSButton* pluginLoadButton = [installedPluginCell viewWithTag: PLUGIN_LOADED_TAG];
NSButton* pluginUninstallButton = [installedPluginCell viewWithTag: PLUGIN_UNINSTALL_TAG];
NSImageView* pluginIcon = [installedPluginCell viewWithTag: PLUGIN_ICON_TAG];
PluginCell* installedPluginCell = [tableView makeViewWithIdentifier:@"PluginCellItem" owner:self];
auto installedPlugins = self.pluginModel->getInstalledPlugins();
if ((installedPlugins.size() - 1) < row) {
return nil;
}
auto plugin = installedPlugins[row];
auto details = self.pluginModel->getPluginDetails(plugin);
if (details.iconPath.endsWith(".svg")) {
details.iconPath.replace(".svg", ".png");
}
NSString* pathIcon = details.iconPath.toNSString();
NSImage *image = [[NSImage alloc] initWithContentsOfFile:pathIcon];
[pluginIcon setImage: image];
[pluginNameLabel setStringValue: details.name.toNSString()];
[pluginLoadButton setState: details.loaded];
[pluginLoadButton setAction:@selector(loadPlugin:)];
[pluginLoadButton setTarget:self];
[pluginUninstallButton setAction:@selector(uninstallPlugin:)];
[pluginUninstallButton setTarget:self];
return installedPluginCell;
}
[installedPluginCell.viewController setPluginModel:self.pluginModel];
PluginItemDelegateCallBacks callbacks;
callbacks.uninstall = ([self](CallBackInfos infos) {
auto installedPlugins = self.pluginModel->getInstalledPlugins();
self.pluginModel->uninstallPlugin(infos.name);
int rowToDelete = 0;
bool match{false};
for (const auto& item : installedPlugins) {
if (item == infos.name) {
match = true;
break;
}
rowToDelete++;
}
if (!match)
return;
NSIndexSet* rowIndex = [NSIndexSet indexSetWithIndex:rowToDelete];
[self.installedPluginsView removeRowsAtIndexes:rowIndex withAnimation:NSTableViewAnimationSlideUp];
});
[installedPluginCell.viewController setup:plugin row:row callbacks:callbacks];
- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
{
if(![tableView isEnabled]) {
return nil;
}
return [tableView makeViewWithIdentifier:@"HoverRowView" owner:nil];
return installedPluginCell;
}
#pragma mark - NSTableViewDataSource methods
......
/*
* Copyright (C) 2021 Savoir-faire Linux Inc.
* Author: Aline Gondim Santos <aline.gondimsantos@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.
*/
#import <Cocoa/Cocoa.h>
#include <qstring.h>
#import "LrcModelsProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface PreferenceTabDelegateVC : NSViewController<LrcModelsProtocol, NSTableViewDataSource, NSTableViewDelegate> {}
- (void) setup:(QString)pluginName category:(QString)category;
- (void) update;
@end
NS_ASSUME_NONNULL_END
/*
* Copyright (C) 2021 Savoir-faire Linux Inc.
* Author: Aline Gondim Santos <aline.gondimsantos@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.
*/
#import "PreferenceTabDelegateVC.h"
#import <api/pluginmodel.h>
@interface PreferenceTabDelegateVC ()
@property (unsafe_unretained) IBOutlet NSTableView *preferencesListView;
@property QString category;
@property QString currentPluginName;
@property VectorMapStringString fullPreferences;
@end