3 (define-condition gtk-call-aborted
(error)
4 ((condition :initarg
:condition
:reader gtk-call-aborted-condition
))
5 (:report
(lambda (c stream
)
6 (format stream
"Call within main loop aborted because of error:~%~A" (gtk-call-aborted-condition c
)))))
8 (defun call-within-main-loop-and-wait (fn)
9 (let ((lock (bt:make-lock
))
10 (cv (bt:make-condition-variable
))
13 (bt:with-lock-held
(lock)
16 (setf result
(multiple-value-list (funcall fn
)))
17 (error (e) (setf error e
)))
18 (bt:with-lock-held
(lock)
19 (bt:condition-notify cv
)))
20 (bt:condition-wait cv lock
)
22 (error 'gtk-call-aborted
:condition error
)
23 (values-list result
)))))
25 (export 'call-within-main-loop-and-wait
)
27 (defmacro within-main-loop-and-wait
(&body body
)
28 `(call-within-main-loop-and-wait (lambda () ,@body
)))
30 (export 'within-main-loop-and-wait
)
32 (defstruct progress-display parent name count bar time-started current
)
34 (export 'progress-display
)
35 (export 'progress-display-parent
)
36 (export 'progress-display-name
)
37 (export 'progress-display-count
)
38 (export 'progress-display-bar
)
39 (export 'progress-display-time-started
)
40 (export 'progress-display-current
)
42 (defstruct (progress-window (:include progress-display
)) window box
)
44 (export 'progress-window
)
45 (export 'progress-window-window
)
46 (export 'progress-window-box
)
48 (defun create-progress-window (name count
)
49 (within-main-loop-and-wait
50 (let* ((window (make-instance 'gtk-window
:type
:toplevel
:title name
:window-position
:center
))
51 (box (make-instance 'v-box
))
52 (bar (make-instance 'progress-bar
:text name
)))
53 (container-add window box
)
54 (box-pack-start box bar
:expand nil
)
56 (make-progress-window :parent nil
:name name
:count count
:bar bar
:window window
:box box
:time-started
(get-internal-real-time) :current
0))))
58 (defun progress-display-root (progress)
59 (if (progress-display-parent progress
)
60 (progress-display-root (progress-display-parent progress
))
63 (defun create-progress-bar (parent name count
)
64 (assert name
) (assert count
)
66 (within-main-loop-and-wait
67 (let* ((root (progress-display-root parent
))
68 (bar (make-instance 'progress-bar
:text name
)))
69 (box-pack-start (progress-window-box root
) bar
:expand nil
)
71 (make-progress-display :parent parent
:name name
:count count
:bar bar
:time-started
(get-internal-real-time) :current
0)))
72 (create-progress-window name count
)))
74 (export 'create-progress-window
)
76 (defgeneric delete-progress-bar
(bar))
78 (export 'delete-progress-bar
)
80 (defmethod delete-progress-bar ((bar progress-window
))
81 (within-main-loop-and-wait (object-destroy (progress-window-window bar
))))
83 (defmethod delete-progress-bar ((bar progress-display
))
84 (let ((root (progress-display-root bar
)))
85 (within-main-loop-and-wait (container-remove (progress-window-box root
) (progress-display-bar bar
)))))
87 (defun format-duration (stream seconds colon-modifier-p at-sign-modifier-p
)
88 (declare (ignore colon-modifier-p at-sign-modifier-p
))
89 (let ((seconds (mod (truncate seconds
) 60))
90 (minutes (mod (truncate seconds
60) 60))
91 (hours (truncate seconds
3600)))
92 (format stream
"~2,'0D:~2,'0D:~2,'0D" hours minutes seconds
)))
94 (defun update-progress-bar-text (bar &optional
(lower-frac 0.0))
95 (let* ((elapsed (coerce (/ (- (get-internal-real-time)
96 (progress-display-time-started bar
))
97 internal-time-units-per-second
)
99 (process-rate (coerce (/ elapsed
(+ lower-frac
(progress-display-current bar
))) 'double-float
))
100 (total-time (coerce (* (progress-display-count bar
) process-rate
) 'double-float
)))
101 (setf (progress-bar-text (progress-display-bar bar
))
102 (format nil
"~A (~/gtk::format-duration/; ETA ~/gtk::format-duration/)" (progress-display-name bar
) elapsed total-time
))))
104 (defun update-progress-bar-texts (bar &optional
(lower-frac 0.0))
106 (update-progress-bar-text bar lower-frac
)
107 (update-progress-bar-texts (progress-display-parent bar
) (coerce (/ (progress-display-current bar
) (progress-display-count bar
)) 'double-float
))))
109 (defun tick-progress-bar (bar)
111 (within-main-loop-and-wait
112 (incf (progress-bar-fraction (progress-display-bar bar
))
113 (coerce (/ (progress-display-count bar
)) 'double-float
))
114 (incf (progress-display-current bar
))
115 (update-progress-bar-text bar
))))
117 (export 'tick-progress-bar
)
119 (defvar *current-progress-bar
* nil
)
121 (defmacro with-progress-bar
((name count
) &body body
)
122 (let ((bar (gensym)))
123 `(let* ((,bar
(create-progress-bar *current-progress-bar
* ,name
,count
))
124 (*current-progress-bar
* ,bar
))
127 (delete-progress-bar ,bar
)))))
129 (export 'with-progress-bar
)
131 (defmacro with-progress-bar-action
(&body body
)
132 `(multiple-value-prog1 (progn ,@body
)
133 (tick-progress-bar *current-progress-bar
*)))
135 (export 'with-progress-bar-action
)
137 (defun test-progress ()
138 (with-progress-bar ("Snowball" 10)
141 do
(with-progress-bar-action
142 (with-progress-bar ("Texts" 10)
145 do
(with-progress-bar-action (sleep 1))))))))
147 (defun show-message (message &key
(buttons :ok
) (message-type :info
) (use-markup nil
))
148 (let ((dialog (make-instance 'message-dialog
151 :message-type message-type
152 :use-markup use-markup
)))
155 (object-destroy dialog
))))
157 (export 'show-message
)