You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1034 lines
34 KiB
JavaScript
1034 lines
34 KiB
JavaScript
|
|
const electron = require('electron');
|
|
const updater = require('electron-updater');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
const process = require('process');
|
|
const url = require('url');
|
|
|
|
class Application {
|
|
|
|
constructor() {
|
|
|
|
this._views = new ViewCollection();
|
|
this._configuration = new ConfigurationService();
|
|
this._menu = new MenuService();
|
|
this._openQueue = [];
|
|
|
|
electron.app.setAppUserModelId('com.lutzroeder.netron');
|
|
electron.app.allowRendererProcessReuse = true;
|
|
|
|
if (!electron.app.requestSingleInstanceLock()) {
|
|
electron.app.quit();
|
|
return;
|
|
}
|
|
|
|
electron.app.on('second-instance', (event, commandLine, workingDirectory) => {
|
|
const currentDirectory = process.cwd();
|
|
process.chdir(workingDirectory);
|
|
const open = this._parseCommandLine(commandLine);
|
|
process.chdir(currentDirectory);
|
|
if (!open) {
|
|
if (this._views.count > 0) {
|
|
const view = this._views.item(0);
|
|
if (view) {
|
|
view.restore();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
electron.ipcMain.on('open-file-dialog', () => {
|
|
this._openFileDialog();
|
|
});
|
|
|
|
electron.ipcMain.on('get-environment', (event) => {
|
|
event.returnValue = {
|
|
version: electron.app.getVersion(),
|
|
package: electron.app.isPackaged
|
|
};
|
|
});
|
|
electron.ipcMain.on('get-configuration', (event, obj) => {
|
|
event.returnValue = this._configuration.has(obj.name) ? this._configuration.get(obj.name) : undefined;
|
|
});
|
|
electron.ipcMain.on('set-configuration', (event, obj) => {
|
|
this._configuration.set(obj.name, obj.value);
|
|
});
|
|
electron.ipcMain.on('drop-paths', (event, data) => {
|
|
const paths = data.paths.filter((path) => {
|
|
if (fs.existsSync(path)) {
|
|
const stat = fs.statSync(path);
|
|
return stat.isFile() || stat.isDirectory();
|
|
}
|
|
return false;
|
|
});
|
|
this._dropPaths(event.sender, paths);
|
|
});
|
|
electron.ipcMain.on('show-message-box', (event, options) => {
|
|
const owner = event.sender.getOwnerBrowserWindow();
|
|
event.returnValue = electron.dialog.showMessageBoxSync(owner, options);
|
|
});
|
|
electron.ipcMain.on('show-save-dialog', (event, options) => {
|
|
const owner = event.sender.getOwnerBrowserWindow();
|
|
event.returnValue = electron.dialog.showSaveDialogSync(owner, options);
|
|
});
|
|
|
|
electron.app.on('will-finish-launching', () => {
|
|
electron.app.on('open-file', (event, path) => {
|
|
this._openPath(path);
|
|
});
|
|
});
|
|
|
|
electron.app.on('ready', () => {
|
|
this._ready();
|
|
});
|
|
|
|
electron.app.on('window-all-closed', () => {
|
|
if (process.platform !== 'darwin') {
|
|
electron.app.quit();
|
|
}
|
|
});
|
|
|
|
electron.app.on('will-quit', () => {
|
|
this._configuration.save();
|
|
});
|
|
|
|
this._parseCommandLine(process.argv);
|
|
this._checkForUpdates();
|
|
}
|
|
|
|
_parseCommandLine(argv) {
|
|
let open = false;
|
|
if (argv.length > 1) {
|
|
for (const arg of argv.slice(1)) {
|
|
if (!arg.startsWith('-') && arg !== path.dirname(__dirname)) {
|
|
const extension = path.extname(arg).toLowerCase();
|
|
if (extension != '' && extension != 'js' && fs.existsSync(arg)) {
|
|
const stat = fs.statSync(arg);
|
|
if (stat.isFile() || stat.isDirectory()) {
|
|
this._openPath(arg);
|
|
open = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return open;
|
|
}
|
|
|
|
_ready() {
|
|
this._configuration.load();
|
|
if (!this._configuration.has('userId')) {
|
|
this._configuration.set('userId', this._uuid());
|
|
}
|
|
if (this._openQueue) {
|
|
const queue = this._openQueue;
|
|
this._openQueue = null;
|
|
while (queue.length > 0) {
|
|
const file = queue.shift();
|
|
this._openPath(file);
|
|
}
|
|
}
|
|
if (this._views.count == 0) {
|
|
this._views.openView();
|
|
}
|
|
this._resetMenu();
|
|
this._views.on('active-view-changed', () => {
|
|
this._updateMenu();
|
|
});
|
|
this._views.on('active-view-updated', () => {
|
|
this._updateMenu();
|
|
});
|
|
}
|
|
|
|
_uuid() {
|
|
const buffer = new Uint8Array(16);
|
|
require("crypto").randomFillSync(buffer);
|
|
buffer[6] = buffer[6] & 0x0f | 0x40;
|
|
buffer[8] = buffer[8] & 0x3f | 0x80;
|
|
const code = Array.from(buffer).map((value) => value < 0x10 ? '0' + value.toString(16) : value.toString(16)).join('');
|
|
return code.slice(0, 8) + '-' + code.slice(8, 12) + '-' + code.slice(12, 16) + '-' + code.slice(16, 20) + '-' + code.slice(20, 32);
|
|
}
|
|
|
|
_openFileDialog() {
|
|
const showOpenDialogOptions = {
|
|
properties: [ 'openFile' ],
|
|
filters: [
|
|
{ name: 'All Model Files', extensions: [
|
|
'onnx', 'ort', 'pb',
|
|
'h5', 'hd5', 'hdf5', 'json', 'keras',
|
|
'mlmodel', 'mlpackage',
|
|
'caffemodel',
|
|
'model', 'dnn', 'cmf', 'mar', 'params',
|
|
'pdmodel', 'pdparams', 'nb',
|
|
'meta',
|
|
'tflite', 'lite', 'tfl',
|
|
'armnn', 'mnn', 'nn', 'uff', 'uff.txt', 'rknn', 'xmodel', 'kmodel',
|
|
'ncnn', 'param', 'tnnproto', 'tmfile', 'ms', 'om',
|
|
'pt', 'pth', 'ptl', 't7',
|
|
'pkl', 'joblib',
|
|
'pbtxt', 'prototxt',
|
|
'cfg', 'xml',
|
|
'zip', 'tar' ] }
|
|
]
|
|
};
|
|
const selectedFiles = electron.dialog.showOpenDialogSync(showOpenDialogOptions);
|
|
if (selectedFiles) {
|
|
for (const file of selectedFiles) {
|
|
this._openPath(file);
|
|
}
|
|
}
|
|
}
|
|
|
|
_openPath(path) {
|
|
if (this._openQueue) {
|
|
this._openQueue.push(path);
|
|
return;
|
|
}
|
|
if (path && path.length > 0 && fs.existsSync(path)) {
|
|
const stat = fs.statSync(path);
|
|
if (stat.isFile() || stat.isDirectory()) {
|
|
// find existing view for this file
|
|
let view = this._views.find(path);
|
|
// find empty welcome window
|
|
if (view == null) {
|
|
view = this._views.find(null);
|
|
}
|
|
// create new window
|
|
if (view == null) {
|
|
view = this._views.openView();
|
|
}
|
|
this._loadPath(path, view);
|
|
}
|
|
}
|
|
}
|
|
|
|
_loadPath(path, view) {
|
|
const recents = this._configuration.get('recents').filter((recent) => path != recent.path);
|
|
view.open(path);
|
|
recents.unshift({ path: path });
|
|
if (recents.length > 9) {
|
|
recents.splice(9);
|
|
}
|
|
this._configuration.set('recents', recents);
|
|
this._resetMenu();
|
|
}
|
|
|
|
_dropPaths(sender, paths) {
|
|
let view = this._views.from(sender);
|
|
for (const path of paths) {
|
|
if (view) {
|
|
this._loadPath(path, view);
|
|
view = null;
|
|
}
|
|
else {
|
|
this._openPath(path);
|
|
}
|
|
}
|
|
}
|
|
|
|
_export() {
|
|
const view = this._views.activeView;
|
|
if (view && view.path) {
|
|
let defaultPath = 'Untitled';
|
|
const file = view.path;
|
|
const lastIndex = file.lastIndexOf('.');
|
|
if (lastIndex != -1) {
|
|
defaultPath = file.substring(0, lastIndex);
|
|
}
|
|
const owner = electron.BrowserWindow.getFocusedWindow();
|
|
const showSaveDialogOptions = {
|
|
title: 'Export',
|
|
defaultPath: defaultPath,
|
|
buttonLabel: 'Export',
|
|
filters: [
|
|
{ name: 'PNG', extensions: [ 'png' ] },
|
|
{ name: 'SVG', extensions: [ 'svg' ] }
|
|
]
|
|
};
|
|
const selectedFile = electron.dialog.showSaveDialogSync(owner, showSaveDialogOptions);
|
|
if (selectedFile) {
|
|
view.execute('export', { 'file': selectedFile });
|
|
}
|
|
}
|
|
}
|
|
|
|
service(name) {
|
|
if (name == 'configuration') {
|
|
return this._configuration;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
execute(command, data) {
|
|
const view = this._views.activeView;
|
|
if (view) {
|
|
view.execute(command, data || {});
|
|
}
|
|
this._updateMenu();
|
|
}
|
|
|
|
_reload() {
|
|
const view = this._views.activeView;
|
|
if (view && view.path) {
|
|
this._loadPath(view.path, view);
|
|
}
|
|
}
|
|
|
|
_checkForUpdates() {
|
|
if (!electron.app.isPackaged) {
|
|
return;
|
|
}
|
|
const autoUpdater = updater.autoUpdater;
|
|
if (autoUpdater.app && autoUpdater.app.appUpdateConfigPath && !fs.existsSync(autoUpdater.app.appUpdateConfigPath)) {
|
|
return;
|
|
}
|
|
const promise = autoUpdater.checkForUpdates();
|
|
if (promise) {
|
|
promise.catch((error) => {
|
|
/* eslint-disable */
|
|
console.log(error.message);
|
|
/* eslint-enable */
|
|
});
|
|
}
|
|
}
|
|
|
|
get package() {
|
|
if (!this._package) {
|
|
const file = path.join(path.dirname(__dirname), 'package.json');
|
|
const data = fs.readFileSync(file);
|
|
this._package = JSON.parse(data);
|
|
this._package.date = new Date(fs.statSync(file).mtime);
|
|
}
|
|
return this._package;
|
|
}
|
|
|
|
_about() {
|
|
let dialog = null;
|
|
const options = {
|
|
show: false,
|
|
backgroundColor: electron.nativeTheme.shouldUseDarkColors ? '#2d2d2d' : '#e6e6e6',
|
|
width: 400,
|
|
height: 250,
|
|
center: true,
|
|
minimizable: false,
|
|
maximizable: false,
|
|
useContentSize: true,
|
|
resizable: true,
|
|
fullscreenable: false,
|
|
webPreferences: {
|
|
nodeIntegration: true,
|
|
}
|
|
};
|
|
if (process.platform === 'darwin') {
|
|
options.title = '';
|
|
dialog = Application._aboutDialog;
|
|
}
|
|
else {
|
|
options.title = 'About ' + electron.app.name;
|
|
options.parent = electron.BrowserWindow.getFocusedWindow();
|
|
options.modal = true;
|
|
options.showInTaskbar = false;
|
|
}
|
|
if (process.platform === 'win32') {
|
|
options.type = 'toolbar';
|
|
}
|
|
if (!dialog) {
|
|
dialog = new electron.BrowserWindow(options);
|
|
if (process.platform === 'darwin') {
|
|
Application._aboutDialog = dialog;
|
|
}
|
|
dialog.removeMenu();
|
|
dialog.excludedFromShownWindowsMenu = true;
|
|
dialog.webContents.on('new-window', (event, url) => {
|
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
event.preventDefault();
|
|
electron.shell.openExternal(url);
|
|
}
|
|
});
|
|
let content = fs.readFileSync(path.join(__dirname, 'index.html'), 'utf-8');
|
|
content = content.replace('{version}', this.package.version);
|
|
content = content.replace('<title>Netron</title>', '');
|
|
content = content.replace('<body class="welcome spinner">', '<body class="about desktop">');
|
|
content = content.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
|
content = content.replace(/<link.*>/gi, '');
|
|
dialog.once('ready-to-show', () => {
|
|
dialog.resizable = false;
|
|
dialog.show();
|
|
});
|
|
dialog.on('close', function() {
|
|
electron.globalShortcut.unregister('Escape');
|
|
Application._aboutDialog = null;
|
|
});
|
|
dialog.loadURL('data:text/html;charset=utf-8,' + encodeURIComponent(content));
|
|
electron.globalShortcut.register('Escape', function() {
|
|
dialog.close();
|
|
});
|
|
}
|
|
else {
|
|
dialog.show();
|
|
}
|
|
}
|
|
|
|
_updateMenu() {
|
|
const window = electron.BrowserWindow.getFocusedWindow();
|
|
this._menu.update({
|
|
window: window,
|
|
webContents: window ? window.webContents : null,
|
|
view: this._views.activeView
|
|
}, this._views.views.map((view) => view.window));
|
|
}
|
|
|
|
_resetMenu() {
|
|
const menuRecentsTemplate = [];
|
|
if (this._configuration.has('recents')) {
|
|
let recents = this._configuration.get('recents');
|
|
recents = recents.filter((recent) => {
|
|
const path = recent.path;
|
|
if (fs.existsSync(path)) {
|
|
const stat = fs.statSync(path);
|
|
if (stat.isFile() || stat.isDirectory()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
if (recents.length > 9) {
|
|
recents.splice(9);
|
|
}
|
|
this._configuration.set('recents', recents);
|
|
for (let i = 0; i < recents.length; i++) {
|
|
const recent = recents[i];
|
|
menuRecentsTemplate.push({
|
|
path: recent.path,
|
|
label: Application.minimizePath(recent.path),
|
|
accelerator: ((process.platform === 'darwin') ? 'Cmd+' : 'Ctrl+') + (i + 1).toString(),
|
|
click: (item) => { this._openPath(item.path); }
|
|
});
|
|
}
|
|
}
|
|
|
|
const menuTemplate = [];
|
|
|
|
if (process.platform === 'darwin') {
|
|
menuTemplate.unshift({
|
|
label: electron.app.name,
|
|
submenu: [
|
|
{
|
|
label: 'About ' + electron.app.name,
|
|
click: () => this._about()
|
|
},
|
|
{ type: 'separator' },
|
|
{ role: 'hide' },
|
|
{ role: 'hideothers' },
|
|
{ role: 'unhide' },
|
|
{ type: 'separator' },
|
|
{ role: 'quit' }
|
|
]
|
|
});
|
|
}
|
|
|
|
menuTemplate.push({
|
|
label: '&File',
|
|
submenu: [
|
|
{
|
|
label: '&Open...',
|
|
accelerator: 'CmdOrCtrl+O',
|
|
click: () => { this._openFileDialog(); }
|
|
},
|
|
{
|
|
label: 'Open &Recent',
|
|
submenu: menuRecentsTemplate
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: 'file.export',
|
|
label: '&Export...',
|
|
accelerator: 'CmdOrCtrl+Shift+E',
|
|
click: () => this._export(),
|
|
},
|
|
{ type: 'separator' },
|
|
{ role: 'close' },
|
|
]
|
|
});
|
|
|
|
if (process.platform !== 'darwin') {
|
|
menuTemplate.slice(-1)[0].submenu.push(
|
|
{ type: 'separator' },
|
|
{ role: 'quit' }
|
|
);
|
|
}
|
|
|
|
if (process.platform == 'darwin') {
|
|
electron.systemPreferences.setUserDefault('NSDisabledDictationMenuItem', 'boolean', true);
|
|
electron.systemPreferences.setUserDefault('NSDisabledCharacterPaletteMenuItem', 'boolean', true);
|
|
}
|
|
|
|
menuTemplate.push({
|
|
label: '&Edit',
|
|
submenu: [
|
|
{
|
|
id: 'edit.cut',
|
|
label: 'Cu&t',
|
|
accelerator: 'CmdOrCtrl+X',
|
|
click: () => this.execute('cut', null),
|
|
},
|
|
{
|
|
id: 'edit.copy',
|
|
label: '&Copy',
|
|
accelerator: 'CmdOrCtrl+C',
|
|
click: () => this.execute('copy', null),
|
|
},
|
|
{
|
|
id: 'edit.paste',
|
|
label: '&Paste',
|
|
accelerator: 'CmdOrCtrl+V',
|
|
click: () => this.execute('paste', null),
|
|
},
|
|
{
|
|
id: 'edit.select-all',
|
|
label: 'Select &All',
|
|
accelerator: 'CmdOrCtrl+A',
|
|
click: () => this.execute('selectall', null),
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: 'edit.find',
|
|
label: '&Find...',
|
|
accelerator: 'CmdOrCtrl+F',
|
|
click: () => this.execute('find', null),
|
|
}
|
|
]
|
|
});
|
|
|
|
const viewTemplate = {
|
|
label: '&View',
|
|
submenu: [
|
|
{
|
|
id: 'view.toggle-attributes',
|
|
accelerator: 'CmdOrCtrl+D',
|
|
click: () => this.execute('toggle', 'attributes'),
|
|
},
|
|
{
|
|
id: 'view.toggle-initializers',
|
|
accelerator: 'CmdOrCtrl+I',
|
|
click: () => this.execute('toggle', 'initializers'),
|
|
},
|
|
{
|
|
id: 'view.toggle-names',
|
|
accelerator: 'CmdOrCtrl+U',
|
|
click: () => this.execute('toggle', 'names'),
|
|
},
|
|
{
|
|
id: 'view.toggle-direction',
|
|
accelerator: 'CmdOrCtrl+K',
|
|
click: () => { this.execute('toggle', 'direction'); }
|
|
},
|
|
{
|
|
id: 'view.toggle-mousewheel',
|
|
accelerator: 'CmdOrCtrl+M',
|
|
click: () => this.execute('toggle', 'mousewheel'),
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: 'view.reload',
|
|
label: '&Reload',
|
|
accelerator: (process.platform === 'darwin') ? 'Cmd+R' : 'F5',
|
|
click: () => this._reload(),
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: 'view.reset-zoom',
|
|
label: 'Actual &Size',
|
|
accelerator: 'Shift+Backspace',
|
|
click: () => this.execute('reset-zoom', null),
|
|
},
|
|
{
|
|
id: 'view.zoom-in',
|
|
label: 'Zoom &In',
|
|
accelerator: 'Shift+Up',
|
|
click: () => this.execute('zoom-in', null),
|
|
},
|
|
{
|
|
id: 'view.zoom-out',
|
|
label: 'Zoom &Out',
|
|
accelerator: 'Shift+Down',
|
|
click: () => this.execute('zoom-out', null),
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: 'view.show-properties',
|
|
label: '&Properties...',
|
|
accelerator: 'CmdOrCtrl+Enter',
|
|
click: () => this.execute('show-properties', null),
|
|
}
|
|
]
|
|
};
|
|
if (!electron.app.isPackaged) {
|
|
viewTemplate.submenu.push({ type: 'separator' });
|
|
viewTemplate.submenu.push({ role: 'toggledevtools' });
|
|
}
|
|
menuTemplate.push(viewTemplate);
|
|
|
|
if (process.platform === 'darwin') {
|
|
menuTemplate.push({
|
|
role: 'window',
|
|
submenu: [
|
|
{ role: 'minimize' },
|
|
{ role: 'zoom' },
|
|
{ type: 'separator' },
|
|
{ role: 'front'}
|
|
]
|
|
});
|
|
}
|
|
|
|
const helpSubmenu = [
|
|
{
|
|
label: '&Search Feature Requests',
|
|
click: () => { electron.shell.openExternal('https://www.github.com/' + this.package.repository + '/issues'); }
|
|
},
|
|
{
|
|
label: 'Report &Issues',
|
|
click: () => { electron.shell.openExternal('https://www.github.com/' + this.package.repository + '/issues/new'); }
|
|
}
|
|
];
|
|
|
|
if (process.platform != 'darwin') {
|
|
helpSubmenu.push({ type: 'separator' });
|
|
helpSubmenu.push({
|
|
label: 'About ' + electron.app.name,
|
|
click: () => this._about()
|
|
});
|
|
}
|
|
|
|
menuTemplate.push({
|
|
role: 'help',
|
|
submenu: helpSubmenu
|
|
});
|
|
|
|
const commandTable = new Map();
|
|
commandTable.set('file.export', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
commandTable.set('edit.cut', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
commandTable.set('edit.copy', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
commandTable.set('edit.paste', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
commandTable.set('edit.select-all', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
commandTable.set('edit.find', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
commandTable.set('view.toggle-attributes', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; },
|
|
label: (context) => { return !context.view || !context.view.get('attributes') ? 'Hide &Attributes' : 'Show &Attributes'; }
|
|
});
|
|
commandTable.set('view.toggle-initializers', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; },
|
|
label: (context) => { return !context.view || context.view.get('initializers') ? 'Hide &Initializers' : 'Show &Initializers'; }
|
|
});
|
|
commandTable.set('view.toggle-names', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; },
|
|
label: (context) => { return !context.view || context.view.get('names') ? 'Hide &Names' : 'Show &Names'; }
|
|
});
|
|
commandTable.set('view.toggle-direction', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; },
|
|
label: (context) => { return !context.view || context.view.get('direction') === 'vertical' ? 'Show &Horizontal' : 'Show &Vertical'; }
|
|
});
|
|
commandTable.set('view.toggle-mousewheel', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; },
|
|
label: (context) => { return !context.view || context.view.get('mousewheel') === 'scroll' ? '&Mouse Wheel: Zoom' : '&Mouse Wheel: Scroll'; }
|
|
});
|
|
commandTable.set('view.reload', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
commandTable.set('view.reset-zoom', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
commandTable.set('view.zoom-in', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
commandTable.set('view.zoom-out', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
commandTable.set('view.show-properties', {
|
|
enabled: (context) => { return context.view && context.view.path ? true : false; }
|
|
});
|
|
|
|
this._menu.build(menuTemplate, commandTable, this._views.views.map((view) => view.window));
|
|
this._updateMenu();
|
|
}
|
|
|
|
static minimizePath(file) {
|
|
if (process.platform != 'win32') {
|
|
const homeDir = os.homedir();
|
|
if (file.startsWith(homeDir)) {
|
|
return '~' + file.substring(homeDir.length);
|
|
}
|
|
}
|
|
return file;
|
|
}
|
|
|
|
}
|
|
|
|
class View {
|
|
|
|
constructor(owner) {
|
|
this._owner = owner;
|
|
this._ready = false;
|
|
this._path = null;
|
|
this._properties = new Map();
|
|
this._location = url.format({ protocol: 'file:', slashes: true, pathname: path.join(__dirname, 'electron.html') });
|
|
|
|
const size = electron.screen.getPrimaryDisplay().workAreaSize;
|
|
const options = {
|
|
show: false,
|
|
title: electron.app.name,
|
|
backgroundColor: electron.nativeTheme.shouldUseDarkColors ? '#1d1d1d' : '#e6e6e6',
|
|
icon: electron.nativeImage.createFromPath(path.join(__dirname, 'icon.png')),
|
|
minWidth: 600,
|
|
minHeight: 400,
|
|
width: size.width > 1024 ? 1024 : size.width,
|
|
height: size.height > 768 ? 768 : size.height,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'electron.js'),
|
|
nodeIntegration: true
|
|
}
|
|
};
|
|
if (this._owner.count > 0 && View._position && View._position.length == 2) {
|
|
options.x = View._position[0] + 30;
|
|
options.y = View._position[1] + 30;
|
|
if (options.x + options.width > size.width) {
|
|
options.x = 0;
|
|
}
|
|
if (options.y + options.height > size.height) {
|
|
options.y = 0;
|
|
}
|
|
}
|
|
this._window = new electron.BrowserWindow(options);
|
|
View._position = this._window.getPosition();
|
|
this._updateCallback = (event, data) => {
|
|
if (event.sender == this._window.webContents) {
|
|
for (const entry of Object.entries(data)) {
|
|
this.update(entry[0], entry[1]);
|
|
}
|
|
this._raise('updated');
|
|
}
|
|
};
|
|
electron.ipcMain.on('update', this._updateCallback);
|
|
this._window.on('closed', () => {
|
|
electron.ipcMain.removeListener('update', this._updateCallback);
|
|
this._owner.closeView(this);
|
|
});
|
|
this._window.on('focus', () => {
|
|
this._raise('activated');
|
|
});
|
|
this._window.on('blur', () => {
|
|
this._raise('deactivated');
|
|
});
|
|
this._window.webContents.on('did-finish-load', () => {
|
|
this._didFinishLoad = true;
|
|
});
|
|
this._window.webContents.on('new-window', (event, url) => {
|
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
event.preventDefault();
|
|
electron.shell.openExternal(url);
|
|
}
|
|
});
|
|
this._window.once('ready-to-show', () => {
|
|
this._window.show();
|
|
});
|
|
this._window.loadURL(this._location);
|
|
}
|
|
|
|
get window() {
|
|
return this._window;
|
|
}
|
|
|
|
get path() {
|
|
return this._path;
|
|
}
|
|
|
|
open(path) {
|
|
this._openPath = path;
|
|
if (this._didFinishLoad) {
|
|
this._window.webContents.send('open', { path: path });
|
|
}
|
|
else {
|
|
this._window.webContents.on('did-finish-load', () => {
|
|
this._window.webContents.send('open', { path: path });
|
|
});
|
|
this._window.loadURL(this._location);
|
|
}
|
|
}
|
|
|
|
restore() {
|
|
if (this._window) {
|
|
if (this._window.isMinimized()) {
|
|
this._window.restore();
|
|
}
|
|
this._window.show();
|
|
}
|
|
}
|
|
|
|
match(path) {
|
|
if (this._openPath) {
|
|
if (path === null) {
|
|
return false;
|
|
}
|
|
if (path === this._openPath) {
|
|
return true;
|
|
}
|
|
}
|
|
return this._path == path;
|
|
}
|
|
|
|
execute(command, data) {
|
|
if (this._window && this._window.webContents) {
|
|
this._window.webContents.send(command, data);
|
|
}
|
|
}
|
|
|
|
update(name, value) {
|
|
if (name === 'path') {
|
|
if (value) {
|
|
this._path = value;
|
|
const title = Application.minimizePath(this._path);
|
|
this._window.setTitle(process.platform !== 'darwin' ? title + ' - ' + electron.app.name : title);
|
|
this._window.focus();
|
|
}
|
|
this._openPath = null;
|
|
return;
|
|
}
|
|
this._properties.set(name, value);
|
|
}
|
|
|
|
get(name) {
|
|
return this._properties.get(name);
|
|
}
|
|
|
|
on(event, callback) {
|
|
this._events = this._events || {};
|
|
this._events[event] = this._events[event] || [];
|
|
this._events[event].push(callback);
|
|
}
|
|
|
|
_raise(event, data) {
|
|
if (this._events && this._events[event]) {
|
|
for (const callback of this._events[event]) {
|
|
callback(this, data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class ViewCollection {
|
|
|
|
constructor() {
|
|
this._views = [];
|
|
}
|
|
|
|
get views() {
|
|
return this._views;
|
|
}
|
|
|
|
get count() {
|
|
return this._views.length;
|
|
}
|
|
|
|
item(index) {
|
|
return this._views[index];
|
|
}
|
|
|
|
openView() {
|
|
const view = new View(this);
|
|
view.on('activated', (sender) => {
|
|
this._activeView = sender;
|
|
this._raise('active-view-changed', { activeView: this._activeView });
|
|
});
|
|
view.on('updated', () => {
|
|
this._raise('active-view-updated', { activeView: this._activeView });
|
|
});
|
|
view.on('deactivated', () => {
|
|
this._activeView = null;
|
|
this._raise('active-view-changed', { activeView: this._activeView });
|
|
});
|
|
this._views.push(view);
|
|
this._updateActiveView();
|
|
return view;
|
|
}
|
|
|
|
closeView(view) {
|
|
for (let i = this._views.length - 1; i >= 0; i--) {
|
|
if (this._views[i] == view) {
|
|
this._views.splice(i, 1);
|
|
}
|
|
}
|
|
this._updateActiveView();
|
|
}
|
|
|
|
find(path) {
|
|
return this._views.find(view => view.match(path));
|
|
}
|
|
|
|
from(contents) {
|
|
return this._views.find(view => view && view.window && view.window.webContents && view.window.webContents == contents);
|
|
}
|
|
|
|
get activeView() {
|
|
return this._activeView;
|
|
}
|
|
|
|
on(event, callback) {
|
|
this._events = this._events || {};
|
|
this._events[event] = this._events[event] || [];
|
|
this._events[event].push(callback);
|
|
}
|
|
|
|
_raise(event, data) {
|
|
if (this._events && this._events[event]) {
|
|
for (const callback of this._events[event]) {
|
|
callback(this, data);
|
|
}
|
|
}
|
|
}
|
|
|
|
_updateActiveView() {
|
|
const window = electron.BrowserWindow.getFocusedWindow();
|
|
const view = this._views.find(view => view.window == window) || null;
|
|
if (view != this._activeView) {
|
|
this._activeView = view;
|
|
this._raise('active-view-changed', { activeView: this._activeView });
|
|
}
|
|
}
|
|
}
|
|
|
|
class ConfigurationService {
|
|
|
|
load() {
|
|
this._data = { 'recents': [] };
|
|
const dir = electron.app.getPath('userData');
|
|
if (dir && dir.length > 0) {
|
|
const file = path.join(dir, 'configuration.json');
|
|
if (fs.existsSync(file)) {
|
|
const data = fs.readFileSync(file);
|
|
if (data) {
|
|
try {
|
|
this._data = JSON.parse(data);
|
|
}
|
|
catch (error) {
|
|
// continue regardless of error
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
save() {
|
|
if (this._data) {
|
|
const data = JSON.stringify(this._data, null, 2);
|
|
if (data) {
|
|
const dir = electron.app.getPath('userData');
|
|
if (dir && dir.length > 0) {
|
|
const file = path.join(dir, 'configuration.json');
|
|
fs.writeFileSync(file, data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
has(name) {
|
|
return this._data && Object.prototype.hasOwnProperty.call(this._data, name);
|
|
}
|
|
|
|
set(name, value) {
|
|
this._data[name] = value;
|
|
}
|
|
|
|
get(name) {
|
|
return this._data[name];
|
|
}
|
|
|
|
}
|
|
|
|
class MenuService {
|
|
|
|
build(menuTemplate, commandTable, windows) {
|
|
this._menuTemplate = menuTemplate;
|
|
this._commandTable = commandTable;
|
|
this._itemTable = new Map();
|
|
for (const menu of menuTemplate) {
|
|
for (const item of menu.submenu) {
|
|
if (item.id) {
|
|
if (!item.label) {
|
|
item.label = '';
|
|
}
|
|
this._itemTable.set(item.id, item);
|
|
}
|
|
}
|
|
}
|
|
this._rebuild(windows);
|
|
}
|
|
|
|
update(context, windows) {
|
|
if (!this._menu && !this._commandTable) {
|
|
return;
|
|
}
|
|
if (this._updateLabel(context)) {
|
|
this._rebuild(windows);
|
|
}
|
|
this._updateEnabled(context);
|
|
}
|
|
|
|
_rebuild(windows) {
|
|
this._menu = electron.Menu.buildFromTemplate(this._menuTemplate);
|
|
if (process.platform === 'darwin') {
|
|
electron.Menu.setApplicationMenu(this._menu);
|
|
}
|
|
else {
|
|
for (const window of windows) {
|
|
window.setMenu(this._menu);
|
|
}
|
|
}
|
|
}
|
|
|
|
_updateLabel(context) {
|
|
let rebuild = false;
|
|
for (const entry of this._commandTable.entries()) {
|
|
const menuItem = this._menu.getMenuItemById(entry[0]);
|
|
const command = entry[1];
|
|
if (command && command.label) {
|
|
const label = command.label(context);
|
|
if (label != menuItem.label) {
|
|
if (this._itemTable.has(entry[0])) {
|
|
this._itemTable.get(entry[0]).label = label;
|
|
rebuild = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rebuild;
|
|
}
|
|
|
|
_updateEnabled(context) {
|
|
for (const entry of this._commandTable.entries()) {
|
|
const menuItem = this._menu.getMenuItemById(entry[0]);
|
|
if (menuItem) {
|
|
const command = entry[1];
|
|
if (command.enabled) {
|
|
menuItem.enabled = command.enabled(context);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
global.application = new Application();
|