import { ipcRenderer } from "electron";
import { Menu, MenuItem, dialog, getCurrentWindow } from "@electron/remote";
import React from "react";
import {
computed,
observable,
toJS,
runInAction,
makeObservable,
action
} from "mobx";
import { observer } from "mobx-react";
import classNames from "classnames";
import { ToolbarHeader } from "eez-studio-ui/header-with-body";
import { ButtonAction } from "eez-studio-ui/action";
import { Icon } from "eez-studio-ui/icon";
import { instruments, InstrumentObject } from "instrument/instrument-object";
import { stringCompare } from "eez-studio-shared/string";
import { beginTransaction, commitTransaction } from "eez-studio-shared/store";
import type { ITabDefinition } from "home/tabs-store";
import { tabs } from "home/tabs-store";
import {
deletedInstruments,
showDeletedInstrumentsDialog
} from "home/instruments/deleted-instruments-dialog";
import { ConnectionParameters } from "instrument/connection/interface";
import { Loader } from "eez-studio-ui/loader";
import { SearchInput } from "eez-studio-ui/search-input";
import { showExportDialog } from "./export-dialog";
import { showImportDialog } from "./import-dialog";
////////////////////////////////////////////////////////////////////////////////
export class InstrumentsStore {
_selectedInstrumentId: string | undefined;
connectionParameters: ConnectionParameters | null;
searchText: string;
onSelectInstrument: (() => void) | undefined;
constructor(public selectInstrument: boolean) {
makeObservable(this, {
_selectedInstrumentId: observable,
searchText: observable,
selectedInstrumentId: computed,
selectedInstrument: computed,
instruments: computed,
onSearchChange: action.bound
});
}
get selectedInstrumentId() {
return this._selectedInstrumentId
? this._selectedInstrumentId
: this.instruments.length > 0
? this.instruments[0].id
: undefined;
}
set selectedInstrumentId(id: string | undefined) {
runInAction(() => {
this._selectedInstrumentId = id;
});
}
get instruments() {
return Array.from(instruments.values())
.filter(
instrument =>
(this.searchText || "").trim().length == 0 ||
instrument.name
.toLowerCase()
.indexOf(this.searchText.trim().toLowerCase()) != -1
)
.sort((a, b) => stringCompare(a.name, b.name));
}
get selectedInstrument() {
const selectedInstrumentId = this.selectedInstrumentId;
return selectedInstrumentId
? instruments.get(selectedInstrumentId)
: undefined;
}
deleteInstruments(instruments: InstrumentObject[]) {
if (instruments.length > 0) {
beginTransaction("Delete workbench items");
} else {
beginTransaction("Delete workbench item");
}
instruments.forEach(instrument =>
deleteInstrument(instrument as InstrumentObject)
);
commitTransaction();
}
createContextMenu(instrument: InstrumentObject) {
const menu = new Menu();
if (instrument.isUnknownExtension) {
if (menu.items.length > 0) {
menu.append(
new MenuItem({
type: "separator"
})
);
}
menu.append(
new MenuItem({
label: "Install Extension",
click: () => {
const { installExtension } =
require("home/instruments/instrument-object-details") as typeof import("home/instruments/instrument-object-details");
installExtension(instrument);
}
})
);
}
if (menu.items.length > 0) {
menu.append(
new MenuItem({
type: "separator"
})
);
}
menu.append(
new MenuItem({
label: "Open in Tab",
click: () => {
instrument.openEditor("tab");
}
})
);
menu.append(
new MenuItem({
label: "Open in New Window",
click: () => {
instrument.openEditor("window");
}
})
);
menu.append(
new MenuItem({
type: "separator"
})
);
menu.append(
new MenuItem({
label: "Delete",
click: () => {
this.deleteInstruments([instrument]);
}
})
);
return menu;
}
selectedInstrumentConnect() {
const instrument = this.selectedInstrument;
if (!instrument) {
return;
}
let connection = instrument.connection;
if (!connection) {
return;
}
if (this.connectionParameters) {
instrument.setConnectionParameters(this.connectionParameters);
this.connectionParameters = null;
} else if (!instrument.lastConnection) {
instrument.setConnectionParameters(
instrument.defaultConnectionParameters
);
}
connection.connect();
}
onSearchChange(event: any) {
this.searchText = ($(event.target).val() as string).trim();
if (this.instruments.length > 0) {
this.selectedInstrumentId = this.instruments[0].id;
} else {
this.selectedInstrumentId = undefined;
}
}
}
export const defaultInstrumentsStore = new InstrumentsStore(false);
////////////////////////////////////////////////////////////////////////////////
function deleteInstrument(instrument: InstrumentObject) {
const { instrumentStore } =
require("instrument/instrument-object") as typeof import("instrument/instrument-object");
instrumentStore.deleteObject({
id: instrument.id
});
}
function openEditor(
instrument: InstrumentObject,
target: "tab" | "window" | "default"
) {
if (target === "default") {
if (
ipcRenderer.sendSync(
"focusWindow",
instrument.getEditorWindowArgs()
)
) {
return;
}
target = "tab";
}
if (target === "tab") {
const tab = tabs.findTab(instrument.id);
if (tab) {
// tab already exists
tabs.makeActive(tab);
} else {
// close window if open
ipcRenderer.send(
"closeWindow",
toJS(instrument.getEditorWindowArgs())
);
// open tab
const tab = tabs.addInstrumentTab(instrument);
tab.makeActive();
}
} else {
// close tab if open
const tab = tabs.findTab(instrument.id);
if (tab) {
tabs.removeTab(tab);
}
// open window
ipcRenderer.send("openWindow", toJS(instrument.getEditorWindowArgs()));
}
}
////////////////////////////////////////////////////////////////////////////////
window.addEventListener("message", (message: any) => {
const { instruments } =
require("instrument/instrument-object") as typeof import("instrument/instrument-object");
for (let key of instruments.keys()) {
const instrument = instruments.get(key);
if (instrument && instrument.id === message.data.instrumentId) {
if (message.data.type === "open-instrument-editor") {
openEditor(instrument, message.data.target);
} else if (message.data.type === "delete-instrument") {
beginTransaction("Delete instrument");
deleteInstrument(instrument);
commitTransaction();
}
return;
}
}
});
const TabButton = observer(function ({ tab }: { tab: ITabDefinition }) {
return (
);
});
///////////////////////////////////////////////////////////////////////////////
const Toolbar = observer(
class Toolbar extends React.Component<{
instrumentsStore: InstrumentsStore;
}> {
render() {
let buttons: {
id: string;
label: string;
icon?: any;
title: string;
className: string;
style?: React.CSSProperties;
onClick: () => void;
enabled?: boolean;
}[] = [
{
id: "instrument-add",
label: "Add Instrument",
title: "Add instrument",
className: "btn-success",
style:
deletedInstruments.size == 0
? {
marginRight: 20
}
: undefined,
onClick: () => {
const { showAddInstrumentDialog } =
require("instrument/add-instrument-dialog") as typeof import("instrument/add-instrument-dialog");
showAddInstrumentDialog(instrumentId => {
setTimeout(() => {
this.props.instrumentsStore.selectedInstrumentId =
instrumentId;
}, 100);
});
}
}
];
if (deletedInstruments.size > 0) {
buttons.push({
id: "show-deleted-instruments",
label: "Deleted Instruments",
title: "Show deleted instruments",
className: "btn-secondary",
style: { marginRight: 20 },
onClick: () => {
showDeletedInstrumentsDialog(
this.props.instrumentsStore
);
}
});
}
buttons.push({
id: "export-instrument",
label: "Export",
icon: (
),
title: "Export to database",
className: "btn-secondary",
onClick: () => {
showExportDialog(this.props.instrumentsStore);
}
});
buttons.push({
id: "import-instrument",
label: "Import",
icon: (
),
title: "Import from database",
className: "btn-secondary",
onClick: 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];
showImportDialog(this.props.instrumentsStore, filePath);
}
}
});
return (