From 9fc474504b87ffbfe0fda6ca639f268d3f8ebe9b Mon Sep 17 00:00:00 2001
From: SnnBcd xt <1160925501@qq.com>
Date: Tue, 16 May 2023 11:57:28 +0000
Subject: [PATCH] Open notes with custom software
---
 .../app/components/root_command_executor.js   |  9 +++
 src/public/app/services/open.js               | 73 +++++++++++++++++++
 .../app/widgets/buttons/note_actions.js       |  5 +-
 3 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/src/public/app/components/root_command_executor.js b/src/public/app/components/root_command_executor.js
index bac07af70..63d86ae46 100644
--- a/src/public/app/components/root_command_executor.js
+++ b/src/public/app/components/root_command_executor.js
@@ -54,6 +54,15 @@ export default class RootCommandExecutor extends Component {
             openService.openNoteExternally(noteId, mime);
         }
     }
+    
+    openNoteCustomCommand() {
+        const noteId = appContext.tabManager.getActiveContextNoteId();
+        const mime = appContext.tabManager.getActiveContextNoteMime()
+
+        if (noteId) {
+            openService.openNoteCustom(noteId, mime);
+        }
+    }
 
     enterProtectedSessionCommand() {
         protectedSessionService.enterProtectedSession();
diff --git a/src/public/app/services/open.js b/src/public/app/services/open.js
index 2d0ef6091..fa5959642 100644
--- a/src/public/app/services/open.js
+++ b/src/public/app/services/open.js
@@ -47,6 +47,78 @@ async function openNoteExternally(noteId, mime) {
     }
 }
 
+async function openNoteCustom(noteId, mime) {
+    if (utils.isElectron()) {
+      const resp = await server.post(`notes/${noteId}/save-to-tmp-dir`);
+      const filePath = resp.tmpFilePath;
+      const { exec } = utils.dynamicRequire('child_process');
+      const platform = process.platform;
+      if (platform === 'linux') {        
+        const terminals = ['gnome-terminal', 'konsole', 'xterm', 'xfce4-terminal', 'mate-terminal', 'rxvt', 'terminator', 'terminology'];
+        let foundTerminal = false;
+        let availableTerminal = null;
+        const openFileWithTerminal = (terminal) => {
+          const command = `${terminal} -e 'mimeopen -d "${filePath}"'`;
+          console.log(`Open Note custom: ${command}. `);
+          exec(command, (error, stdout, stderr) => {
+            if (error) {
+              console.error(`Open Note custom: Failed to open file with ${terminal}: ${error}`);
+              searchTerminal(terminals.indexOf(terminal) + 1);
+            } else {
+              console.log(`Open Note custom: File opened with ${terminal}. `);
+              console.log(`Open Note custom: ${stdout}. `);
+            }
+          });
+        };
+        const searchTerminal = (index) => {
+          const terminal = terminals[index];
+          if (!terminal) {
+            console.error('Open Note custom: No terminal found!');
+            open(getFileUrl(noteId), { url: true });
+            return;
+          }
+          exec(`which ${terminal}`, (error, stdout, stderr) => {
+            if (stdout.trim()) {
+              foundTerminal = true;
+              availableTerminal = terminal;
+            }
+            if (foundTerminal) {
+              openFileWithTerminal(availableTerminal);
+            } else {
+              searchTerminal(index + 1);
+            }
+          });
+        };
+        searchTerminal(0);
+      } else if (platform === 'win32') {
+        if (filePath.indexOf("/") !== -1) {
+          //Note that the path separator must be \ instead of /
+          filePath = filePath.replace(/\//g, "\\");
+        }
+        const command = `rundll32.exe shell32.dll,OpenAs_RunDLL ` + filePath;
+        exec(command, (err, stdout, stderr) => {
+          if (err) {
+            console.error("Open Note custom: ", err);
+            open(getFileUrl(noteId), { url: true });
+            return;
+          }
+        });
+      } else {
+        console.log('Currently "Open Note custom" only supports linux and windows systems');
+        open(getFileUrl(noteId), { url: true });
+      }
+    }
+    else {
+      // allow browser to handle opening common file
+      if (mime === "application/pdf" || mime.startsWith("image") || mime.startsWith("audio") || mime.startsWith("video")) {
+        window.open(getOpenFileUrl(noteId));
+      }
+      else {
+        window.location.href = getFileUrl(noteId);
+      }
+    }
+  }
+
 function downloadNoteRevision(noteId, noteRevisionId) {
     const url = getUrlForDownload(`api/notes/${noteId}/revisions/${noteRevisionId}/download`);
 
@@ -76,6 +148,7 @@ export default {
     download,
     downloadFileNote,
     openNoteExternally,
+    openNoteCustom,
     downloadNoteRevision,
     getUrlForDownload
 }
diff --git a/src/public/app/widgets/buttons/note_actions.js b/src/public/app/widgets/buttons/note_actions.js
index 68406eb08..de2ad33f9 100644
--- a/src/public/app/widgets/buttons/note_actions.js
+++ b/src/public/app/widgets/buttons/note_actions.js
@@ -28,7 +28,8 @@ const TPL = `
          Re-render note
         Search in note 
          Note source
-         Open note externally11
+         Open note externally
+         Open note custom (beta)
         Import files
         Export note
         Delete note
@@ -67,6 +68,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
         this.$widget.on('click', '.dropdown-item', () => this.$widget.find("[data-toggle='dropdown']").dropdown('toggle'));
 
         this.$openNoteExternallyButton = this.$widget.find(".open-note-externally-button");
+        this.$openNoteCustomButton = this.$widget.find(".open-note-custom-button");
 
         this.$deleteNoteButton = this.$widget.find(".delete-note-button");
         this.$deleteNoteButton.on("click", () => {
@@ -88,6 +90,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
         this.$renderNoteButton.toggle(note.type === 'render');
 
         this.$openNoteExternallyButton.toggle(utils.isElectron());
+        this.$openNoteCustomButton.toggle(utils.isElectron());
     }
 
     toggleDisabled($el, enable) {