/**
* _ _
* | \| |
* | .` |
* |_|\_|eptune
*
* Capstone Project 2022
*
*/
const NodeGUI = require("@nodegui/nodegui");
const NeptuneWindow = require("./NeptuneWindow");
const { Logger } = require("./../Classes/LogMan");
const NepConfig = require('../Classes/NeptuneConfig.js');
/** @type {NepConfig} */
var NeptuneConfig = global.Neptune.config;
class preferencePage extends NeptuneWindow {
/**
* @type {Logger}
*/
log;
/**
* @type {NodeGUI.QPushButton}
*/
btnReset;
/**
* @type {NodeGUI.QPushButton}
*/
btnSave;
/**
* @type {NodeGUI.QPlainTextEdit}
*/
txtDeviceName;
/**
* @type {NodeGUI.QFrame}
*/
nameCheckboxesDivider;
/**
* @type {NodeGUI.QCheckBox}
*/
chkBroadcastServer;
/**
* @type {NodeGUI.QCheckBox}
*/
chkMainWindowSaveSettingsOnChange;
/**
* @type {NodeGUI.QCheckBox}
*/
chkStartMinimized;
/**
* @type {NodeGUI.QCheckBox}
*/
chkEnableEncryption;
/**
* @type {NodeGUI.QPushButton}
*/
btnRekey;
closeWindowEvent() {
if (this.compareSettings()) {
let yesButton = new NodeGUI.QPushButton();
yesButton.setText("Yes");
let noButton = new NodeGUI.QPushButton();
noButton.setText("No");
let result = this.displayMessageBox("Discard changes?",
"You have unsaved changes, are you sure you want to discard them?",
[
{ button: yesButton, buttonRole: NodeGUI.ButtonRole.AcceptRole },
{ button: noButton, buttonRole: NodeGUI.ButtonRole.RejectRole },
]);
if (result != NodeGUI.DialogCode.Accepted) { // ? ???
this.close();
} else {
this.pushSettings();
}
} else {
this.close();
}
}
constructor(arg) {
super(arg);
try {
this.log = global.Neptune.logMan.getLogger("PreferencePage");
this.setWindowTitle('Neptune | Preference Page');
// NodeGUI.WindowType.WindowStaysOnTopHint <-- not amazing, covers the dialog messages!
this.setWindowFlag(NodeGUI.WindowType.Dialog | NodeGUI.WindowType.MSWindowsFixedSizeDialogHint, true);
let centralWidget = new NodeGUI.QWidget(this);
centralWidget.setObjectName("centralWidget");
this.setWindowFlag(NodeGUI.WindowType.WindowCloseButtonHint, false);
this.setObjectName("PreferenceWindow");
this.resize(400, 300);
this.setMinimumSize(350, 200);
this.setMaximumSize(500, 350);
let verticalLayout = new NodeGUI.QBoxLayout(NodeGUI.Direction.TopToBottom, centralWidget);
verticalLayout.setObjectName("verticalLayout");
verticalLayout.setContentsMargins(0, 0, 0, 0);
let gridLayout = new NodeGUI.QGridLayout();
gridLayout.setObjectName("gridLayout");
gridLayout.setContentsMargins(10, 10, 10, 10);
let bottomButtonsLayout = new NodeGUI.QGridLayout();
bottomButtonsLayout.setSpacing(0);
bottomButtonsLayout.setObjectName("bottomButtonsLayout");
bottomButtonsLayout.setContentsMargins(0, 10, 0, 0);
let horizontalLayout = new NodeGUI.QBoxLayout(NodeGUI.Direction.LeftToRight);
horizontalLayout.setSpacing(10);
horizontalLayout.setObjectName("horizontalLayout");
this.btnReset = new NodeGUI.QPushButton();
this.btnReset.setObjectName("btnReset");
this.btnReset.setCursor(NodeGUI.CursorShape.PointingHandCursor);
this.btnReset.setText("Reset");
this.btnReset.addEventListener('clicked', () => {
if (this.compareSettings()) {
let yesButton = new NodeGUI.QPushButton();
yesButton.setText("Yes");
let noButton = new NodeGUI.QPushButton();
noButton.setText("No");
let result = this.displayMessageBox("Reset to current settings?",
"Are you sure you want to discard your changes and load from the configuration?",
[
{ button: yesButton, buttonRole: NodeGUI.ButtonRole.AcceptRole },
{ button: noButton, buttonRole: NodeGUI.ButtonRole.RejectRole },
]);
if (result != NodeGUI.DialogCode.Accepted) { // ? ???
this.pullSettings()
}
} else {
this.pullSettings();
}
});
horizontalLayout.addWidget(this.btnReset);
this.btnSave = new NodeGUI.QPushButton();
this.btnSave.setObjectName("btnSave");
this.btnSave.setCursor(NodeGUI.CursorShape.PointingHandCursor);
this.btnSave.setText("Save");
this.btnSave.setEnabled(false);
this.btnSave.setAutoDefault(true);
this.btnSave.addEventListener('clicked', () => {
this.pushSettings();
});
horizontalLayout.addWidget(this.btnSave);
this.btnClose = new NodeGUI.QPushButton();
this.btnClose.setObjectName("btnClose");
this.btnClose.setCursor(NodeGUI.CursorShape.PointingHandCursor);
this.btnClose.setText("Close");
this.btnClose.addEventListener('clicked', () => {
this.closeWindowEvent();
});
horizontalLayout.addWidget(this.btnClose);
bottomButtonsLayout.addLayout(horizontalLayout, 0, 0, 1, 1);
gridLayout.addLayout(bottomButtonsLayout, 1, 0, 1, 1);
// Server settings
let scrollArea = new NodeGUI.QScrollArea(centralWidget);
scrollArea.setObjectName("scrollArea");
scrollArea.setEnabled(true);
scrollArea.setMinimumSize(125, 0);
scrollArea.setHorizontalScrollBarPolicy(NodeGUI.ScrollBarPolicy.ScrollBarAsNeeded);
scrollArea.setWidgetResizable(true);
let scrollAreaWidgetContents = new NodeGUI.QWidget();
scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents");
scrollAreaWidgetContents.setGeometry(0, 0, 440, 498);
scrollAreaWidgetContents.setMinimumSize(175, 0);
let gridLayout_3 = new NodeGUI.QGridLayout(scrollAreaWidgetContents);
gridLayout_3.setSpacing(0);
gridLayout_3.setObjectName("gridLayout_3");
gridLayout_3.setSizeConstraint(NodeGUI.SizeConstraint.SetDefaultConstraint);
gridLayout_3.setContentsMargins(0, 0, 0, 0);
let scrollAreaContents = new NodeGUI.QBoxLayout(NodeGUI.Direction.TopToBottom);
scrollAreaContents.setSpacing(10);
scrollAreaContents.setObjectName("scrollAreaContents");
scrollAreaContents.setSizeConstraint(NodeGUI.SizeConstraint.SetMinimumSize);
let nameLayout = new NodeGUI.QBoxLayout(NodeGUI.Direction.RightToLeft, this.MainWindow);
nameLayout.setObjectName("nameLayout");
this.lblDeviceName = new NodeGUI.QLabel(scrollAreaWidgetContents);
this.lblDeviceName.setObjectName("lblDeviceName");
this.lblDeviceName.setMinimumSize(0, 25);
this.lblDeviceName.setMaximumSize(16777215, 25);
this.lblDeviceName.setText("Server's name: ");
this.txtDeviceName = new NodeGUI.QPlainTextEdit(scrollAreaWidgetContents);
this.txtDeviceName.setObjectName("txtDeviceName");
this.txtDeviceName.setMinimumSize(0, 25);
this.txtDeviceName.setMaximumSize(16777215, 25);
this.txtDeviceName.setLineWrapMode(NodeGUI.WrapMode.NoWrap);
this.txtDeviceName.setPlainText("");
this.txtDeviceName.setPlaceholderText("");
this.txtDeviceName.addEventListener('textChanged', () => {
this.compareSettings();
});
nameLayout.addWidget(this.txtDeviceName);
nameLayout.addWidget(this.lblDeviceName);
scrollAreaContents.addLayout(nameLayout);
this.nameCheckboxesDivider = new NodeGUI.QFrame(scrollAreaWidgetContents);
this.nameCheckboxesDivider.setObjectName("nameCheckboxesDivider");
this.nameCheckboxesDivider.setEnabled(true);
this.nameCheckboxesDivider.setFrameShape(NodeGUI.Shape.HLine);
this.nameCheckboxesDivider.setFrameShadow(NodeGUI.Shadow.Sunken);
scrollAreaContents.addWidget(this.nameCheckboxesDivider);
this.chkBroadcastServer = new NodeGUI.QCheckBox(scrollAreaWidgetContents);
this.chkBroadcastServer.setObjectName("chkBroadcastServer");
this.chkBroadcastServer.setCursor(NodeGUI.CursorShape.PointingHandCursor);
this.chkBroadcastServer.setText("Broadcast Neptune on network\n(allow server to appear in the add a device page)");
this.chkBroadcastServer.addEventListener('clicked', () => {
this.compareSettings();
});
scrollAreaContents.addWidget(this.chkBroadcastServer);
this.chkMainWindowSaveSettingsOnChange = new NodeGUI.QCheckBox(scrollAreaWidgetContents);
this.chkMainWindowSaveSettingsOnChange.setObjectName("chkMainWindowSaveSettingsOnChange");
this.chkMainWindowSaveSettingsOnChange.setCursor(NodeGUI.CursorShape.PointingHandCursor);
this.chkMainWindowSaveSettingsOnChange.setText("Save settings on change in main window\n(toggles save button on client settings page)");
this.chkMainWindowSaveSettingsOnChange.addEventListener('clicked', () => {
this.compareSettings();
});
scrollAreaContents.addWidget(this.chkMainWindowSaveSettingsOnChange);
this.chkStartMinimized = new NodeGUI.QCheckBox(scrollAreaWidgetContents);
this.chkStartMinimized.setObjectName("chkStartMinimized");
this.chkStartMinimized.setCursor(NodeGUI.CursorShape.PointingHandCursor);
this.chkStartMinimized.setText("Start Neptune minimized");
this.chkStartMinimized.addEventListener('clicked', () => {
this.compareSettings();
});
scrollAreaContents.addWidget(this.chkStartMinimized);
let encryptionDivider = new NodeGUI.QFrame(scrollAreaWidgetContents);
encryptionDivider.setObjectName("encryptionDivider");
encryptionDivider.setFrameShape(NodeGUI.Shape.HLine);
encryptionDivider.setFrameShadow(NodeGUI.Shadow.Sunken);
scrollAreaContents.addWidget(encryptionDivider);
this.chkEnableEncryption = new NodeGUI.QCheckBox(scrollAreaWidgetContents);
this.chkEnableEncryption.setObjectName("chkEnableEncryption");
this.chkEnableEncryption.setCursor(NodeGUI.CursorShape.PointingHandCursor);
this.chkEnableEncryption.setText("Encrypt configuration files")
this.chkEnableEncryption.addEventListener('clicked', () => {
this.compareSettings();
});
scrollAreaContents.addWidget(this.chkEnableEncryption);
this.btnRekey = new NodeGUI.QPushButton(scrollAreaWidgetContents);
this.btnRekey.setObjectName("btnRekey");
this.btnRekey.setEnabled(false);
this.btnRekey.setCursor(NodeGUI.CursorShape.PointingHandCursor);
this.btnRekey.setText("Regenerate encryption key");
this.btnRekey.setToolTip("If encryption is enabled, changes the encryption key.");
this.btnRekey.addEventListener('clicked', () => {
let maybeThis = this;
let okayButton = new NodeGUI.QPushButton();
okayButton.setText("Okay");
global.Neptune.configManager.rekey(true).then((didIt) => {
maybeThis.log.info("Successfully changed encryption key.");
}).catch(err => {
maybeThis.log.error("Failed to change encryption key. Error: " + err);
maybeThis.chkEnableEncryption.setChecked(false);
maybeThis.displayMessageBox("Failed to change encryption key", "Neptune was unable to change the configuration encryption key!\nError: " + err,
[
{button: okayButton, buttonRole: NodeGUI.ButtonRole.AcceptRole}
]);
});
});
scrollAreaContents.addWidget(this.btnRekey);
gridLayout_3.addLayout(scrollAreaContents, 0, 0, 1, 1);
scrollArea.setWidget(scrollAreaWidgetContents);
gridLayout.addWidget(scrollArea, 0, 0, 1, 1);
verticalLayout.addLayout(gridLayout);
this.setCentralWidget(centralWidget);
this.btnSave.setDefault(true);
this.pullSettings();
}
catch (e) {
console.log(e);
}
}
/**
* Enables/disables the save button whether or not settings are different than the saved configuration .
*
* @return {boolean} Unsaved settings
*/
compareSettings() {
let settingsDiffer = (this.txtDeviceName.toPlainText().toLowerCase() !== NeptuneConfig.friendlyName.toLowerCase()
|| this.chkBroadcastServer.isChecked() !== NeptuneConfig.applicationSettings.advertiseNeptune
|| this.chkMainWindowSaveSettingsOnChange.isChecked() !== !NeptuneConfig.applicationSettings.requireSaveButton
|| this.chkStartMinimized.isChecked() !== NeptuneConfig.applicationSettings.startMinimized
|| this.chkEnableEncryption.isChecked() !== NeptuneConfig.encryption.enabled
);
this.btnSave.setEnabled(settingsDiffer);
return settingsDiffer;
}
/**
* Sets the window's checkboxes and other settings from the configuration data currently saved
*/
pullSettings() {
this.log.info("Loading settings from config.");
// Grab configuration settings from current config
this.txtDeviceName.setPlainText(NeptuneConfig.friendlyName);
this.txtDeviceName.setPlaceholderText(NeptuneConfig.friendlyName);
this.chkBroadcastServer.setChecked(NeptuneConfig.applicationSettings.advertiseNeptune === true);
this.chkMainWindowSaveSettingsOnChange.setChecked(NeptuneConfig.applicationSettings.requireSaveButton === false);
this.chkStartMinimized.setChecked(NeptuneConfig.applicationSettings.startMinimized === true);
this.chkEnableEncryption.setChecked(NeptuneConfig.encryption.enabled === true);
this.btnRekey.setEnabled(NeptuneConfig.encryption.enabled === true);
this.compareSettings();
}
/**
* Sets the configuration data using this window's state and saves
*/
pushSettings() {
// this.log.info("Saving settings to config.");
// Save configuration settings
NeptuneConfig.friendlyName = this.txtDeviceName.toPlainText();
NeptuneConfig.applicationSettings.advertiseNeptune = this.chkBroadcastServer.isChecked();
NeptuneConfig.applicationSettings.requireSaveButton = !this.chkMainWindowSaveSettingsOnChange.isChecked();
NeptuneConfig.applicationSettings.startMinimized = this.chkStartMinimized.isChecked();
let maybeThis = this;
let okayButton = new NodeGUI.QPushButton();
okayButton.setText("Okay");
if (this.chkEnableEncryption.isChecked() != NeptuneConfig.encryption.enabled) {
if (!this.chkEnableEncryption.isChecked()) {
global.Neptune.configManager.rekey(false).then((didIt) => {
maybeThis.log.info("Successfully disabled encryption.");
maybeThis.btnRekey.setEnabled(false);
}).catch(err => {
maybeThis.log.error("Failed to disable encryption: " + err);
maybeThis.chkEnableEncryption.setChecked(true);
maybeThis.btnRekey.setEnabled(true);
maybeThis.displayMessageBox("Failed to disable encryption", "Neptune was unable to disable configuration encryption! Type \"rekey\" into the console to try again.\nError: " + err,
[
{button: okayButton, buttonRole: NodeGUI.ButtonRole.AcceptRole}
]);
});
} else if (this.chkEnableEncryption.isChecked() && !NeptuneConfig.encryption.enabled) {
global.Neptune.configManager.rekey(true).then((didIt) => {
maybeThis.log.info("Successfully enabled encryption.");
maybeThis.btnRekey.setEnabled(true);
}).catch(err => {
maybeThis.log.error("Failed to enable encryption!!: " + err);
maybeThis.chkEnableEncryption.setChecked(false);
maybeThis.btnRekey.setEnabled(false);
maybeThis.displayMessageBox("Failed to enable encryption", "Neptune was unable to enable configuration encryption!\nError: " + err,
[
{button: okayButton, buttonRole: NodeGUI.ButtonRole.AcceptRole}
]);
});
}
}
NeptuneConfig.saveSync();
this.compareSettings();
}
/**
* @typedef {object} messageboxButtons
* @property {NodeGUI.QPushButton} button
* @property {NodeGUI.ButtonRole} buttonRole
*/
/**
* Displays a message box with custom buttons.
*
* @param {string} title - The title of the message box.
* @param {string} message - The message text of the message box.
* @param {messageboxButtons[]} buttons - An array of objects representing each custom button to add to the message box. Each object should contain a `button` property representing the `QPushButton` object, and an optional `buttonRole` property representing the button role (defaults to `ButtonRole.AcceptRole` if not specified).
*
* @returns {NodeGUI.DialogCode} - Whether the dialog was accepted `NodeGUI.DialogCode.Accepted` or rejected `NodeGUI.DialogCode.Rejected`.
*/
displayMessageBox(title, message, buttons) {
if (!this.isVisible())
return;
const messageBox = new NodeGUI.QMessageBox();
messageBox.setWindowTitle(title);
messageBox.setText(message);
if (!global.RunningTest) // I hate this
messageBox.setWindowIcon(process.ResourceManager.ApplicationIcon);
if (process.platform == 'win32') {
try {
// This allows NeptuneRunner to fix the window's taskbar data
messageBox.addEventListener(NodeGUI.WidgetEventTypes.Show, () => {
global.NeptuneRunnerIPC.pipe.write("fixwinhwnd" + this.winId() + "");
});
} catch (_) {}
}
// Add custom buttons to the message box
buttons.forEach(({ button, buttonRole }) => {
messageBox.addButton(button, buttonRole || NodeGUI.ButtonRole.AcceptRole);
button.addEventListener('clicked', () => {
messageBox.done(buttonRole || NodeGUI.ButtonRole.AcceptRole);
});
});
// Show the message box and return the result
const result = messageBox.exec();
return result;
}
}
module.exports = preferencePage;