chiark / gitweb /
Bug fix
[clg] / examples / ginspect.lisp
CommitLineData
55212af1 1;; Common Lisp bindings for GTK+ 2.x
2;; Copyright 2005 Espen S. Johnsen <espen@users.sf.net>
3;;
4;; Permission is hereby granted, free of charge, to any person obtaining
5;; a copy of this software and associated documentation files (the
6;; "Software"), to deal in the Software without restriction, including
7;; without limitation the rights to use, copy, modify, merge, publish,
8;; distribute, sublicense, and/or sell copies of the Software, and to
9;; permit persons to whom the Software is furnished to do so, subject to
10;; the following conditions:
11;;
12;; The above copyright notice and this permission notice shall be
13;; included in all copies or substantial portions of the Software.
14;;
15;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
48acc6ae 23;; $Id: ginspect.lisp,v 1.10 2006/04/26 14:56:59 espen Exp $
55212af1 24
ce5b19ea 25#+sbcl(require :gtk)
26#+cmu(asdf:oos 'asdf:load-op :gtk)
27
28(defpackage "GINSPECT"
29 (:use "COMMON-LISP" "GLIB" "GTK" #+cmu"PCL" #+sbcl"SB-PCL")
46471546 30 (:export "GINSPECT" "GINSPECT-TOPLEVELS"))
ce5b19ea 31
32(in-package "GINSPECT")
9d059f24 33
e7b35488 34(defvar *ginspect-unbound-object-marker*
35 #+cmu (gensym "UNBOUND-OBJECT-")
36 #+sbcl sb-impl::*inspect-unbound-object-marker*)
37
38
9d059f24 39(defgeneric insert-object (object store parent &optional prefix))
40(defgeneric insert-parts (object store parent))
e7b35488 41(defgeneric object-has-parts-p (object))
42(defgeneric decompose-describe-object (object))
9d059f24 43
44
ce5b19ea 45;; A container to hold lisp objects "inside" the tree store
46(defclass object-container (gobject)
47 ((object :initarg :object))
48 (:metaclass gobject-class))
49
50
9d059f24 51(defun ginspect (object)
52 (let* ((store (make-instance 'tree-store
53 :column-types '(string string gobject boolean)
e7b35488 54 :column-names '(name pprinted object expanded)))
9d059f24 55 (view (make-instance 'tree-view :model store :headers-visible nil)))
56
57 (let ((column (make-instance 'tree-view-column))
e7b35488 58 (name (make-instance 'cell-renderer-text))
9d059f24 59 (object (make-instance 'cell-renderer-text)))
e7b35488 60 (tree-view-append-column view column)
61 (cell-layout-pack column name :expand nil)
26dbb426 62 (cell-layout-add-attribute column name 'text (column-index store 'name))
9d059f24 63 (cell-layout-pack column object :expand t)
26dbb426 64 (cell-layout-add-attribute column object 'text (column-index store 'pprinted)))
9d059f24 65
66 (insert-object object store nil)
67
68 (signal-connect view 'row-expanded
69 #'(lambda (iter path)
e7b35488 70 (when (setf
26dbb426 71 (tree-model-value store iter 'expanded)
72 (not (tree-model-value store iter 'expanded)))
e7b35488 73 (multiple-value-bind (valid child-iter)
9d059f24 74 (tree-model-iter-children store iter)
e7b35488 75 ;; Remove old children
9d059f24 76 (when valid
e7b35488 77 (loop while (tree-store-remove store child-iter))))
ce5b19ea 78 (let ((container (tree-model-value store iter 'object)))
79 (insert-parts (slot-value container 'object) store iter))
9d059f24 80 (tree-view-expand-row view path nil))))
81
82 (make-instance 'dialog
e7b35488 83 :title "Object Inspector" :show-children t :visible t
9d059f24 84 :default-width 600 :default-height 600
85 :button (list "gtk-close" #'widget-destroy :object t)
86 :child (make-instance 'scrolled-window
87 :hscrollbar-policy :automatic :child view))))
88
89
e7b35488 90(defmethod decompose-describe-object ((object t))
91 #+cmu
92 (destructuring-bind (description named-p &rest parts)
93 (inspect::describe-parts object)
94 (if (equal parts (list object))
95 (values description nil nil)
96 (values description named-p parts)))
b205ac7c 97 #+sbcl(sb-impl::inspected-parts object))
e7b35488 98
99(defmethod decompose-describe-object ((object (eql t)))
100 (values (call-next-method) nil nil))
101
102(defmethod decompose-describe-object ((object (eql nil)))
103 (values (call-next-method) nil nil))
104
105(defun propper-list-p (object)
106 (and (listp object) (null (cdr (last object)))))
107
108(defmethod decompose-describe-object ((object cons))
109 (if (propper-list-p object)
110 (values (call-next-method) nil object)
111 (values "The object is a CONS." nil (list (car object) (cdr object)))))
112
113(defmethod decompose-describe-object ((object #+cmu alien:system-area-pointer
114 #+sbcl sb-alien:system-area-pointer))
115 (values "The object is a SYSTEM-AREA-POINTER" nil nil))
116
117(defmethod decompose-describe-object ((object (eql *ginspect-unbound-object-marker*)))
118 (values "The slot is unbound" nil nil))
119
120#+cmu
121(defmethod decompose-describe-object ((object symbol))
122 (values
123 (call-next-method) t
59aecf50 124 (list
125 (cons "Name" (symbol-name object))
126 (cons "Package" (symbol-package object))
127 (cons "Value" (if (boundp object)
128 (symbol-value object)
129 *ginspect-unbound-object-marker*))
130 (cons "Function" (if (fboundp object)
131 (symbol-function object)
132 *ginspect-unbound-object-marker*))
133 (cons "Plist" (symbol-plist object)))))
e7b35488 134
e7b35488 135(defmethod decompose-describe-object ((object standard-object))
136 (values
48acc6ae 137 (format nil "The instance is an object of type ~A."
138 (class-name (class-of object)))
139 t
e7b35488 140 (loop
141 for slotd in (class-slots (class-of object))
48acc6ae 142 when (slot-readable-p slotd)
143 collect (let* ((slot-name (slot-definition-name slotd))
e7b35488 144 (slot-value (if (slot-boundp object slot-name)
145 (slot-value object slot-name)
d90e21ff 146 *ginspect-unbound-object-marker*)))
e7b35488 147 (cons (string slot-name) slot-value)))))
148
149
150(defmethod object-has-parts-p ((object t))
151 (nth-value 2 (decompose-describe-object object)))
152
153(defmethod object-has-parts-p ((object cons))
154 t)
155
156(defmethod object-has-parts-p ((object standard-object))
157 (class-slots (class-of object)))
158
159(defmethod object-has-parts-p ((object vector))
160 (not (zerop (length object))))
161
162
163(defmethod object-to-string ((object t))
9d059f24 164 (with-output-to-string (stream)
165 (write object :stream stream :lines 1 :right-margin 80)))
166
e7b35488 167(defmethod object-to-string ((object (eql *ginspect-unbound-object-marker*)))
168 "<unbound>")
169
170(defmethod insert-object ((object t) store parent &optional (name ""))
ce5b19ea 171 (let ((container (make-instance 'object-container :object object))
9d059f24 172 (has-parts (object-has-parts-p object)))
9d059f24 173 (let ((iter (tree-store-append store parent
e7b35488 174 (vector name (object-to-string object)
ce5b19ea 175 container (not has-parts)))))
9d059f24 176 (when has-parts
177 ;; Insert dummy child
ce5b19ea 178 (tree-store-append store iter (vector "" "" container t))))))
9d059f24 179
e7b35488 180(defmethod insert-parts :around ((object t) store parent)
181 (when (object-has-parts-p object)
182 (call-next-method)))
9d059f24 183
184(defmethod insert-parts ((object t) store parent)
e7b35488 185 (multiple-value-bind (description named-p parts)
186 (decompose-describe-object object)
187 (declare (ignore description))
188 (loop
189 for part in parts
190 do (if named-p
191 (insert-object (cdr part) store parent (string (car part)))
192 (insert-object part store parent)))))
193
194
195(defun ginspect-toplevels ()
196 (ginspect (window-list-toplevels)))