1
0
mirror of https://codeberg.org/cage/tinmop/ synced 2025-03-10 11:00:04 +01:00

- [GUI] made link working for local files and gemtext files over gemini protocol.

This commit is contained in:
cage 2023-03-06 18:38:46 +01:00
parent d6fdf00253
commit 24b0b07bb3
8 changed files with 164 additions and 24 deletions

View File

@ -160,3 +160,5 @@ General Public License for more details."
:documentation "Ed is the standard editor!")
(define-constant +octect-type+ '(unsigned-byte 8) :test #'equalp)
(define-constant +gemini-file-extension+ "gmi" :test #'string=)

View File

@ -285,6 +285,14 @@
(let ((parsed (or (ignore-errors (iri:iri-parse link-value))
(iri:make-iri nil nil nil nil link-value nil nil))))
(cond
((null original-host)
(if (fs:extension-dir-p original-path)
(normalize-path (strcat original-path
link-value))
(let ((path-to-last-dir (fs:parent-dir-path original-path)))
(normalize-path (strcat path-to-last-dir
fs:*directory-sep*
link-value)))))
((null (uri:host parsed))
(let* ((absolute-path-p (string-starts-with-p "/" link-value))
(query-path-p (uri:query parsed))

View File

@ -2,13 +2,19 @@
(named-readtables:in-readtable nodgui.syntax:nodgui-syntax)
(a:define-constant +stream-status-streaming+ :streaming :test #'eq)
(a:define-constant +stream-status-canceled+ :canceled :test #'eq)
(a:define-constant +stream-status-downloading+ :downloading :test #'eq)
(defclass gemini-stream ()
((server-stream-handle
:initform nil
:initarg :server-stream-handle
:accessor server-stream-handle)
(status
:initform :streaming
:initform +stream-status-streaming+
:initarg :status
:accessor status)
(status-lock
@ -45,9 +51,8 @@
(remove stream-object *gemini-streams-db*))
*gemini-streams-db*)
(defmethod abort-downloading ((object gemini-stream))
(setf (status object) :canceled))
(setf (status object) +stream-status-canceled+))
(defun remove-all-db-stream ()
(map nil
@ -62,9 +67,32 @@
(defun find-db-stream-url (url)
(find-db-stream-if (lambda (a) (string= (server-stream-handle a) url))))
(defun find-streaming-stream-url ()
(find-db-stream-if (lambda (a) (eq (status a) +stream-status-streaming+))))
(defun notify-request-error (error)
(gui-goodies:error-dialog gui-goodies:*toplevel* error))
(defgeneric stop-stream-thread (object))
(defmethod stop-stream-thread ((object gemini-stream))
(with-accessors ((fetching-thread fetching-thread)) object
(abort-downloading object)
(when (bt:threadp fetching-thread)
(bt:join-thread fetching-thread))))
(defmethod stop-stream-thread ((object string))
(let ((stream-wrapper (find-db-stream-url object)))
(stop-stream-thread stream-wrapper)))
(defun stop-steaming-stream-thread ()
(let ((stream-wrapper (find-streaming-stream-url)))
(stop-stream-thread stream-wrapper)))
(defun maybe-stop-steaming-stream-thread ()
(a:when-let ((stream-wrapper (find-streaming-stream-url)))
(stop-stream-thread stream-wrapper)))
(defmacro with-notify-errors (&body body)
`(handler-case
(progn ,@body)
@ -114,13 +142,19 @@
(defun start-streaming-thread (iri &key
(use-cache t)
(process-function #'identity)
(status :streaming))
(when (not (find-db-stream-url iri))
(let ((stream-wrapper (make-instance 'gemini-stream
:server-stream-handle iri
:status status)))
(status +stream-status-streaming+))
(let ((existing-stream (find-db-stream-url iri)))
(when existing-stream
(stop-stream-thread existing-stream)
(setf (status existing-stream) status))
(let ((stream-wrapper (or existing-stream
(make-instance 'gemini-stream
:server-stream-handle iri
:status status))))
(when (not existing-stream)
(push-db-stream stream-wrapper))
(flet ((aborting-function ()
(eq (status stream-wrapper) :canceled)))
(eq (status stream-wrapper) +stream-status-canceled+)))
(let ((stream-thread (bt:make-thread (lambda ()
(slurp-gemini-stream iri
:use-cache use-cache
@ -129,7 +163,8 @@
:aborting-function
#'aborting-function)))))
(setf (fetching-thread stream-wrapper) stream-thread)
(push-db-stream stream-wrapper))))))
stream-wrapper)))))
(defun initialize-menu (parent)
(with-accessors ((main-window main-window)) parent
@ -193,7 +228,7 @@
(gen-ir-access pre-alt-text)
(defun link-click-mouse-1-callback (link-value main-window &key (use-cache t))
(defun link-click-mouse-1-callback-clsr (link-value main-window &key (use-cache t))
(lambda ()
(open-iri link-value main-window use-cache)))
@ -300,8 +335,8 @@
link-font
link-fg
link-bg
(link-click-mouse-1-callback target-iri
main-window)
(link-click-mouse-1-callback-clsr target-iri
main-window)
:over-callback
(lambda () (print-info-message target-iri))
:leave-callback
@ -391,6 +426,27 @@
(getf response :cached)
(getf response :iri)))
(defun open-local-path (path main-window)
(cond
((fs:file-exists-p path)
(if (fs:has-extension path +gemini-file-extension+)
(let ((parsed-lines (cev:enqueue-request-and-wait-results :gemini-parse-local-file
1
ev:+standard-event-priority+
path)))
(ev:with-enqueued-process-and-unblock ()
(clean-gemtext main-window)
(collect-ir-lines path gui-goodies:*main-frame* parsed-lines)))
(let ((lines (cev:enqueue-request-and-wait-results :gemini-slurp-local-file
1
ev:+standard-event-priority+
path)))
(ev:with-enqueued-process-and-unblock ()
(clean-gemtext main-window)
(set-text-gemtext main-window lines)))))
((fs:directory-exists-p path))))
(defun open-iri (iri main-window use-cache)
(handler-case
(let ((parsed-iri (iri:iri-parse iri)))
@ -398,13 +454,12 @@
(if (string= (uri:scheme parsed-iri)
gemini-constants:+gemini-scheme+)
(start-stream-iri iri main-window use-cache)
(progn))
(progn)))
(client-os-utils:open-resource-with-external-program main-window iri))
(open-local-path iri main-window)))
(error (e)
#+debug-mode (misc:dbg "error quen getting iri from autocomplete ~a" e)
(notify-request-error e))))
(defun start-stream-iri (iri main-window use-cache &optional (status :streaming))
(defun start-stream-iri (iri main-window use-cache &optional (status +stream-status-streaming+))
(let ((connecting-response (cev:enqueue-request-and-wait-results :gemini-request
1
ev:+maximum-event-priority+
@ -418,13 +473,21 @@
(displace-gemini-response connecting-response)
(cond
((gemini-client:header-success-p status-code)
(start-streaming-thread iri
:use-cache nil
:process-function (lambda (lines)
(collect-ir-lines iri main-window lines)
(misc:dbg "lines ~a" lines))
:status status))))))
(cond
((eq status +stream-status-streaming+)
(maybe-stop-steaming-stream-thread)
(clean-gemtext main-window)
(start-streaming-thread iri
:use-cache nil
:process-function (lambda (lines)
(collect-ir-lines iri main-window lines)
(misc:dbg "lines ~a" lines))
:status status))
((eq status +stream-status-downloading+)
(when (not (find-db-stream-url iri))
(enqueue-request-notify-error :gemini-request 1 iri use-cache)))
(t
(error "Unrecognized stream status for address ~s: ~s" iri status))))))))
(defun open-iri-clsr (main-window use-cache)
(lambda ()
@ -580,6 +643,12 @@
(defun print-error-message (message)
(print-info-message message :color (gui-goodies:parse-color "red") :bold t))
(defun clean-gemtext (main-window)
(setf (gui:text (gemtext-widget main-window)) ""))
(defun set-text-gemtext (main-window text)
(setf (gui:text (gemtext-widget main-window)) text))
(defun init-main-window ()
(gui:with-nodgui (:title +program-name+)
(icons:load-icons)

View File

@ -0,0 +1,36 @@
;; tinmop: an humble gemini and pleroma client
;; Copyright (C) 2020 cage
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
(in-package :client-os-utils)
(defun open-resource-with-external-program (main-window resource &key (open-for-edit nil))
(flet ((edit (file)
(croatoan:end-screen)
(os-utils:open-with-editor file)))
(let ((program (swconf:link-regex->program-to-use resource)))
(if program
(cond
((swconf:use-editor-as-external-program-p program)
(edit resource))
((swconf:use-tinmop-as-external-program-p program)
(if open-for-edit
(edit resource)
(client-main-window:open-local-path resource main-window)))
(t
(os-utils:open-link-with-program program resource :wait open-for-edit)))
(if open-for-edit
(error (_ "No program defined in configuration file to edit this kind of files."))
(os-utils:xdg-open resource))))))

View File

@ -415,3 +415,11 @@
(defmethod yason:encode ((object gemini-toc) &optional (stream *standard-output*))
(encode-flat-array-of-plists (unbox object) stream))
(defun gemini-parse-local-file (path)
(let ((parsed-lines (gemini-parser:parse-gemini-file (fs:slurp-file path))))
(make-instance 'parsed-lines-slice
:contents (rearrange-parsed-line-for-encoding parsed-lines))))
(defun gemini-slurp-local-file (path)
(fs:slurp-file path))

View File

@ -72,6 +72,12 @@
(gen-rpc "gemini-toc"
'gemini-table-of-contents
"iri" 0)
(gen-rpc "gemini-parse-local-file"
'gemini-parse-local-file
"path" 0)
(gen-rpc "gemini-slurp-local-file"
'gemini-slurp-local-file
"path" 0)
(gen-rpc "tour-shuffle" 'tour-shuffle)
(gen-rpc "tour-add-link"
'tour-add-link

View File

@ -67,6 +67,7 @@
:+cache-tls-certificate-type+
:+standard-editor+
:+octect-type+
:+gemini-file-extension+
;; GUI
:+minimum-padding+
:+ps-file-dialog-filter+))
@ -3264,6 +3265,14 @@
:gemini-h3-justification
:gemini-preformatted-text-justification))
(defpackage :client-os-utils
(:use
:cl
:config
:constants)
(:export
:open-resource-with-external-program))
(defpackage :client-events
(:use
:cl
@ -3386,6 +3395,7 @@
(:gui-shapes :nodgui.shapes)
(:menu :client-menu-command))
(:export
:open-local-path
:init-main-window))
(defpackage :main

View File

@ -160,6 +160,7 @@
:components ((:file "constants")
(:file "gui-goodies")
(:file "client-configuration")
(:file "os-utils")
(:file "program-events")
(:file "json-rpc-communication")
(:file "validation")