zenity.scm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  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 (title #f)
  59. (icon #f)
  60. (width #f)
  61. (height #f)
  62. (timeout #f))
  63. (let ((args (if (null? args)
  64. '()
  65. (append (list (car args))
  66. (if title (list (string-append "--title=" title)) (list))
  67. (if icon (list (string-append "--window-icon=" icon)) (list))
  68. (if width (list (string-append "--width=" (number->string width))) (list))
  69. (if height (list (string-append "--height=" (number->string height))) (list))
  70. (if timeout (list (string-append "--timeout=" (number->string timeout))) (list))
  71. (cdr args)))))
  72. (let* ((pipe (apply open-pipe* OPEN_READ "zenity" args))
  73. (text (list->string (drain-input pipe)))
  74. (ret (close-pipe pipe)))
  75. (values ret text))))
  76. (define* (zenity/check args thunk #:key (title #f)
  77. (icon #f)
  78. (width #f)
  79. (height #f)
  80. (timeout #f))
  81. (let-values (((ret text) (zenity args #:title title #:icon icon #:width width
  82. #:height height #:timeout timeout)))
  83. (cond ((= ret 0) (thunk text))
  84. ((= ret 256) #f)
  85. (else (error "unexpected return code in zenity")))))
  86. (define* (zenity-input-pipe args #:key (title #f)
  87. (icon #f)
  88. (width #f)
  89. (height #f)
  90. (timeout #f))
  91. (apply open-pipe* OPEN_WRITE "zenity"
  92. (if (null? args)
  93. '()
  94. (append (list (car args))
  95. (if title (list (string-append "--title=" title)) (list))
  96. (if icon (list (string-append "--window-icon=" icon)) (list))
  97. (if width (list (string-append "--width=" (number->string width))) (list))
  98. (if height (list (string-append "--height=" (number->string height))) (list))
  99. (if timeout (list (string-append "--timeout=" (number->string timeout))) (list))
  100. (cdr args)))))
  101. ;; Calendar Dialog — Use the --calendar option.
  102. (define* (zenity-calendar message #:key (title #f)
  103. (icon #f)
  104. (width #f)
  105. (height #f)
  106. (timeout #f))
  107. (zenity/check (list "--calendar" "--date-format=%d/%m/%Y" (string-append "--text=" message))
  108. parse-date
  109. #:title title
  110. #:icon icon
  111. #:width width
  112. #:height height
  113. #:timeout timeout))
  114. ;; Color Selection Dialog — Use the --color-selection option.
  115. (define* (zenity-color-selection #:key (color #f) (show-palette #f))
  116. (zenity/check (append
  117. (list "--color-selection")
  118. (if color (list (string-append "--color=" color)) (list))
  119. (if show-palette (list "--show-palette") (list)))
  120. chomp))
  121. ;; File Selection Dialog — Use the --file-selection option.
  122. (define* (zenity-file-selection #:key (multiple #f)
  123. (directory #f)
  124. (save #f)
  125. (filename #f)
  126. (title #f)
  127. (icon #f)
  128. (width #f)
  129. (height #f)
  130. (timeout #f))
  131. (let ((args (append
  132. (if multiple (list "--multiple") (list))
  133. (if directory (list "--directory") (list))
  134. (if save (list "--save") (list))
  135. (if filename (list (string-append "--filename=" filename)) (list)))))
  136. (let-values (((ret text) (zenity (cons* "--file-selection" args)
  137. #:title title
  138. #:icon icon
  139. #:width width
  140. #:height height
  141. #:timeout timeout)))
  142. (if (= ret 256)
  143. #f
  144. (if multiple
  145. ;; I hope you don't have files with | in the name.
  146. (string-split (chomp text) #\|)
  147. (chomp text))))))
  148. ;; Forms Dialog — Use the --forms option.
  149. (define* (zenity-forms text layout #:key (icon #f)
  150. (title #f)
  151. (width #f)
  152. (height #f)
  153. (timeout #f))
  154. (zenity/check (cons* "--forms"
  155. ;; must not use , in anything you enter
  156. "--separator=,"
  157. "--forms-date-format=%d/%m/%Y"
  158. (string-append "--text=" text)
  159. (map (lambda (layout-entry)
  160. (string-append
  161. (case (car layout-entry)
  162. ((entry) "--add-entry=")
  163. ((password) "--add-password=")
  164. ((calendar) "--add-calendar="))
  165. (cdr layout-entry)))
  166. layout))
  167. (lambda (text)
  168. (let loop ((layout layout)
  169. (data (string-split (chomp text) #\,)))
  170. (cond ((and (null? layout) (null? data)) '())
  171. ((or (null? layout) (null? data))
  172. (error "wrong number of form data fields in zenity"))
  173. (else (cons (case (caar layout)
  174. ((entry password) (car data))
  175. ((calendar) (parse-date (car data))))
  176. (loop (cdr layout)
  177. (cdr data)))))))
  178. #:title title
  179. #:icon icon
  180. #:width width
  181. #:height height
  182. #:timeout timeout))
  183. ;; List Dialog — Use the --list option.
  184. (define* (zenity-list message columns rows #:key (title #f)
  185. (icon #f)
  186. (width #f)
  187. (height #f)
  188. (timeout #f))
  189. ;; TODO: Emit a warning if two columns are keyed by the same name
  190. (let ((columns^ (map (lambda (col) (string-append "--column=" col)) columns))
  191. (rows^ (apply append (map (lambda (row) (map ->string row)) rows))))
  192. (zenity/check (cons "--list" (append columns^ rows^))
  193. (lambda (text)
  194. (assoc (chomp text) rows (lambda (k c) (string=? k (->string c)))))
  195. #:title title
  196. #:icon icon
  197. #:width width
  198. #:height height
  199. #:timeout timeout)))
  200. (define* (zenity-checklist message columns rows #:key (title #f)
  201. (icon #f)
  202. (width #f)
  203. (height #f)
  204. (timeout #f))
  205. (let ((columns^ (map (lambda (col) (string-append "--column=" col)) columns))
  206. (rows^ (apply append (map (lambda (row) (map ->string row)) rows))))
  207. (zenity/check (append (list "--list" "--checklist") (append columns^ rows^))
  208. (lambda (text)
  209. (string-split (chomp text) #\|))
  210. #:title title
  211. #:icon icon
  212. #:width width
  213. #:height height
  214. #:timeout timeout)))
  215. ;; Message Dialog — Error, Info, Question, Warning
  216. (define* (zenity-error message #:key (title #f)
  217. (icon #f)
  218. (width #f)
  219. (height #f)
  220. (timeout #f))
  221. (zenity (list "--error" (string-append "--text=" message))
  222. #:title title
  223. #:icon icon
  224. #:width width
  225. #:height height
  226. #:timeout timeout)
  227. #t)
  228. (define* (zenity-info message #:key (title #f)
  229. (icon #f)
  230. (width #f)
  231. (height #f)
  232. (timeout #f))
  233. (zenity (list "--info" (string-append "--text=" message))
  234. #:title title
  235. #:icon icon
  236. #:width width
  237. #:height height
  238. #:timeout timeout)
  239. #t)
  240. (define* (zenity-question message #:key (title #f)
  241. (icon #f)
  242. (width #f)
  243. (height #f)
  244. (timeout #f))
  245. (zenity/check (list "--question" (string-append "--text=" message))
  246. (lambda (_) #t)
  247. #:title title
  248. #:icon icon
  249. #:width width
  250. #:height height
  251. #:timeout timeout))
  252. (define* (zenity-warning message #:key (title #f)
  253. (icon #f)
  254. (width #f)
  255. (height #f)
  256. (timeout #f))
  257. (zenity (list "--warning" (string-append "--text=" message))
  258. #:title title
  259. #:icon icon
  260. #:width width
  261. #:height height
  262. #:timeout timeout)
  263. #t)
  264. ;; Notification Icon — Use the --notification option.
  265. ;; Password Dialog — Use the --password option.
  266. (define* (zenity-password message #:key (title #f)
  267. (icon #f)
  268. (width #f)
  269. (height #f)
  270. (timeout #f))
  271. (zenity/check (list "--password" message (string-append "--text=" message))
  272. chomp
  273. #:title title
  274. #:icon icon
  275. #:width width
  276. #:height height
  277. #:timeout timeout))
  278. ;; Progress Dialog — Use the --progress option.
  279. (define* (zenity-pulsate message #:key (auto-close #f)
  280. (no-cancel #f)
  281. (title #f)
  282. (icon #f)
  283. (width #f)
  284. (height #f)
  285. (timeout #f))
  286. (let ((pipe (zenity-input-pipe (append
  287. (list "--progress" "--pulsate" (string-append "--text=" message))
  288. (if auto-close (list "--auto-close") (list))
  289. (if no-cancel (list "--no-cancel") (list)))
  290. #:title title
  291. #:icon icon
  292. #:width width
  293. #:height height
  294. #:timeout timeout)))
  295. (lambda () (close-pipe pipe))))
  296. (define* (zenity-progress message #:key (value #f)
  297. (auto-close #f)
  298. (no-cancel #f)
  299. (title #f)
  300. (icon #f)
  301. (width #f)
  302. (height #f)
  303. (timeout #f))
  304. (let ((pipe (zenity-input-pipe (append
  305. (list "--progress" (string-append "--text=" message))
  306. (if value
  307. (list (string-append "--percentage=" (number->string value)))
  308. (list))
  309. (if auto-close (list "--auto-close") (list))
  310. (if no-cancel (list "--no-cancel") (list)))
  311. #:title title
  312. #:icon icon
  313. #:width width
  314. #:height height
  315. #:timeout timeout)))
  316. (let ((done #f))
  317. (lambda (message)
  318. (unless done
  319. (if message
  320. (begin
  321. (display message pipe)
  322. (newline pipe)
  323. (when (>= message 100)
  324. (set! done #t)))
  325. (begin
  326. (close-pipe pipe)
  327. (set! done #t))))))))
  328. ;; Scale Dialog — Use the --scale option.
  329. (define* (zenity-scale message
  330. #:key (value #f)
  331. (minimum #f)
  332. (maximum #f)
  333. (step #f)
  334. (hide-value #f)
  335. (title #f)
  336. (icon #f)
  337. (width #f)
  338. (height #f)
  339. (timeout #f))
  340. (zenity/check (append
  341. (list "--scale" (string-append "--text=" message))
  342. (if value (list (string-append "--value=" (->string value)))
  343. (if minimum
  344. (list (string-append "--value=" (->string minimum)))
  345. (list)))
  346. (if minimum (list (string-append "--min-value="
  347. (->string minimum)))
  348. (list))
  349. (if maximum (list (string-append "--max-value="
  350. (->string maximum)))
  351. (list))
  352. (if step (list (string-append "--step=" (->string step)))
  353. (list))
  354. (if hide-value (list "--hide-value") (list)))
  355. (lambda (text) (string->number (chomp text)))
  356. #:title title
  357. #:icon icon
  358. #:width width
  359. #:height height
  360. #:timeout timeout))
  361. ;; Text Entry Dialog — Use the --entry option.
  362. (define* (zenity-entry message #:key (title #f)
  363. (icon #f)
  364. (width #f)
  365. (height #f)
  366. (timeout #f))
  367. (zenity/check (list "--entry" message (string-append "--text=" message))
  368. chomp
  369. #:title title
  370. #:icon icon
  371. #:width width
  372. #:height height
  373. #:timeout timeout))
  374. ;; Text Information Dialog — Use the --text-info option.