blockbench/js/desktop.js
JannisX11 e3ac34f4c5 Fix typo in splash screen
Fix Update Notification button not working
Split event for new project / setup project
Fix issue where deleting theme files would cause error pop up on start
Fix issue with field in texture generator dialog toggling on input
(caused by underlying bug in Condition system)
Fix wrong icon in "Display UV" option
Fix new cubes not updating position
2021-10-20 22:55:54 +02:00

451 lines
12 KiB
JavaScript

const electron = require('@electron/remote');
const {clipboard, shell, nativeImage, ipcRenderer, dialog} = require('electron');
const app = electron.app;
const fs = require('fs');
const NodeBuffer = require('buffer');
const zlib = require('zlib');
const exec = require('child_process').exec;
const originalFs = require('original-fs');
const https = require('https');
const PathModule = require('path');
const currentwindow = electron.getCurrentWindow();
var dialog_win = null,
latest_version = false;
const recent_projects = (function() {
let array = [];
var raw = localStorage.getItem('recent_projects')
if (raw) {
try {
array = JSON.parse(raw).slice().reverse()
} catch (err) {}
array = array.filter(project => {
return fs.existsSync(project.path);
})
}
return array
})();
app.setAppUserModelId('blockbench')
function initializeDesktopApp() {
//Setup
$(document.body).on('click', 'a[href]', (event) => {
event.preventDefault();
shell.openExternal(event.target.href);
return true;
});
function makeUtilFolder(name) {
let path = PathModule.join(app.getPath('userData'), name)
if (!fs.existsSync(path)) fs.mkdirSync(path)
}
['backups', 'thumbnails'].forEach(makeUtilFolder)
createBackup(true)
$('.web_only').remove()
if (__dirname.includes('C:\\xampp\\htdocs\\blockbench')) {
Blockbench.addFlag('dev')
}
settings.interface_scale.onChange();
if (Blockbench.platform == 'darwin') {
//Placeholder
$('#mac_window_menu').show()
currentwindow.on('enter-full-screen', () => {
$('#mac_window_menu').hide()
})
currentwindow.on('leave-full-screen', () => {
$('#mac_window_menu').show()
})
} else {
$('#windows_window_menu').show()
}
ipcRenderer.send('app-loaded')
}
//Load Model
function loadOpenWithBlockbenchFile() {
ipcRenderer.on('open-model', (event, path) => {
Blockbench.read([path], {}, function(files) {
files.forEach(file => {
loadModelFile(file);
})
})
})
if (electron.process.argv.length >= 2) {
var extension = pathToExtension(electron.process.argv.last())
if (Codec.getAllExtensions().includes(extension)) {
Blockbench.read([electron.process.argv.last()], {}, (files) => {
loadModelFile(files[0])
})
}
}
}
(function() {
console.log('Electron '+process.versions.electron+', Node '+process.versions.node)
})()
window.confirm = function(message, title) {
let index = electron.dialog.showMessageBoxSync(currentwindow, {
title: title || electron.app.name,
detail: message,
type: 'none',
noLink: true,
buttons: [tl('dialog.ok'), tl('dialog.cancel')]
});
return index == 0;
}
window.alert = function(message, title) {
electron.dialog.showMessageBoxSync(electron.getCurrentWindow(), {
title: title || electron.app.name,
detail: message
});
}
//Recent Projects
function updateRecentProjects() {
//Set Local Storage
localStorage.setItem('recent_projects', JSON.stringify(recent_projects.slice().reverse()))
}
function addRecentProject(data) {
var i = recent_projects.length-1
while (i >= 0) {
var p = recent_projects[i]
if (p.path === data.path) {
recent_projects.splice(i, 1)
}
i--;
}
if (data.name.length > 48) data.name = data.name.substr(0, 20) + '...' + data.name.substr(-20);
let project = {
name: data.name,
path: data.path,
icon: data.icon,
day: new Date().dayOfYear()
}
recent_projects.splice(0, 0, project)
ipcRenderer.send('add-recent-project', data.path);
if (recent_projects.length > Math.clamp(settings.recent_projects.value, 0, 256)) {
recent_projects.pop()
}
updateRecentProjects()
}
async function updateRecentProjectThumbnail() {
if (Outliner.elements.length == 0) return;
let path = Project.export_path || Project.save_path;
let project = recent_projects.find(p => p.path == path);
if (!project) return;
MediaPreview.resize(180, 100)
MediaPreview.loadAnglePreset(DefaultCameraPresets[0])
let center = getSelectionCenter(true);
MediaPreview.controls.target.fromArray(center);
MediaPreview.controls.target.add(scene.position);
let box = Canvas.getModelSize();
let size = Math.max(box[0], box[1]*2)
MediaPreview.camera.position.multiplyScalar(size/50)
await new Promise((resolve, reject) => {
MediaPreview.screenshot({crop: false}, url => {
let hash = project.path.hashCode().toString().replace(/^-/, '0');
let path = PathModule.join(app.getPath('userData'), 'thumbnails', `${hash}.png`)
Blockbench.writeFile(path, {
savetype: 'image',
content: url
}, resolve)
let store_path = project.path;
project.path = '';
project.path = store_path;
})
})
// Clean old files
if (Math.random() < 0.2) {
let folder_path = PathModule.join(app.getPath('userData'), 'thumbnails')
let existing_names = [];
recent_projects.forEach(project => {
let hash = project.path.hashCode().toString().replace(/^-/, '0');
existing_names.safePush(hash)
})
fs.readdir(folder_path, (err, files) => {
if (!err) {
files.forEach((name, i) => {
if (existing_names.includes(name.replace(/\..+$/, '')) == false) {
try {
fs.unlinkSync(folder_path +osfs+ name)
} catch (err) {console.log(err)}
}
})
}
})
}
}
//Window Controls
function updateWindowState(e, type) {
$('#header_free_bar').toggleClass('resize_space', !currentwindow.isMaximized());
}
currentwindow.on('maximize', e => updateWindowState(e, 'maximize'));
currentwindow.on('unmaximize', e => updateWindowState(e, 'unmaximize'));
currentwindow.on('enter-full-screen', e => updateWindowState(e, 'screen'));
currentwindow.on('leave-full-screen', e => updateWindowState(e, 'screen'));
currentwindow.on('ready-to-show', e => updateWindowState(e, 'load'));
//Image Editor
function changeImageEditor(texture, from_settings) {
var dialog = new Dialog({
title: tl('message.image_editor.title'),
id: 'image_editor',
lines: ['<div class="dialog_bar"><select class="input_wide">'+
'<option id="ps">Photoshop</option>'+
'<option id="gimp">Gimp</option>'+
(Blockbench.platform == 'win32' ? '<option id="pdn">Paint.NET</option>' : '')+
'<option id="other">'+tl('message.image_editor.file')+'</option>'+
'</select></div>'],
draggable: true,
onConfirm() {
var id = $('.dialog#image_editor option:selected').attr('id')
var path;
if (Blockbench.platform == 'darwin') {
switch (id) {
case 'ps': path = '/Applications/Adobe Photoshop 2021/Adobe Photoshop 2021.app'; break;
case 'gimp':path = '/Applications/Gimp-2.10.app'; break;
}
} else {
switch (id) {
case 'ps': path = 'C:\\Program Files\\Adobe\\Adobe Photoshop 2021\\Photoshop.exe'; break;
case 'gimp':path = 'C:\\Program Files\\GIMP 2\\bin\\gimp-2.10.exe'; break;
case 'pdn': path = 'C:\\Program Files\\paint.net\\PaintDotNet.exe'; break;
}
}
if (id === 'other') {
selectImageEditorFile(texture)
} else if (path) {
settings.image_editor.value = path
if (texture) {
texture.openEditor()
}
}
dialog.hide()
if (from_settings) {
Settings.open()
}
},
onCancel() {
dialog.hide()
if (from_settings) {
Settings.open()
}
}
}).show()
}
function selectImageEditorFile(texture) {
let filePaths = electron.dialog.showOpenDialogSync(currentwindow, {
title: tl('message.image_editor.exe'),
filters: [{name: 'Executable Program', extensions: ['exe', 'app']}]
})
if (filePaths) {
settings.image_editor.value = filePaths[0]
if (texture) {
texture.openEditor();
}
}
}
//Default Pack
function openDefaultTexturePath() {
let detail = tl('message.default_textures.detail');
if (settings.default_path.value) {
detail += '\n\n' + tl('message.default_textures.current') + ': ' + settings.default_path.value;
}
let buttons = (
settings.default_path.value ? [tl('dialog.continue'), tl('generic.remove'), tl('dialog.cancel')]
: [tl('dialog.continue'), tl('dialog.cancel')]
)
var answer = electron.dialog.showMessageBoxSync(currentwindow, {
type: 'info',
buttons,
noLink: true,
title: tl('message.default_textures.title'),
message: tl('message.default_textures.message'),
detail
})
if (answer === buttons.length-1) {
return;
} else if (answer === 0) {
let path = Blockbench.pickDirectory({
title: tl('message.default_textures.select'),
resource_id: 'texture',
});
if (path) {
settings.default_path.value = path;
Settings.saveLocalStorages();
}
} else {
settings.default_path.value = false;
Settings.saveLocalStorages();
}
}
function findExistingFile(paths) {
for (var path of paths) {
if (fs.existsSync(path)) {
return path;
}
}
}
//Backup
function createBackup(init) {
setTimeout(createBackup, limitNumber(parseFloat(settings.backup_interval.value), 1, 10e8)*60000)
var duration = parseInt(settings.backup_retain.value)+1
var folder_path = app.getPath('userData')+osfs+'backups'
var d = new Date()
var days = d.getDate() + (d.getMonth()+1)*30.44 + (d.getYear()-100)*365.25
if (init) {
//Clear old backups
fs.readdir(folder_path, (err, files) => {
if (!err) {
files.forEach((name, i) => {
var date = name.split('_')[1]
if (date) {
var nums = date.split('.')
nums.forEach((n, ni) => {
nums[ni] = parseInt(n)
})
var b_days = nums[0] + nums[1]*30.44 + nums[2]*365.25
if (!isNaN(b_days) && days - b_days > duration) {
try {
fs.unlinkSync(folder_path +osfs+ name)
} catch (err) {console.log(err)}
}
}
})
}
})
}
if (init || elements.length === 0) return;
var model = Codecs.project.compile({compressed: true, backup: true})
localStorage.setItem('backup_model', model)
var file_name = 'backup_'+d.getDate()+'.'+(d.getMonth()+1)+'.'+(d.getYear()-100)+'_'+d.getHours()+'.'+d.getMinutes()
var file_path = folder_path+osfs+file_name+'.bbmodel'
fs.writeFile(file_path, model, function (err) {
if (err) {
console.log('Error creating backup: '+err)
}
})
}
//Close
window.onbeforeunload = function (event) {
try {
updateRecentProjectThumbnail()
} catch(err) {}
if (Blockbench.hasFlag('allow_closing')) {
try {
if (!Blockbench.hasFlag('allow_reload')) {
currentwindow.webContents.closeDevTools()
}
} catch (err) {}
} else {
setTimeout(async function() {
let projects = ModelProject.all.slice();
for (let project of projects) {
let closed = await project.close();
if (!closed) return false;
}
if (ModelProject.all.length === 0) {
closeBlockbenchWindow()
return true;
} else {
return false;
}
}, 2)
event.returnValue = true;
return true;
}
}
function closeBlockbenchWindow() {
window.onbeforeunload = null;
Blockbench.addFlag('allow_closing');
Blockbench.dispatchEvent('before_closing')
localStorage.removeItem('backup_model')
if (Project.EditSession) Project.EditSession.quit()
return currentwindow.close();
};
ipcRenderer.on('update-available', (event, arg) => {
console.log('Found new update')
if (settings.automatic_updates.value) {
ipcRenderer.send('allow-auto-update');
let icon_node = Blockbench.getIconNode('donut_large');
icon_node.classList.add('spinning');
let click_action;
let action = new Action('update_status', {
name: tl('menu.help.updating', [0]),
icon: icon_node,
click() {
if (click_action) click_action()
}
})
action.toElement('#update_menu');
MenuBar.menus.help.addAction('_');
MenuBar.menus.help.addAction(action);
ipcRenderer.on('update-progress', (event, status) => {
action.setName(tl('menu.help.updating', [Math.round(status.percent)]));
})
ipcRenderer.on('update-error', (event, err) => {
action.setName(tl('menu.help.update_failed'));
icon_node.textContent = 'warning';
icon_node.classList.remove('spinning')
click_action = function() {
currentwindow.openDevTools()
}
console.error(err);
})
ipcRenderer.on('update-downloaded', (event) => {
action.setName(tl('message.update_after_restart'));
MenuBar.menus.help.removeAction(action);
icon_node.textContent = 'done';
icon_node.classList.remove('spinning');
icon_node.style.color = '#5ef570';
click_action = function() {
Blockbench.showQuickMessage('message.update_after_restart')
}
})
} else {
addStartScreenSection({
color: 'var(--color-back)',
graphic: {type: 'icon', icon: 'update'},
text: [
{type: 'h2', text: tl('message.update_notification.title')},
{text: tl('message.update_notification.message')},
{type: 'button', text: tl('generic.enable'), click: (e) => {
settings.automatic_updates.set(true);
}}
]
})
}
})