import fs from "fs";
import { ipcRenderer, shell, clipboard } from "electron";
import { dialog, getCurrentWindow } from "@electron/remote";
import { confirm } from "eez-studio-ui/dialog-electron";
import path from "path";
import React from "react";
import {
observable,
computed,
action,
runInAction,
toJS,
makeObservable,
reaction
} from "mobx";
import { observer } from "mobx-react";
import classNames from "classnames";
import * as FlexLayout from "flexlayout-react";
import { app, createEmptyFile } from "eez-studio-shared/util-electron";
import { stringCompare } from "eez-studio-shared/string";
import {
initInstrumentDatabase,
InstrumentDatabase,
instrumentDatabases
} from "eez-studio-shared/db";
import {
LOCALES,
getLocale,
setLocale,
DATE_FORMATS,
getDateFormat,
setDateFormat,
TIME_FORMATS,
getTimeFormat,
setTimeFormat
} from "eez-studio-shared/i10n";
import { formatBytes } from "eez-studio-shared/formatBytes";
import { showDialog, Dialog } from "eez-studio-ui/dialog";
import { Loader } from "eez-studio-ui/loader";
import {
AbsoluteFileInputProperty,
BooleanProperty,
PropertyList,
SelectProperty,
StaticProperty
} from "eez-studio-ui/properties";
import * as notification from "eez-studio-ui/notification";
import {
Body,
Header,
ToolbarHeader,
VerticalHeaderWithBody
} from "eez-studio-ui/header-with-body";
import dbVacuum from "db-services/vacuum";
import { getMoment } from "eez-studio-shared/util";
import type { IMruItem } from "main/settings";
import { IconAction } from "eez-studio-ui/action";
import { HOME_TAB_OPEN_ICON } from "project-editor/ui-components/icons";
import { FlexLayoutContainer } from "eez-studio-ui/FlexLayout";
import { homeLayoutModels } from "./home-layout-models";
////////////////////////////////////////////////////////////////////////////////
export const COMPACT_DATABASE_MESSAGE =
"It is recommended to compact the database every 30 days.";
////////////////////////////////////////////////////////////////////////////////
const getIsDarkTheme = function () {
return ipcRenderer.sendSync("getIsDarkTheme");
};
const setIsDarkTheme = function (value: boolean) {
ipcRenderer.send("setIsDarkTheme", value);
};
////////////////////////////////////////////////////////////////////////////////
const getMRU: () => IMruItem[] = function () {
return ipcRenderer.sendSync("getMRU");
};
const setMRU = function (value: IMruItem[]) {
ipcRenderer.send("setMRU", toJS(value));
};
ipcRenderer.on("mru-changed", async (sender: any, mru: IMruItem[]) => {
function isMruChanged(mru1: IMruItem[], mru2: IMruItem[]) {
if (!!mru1 != !!mru) {
return true;
}
if (mru1.length != mru2.length) {
return true;
}
for (let i = 0; i < mru1.length; i++) {
if (
mru1[i].filePath != mru2[i].filePath ||
mru1[i].projectType != mru2[i].projectType
) {
return true;
}
}
return false;
}
if (isMruChanged(mru, settingsController.mru)) {
runInAction(() => (settingsController.mru = mru));
}
});
////////////////////////////////////////////////////////////////////////////////
const getShowComponentsPaletteInProjectEditor = function () {
return ipcRenderer.sendSync("getShowComponentsPaletteInProjectEditor");
};
const setShowComponentsPaletteInProjectEditor = function (value: boolean) {
ipcRenderer.send("setShowComponentsPaletteInProjectEditor", value);
};
////////////////////////////////////////////////////////////////////////////////
class SettingsController {
activetLocale = getLocale();
activeDateFormat = getDateFormat();
activeTimeFormat = getTimeFormat();
selectedDatabase: InstrumentDatabase | undefined;
locale: string = getLocale();
dateFormat: string = getDateFormat();
timeFormat: string = getTimeFormat();
isDarkTheme: boolean = getIsDarkTheme();
mru: IMruItem[] = getMRU();
pythonUseCustomPath: boolean = false;
pythonCustomPath: string = "";
_showComponentsPaletteInProjectEditor: boolean =
getShowComponentsPaletteInProjectEditor();
constructor() {
this.pythonUseCustomPath =
window.localStorage.getItem("pythonUseCustomPath") == "1"
? true
: false;
this.pythonCustomPath =
window.localStorage.getItem("pythonCustomPath") ?? "";
this.selectedDatabase = instrumentDatabases.activeDatabase;
makeObservable(this, {
selectedDatabase: observable,
locale: observable,
dateFormat: observable,
timeFormat: observable,
isDarkTheme: observable,
mru: observable,
restartRequired: computed,
onLocaleChange: action.bound,
onDateFormatChanged: action.bound,
onTimeFormatChanged: action.bound,
switchTheme: action.bound,
removeItemFromMRU: action,
pythonUseCustomPath: observable,
pythonCustomPath: observable
});
this.onThemeSwitched();
reaction(
() => ({
setCustomPath: this.pythonUseCustomPath,
customPythonPath: this.pythonCustomPath
}),
({ setCustomPath, customPythonPath }) => {
window.localStorage.setItem(
"pythonUseCustomPath",
setCustomPath ? "1" : "0"
);
window.localStorage.setItem(
"pythonCustomPath",
customPythonPath
);
}
);
}
get restartRequired() {
return (
instrumentDatabases.activeDatabase?.filePath !==
instrumentDatabases.activeDatabasePath ||
this.locale !== this.activetLocale ||
this.dateFormat !== this.activeDateFormat ||
this.timeFormat !== this.activeTimeFormat
);
}
onLocaleChange(value: string) {
this.locale = value;
setLocale(value);
}
onDateFormatChanged(value: string) {
this.dateFormat = value;
setDateFormat(value);
}
onTimeFormatChanged(value: string) {
this.timeFormat = value;
setTimeFormat(value);
}
onThemeSwitchedTimeout: any;
switchTheme(value: boolean) {
if (this.onThemeSwitchedTimeout) {
return;
}
this.isDarkTheme = value;
setIsDarkTheme(value);
this.onThemeSwitched();
}
onThemeSwitched() {
const content = document.getElementById(
"EezStudio_Content"
) as HTMLDivElement;
content.style.opacity = "0";
const mainLinkElement = document.getElementById(
"main-css"
) as HTMLLinkElement;
const flexlayoutLinkElement = document.getElementById(
"flexlayout-css"
) as HTMLLinkElement;
if (this.isDarkTheme) {
document.body.parentElement?.setAttribute("data-bs-theme", "dark");
mainLinkElement.href =
"../eez-studio-ui/_stylesheets/main-dark.css";
flexlayoutLinkElement.href =
"../../node_modules/flexlayout-react/style/dark.css";
} else {
document.body.parentElement?.setAttribute("data-bs-theme", "light");
mainLinkElement.href = "../eez-studio-ui/_stylesheets/main.css";
flexlayoutLinkElement.href =
"../../node_modules/flexlayout-react/style/light.css";
}
this.onThemeSwitchedTimeout = setTimeout(() => {
this.onThemeSwitchedTimeout = undefined;
content.style.opacity = "";
}, 500);
}
removeItemFromMRU(mruItem: IMruItem) {
const i = this.mru.indexOf(mruItem);
if (i != -1) {
this.mru.splice(i, 1);
setMRU(this.mru);
}
}
addDatabase(filePath: string, isActive: boolean) {
instrumentDatabases.addDatabase(filePath, isActive);
runInAction(() => {
this.selectedDatabase = instrumentDatabases.databases.find(
database => database.filePath == filePath
);
});
}
createNewDatabase = async () => {
let defaultPath = window.localStorage.getItem("lastDatabaseSavePath");
const result = await dialog.showSaveDialog(getCurrentWindow(), {
filters: [
{ name: "DB files", extensions: ["db"] },
{ name: "All Files", extensions: ["*"] }
],
defaultPath: defaultPath ?? undefined
});
const filePath = result.filePath;
if (filePath) {
try {
createEmptyFile(filePath);
await initInstrumentDatabase(filePath);
const onFinish = action((isActive: boolean) => {
this.addDatabase(filePath, isActive);
window.localStorage.setItem(
"lastDatabaseSavePath",
path.dirname(filePath)
);
if (isActive) {
this.askForRestart();
}
});
confirm(
"Do you want to make this database active?",
undefined,
() => onFinish(true),
() => onFinish(false)
);
} catch (error) {
notification.error(error.toString());
}
}
};
openDatabase = async () => {
let defaultPath = window.localStorage.getItem("lastDatabaseOpenPath");
const result = await dialog.showOpenDialog(getCurrentWindow(), {
properties: ["openFile"],
filters: [
{ name: "DB files", extensions: ["db"] },
{ name: "All Files", extensions: ["*"] }
],
defaultPath: defaultPath ?? undefined
});
const filePaths = result.filePaths;
if (filePaths && filePaths[0]) {
const filePath = filePaths[0];
const onFinish = action((isActive: boolean) => {
this.addDatabase(filePath, isActive);
window.localStorage.setItem(
"lastDatabaseOpenPath",
path.dirname(filePath)
);
if (isActive) {
this.askForRestart();
}
});
confirm(
"Do you want to make this database active?",
undefined,
() => onFinish(true),
() => onFinish(false)
);
}
};
askForRestart = () => {
if (
instrumentDatabases.activeDatabase &&
instrumentDatabases.activeDatabase.filePath !=
instrumentDatabases.activeDatabasePath
) {
confirm(
"Do you want to restart the application?",
"Restart is required to finish activation of new database.",
this.restart
);
}
};
restart = () => {
app.relaunch();
app.exit();
};
setAsActiveDatabase = action(() => {
if (this.selectedDatabase) {
instrumentDatabases.setAsActiveDatabase(this.selectedDatabase);
this.askForRestart();
}
});
deleteDatabase = () => {
if (this.selectedDatabase) {
instrumentDatabases.removeDatabase(this.selectedDatabase);
}
};
showDatabasePathInFolder = () => {
if (this.selectedDatabase) {
shell.showItemInFolder(this.selectedDatabase.filePath);
}
};
compactDatabase = () => {
if (!this.selectedDatabase) {
return;
}
showDialog(