DawnLauncher/src/main/item/index.js

791 lines
24 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { shell, dialog, app } from "electron";
import path from "path";
import fs from "fs";
import util from "../util";
import { v4 } from "uuid";
import Jimp from "jimp";
import ClassificationJS from "../classification/index.js";
import data from "../data";
const { exec } = require("child_process");
/**
* 校验无效项目
* @param itemList
*/
function checkInvalidItemList(itemList) {
let list = [];
if (!util.arrayIsEmpty(itemList)) {
for (let item of itemList) {
// 只校验文件和文件夹
if (item.type == 0 || item.type == 1) {
// 获取绝对路径
item.path = getAbsolutePath(item.path);
try {
fs.statSync(item.path);
} catch (e) {
if (item.classificationParentId != null) {
list.push(item.classificationParentId + "-" + item.classificationId + "-" + item.id);
} else {
list.push(item.classificationId + "-" + item.id);
}
}
}
}
}
return list;
}
/**
* 校验无效项目
* @returns {*[]}
*/
function checkInvalidItem() {
let list = [];
for (let c of global.list) {
if (!util.arrayIsEmpty(c.childList)) {
for (let cc of c.childList) {
if (util.strIsEmpty(cc.mapDirectory)) {
list.push(...checkInvalidItemList(cc.itemList));
}
}
} else {
if (util.strIsEmpty(c.mapDirectory)) {
list.push(...checkInvalidItemList(c.itemList));
}
}
}
return list;
}
/**
* 解析环境变量
* @param p
*/
function parseEnvPath(p) {
// 尝试解析环境变量
let parsedPath = path.parse(p);
let isBase = false;
let dirArr;
if (util.strIsEmpty(parsedPath.dir)) {
dirArr = parsedPath.base.split("\\");
isBase = true;
} else {
dirArr = parsedPath.dir.split("\\");
}
let newPathArr = [];
const pattern = /^%.*%$/;
for (let string of dirArr) {
if (pattern.test(string)) {
let nString = string.substring(1, string.length - 1);
if (!util.strIsEmpty(process.env[nString])) {
newPathArr.push(process.env[nString]);
} else {
newPathArr.push(string);
}
} else {
newPathArr.push(string);
}
}
if (!isBase) {
newPathArr.push(parsedPath.base);
}
return newPathArr.join("\\");
}
/**
* 是否是绝对路径
* @param p
*/
function isAbsolutePath(p) {
const regex = /^[a-zA-Z]:\\/;
return regex.test(p);
}
/**
* 获取绝对路径
* @param path
*/
function getAbsolutePath(p) {
if (!isAbsolutePath(p)) {
// 尝试解析环境变量
let newPath = parseEnvPath(p);
// 判断解析之后的路径是否是绝对路径
if (isAbsolutePath(newPath)) {
return newPath;
} else {
return path.resolve(process.env.NODE_ENV !== "production" ? path.resolve(".") : path.dirname(process.execPath), p);
}
}
return p;
}
/**
* 运行项目
* @param item
* @param location 是否打开文件所在的位置
* @param openWith 打开方式
*/
function itemRun(item, location, openWith) {
// 系统
if (item.type == 3) {
if (item.shell.indexOf("shell:") >= 0) {
shell.openExternal(item.shell);
} else {
if (item.shell == "cmd") {
if (item.admin) {
exec('powershell -Command "Start-Process cmd -Verb RunAs"', (error, stdout, stderr) => {});
} else {
exec("start cmd.exe", (error, stdout, stderr) => {});
}
} else if (item.shell == "turnOffMonitor") {
global.api.TurnOffMonitor();
} else {
exec(item.shell, (error, stdout, stderr) => {});
}
}
return;
} else if (item.type == 5) {
exec("start " + item.shell, (error, stdout, stderr) => {});
return;
}
// 如果是类型是0或者1并且是相对路径的话恢复为绝对路径
if (item.type == 0 || item.type == 1) {
// 获取路径
item.path = getAbsolutePath(item.path);
}
let t = item.path;
let params = !util.strIsEmpty(item.params) ? item.params.trim() : "";
if (openWith != null && openWith && item.type == 0) {
exec("RUNDLL32.EXE SHELL32.DLL,OpenAs_RunDLL " + t + "", (error, stdout, stderr) => {});
} else {
let type = "open";
if (item.type == 0) {
if (location) {
// 如果是打开文件所在位置
exec('start %windir%\\explorer.exe /select, "' + item.path + '"', (error, stdout, stderr) => {});
return;
} else {
// 以管理员身份运行
if (item.admin && (item.extension == ".exe" || item.extension == ".bat")) {
type = "runas";
}
}
} else if (item.type == 2) {
// 网址
t = item.url;
}
if (item.type == 0 || item.type == 1) {
// 判断文件或文件夹是否存在
try {
fs.accessSync(t);
global.api.RunItem(type, t, params, item.type == 0 && !util.strIsEmpty(item.startLocation) ? item.startLocation : path.dirname(item.path));
} catch (e) {
let message;
if (item.type == 0 && !location) {
message = global.currentLanguage.notFoundFileMessage;
} else {
message = global.currentLanguage.notFoundFolderMessage;
}
message += '"' + t + '"。';
dialog.showMessageBox(global.mainWindow, {
title: "Dawn Launcher",
message: message,
buttons: [global.currentLanguage.ok],
type: "error",
noLink: true,
});
}
} else {
global.api.RunItem(type, t, params, null);
}
}
}
/**
* 返回排序菜单
* @param classificationParentId
* @param classificationChildId
* @param haveClassificationChild
* @param sort
*/
function itemSortMenu(classificationParentId, classificationChildId, haveClassificationChild, sort) {
let submenu = [
{
label: global.currentLanguage.default,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
sort: "default",
};
global.mainWindow.webContents.send("itemSort", JSON.stringify(params));
},
icon: (sort == null || sort == "default") && !haveClassificationChild ? util.getDot() : null,
},
{
label: global.currentLanguage.byInitial,
type: "normal",
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
sort: "initial",
};
global.mainWindow.webContents.send("itemSort", JSON.stringify(params));
},
icon: sort != null && sort == "initial" ? util.getDot() : null,
},
];
if (global.setting.item.openNumber) {
submenu.push({
label: global.currentLanguage.byOpenNumber,
type: "normal",
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
sort: "openNumber",
};
global.mainWindow.webContents.send("itemSort", JSON.stringify(params));
},
icon: sort != null && sort == "openNumber" ? util.getDot() : null,
});
}
return {
label: global.currentLanguage.sort,
type: "submenu",
submenu: submenu,
};
}
/**
* 返回布局和图标大小
* @param classificationParentId
* @param classificationChildId
* @param haveClassificationChild
* @param layout
* @param iconSize
*/
function itemLayoutIconSize(classificationParentId, classificationChildId, haveClassificationChild, layout, iconSize) {
let menuList = [
{
label: global.currentLanguage.layout,
type: "submenu",
submenu: [
{
label: global.currentLanguage.default,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: "default",
};
global.mainWindow.webContents.send("itemTile", JSON.stringify(params));
},
icon: (layout == null || layout == "default") && !haveClassificationChild ? util.getDot() : null,
},
{
label: global.currentLanguage.tile,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: "tile",
};
global.mainWindow.webContents.send("itemTile", JSON.stringify(params));
},
icon: layout != null && layout == "tile" ? util.getDot() : null,
},
{
label: global.currentLanguage.list,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: "list",
};
global.mainWindow.webContents.send("itemTile", JSON.stringify(params));
},
icon: layout != null && layout == "list" ? util.getDot() : null,
},
],
},
];
menuList.push({
label: global.currentLanguage.iconSize,
type: "submenu",
submenu: [
{
label: global.currentLanguage.default,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: null,
};
global.mainWindow.webContents.send("itemIconSize", JSON.stringify(params));
},
icon: iconSize == null && !haveClassificationChild ? util.getDot() : null,
},
{
label: global.currentLanguage.extraLarge,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: 48,
};
global.mainWindow.webContents.send("itemIconSize", JSON.stringify(params));
},
icon: iconSize != null && iconSize == 48 ? util.getDot() : null,
},
{
label: global.currentLanguage.large,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: 40,
};
global.mainWindow.webContents.send("itemIconSize", JSON.stringify(params));
},
icon: iconSize != null && iconSize == 40 ? util.getDot() : null,
},
{
label: global.currentLanguage.medium,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: 32,
};
global.mainWindow.webContents.send("itemIconSize", JSON.stringify(params));
},
icon: iconSize != null && iconSize == 32 ? util.getDot() : null,
},
{
label: global.currentLanguage.small,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: 24,
};
global.mainWindow.webContents.send("itemIconSize", JSON.stringify(params));
},
icon: iconSize != null && iconSize == 24 ? util.getDot() : null,
},
],
});
return menuList;
}
/**
* 返回显示菜单
* @param classificationParentId
* @param classificationChildId
* @param haveClassificationChild
* @param showOnly
*/
function itemShowOnly(classificationParentId, classificationChildId, haveClassificationChild, showOnly) {
let submenu = [
{
label: global.currentLanguage.default,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
showOnly: "default",
};
global.mainWindow.webContents.send("itemShowOnly", JSON.stringify(params));
},
icon: (showOnly == null || showOnly == "default") && !haveClassificationChild ? util.getDot() : null,
},
{
label: global.currentLanguage.showOnlyFiles,
type: "normal",
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
showOnly: "file",
};
global.mainWindow.webContents.send("itemShowOnly", JSON.stringify(params));
},
icon: showOnly != null && showOnly == "file" ? util.getDot() : null,
},
{
label: global.currentLanguage.showOnlyFolders,
type: "normal",
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
showOnly: "folder",
};
global.mainWindow.webContents.send("itemShowOnly", JSON.stringify(params));
},
icon: showOnly != null && showOnly == "folder" ? util.getDot() : null,
},
];
return {
label: global.currentLanguage.show,
type: "submenu",
submenu: submenu,
};
}
/**
* 返回列数菜单
* @param classificationParentId
* @param classificationChildId
* @param haveClassificationChild
* @param columnNumber
*/
function itemColumnNumber(classificationParentId, classificationChildId, haveClassificationChild, columnNumber) {
let submenu = [
{
label: global.currentLanguage.default,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
columnNumber: 0,
};
global.mainWindow.webContents.send("itemColumnNumber", JSON.stringify(params));
},
icon: (columnNumber == null || columnNumber == 0) && !haveClassificationChild ? util.getDot() : null,
},
];
for (let i = 0; i < 20; i++) {
submenu.push({
label: (i + 1).toString(),
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
columnNumber: i + 1,
};
global.mainWindow.webContents.send("itemColumnNumber", JSON.stringify(params));
},
icon: columnNumber != null && columnNumber == i + 1 ? util.getDot() : null,
});
}
return {
label: global.currentLanguage.numberOfColumns,
type: "submenu",
submenu: submenu,
};
}
/**
* 获取文件图标
* @param target
* @param message
*/
async function getFileIcon(target, message) {
// 获取绝对路径
target = getAbsolutePath(target);
let size = 256;
try {
// 先获取一下文件判断是否存在,不存在抛出异常
let stats = fs.statSync(target);
// 图标临时地址
let tempPath = app.getPath("temp") + "\\" + v4() + ".png";
// 获取图标
let result = global.api.GetFileIcon(target, tempPath, size);
// 1为成功
if (result == 1) {
// 读取图标文件
let buffer = fs.readFileSync(tempPath);
// 如果透明区域占比大于80代表这个图标没有超大图标那么就获取48*48图标
let tempResult = await Jimp.read(tempPath);
let zero = 0;
let color = 0;
for (const { x, y, image } of tempResult.scanIterator(0, 0, tempResult.bitmap.width, tempResult.bitmap.height)) {
if (image.getPixelColor(x, y) == 0) {
zero++;
} else {
color++;
}
}
// 计算占比
let proportion = Math.round((zero / (zero + color)) * 100);
// 删除临时文件
fs.unlink(tempPath, (err) => {});
// 透明区域大于80获取48*48图标
if (proportion >= 80) {
// 图标临时地址
tempPath = app.getPath("temp") + "\\" + v4() + ".png";
// 获取48*48图标
result = global.api.GetFileIcon(target, tempPath, 48);
// 1成功
if (result == 1) {
// 读取图标文件
buffer = fs.readFileSync(tempPath);
// 删除文件
fs.unlink(tempPath, (err) => {});
}
}
// 图标
let base64 = "data:image/png;base64," + buffer.toString("base64");
// 返回base64
return base64;
}
} catch (e) {
if (message) {
dialog.showMessageBox(global.mainWindow, {
title: "Dawn Launcher",
message: global.currentLanguage.targetNotExist + '"' + target + '"。',
buttons: [global.currentLanguage.ok],
type: "error",
noLink: true,
cancelId: 1,
});
}
}
return null;
}
/**
* 新建文件夹监听
*/
function addMapDirectoryWatcher(classificationParentId, classificationChildId, mapDirectory) {
// key
let key = classificationParentId + (classificationChildId != null ? "-" + classificationChildId : "");
// 先删除原有监听
deleteMapDirectoryWatcher(classificationParentId, classificationChildId);
// 新建监听
let data = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
mapDirectory: mapDirectory,
};
let timer;
let watch = fs.watch(mapDirectory, (event, filename) => {
if (timer) {
clearTimeout(timer);
timer = null;
}
// 启动定时器,在指定的时间间隔后发送合并后的通知
timer = setTimeout(() => {
readMapDirectory(classificationParentId, classificationChildId, mapDirectory, false, true, true);
clearTimeout(timer);
timer = null;
}, 1000);
});
watch.on("error", (error) => {
watch.close();
global.mapDirectoryWatcher.delete(key);
});
// 保存
data.watch = watch;
global.mapDirectoryWatcher.set(key, data);
}
/**
* 删除文件夹监听
*/
function deleteMapDirectoryWatcher(classificationParentId, classificationChildId) {
// 判断是否存在
let key = classificationParentId + (classificationChildId != null ? "-" + classificationChildId : "");
let watcherData = global.mapDirectoryWatcher.get(key);
if (watcherData != null) {
// 存在
if (watcherData.watch != null && watcherData.watch) {
watcherData.watch.close();
watcherData.watch = null;
}
global.mapDirectoryWatcher.delete(key);
}
}
/**
* 读取映射文件夹内容
* @param classificationParentId
* @param classificationChildId
* @param mapDirectory
* @param listener
* @param old
* @param notice
*/
async function readMapDirectory(classificationParentId, classificationChildId, mapDirectory, listener, old, notice) {
let itemList = [];
try {
// 判断是否含有环境变量
mapDirectory = parseEnvPath(mapDirectory);
// 获取图标数据
let iconData = data.store.get("iconData");
let iconDataMap = new Map();
// 转为Map
if (!util.arrayIsEmpty(iconData)) {
for (let icon of iconData) {
iconDataMap.set(util.getKey(icon.classificationParentId, icon.classificationChildId, icon.itemId), icon);
}
}
// 文件类型
let stats = fs.statSync(mapDirectory);
// 必须是文件夹
if (stats.isDirectory()) {
// 获取旧列表
let oldItemList = [];
let hiddenItem;
// 分类
let classification = ClassificationJS.getClassificationById(classificationParentId, classificationChildId);
if (classification != null) {
if (old != null && old) {
oldItemList = classification.itemList;
}
hiddenItem = classification.hiddenItem;
}
// 转为数组
let hiddenItemArr = [];
if (!util.strIsEmpty(hiddenItem)) {
hiddenItemArr = hiddenItem.split(",");
}
// 读取文件夹下面的所有文件
let pathList = fs.readdirSync(mapDirectory);
let i = 1;
for (let p of pathList) {
try {
// 判断是否隐藏
let flag = false;
for (let item of hiddenItemArr) {
if (item != null && p == item.trim()) {
flag = true;
break;
}
}
if (flag) {
continue;
}
// 组合路径
let np = path.join(mapDirectory, p);
// 获取类型 0:文件 1:文件夹
let type = fs.statSync(np).isDirectory() ? 1 : 0;
// 如果旧数据有的话,获取旧数据
let icon;
let openNumber;
let lastOpen;
let quickSearchOpenNumber;
let quickSearchLastOpen;
if (!util.arrayIsEmpty(oldItemList)) {
for (let oldItem of oldItemList) {
if (
(type == 0 && oldItem.name == util.removeSuffix(p) && oldItem.path == np && oldItem.type == type) ||
(type == 1 && oldItem.name == p && oldItem.path == np && oldItem.type == type)
) {
// 通过旧数据获取图标
let oldIcon = iconDataMap.get(util.getKey(classificationParentId, classificationChildId, oldItem.id));
if (oldIcon != null) {
icon = oldIcon.icon;
}
openNumber = oldItem.openNumber;
lastOpen = oldItem.lastOpen;
quickSearchOpenNumber = oldItem.quickSearchOpenNumber;
quickSearchLastOpen = oldItem.quickSearchLastOpen;
break;
}
}
}
let item = {
// id
id: i++,
// 路径
path: np,
// 名称
name: type == 1 ? p : util.removeSuffix(p),
// 全称
fullName: p,
// 图标
icon: icon != null ? icon : await getFileIcon(np, false),
// 类型 0:文件 1:文件夹
type: type,
// 打开次数
openNumber: openNumber,
// 最近打开
lastOpen: lastOpen,
// 快速搜索 打开次数
quickSearchOpenNumber: quickSearchOpenNumber,
// 快速搜索 最近打开
quickSearchLastOpen: quickSearchLastOpen,
};
itemList.push(item);
} catch (e) {
if (process.env.NODE_ENV !== "production") {
console.log(e);
}
}
}
if (listener != null && listener) {
// 新建监听
addMapDirectoryWatcher(classificationParentId, classificationChildId, mapDirectory);
}
if (notice) {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
itemList: itemList,
clear: true,
};
// 添加项目
global.mainWindow.webContents.send("itemAdd", JSON.stringify(params));
}
}
} catch (e) {
if (process.env.NODE_ENV !== "production") {
console.log(e);
}
}
return itemList;
}
/**
* 初始化映射文件夹
*/
async function initMapDirectory() {
let list = [];
for (let c of global.list) {
if (util.arrayIsEmpty(c.childList)) {
// 没有子级
if (!util.strIsEmpty(c.mapDirectory)) {
let { classificationParentId, classificationChildId } = ClassificationJS.convertClassificationId(c.id, c.parentId);
let itemList = await readMapDirectory(classificationParentId, classificationChildId, c.mapDirectory, true, true, false);
list.push({
classificationParentId: classificationParentId,
itemList: itemList,
});
}
} else {
for (let cc of c.childList) {
if (!util.strIsEmpty(cc.mapDirectory)) {
let { classificationParentId, classificationChildId } = ClassificationJS.convertClassificationId(cc.id, cc.parentId);
let itemList = await readMapDirectory(classificationParentId, classificationChildId, cc.mapDirectory, true, true, false);
list.push({
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
itemList: itemList,
});
}
}
}
}
return list;
}
export default {
checkInvalidItem,
isAbsolutePath,
getAbsolutePath,
itemRun,
itemSortMenu,
itemLayoutIconSize,
itemShowOnly,
itemColumnNumber,
getFileIcon,
addMapDirectoryWatcher,
readMapDirectory,
deleteMapDirectoryWatcher,
initMapDirectory,
};