zenity.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. ;;; guile-zenity --- Scheme wrapper for Zenity
  2. ;;; Copyright © 2016 Raymond Nicholson <rain1@openmailbox.org>
  3. ;;; Copyright © 2016 Fabian Harfert <fhmgufs@opmbx.org>
  4. ;;;
  5. ;;; guile-zenity is free software; you can redistribute it and/or modify it
  6. ;;; under the terms of the GNU General Public License as published by
  7. ;;; the Free Software Foundation; either version 3 of the License, or (at
  8. ;;; your option) any later version.
  9. ;;;
  10. ;;; guile-zenity is distributed in the hope that it will be useful, but
  11. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;;; GNU General Public License for more details.
  14. ;;;
  15. ;;; You should have received a copy of the GNU General Public License
  16. ;;; along with guile-zenity. If not, see <http://www.gnu.org/licenses/>.
  17. (define-module (zenity)
  18. #:export (zenity-calendar
  19. zenity-color-selection
  20. zenity-file-selection
  21. zenity-forms
  22. zenity-list zenity-checklist
  23. zenity-error zenity-info zenity-question zenity-warning
  24. zenity-password
  25. zenity-pulsate zenity-progress
  26. zenity-scale
  27. zenity-entry)
  28. #:use-module (ice-9 popen)
  29. #:use-module (ice-9 threads)
  30. #:use-module (srfi srfi-1)
  31. #:use-module (srfi srfi-11)
  32. #:use-module (srfi srfi-14))
  33. ;; https://help.gnome.org/users/zenity/stable/
  34. ;; Utility functions
  35. (define (drain-input port)
  36. (let ((ch (read-char port)))
  37. (if (eof-object? ch)
  38. '()
  39. (cons ch (drain-input port)))))
  40. (define (chomp text)
  41. (string-trim-right text (char-set #\newline)))
  42. (define (boolean->zenity-boolean b)
  43. (if b
  44. "TRUE"
  45. "FALSE"))
  46. (define (->string thing)
  47. (cond ((string? thing) thing)
  48. ((symbol? thing) (symbol->string thing))
  49. ((number? thing) (number->string thing))
  50. ((boolean? thing) (boolean->zenity-boolean thing))
  51. (else (error "unsupported type in ->string"))))
  52. (define (parse-date text)
  53. (let ((dmy (string-split (chomp text) #\/)))
  54. (list (cons 'day (string->number (car dmy)))
  55. (cons 'month (string->number (cadr dmy)))
  56. (cons 'year (string->number (caddr dmy))))))
  57. ;;
  58. (define* (zenity args #:key (width #f) (height #f))
  59. (let ((args (if (null? args)
  60. '()
  61. (append (list (car args))
  62. (if width (list (string-append "--width=" (number->string width))) (list))
  63. (if height (list (string-append "--height=" (number->string height))) (list))
  64. (cdr args)))))
  65. (let* ((pipe (apply open-pipe* OPEN_READ "zenity" args))
  66. (text (list->string (drain-input pipe)))
  67. (ret (close-pipe pipe)))
  68. (values ret text))))
  69. (define* (zenity/check args thunk #:key (width #f) (height #f))
  70. (let-values (((ret text) (zenity args #:width width #:height height)))
  71. (cond ((= ret 0) (thunk text))
  72. ((= ret 256) #f)
  73. (else (error "unexpected return code in zenity")))))
  74. (define* (zenity-input-pipe args #:key (width #f) (height #f))
  75. (apply open-pipe* OPEN_WRITE "zenity"
  76. (if (null? args)
  77. '()
  78. (append (list (car args))
  79. (if width (list (string-append "--width=" (number->string width))) (list))
  80. (if height (list (string-append "--height=" (number->string height))) (list))
  81. (cdr args)))))
  82. ;; Calendar Dialog — Use the --calendar option.
  83. (define* (zenity-calendar message #:key (width #f) (height #f))
  84. (zenity/check (list "--calendar" "--date-format=%d/%m/%Y" (string-append "--text=" message))
  85. parse-date
  86. #:width width #:height height))
  87. ;; Color Selection Dialog — Use the --color-selection option.
  88. (define* (zenity-color-selection #:key (color #f) (show-palette #f))
  89. (zenity/check (append
  90. (list "--color-selection")
  91. (if color (list (string-append "--color=" color)) (list))
  92. (if show-palette (list "--show-palette") (list)))
  93. chomp))
  94. ;; File Selection Dialog — Use the --file-selection option.
  95. (define* (zenity-file-selection title
  96. #:key (multiple #f)
  97. (directory #f)
  98. (save #f)
  99. (filename #f)
  100. (width #f) (height #f))
  101. (let ((args (append
  102. (if multiple (list "--multiple") (list))
  103. (if directory (list "--directory") (list))
  104. (if save (list "--save") (list))
  105. (if filename (list (string-append "--filename=" filename)) (list)))))
  106. (let-values (((ret text) (zenity (cons* "--file-selection"
  107. (string-append "--title=" title)
  108. args)
  109. #:width width #:height height)))
  110. (if (= ret 256)
  111. #f
  112. (if multiple
  113. ;; I hope you don't have files with | in the name.
  114. (string-split (chomp text) #\|)
  115. (chomp text))))))
  116. ;; Forms Dialog — Use the --forms option.
  117. (define* (zenity-forms title text layout #:key (width #f) (height #f))
  118. (zenity/check (cons* "--forms"
  119. ;; must not use , in anything you enter
  120. "--separator=,"
  121. "--forms-date-format=%d/%m/%Y"
  122. (string-append "--title=" title)
  123. (string-append "--text=" text)
  124. (map (lambda (layout-entry)
  125. (string-append
  126. (case (car layout-entry)
  127. ((entry) "--add-entry=")
  128. ((password) "--add-password=")
  129. ((calendar) "--add-calendar="))
  130. (cdr layout-entry)))
  131. layout))
  132. (lambda (text)
  133. (let loop ((layout layout)
  134. (data (string-split (chomp text) #\,)))
  135. (cond ((and (null? layout) (null? data)) '())
  136. ((or (null? layout) (null? data))
  137. (error "wrong number of form data fields in zenity"))
  138. (else (cons (case (caar layout)
  139. ((entry password) (car data))
  140. ((calendar) (parse-date (car data))))
  141. (loop (cdr layout)
  142. (cdr data)))))))
  143. #:width width #:height height))
  144. ;; List Dialog — Use the --list option.
  145. (define* (zenity-list message columns rows #:key (width #f) (height #f))
  146. ;; TODO: Emit a warning if two columns are keyed by the same name
  147. (let ((columns^ (map (lambda (col) (string-append "--column=" col)) columns))
  148. (rows^ (apply append (map (lambda (row) (map ->string row)) rows))))
  149. (zenity/check (cons "--list" (append columns^ rows^))
  150. (lambda (text)
  151. (assoc (chomp text) rows (lambda (k c) (string=? k (->string c)))))
  152. #:width width #:height height)))
  153. (define* (zenity-checklist message columns rows #:key (width #f) (height #f))
  154. (let ((columns^ (map (lambda (col) (string-append "--column=" col)) columns))
  155. (rows^ (apply append (map (lambda (row) (map ->string row)) rows))))
  156. (zenity/check (append (list "--list" "--checklist") (append columns^ rows^))
  157. (lambda (text)
  158. (string-split (chomp text) #\|))
  159. #:width width #:height height)))
  160. ;; Message Dialog — Error, Info, Question, Warning
  161. (define* (zenity-error message #:key (width #f) (height #f))
  162. (zenity (list "--error" (string-append "--text=" message))
  163. #:width width #:height height)
  164. #t)
  165. (define* (zenity-info message #:key (width #f) (height #f))
  166. (zenity (list "--info" (string-append "--text=" message))
  167. #:width width #:height height)
  168. #t)
  169. (define* (zenity-question message #:key (width #f) (height #f))
  170. (zenity/check (list "--question" (string-append "--text=" message))
  171. (lambda (_) #t)
  172. #:width width #:height height))
  173. (define* (zenity-warning message #:key (width #f) (height #f))
  174. (zenity (list "--warning" (string-append "--text=" message))
  175. #:width width #:height height)
  176. #t)
  177. ;; Notification Icon — Use the --notification option.
  178. ;; Password Dialog — Use the --password option.
  179. (define* (zenity-password message #:key (width #f) (height #f))
  180. (zenity/check (list "--password" message (string-append "--text=" message))
  181. chomp
  182. #:width width #:height height))
  183. ;; Progress Dialog — Use the --progress option.
  184. (define* (zenity-pulsate message #:key (auto-close #f)
  185. (no-cancel #f)
  186. (width #f) (height #f))
  187. (let ((pipe (zenity-input-pipe (append
  188. (list "--progress" "--pulsate" (string-append "--text=" message))
  189. (if auto-close (list "--auto-close") (list))
  190. (if no-cancel (list "--no-cancel") (list)))
  191. #:width width #:height height)))
  192. (lambda () (close-pipe pipe))))
  193. (define* (zenity-progress message #:key (value #f)
  194. (auto-close #f)
  195. (no-cancel #f)
  196. (width #f) (height #f))
  197. (let ((pipe (zenity-input-pipe (append
  198. (list "--progress" (string-append "--text=" message))
  199. (if value
  200. (list (string-append "--percentage=" (number->string value)))
  201. (list))
  202. (if auto-close (list "--auto-close") (list))
  203. (if no-cancel (list "--no-cancel") (list)))
  204. #:width width #:height height)))
  205. (let ((done #f))
  206. (lambda (message)
  207. (unless done
  208. (if message
  209. (begin
  210. (display message pipe)
  211. (newline pipe)
  212. (when (>= message 100)
  213. (set! done #t)))
  214. (begin
  215. (close-pipe pipe)
  216. (set! done #t))))))))
  217. ;; Scale Dialog — Use the --scale option.
  218. (define* (zenity-scale message
  219. #:key (value #f)
  220. (minimum #f)
  221. (maximum #f)
  222. (step #f)
  223. (hide-value #f)
  224. (width #f) (height #f))
  225. (zenity/check (append
  226. (list "--scale" (string-append "--text=" message))
  227. (if value (list (string-append "--value=" (->string value)))
  228. (if minimum
  229. (list (string-append "--value=" (->string minimum)))
  230. (list)))
  231. (if minimum (list (string-append "--min-value="
  232. (->string minimum)))
  233. (list))
  234. (if maximum (list (string-append "--max-value="
  235. (->string maximum)))
  236. (list))
  237. (if step (list (string-append "--step=" (->string step)))
  238. (list))
  239. (if hide-value (list "--hide-value") (list)))
  240. (lambda (text) (string->number (chomp text)))))
  241. ;; Text Entry Dialog — Use the --entry option.
  242. (define* (zenity-entry message #:key (width #f) (height #f))
  243. (zenity/check (list "--entry" message (string-append "--text=" message))
  244. chomp
  245. #:width width #:height height))
  246. ;; Text Information Dialog — Use the --text-info option.