123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- ;;; guile-zenity --- Scheme wrapper for Zenity
- ;;; Copyright © 2016 Raymond Nicholson <rain1@openmailbox.org>
- ;;; Copyright © 2016 Fabian Harfert <fhmgufs@opmbx.org>
- ;;;
- ;;; guile-zenity 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.
- ;;;
- ;;; guile-zenity 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 guile-zenity. If not, see <http://www.gnu.org/licenses/>.
- (define-module (zenity)
- #:export (zenity-calendar
- zenity-color-selection
- zenity-file-selection
- zenity-forms
- zenity-list zenity-checklist
- zenity-error zenity-info zenity-question zenity-warning
- zenity-password
- zenity-pulsate zenity-progress
- zenity-scale
- zenity-entry)
- #:use-module (ice-9 popen)
- #:use-module (ice-9 threads)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-11)
- #:use-module (srfi srfi-14))
- ;; https://help.gnome.org/users/zenity/stable/
- ;; Utility functions
- (define (drain-input port)
- (let ((ch (read-char port)))
- (if (eof-object? ch)
- '()
- (cons ch (drain-input port)))))
- (define (chomp text)
- (string-trim-right text (char-set #\newline)))
- (define (boolean->zenity-boolean b)
- (if b
- "TRUE"
- "FALSE"))
- (define (->string thing)
- (cond ((string? thing) thing)
- ((symbol? thing) (symbol->string thing))
- ((number? thing) (number->string thing))
- ((boolean? thing) (boolean->zenity-boolean thing))
- (else (error "unsupported type in ->string"))))
- (define (parse-date text)
- (let ((dmy (string-split (chomp text) #\/)))
- (list (cons 'day (string->number (car dmy)))
- (cons 'month (string->number (cadr dmy)))
- (cons 'year (string->number (caddr dmy))))))
- ;;
- (define* (zenity args #:key (width #f) (height #f))
- (let ((args (if (null? args)
- '()
- (append (list (car args))
- (if width (list (string-append "--width=" (number->string width))) (list))
- (if height (list (string-append "--height=" (number->string height))) (list))
- (cdr args)))))
- (let* ((pipe (apply open-pipe* OPEN_READ "zenity" args))
- (text (list->string (drain-input pipe)))
- (ret (close-pipe pipe)))
- (values ret text))))
- (define* (zenity/check args thunk #:key (width #f) (height #f))
- (let-values (((ret text) (zenity args #:width width #:height height)))
- (cond ((= ret 0) (thunk text))
- ((= ret 256) #f)
- (else (error "unexpected return code in zenity")))))
- (define* (zenity-input-pipe args #:key (width #f) (height #f))
- (apply open-pipe* OPEN_WRITE "zenity"
- (if (null? args)
- '()
- (append (list (car args))
- (if width (list (string-append "--width=" (number->string width))) (list))
- (if height (list (string-append "--height=" (number->string height))) (list))
- (cdr args)))))
- ;; Calendar Dialog — Use the --calendar option.
- (define* (zenity-calendar message #:key (width #f) (height #f))
- (zenity/check (list "--calendar" "--date-format=%d/%m/%Y" (string-append "--text=" message))
- parse-date
- #:width width #:height height))
- ;; Color Selection Dialog — Use the --color-selection option.
- (define* (zenity-color-selection #:key (color #f) (show-palette #f))
- (zenity/check (append
- (list "--color-selection")
- (if color (list (string-append "--color=" color)) (list))
- (if show-palette (list "--show-palette") (list)))
- chomp))
- ;; File Selection Dialog — Use the --file-selection option.
- (define* (zenity-file-selection title
- #:key (multiple #f)
- (directory #f)
- (save #f)
- (filename #f)
- (width #f) (height #f))
- (let ((args (append
- (if multiple (list "--multiple") (list))
- (if directory (list "--directory") (list))
- (if save (list "--save") (list))
- (if filename (list (string-append "--filename=" filename)) (list)))))
- (let-values (((ret text) (zenity (cons* "--file-selection"
- (string-append "--title=" title)
- args)
- #:width width #:height height)))
- (if (= ret 256)
- #f
- (if multiple
- ;; I hope you don't have files with | in the name.
- (string-split (chomp text) #\|)
- (chomp text))))))
- ;; Forms Dialog — Use the --forms option.
- (define* (zenity-forms title text layout #:key (width #f) (height #f))
- (zenity/check (cons* "--forms"
- ;; must not use , in anything you enter
- "--separator=,"
- "--forms-date-format=%d/%m/%Y"
- (string-append "--title=" title)
- (string-append "--text=" text)
- (map (lambda (layout-entry)
- (string-append
- (case (car layout-entry)
- ((entry) "--add-entry=")
- ((password) "--add-password=")
- ((calendar) "--add-calendar="))
- (cdr layout-entry)))
- layout))
- (lambda (text)
- (let loop ((layout layout)
- (data (string-split (chomp text) #\,)))
- (cond ((and (null? layout) (null? data)) '())
- ((or (null? layout) (null? data))
- (error "wrong number of form data fields in zenity"))
- (else (cons (case (caar layout)
- ((entry password) (car data))
- ((calendar) (parse-date (car data))))
- (loop (cdr layout)
- (cdr data)))))))
- #:width width #:height height))
- ;; List Dialog — Use the --list option.
- (define* (zenity-list message columns rows #:key (width #f) (height #f))
- ;; TODO: Emit a warning if two columns are keyed by the same name
- (let ((columns^ (map (lambda (col) (string-append "--column=" col)) columns))
- (rows^ (apply append (map (lambda (row) (map ->string row)) rows))))
- (zenity/check (cons "--list" (append columns^ rows^))
- (lambda (text)
- (assoc (chomp text) rows (lambda (k c) (string=? k (->string c)))))
- #:width width #:height height)))
- (define* (zenity-checklist message columns rows #:key (width #f) (height #f))
- (let ((columns^ (map (lambda (col) (string-append "--column=" col)) columns))
- (rows^ (apply append (map (lambda (row) (map ->string row)) rows))))
- (zenity/check (append (list "--list" "--checklist") (append columns^ rows^))
- (lambda (text)
- (string-split (chomp text) #\|))
- #:width width #:height height)))
- ;; Message Dialog — Error, Info, Question, Warning
- (define* (zenity-error message #:key (width #f) (height #f))
- (zenity (list "--error" (string-append "--text=" message))
- #:width width #:height height)
- #t)
- (define* (zenity-info message #:key (width #f) (height #f))
- (zenity (list "--info" (string-append "--text=" message))
- #:width width #:height height)
- #t)
- (define* (zenity-question message #:key (width #f) (height #f))
- (zenity/check (list "--question" (string-append "--text=" message))
- (lambda (_) #t)
- #:width width #:height height))
- (define* (zenity-warning message #:key (width #f) (height #f))
- (zenity (list "--warning" (string-append "--text=" message))
- #:width width #:height height)
- #t)
- ;; Notification Icon — Use the --notification option.
- ;; Password Dialog — Use the --password option.
- (define* (zenity-password message #:key (width #f) (height #f))
- (zenity/check (list "--password" message (string-append "--text=" message))
- chomp
- #:width width #:height height))
- ;; Progress Dialog — Use the --progress option.
- (define* (zenity-pulsate message #:key (auto-close #f)
- (no-cancel #f)
- (width #f) (height #f))
- (let ((pipe (zenity-input-pipe (append
- (list "--progress" "--pulsate" (string-append "--text=" message))
- (if auto-close (list "--auto-close") (list))
- (if no-cancel (list "--no-cancel") (list)))
- #:width width #:height height)))
- (lambda () (close-pipe pipe))))
- (define* (zenity-progress message #:key (value #f)
- (auto-close #f)
- (no-cancel #f)
- (width #f) (height #f))
- (let ((pipe (zenity-input-pipe (append
- (list "--progress" (string-append "--text=" message))
- (if value
- (list (string-append "--percentage=" (number->string value)))
- (list))
- (if auto-close (list "--auto-close") (list))
- (if no-cancel (list "--no-cancel") (list)))
- #:width width #:height height)))
- (let ((done #f))
- (lambda (message)
- (unless done
- (if message
- (begin
- (display message pipe)
- (newline pipe)
- (when (>= message 100)
- (set! done #t)))
- (begin
- (close-pipe pipe)
- (set! done #t))))))))
- ;; Scale Dialog — Use the --scale option.
- (define* (zenity-scale message
- #:key (value #f)
- (minimum #f)
- (maximum #f)
- (step #f)
- (hide-value #f)
- (width #f) (height #f))
- (zenity/check (append
- (list "--scale" (string-append "--text=" message))
- (if value (list (string-append "--value=" (->string value)))
- (if minimum
- (list (string-append "--value=" (->string minimum)))
- (list)))
- (if minimum (list (string-append "--min-value="
- (->string minimum)))
- (list))
- (if maximum (list (string-append "--max-value="
- (->string maximum)))
- (list))
- (if step (list (string-append "--step=" (->string step)))
- (list))
- (if hide-value (list "--hide-value") (list)))
- (lambda (text) (string->number (chomp text)))))
- ;; Text Entry Dialog — Use the --entry option.
- (define* (zenity-entry message #:key (width #f) (height #f))
- (zenity/check (list "--entry" message (string-append "--text=" message))
- chomp
- #:width width #:height height))
- ;; Text Information Dialog — Use the --text-info option.
|