From strandh at labri.fr Fri Jul 1 04:52:21 2005 From: strandh at labri.fr (Robert Strandh) Date: Fri, 1 Jul 2005 06:52:21 +0200 Subject: [mcclim-devel] incremental redisplay and with-first-quadrant-coordinates In-Reply-To: <87fyv0faz6.fsf@plato.moon.paoloamoroso.it> References: <17090.36618.396341.419206@serveur5.labri.fr> <200506300254.j5U2sZ406835@saturn.mikemac.com> <17091.36103.854051.822104@serveur5.labri.fr> <87fyv0faz6.fsf@plato.moon.paoloamoroso.it> Message-ID: <17092.52229.284763.345677@serveur5.labri.fr> Paolo Amoroso writes: > Robert Strandh writes: > > Or maybe because Lisp vendor resources for producing additional CLIM > documentation or instructional material have always been limited. I considered that possibility, but then imagined what a complete user manual would look like, and it would necessarily contain most, if not all, of the spec. -- Robert Strandh --------------------------------------------------------------------- Greenspun's Tenth Rule of Programming: any sufficiently complicated C or Fortran program contains an ad hoc informally-specified bug-ridden slow implementation of half of Common Lisp. --------------------------------------------------------------------- From rtoy at earthlink.net Sat Jul 2 04:14:44 2005 From: rtoy at earthlink.net (Raymond Toy) Date: Sat, 02 Jul 2005 00:14:44 -0400 Subject: [mcclim-devel] test Message-ID: ignore From frido at q-software-solutions.de Sat Jul 2 14:59:26 2005 From: frido at q-software-solutions.de (Friedrich Dominicus) Date: Sat, 02 Jul 2005 16:59:26 +0200 Subject: [mcclim-devel] mcclim and actual SBCL In-Reply-To: <17092.52229.284763.345677@serveur5.labri.fr> (Robert Strandh's message of "Fri, 1 Jul 2005 06:52:21 +0200") References: <17090.36618.396341.419206@serveur5.labri.fr> <200506300254.j5U2sZ406835@saturn.mikemac.com> <17091.36103.854051.822104@serveur5.labri.fr> <87fyv0faz6.fsf@plato.moon.paoloamoroso.it> <17092.52229.284763.345677@serveur5.labri.fr> Message-ID: <877jg945kh.fsf_-_@flarge.here> Does anyone else have encountered the following problem while trying to start the clim examples with (clim-demo::demodemo) the error I got is: error opening #P"/tmp/.X11-unix/X0": File exists [Condition of type SB-INT:SIMPLE-FILE-ERROR] Restarts: 0: [ABORT] Abort handling SLIME request. 1: [ABORT] Exit debugger, returning to top level. Of course the file is there I'm running X11 and I suppose that is good thing. So what does good for is this error message? Regards Friedrich From rtoy at earthlink.net Sat Jul 2 20:46:56 2005 From: rtoy at earthlink.net (Raymond Toy) Date: Sat, 02 Jul 2005 16:46:56 -0400 Subject: [mcclim-devel] No scrolling in interactor pane Message-ID: I've been playing around a bit over the last few days and have kind of hooked up maxima with mcclim, basically cargo-culting the listener app. The basic interaction works, and the computed expressions are displayed correctly. (Some random screenshots can be found at http://common-lisp.net/~rtoy. In particular, maxima-repl-boxed.png shows what the subexpressions should be.) In this case, the output is computed as a string by maxima, and that is printed to the mcclim's *standard-output*. However, to be able to present subexpressions (eventually), I changed the way output is done. I use maxima's capabilities of drawing to a terminal with cursor positioning and thus set the stream-cursor-position as needed. This also works. Until I reach the bottom of the interactor pane. The pane never scrolls down and the prompt keeps getting overwritten. I'm obviously missing something, but I don't know what. Anyone have some hints for this clim newbie? Ray P.S. A current very hackish version of the code is available at common-lisp.net/~rtoy/maxima-repl-2005-07-02.lisp. From moore at bricoworks.com Sat Jul 2 21:40:13 2005 From: moore at bricoworks.com (Timothy Moore) Date: Sat, 2 Jul 2005 23:40:13 +0200 Subject: [mcclim-devel] No scrolling in interactor pane In-Reply-To: References: Message-ID: On Jul 2, 2005, at 10:46 PM, Raymond Toy wrote: > I've been playing around a bit over the last few days and have kind of > hooked up maxima with mcclim, basically cargo-culting the listener > app. The basic interaction works, and the computed expressions are > displayed correctly. (Some random screenshots can be found at > http://common-lisp.net/~rtoy. In particular, maxima-repl-boxed.png > shows what the subexpressions should be.) > > In this case, the output is computed as a string by maxima, and that > is printed to the mcclim's *standard-output*. > > However, to be able to present subexpressions (eventually), I changed > the way output is done. I use maxima's capabilities of drawing to a > terminal with cursor positioning and thus set the > stream-cursor-position as needed. > > This also works. Until I reach the bottom of the interactor pane. > The pane never scrolls down and the prompt keeps getting overwritten. > > I'm obviously missing something, but I don't know what. > Anyone have some hints for this clim newbie? > This is annoying. If you're outputing a stream of expressions, like in a listener, you could try a call to fresh-line after each one. Otherwise You can try code like: (defclass redisplay-frame-mixin () ()) (defmethod redisplay-frame-pane :after ((frame redisplay-frame-mixin) (pane application-pane) &key force-p) (declare (ignore force-p)) (change-space-requirements pane :height (bounding-rectangle-height (stream-output-history pane)))) Tim From rtoy at earthlink.net Sat Jul 2 23:19:59 2005 From: rtoy at earthlink.net (Raymond Toy) Date: Sat, 02 Jul 2005 19:19:59 -0400 Subject: [mcclim-devel] Re: No scrolling in interactor pane In-Reply-To: References: Message-ID: <42C7211F.90805@earthlink.net> Timothy Moore wrote: > > On Jul 2, 2005, at 10:46 PM, Raymond Toy wrote: > [snip] > This is annoying. If you're outputing a stream of expressions, like in a > listener, you could try a call to fresh-line after each one. Otherwise What I do is move the stream cursor somewhere, then write-char one or more characters, then move the cursor somewhere else and write-char some more. I don't really know where to put a fresh-line. > You can try code like: > > (defclass redisplay-frame-mixin () > ()) > > (defmethod redisplay-frame-pane :after > ((frame redisplay-frame-mixin) (pane application-pane) &key force-p) > (declare (ignore force-p)) > (change-space-requirements > pane :height (bounding-rectangle-height (stream-output-history pane)))) Thanks, this helps a lot! The output now causes the window to scroll. That's good. But the prompt refuses to go past the (original) bottom of the window, so all prompts are eventually on top of each other, as well as the input. The prompt is just princ some string to *standard-output*. Thanks for your help! Ray From rtoy at earthlink.net Sat Jul 2 23:19:59 2005 From: rtoy at earthlink.net (Raymond Toy) Date: Sat, 02 Jul 2005 19:19:59 -0400 Subject: [mcclim-devel] Re: No scrolling in interactor pane In-Reply-To: References: Message-ID: <42C7211F.90805@earthlink.net> Timothy Moore wrote: > > On Jul 2, 2005, at 10:46 PM, Raymond Toy wrote: > [snip] > This is annoying. If you're outputing a stream of expressions, like in a > listener, you could try a call to fresh-line after each one. Otherwise What I do is move the stream cursor somewhere, then write-char one or more characters, then move the cursor somewhere else and write-char some more. I don't really know where to put a fresh-line. > You can try code like: > > (defclass redisplay-frame-mixin () > ()) > > (defmethod redisplay-frame-pane :after > ((frame redisplay-frame-mixin) (pane application-pane) &key force-p) > (declare (ignore force-p)) > (change-space-requirements > pane :height (bounding-rectangle-height (stream-output-history pane)))) Thanks, this helps a lot! The output now causes the window to scroll. That's good. But the prompt refuses to go past the (original) bottom of the window, so all prompts are eventually on top of each other, as well as the input. The prompt is just princ some string to *standard-output*. Thanks for your help! Ray From c.rhodes at gold.ac.uk Fri Jul 8 21:27:11 2005 From: c.rhodes at gold.ac.uk (Christophe Rhodes) Date: Fri, 08 Jul 2005 22:27:11 +0100 Subject: [mcclim-devel] Tablature editor: incremental redisplay performance Message-ID: Hi, As some people here know, we've been working on a CLIM- and Climacs-based editor for lute tablature. We're at the stage now where the performance characteristics are getting beyond our abilities to manage, so we thought we would try to open up the problem to specialists. The issue seems to be that (with the patch I sent a week or so ago, to use the bounding-rectangle of a region union rather than the precise union itself), essentially all of the time spent in moving around a Tabcode editor buffer is spent in COMPUTE-DIFFERENCE SET. (Without the patch I sent, essentially all the time is spent in computing region unions: and it takes about 100 times as long doing that as it does computing a difference set...). Holding down a cursor key, on one-year-old hardware, leads to perceptible lag and continued cursor motion after the key is released. We'd be happy to provide more details, or to explain other things about our application and our aims for it; any advice or suggestions for improvements to redisplay performance would be greatly appreciated. The code itself is at ; papers discussing the application are available from . Any contributions and observations welcome (though please try not to laugh too much :-) Cheers, Christophe From pjb at informatimago.com Sat Jul 9 16:18:50 2005 From: pjb at informatimago.com (Pascal Bourguignon) Date: Sat, 9 Jul 2005 18:18:50 +0200 (CEST) Subject: [mcclim-devel] A fix to fix-clisp.lisp Message-ID: <20050709161850.F29DC10FBF3@thalassa.informatimago.com> By the way, input-stream-p and output-stream-p are not symbols of GRAY, but of the COMMON-LISP package. (defmacro make-generic (funame arguments) (let ((old (gensym (string funame))) (gargs (mapcar (lambda (arg) (if (consp arg) (first arg) arg)) arguments))) `(progn (setf (fdefinition ',old) (function ,funame)) (fmakunbound ',funame) (defgeneric ,funame ,gargs (:method ,arguments (,old , at gargs)))))) (defpackage #:clim-mop (:use #:clos)) (eval-when (:compile-toplevel :load-toplevel :execute) (loop for sym being the symbols of :clim-mop do (export sym :clim-mop))) (ext:without-package-lock ("COMMON-LISP") ; <--- ;; CLIM expects INPUT-STREAM-P to be a generic function. (unless (typep #'input-stream-p 'generic-function) (make-generic input-stream-p ((stream stream)))) ;; CLIM expects OUTPUT-STREAM-P to be a generic function. (unless (typep #'output-stream-p 'generic-function) (make-generic output-stream-p ((stream stream)))) ) -- __Pascal Bourguignon__ http://www.informatimago.com/ Cats meow out of angst "Thumbs! If only we had thumbs! We could break so much!" From pjb at informatimago.com Sat Jul 9 16:14:27 2005 From: pjb at informatimago.com (Pascal Bourguignon) Date: Sat, 9 Jul 2005 18:14:27 +0200 (CEST) Subject: [mcclim-devel] A fix to fix-clisp.lisp Message-ID: <20050709161427.4410010FBF3@thalassa.informatimago.com> Think about this: when is interned gray::original-input-stream-p in: (ext:without-package-lock ("GRAY") (setf (fdefinition 'gray::original-input-stream-p) ...)) ? I'd propose to put these symbols in a user package: a specific gray-user, or they could as well be left in the current package. fix-clisp.lisp: (defpackage #:clim-mop (:use #:clos)) (eval-when (:compile-toplevel :load-toplevel :execute) (loop for sym being the symbols of :clim-mop do (export sym :clim-mop))) (defpackage #:gray-user (:use)) (ext:without-package-lock ("GRAY") ;; CLIM expects INPUT-STREAM-P to be a generic function. (unless (typep #'input-stream-p 'generic-function) (setf (fdefinition 'gray-user::original-input-stream-p) #'input-stream-p) (fmakunbound 'input-stream-p) (defgeneric input-stream-p (stream) (:method ((stream stream)) (gray-user::original-input-stream-p stream)))) ;; CLIM expects OUTPUT-STREAM-P to be a generic function. (unless (typep #'output-stream-p 'generic-function) (setf (fdefinition 'gray-user::original-output-stream-p) #'output-stream-p) (fmakunbound 'output-stream-p) (defgeneric output-stream-p (stream) (:method ((stream stream)) (gray-user::original-output-stream-p stream)))) ) An alternative could be to gensym them. (defmacro make-generic (funame arguments) (let ((old (gensym (string funame))) (gargs (mapcar (lambda (arg) (if (consp arg) (first arg) arg)) arguments))) `(progn (setf (fdefinition ',old) (function ,funame)) (fmakunbound ',funame) (defgeneric ,funame ,gargs (:method ,arguments (,old , at gargs)))))) (defpackage #:clim-mop (:use #:clos)) (eval-when (:compile-toplevel :load-toplevel :execute) (loop for sym being the symbols of :clim-mop do (export sym :clim-mop))) (ext:without-package-lock ("GRAY") ;; CLIM expects INPUT-STREAM-P to be a generic function. (unless (typep #'input-stream-p 'generic-function) (make-generic input-stream-p ((stream stream)))) ;; CLIM expects OUTPUT-STREAM-P to be a generic function. (unless (typep #'output-stream-p 'generic-function) (make-generic output-stream-p ((stream stream)))) ) -- __Pascal Bourguignon__ http://www.informatimago.com/ Cats meow out of angst "Thumbs! If only we had thumbs! We could break so much!" From pjb at informatimago.com Sat Jul 9 16:40:51 2005 From: pjb at informatimago.com (Pascal Bourguignon) Date: Sat, 9 Jul 2005 18:40:51 +0200 (CEST) Subject: [mcclim-devel] with-system-redefinition-allowed for clisp Message-ID: <20050709164051.C004A10FBF3@thalassa.informatimago.com> Please, add this to utils.lisp: #+clisp (defmacro with-system-redefinition-allowed (&body body) `(eval-when (:compile-toplevel :load-toplevel :execute) (ext:without-package-lock ("COMMON-LISP") , at body))) -- __Pascal Bourguignon__ http://www.informatimago.com/ Cats meow out of angst "Thumbs! If only we had thumbs! We could break so much!" From frido at q-software-solutions.de Sun Jul 10 08:08:37 2005 From: frido at q-software-solutions.de (Friedrich Dominicus) Date: Sun, 10 Jul 2005 10:08:37 +0200 Subject: [mcclim-devel] mcclim and clx and SBCL 0.9.2.x Message-ID: <87ackvccca.fsf@flarge.here> Ok, had my "success" today. I installed the newest portable-clx. And applied some patches Christophe has suggested. I just extended them a bit. and now I'm able to run mcclim again. I was not successful with the debian cl-clx-sbcl package! My system is an AMD64, I'm using SBCL 0.9.2.14 IIRC, and the latest I got from the CVS tree (be it mcclim or portable-clx) Regards Friedrich From csr21 at cam.ac.uk Thu Jul 14 08:30:44 2005 From: csr21 at cam.ac.uk (Christophe Rhodes) Date: Thu, 14 Jul 2005 09:30:44 +0100 Subject: [mcclim-devel] with-output-as-gadget Message-ID: Hello, What's the status of with-output-as-gadget? At the listener, (clim:with-output-as-gadget (*standard-output*) (clim:with-look-and-feel-realization ((clim:frame-manager clim:*application-frame*) clim:*application-frame*) (clim:labelling (:label "Hello")))) gets me (interestingly) /half/ a label -- the top half, to be precise. Meanwhile, attempts to do something a bit more interesting, such as (clim:with-output-as-gadget (*standard-output*) (clim:with-look-and-feel-realization ((clim:frame-manager clim:*application-frame*) clim:*application-frame*) (clim:labelling (:label "Hello") (clim:make-pane 'clim:text-field :activate-callback (lambda (g) (princ (clim:gadget-value g))))))) give me NO-APPLICABLE-METHOD on sheet-native-transformation when called with an argument of NIL, so presumably something's not getting initialized properly (whose fault is that? Mine or McCLIM's? :) (This question is motivated by the potential for implementing UI elements of forms in the closure web browser -- see -- with clim gadgets; as far as I can tell, this is a theoretical perfect match, but if there's a reason that this is a bad idea, that would be good to know too.) Cheers, Christophe From rpgoldman at real-time.com Mon Jul 18 19:13:15 2005 From: rpgoldman at real-time.com (rpgoldman at real-time.com) Date: Mon, 18 Jul 2005 14:13:15 -0500 Subject: [mcclim-devel] question about CLIM spec [format-graph-from-roots] Message-ID: <17115.65355.828663.283063@gargle.gargle.HOWL> AFAICT, the CLIM spec doesn't say anything about what are the acceptable values for the DUPLICATE-TEST argument to FORMAT-GRAPH-FROM-ROOTS (although it does specify that the default value is EQL). This seems very unfortunate. If we were to be limited to the conventional equality test values, then we would be able to use a hash-table to store the graph nodes. However, if the spec permits arbitrary functions of two arguments, that seems to make use of hash-tables impossible. Am I overlooking something? Is this a deficiency in the spec? thanks, R From rpgoldman at real-time.com Mon Jul 18 22:50:08 2005 From: rpgoldman at real-time.com (rpgoldman at real-time.com) Date: Mon, 18 Jul 2005 17:50:08 -0500 Subject: [mcclim-devel] LAYOUT-GRAPH-NODES patch for DAG graph type... Message-ID: <17116.12832.904549.407417@gargle.gargle.HOWL> META DISCUSSION: Are the maintainers of McCLIM OK with me rooting around in the code for graph formatting? I readily confess I'm not a serious graphics hacker, but I need to debug a lot of code that uses DAGs, and I'd like to see this pushed along... Does anyone have the time and patience to give patches like this a cursory once-over and either hurl abuse or commit them? NOTES ON CODE: I have had a first shot at making a graph layout method for DAGs. This is a little brittle, but since if you try to format-graph-from-roots with :dag as the :graph-type, it will just crash McCLIM, maybe that's not too bad ;-) Rather than trying to build a new graph layout method ab initio, I have simply adapted Gilbert's original code. As far as I could tell, when laying out one level of the graph, Gilbert's code would look ahead, and try to allocate space for the next layer. My code keeps that structure, but just lets nodes go to the first place where the fit, rather than thinking hard about who their "tree parent" should be. Ugliness: 1. Interested code readers will see some ugliness in the handling of the DUPLICATE-TEST and DUPLICATE-KEY arguments to FORMAT-GRAPH-FROM-ROOTS. For some reason, these are pulled out of the argument list and thrown away rather than being given as initargs to the GRAPH-OUTPUT-RECORD that's created by FORMAT-GRAPH-FROM-ROOTS. I'm not sure why this was done. Since I'm ignorant about this code, instead of arrogating it to myself to add them to the STANDARD-GRAPH-OUTPUT-RECORD, I simply added DUPLICATE-TEST and DUPLICATE-KEY slots to the DAG-GRAPH-OUTPUT-RECORD type. Unless someone can see a reason why not, though, I would argue for pushing them up to STANDARD-GRAPH-OUTPUT-RECORD. (Or possibly doing a minor refactoring that would split STANDARD-GRAPH-OUTPUT-RECORD into TREE-GRAPH-OUTPUT-RECORD and NON-TREE-GRAPH-OUTPUT record, only the latter being allowed to have DUPLICATE-TEST and DUPLICATE-KEY.) Because I didn't want to muck with the current graph-output-record classes, I ended up with some ugliness in FORMAT-GRAPH-FROM-ROOTS argument handling. This could easily be removed, if the above change is accepted. 2. Per my earlier email, I arrogated it to myself to limit the set of permissible values for the duplicate-test keyword arg of FORMAT-GRAPH-FROM-ROOTS. I think that this is reasonable, but it arguably violates the CLIM spec. On the other hand, I think that McCLIM already does this --- there are some comments by GB to that effect at the head of hte file... 3. There's a little oddity in Gilbert's code where he seems to take a vector data structure and wham it into a list in order to be able to use MAP over it. I'm inclined to whack it into an array loop, but if someone thinks that would be really wrong, I'd like to hear. Cheers, Robert Patch file: Index: graph-formatting.lisp =================================================================== RCS file: /project/mcclim/cvsroot/mcclim/graph-formatting.lisp,v retrieving revision 1.15 diff -u -F^(def -r1.15 graph-formatting.lisp --- graph-formatting.lisp 13 May 2005 03:00:25 -0000 1.15 +++ graph-formatting.lisp 18 Jul 2005 22:41:54 -0000 @@ -164,7 +164,11 @@ (defun format-graph-from-roots (root-obj (find-graph-type graph-type) nil :hash-table (make-hash-table :test duplicate-test) - graph-options)))) + (append + (when (and (eq graph-type :dag) merge-duplicates) + (list :duplicate-test duplicate-test + :duplicate-key duplicate-key)) + graph-options))))) (setf (output-record-position graph-output-record) (values cursor-old-x cursor-old-y)) (with-output-recording-options (stream :draw t :record nil) @@ -210,7 +214,17 @@ (defclass tree-graph-output-record (stan ()) (defclass dag-graph-output-record (standard-graph-output-record) - ()) + ((duplicate-key + :initarg :duplicate-key + :reader duplicate-key + ) + (duplicate-test + :initarg :duplicate-test + :reader duplicate-test + :documentation "The DUPLICATE-TEST of a DAG-GRAPH-OUTPUT-RECORD +should be one of the :TEST values acceptable to MAKE-HASH-TABLE." + ) + )) (defclass digraph-graph-output-record (standard-graph-output-record) ()) @@ -300,6 +314,8 @@ (defmethod layout-graph-nodes ((graph-ou (:horizontal :vertical) (:vertical :horizontal)))) (generation-separation (parse-space stream generation-separation orientation))) + ;; generation sizes is an adjustable array that tracks the major + ;; dimension of each of the generations [2005/07/18:rpg] (let ((generation-sizes (make-array 10 :adjustable t :initial-element 0))) (labels ((node-major-dimension (node) (if (eq orientation :vertical) @@ -309,6 +325,9 @@ (defmethod layout-graph-nodes ((graph-ou (if (eq orientation :vertical) (bounding-rectangle-width node) (bounding-rectangle-height node))) + ;; WALK returns a node minor dimension for the node, + ;; AFAICT, allowing space for that node's children + ;; along the minor dimension. [2005/07/18:rpg] (walk (node depth) (unless (graph-node-minor-size node) (when (>= depth (length generation-sizes)) @@ -367,6 +386,126 @@ (defmethod layout-graph-nodes ((graph-ou (unless (null rest) (incf v within-generation-separation))) (graph-root-nodes graph-output-record))))))))))) + + +(defmethod layout-graph-nodes ((graph-output-record dag-graph-output-record) + stream arc-drawer arc-drawing-options) + "This is a first shot at a DAG layout. First does a TOPO sort that associates +each node with a depth, then lays out by depth. Tries to reuse a maximum of the +tree graph layout code. +PRECONDITION: This code assumes that we have generated only nodes up to the +cutoff-depth. GENERATE-GRAPH-NODES seems to obey this precondition." + (declare (ignore arc-drawer arc-drawing-options)) + (with-slots (orientation center-nodes generation-separation within-generation-separation root-nodes + duplicate-key + merge-duplicates) graph-output-record + ;; this code is snarly enough, handling merge-duplicates. If + ;; you're not merging duplicates, you're out of luck, at least for + ;; now... [2005/07/18:rpg] + (unless merge-duplicates + (cerror "Set to T and continue?" "DAG graph-layout type only supports merge-duplicates to be T") + (setf merge-duplicates t)) + + (check-type orientation (member :horizontal :vertical)) ;xxx move to init.-inst. + + ;; here major dimension is the dimension in which we grow the + ;; tree. + (let ((within-generation-separation (parse-space stream within-generation-separation + (case orientation + (:horizontal :vertical) + (:vertical :horizontal)))) + (generation-separation (parse-space stream generation-separation orientation))) + ;; generation sizes is an adjustable array that tracks the major + ;; dimension of each of the generations [2005/07/18:rpg] + (let ((generation-sizes (make-array 10 :adjustable t :initial-element 0)) + (visited (make-hash-table :test (duplicate-test graph-output-record))) + (parent-hash (make-hash-table :test (duplicate-test graph-output-record)))) + (labels ((node-major-dimension (node) + (if (eq orientation :vertical) + (bounding-rectangle-height node) + (bounding-rectangle-width node))) + (node-minor-dimension (node) + (if (eq orientation :vertical) + (bounding-rectangle-width node) + (bounding-rectangle-height node))) + ;; WALK returns a node minor dimension for the node, + ;; AFAICT, allowing space for that node's children + ;; along the minor dimension. [2005/07/18:rpg] + (walk (node depth &optional parent) + ;; the following is possibly unnecessary --- it + ;; would only be a win if the key-value is + ;; expensive to compute. [2005/07/18:rpg] + (let ((key-value (funcall duplicate-key node))) + (unless (gethash key-value visited) + (setf (gethash key-value visited) depth) + (when parent + (setf (gethash key-value parent-hash) parent)) + (unless (graph-node-minor-size node) + (when (>= depth (length generation-sizes)) + (setf generation-sizes (adjust-array generation-sizes (ceiling (* depth 1.2)) + :initial-element 0))) + (setf (aref generation-sizes depth) + (max (aref generation-sizes depth) (node-major-dimension node))) + (setf (graph-node-minor-size node) 0) + (max (node-minor-dimension node) + (setf (graph-node-minor-size node) + (let ((sum 0) (n 0)) + (map nil (lambda (child) + (let ((x (walk child (+ depth 1) node))) + (when x + (incf sum x) + (incf n)))) + (graph-node-children node)) + (+ sum + (* (max 0 (- n 1)) within-generation-separation)))))))))) + (map nil #'(lambda (x) (walk x 0)) root-nodes) + (let ((hash (make-hash-table :test #'eq))) + (labels ((foo (node majors u0 v0) + (cond ((gethash node hash) + v0) + (t + (setf (gethash node hash) t) + (let ((d (- (node-minor-dimension node) + (graph-node-minor-size node)))) + (let ((v (+ v0 (/ (min 0 d) -2)))) + (setf (output-record-position node) + (if (eq orientation :vertical) + (transform-position (medium-transformation stream) v u0) + (transform-position (medium-transformation stream) u0 v))) + (add-output-record node graph-output-record)) + ;; + (let ((u (+ u0 (car majors))) + (v (+ v0 (max 0 (/ d 2)))) + (firstp t)) + (map nil (lambda (q) + (unless (gethash q hash) + (if firstp + (setf firstp nil) + (incf v within-generation-separation)) + (setf v (foo q (cdr majors) + u v)))) + ;; when computing the sizes, to + ;; make the tree-style layout + ;; work, we have to have each + ;; node have a unique + ;; parent. [2005/07/18:rpg] + (remove-if-not #'(lambda (x) (eq (gethash x parent-hash) node)) + (graph-node-children node)))) + ;; + (+ v0 (max (node-minor-dimension node) + (graph-node-minor-size node)))))))) + ;; + (let ((majors (mapcar (lambda (x) (+ x generation-separation)) + (coerce generation-sizes 'list)))) + (let ((u (+ 0 (car majors))) + (v 0)) + (maplist (lambda (rest) + (setf v (foo (car rest) majors u v)) + (unless (null rest) + (incf v within-generation-separation))) + (graph-root-nodes graph-output-record))))))))))) + + #+ignore (defmethod layout-graph-edges ((graph-output-record standard-graph-output-record) From duncan at robotcat.demon.co.uk Thu Jul 21 17:21:05 2005 From: duncan at robotcat.demon.co.uk (Duncan Rose) Date: Thu, 21 Jul 2005 18:21:05 +0100 Subject: [mcclim-devel] question about CLIM spec [format-graph-from-roots] In-Reply-To: <17115.65355.828663.283063@gargle.gargle.HOWL> Message-ID: On Monday, July 18, 2005, at 08:13 pm, rpgoldman at real-time.com wrote: > > AFAICT, the CLIM spec doesn't say anything about what are the > acceptable values for the DUPLICATE-TEST argument to > FORMAT-GRAPH-FROM-ROOTS (although it does specify that the default > value is EQL). This seems very unfortunate. If we were to be limited It says that it is a '...function of two arguments that is used to compare two objects...' I think any function of two arguments that can be used to compare two objects should be ok. > to the conventional equality test values, then we would be able to use > a hash-table to store the graph nodes. However, if the spec permits > arbitrary functions of two arguments, that seems to make use of > hash-tables impossible. Am I overlooking something? Is this a > deficiency in the spec? I don't understand what the benefit of using hashtables to store the nodes is. That said, it seems to me that whilst the spec permits arbitrary functions of two arguments, any specific node type will need a particular DUPLICATE-TEST function... or am I missing the point of the question totally? -Duncan > > thanks, > R > _______________________________________________ > mcclim-devel mailing list > mcclim-devel at common-lisp.net > http://common-lisp.net/cgi-bin/mailman/listinfo/mcclim-devel > From rpgoldman at real-time.com Thu Jul 21 17:55:47 2005 From: rpgoldman at real-time.com (Robert P. Goldman) Date: Thu, 21 Jul 2005 12:55:47 -0500 Subject: [mcclim-devel] question about CLIM spec [format-graph-from-roots] In-Reply-To: References: Message-ID: <42DFE1A3.3040103@real-time.com> Duncan Rose wrote: > > On Monday, July 18, 2005, at 08:13 pm, rpgoldman at real-time.com wrote: > >> >> AFAICT, the CLIM spec doesn't say anything about what are the >> acceptable values for the DUPLICATE-TEST argument to >> FORMAT-GRAPH-FROM-ROOTS (although it does specify that the default >> value is EQL). This seems very unfortunate. If we were to be limited > > > It says that it is a '...function of two arguments that is used to > compare two objects...' > I think any function of two arguments that can be used to compare two > objects should be ok. > >> to the conventional equality test values, then we would be able to use >> a hash-table to store the graph nodes. However, if the spec permits >> arbitrary functions of two arguments, that seems to make use of >> hash-tables impossible. Am I overlooking something? Is this a >> deficiency in the spec? > > > I don't understand what the benefit of using hashtables to store the > nodes is. That said, it seems to me that whilst the spec permits > arbitrary functions of two arguments, any specific node type will need a > particular DUPLICATE-TEST function... or am I missing the point of the > question totally? Given the fact that there might be a very large number of nodes, and that we have to check for duplicates, a hash-table providing constant time lookup seems pretty desirable, so one could do (make-hash-table :test duplicate-test) (gethash (funcall duplicate-key node) hash-table) to find duplicates. Perhaps /I'm/ missing the point --- do you have a different vision of how the duplicate detection could be performed? I haven't thought long and hard about it, but in the worst case, given an arbitrary duplicate-test function, isn't it the case that we can't do better than linear search to check for duplicates? I.e., we have no idea how to thin the field of possible match candidates, so we can't do better than check EVERY existing graph node to see if a newly-added one matches. That seems unacceptable. Or am I overlooking somethign? Note also that since you have the freedom to define the DUPLICATE-KEY funciton yourself, restricting the DUPLICATE-TEST values to one of the hash-table-acceptable alternatives doesn't seem like a profound sacrifice. Can anyone think of a case where it would be a substantial sacrifice? Best, Robert P.S. This discussion has reminded me to add an annotation to the CLIM spec to this effect. From nicolas.sceaux at free.fr Wed Jul 20 21:20:34 2005 From: nicolas.sceaux at free.fr (Nicolas Sceaux) Date: Wed, 20 Jul 2005 23:20:34 +0200 Subject: [mcclim-devel] little patch when using clx+MacOSX Message-ID: Hi there I'm running SBCL 0.9, with telent clx 0.7, on Mac OS X.4, with Apple's X11, mcclim from CVS. In order to run the demos, I had to make the attached change. When $DISPLAY is not set, (get-environment-variable "DISPLAY") returns NIL, and thus a condition is raised when calling (POSITION #\/ NIL) in the next form. nicolas -------------- next part -------------- A non-text attachment was scrubbed... Name: mcclim-clx-osx.patch Type: text/x-patch Size: 677 bytes Desc: not available URL: From amoroso at mclink.it Fri Jul 22 16:24:56 2005 From: amoroso at mclink.it (Paolo Amoroso) Date: Fri, 22 Jul 2005 18:24:56 +0200 Subject: [mcclim-devel] Poor man's print dialog Message-ID: <87vf32lsfb.fsf@plato.moon.paoloamoroso.it> The KDE Linux desktop environment provides a standard dialog for printing a document to a printer, faxing it, generating a PDF/PostScript file or emailing a PDF file. This functionality is accessible from the shell via the `kprinter' command, which accepts a PostScript file from stdin. I tried to use kprinter with McCLIM's PostScript backend and CMUCL Snapshot 2005-07 (19B) under Slackware Linux 10.0 with code like this: ;;; Requires CMUCL and KDE (in-package :clim-user) (define-application-frame kprinter () () (:panes (display :application :scroll-bars t :display-function 'display-graphics)) (:layouts (default display))) (defun display-graphics (frame stream) (declare (ignore frame)) (draw-rectangle* stream 20 50 120 100 :filled t :ink +red+) (draw-circle* stream 220 150 30 :filled t :ink +green+) (draw-text* stream "CLIMz rulez" 320 250 :text-size :huge :ink +blue+)) (define-kprinter-command (com-quit :menu t :name t) () (frame-exit *application-frame*)) (define-kprinter-command (com-print :menu t :name t) () (let* ((process (ext:run-program "kprinter" nil :input :stream :wait nil)) (process-stream (ext:process-input process))) (unwind-protect (with-output-to-postscript-stream (stream process-stream) (display-graphics *application-frame* stream)) (finish-output process-stream) (ext:process-close process) (ext:process-wait process t)))) (defun print-dialog () (run-frame-top-level (make-application-frame 'kprinter :width 550 :height 350))) This doesn't work as expected. If I confirm the dialog with the "Print" button, the print jobs is correctly generated, but the McCLIM application freezes and I have to abort the session. If I dismiss the dialog with "Cancel", I get this error: * (clim-user::print-dialog) Error in function UNIX::SIGPIPE-HANDLER: SIGPIPE at #x4011EC58. [Condition of type SIMPLE-ERROR] Restarts: 0: [ABORT] Return to application command loop 1: Return to Top-Level. Debug (type H for help) (UNIX::SIGPIPE-HANDLER # # #.(SYSTEM:INT-SAP #x3FFFC5A0)) Source: Error finding source: Error in function DEBUG::GET-FILE-TOP-LEVEL-FORM: Source file no longer exists: target:code/signal.lisp. 0] backtrace 0: (UNIX::SIGPIPE-HANDLER # # #.(SYSTEM:INT-SAP #x3FFFC5A0)) 1: (UNIX::SIGPIPE-HANDLER 3 # # #.(SYSTEM:INT-SAP #x3FFFC5A0))[:EXTERNAL] 2: ("call_into_lisp+#x8C [#x805529C] cmucl") 3: ("funcall3+#x29 [#x805508D] cmucl") 4: ("interrupt_handle_now+#xFF [#x8050A03] cmucl") 5: ("NIL+#x8050E1B [#x8050E1B] cmucl") 6: ("NIL+#x4007A0A8 [#x4007A0A8] /lib/libc.so.6") 7: (LISP::DO-OUTPUT # #.(SYSTEM:INT-SAP #x4030C000) 0 3972 ...) 8: (LISP::FLUSH-OUTPUT-BUFFER #) 9: (LISP::FD-STREAM-MISC-ROUTINE # :FINISH-OUTPUT NIL #) 10: (FINISH-OUTPUT #) 11: ((FLET #:G0 CLIM-POSTSCRIPT::INVOKE-WITH-OUTPUT-TO-POSTSCRIPT-STREAM))[:CLEANUP] 12: (CLIM-POSTSCRIPT::INVOKE-WITH-OUTPUT-TO-POSTSCRIPT-STREAM # # :DEVICE-TYPE NIL ...) 13: (CLIM-USER::COM-PRINT) 14: ((METHOD CLIM:DEFAULT-FRAME-TOP-LEVEL NIL (CLIM:APPLICATION-FRAME)) (#() . #(# # # # # ...)) # # NIL) 15: ("LAMBDA (.KEYARGS-START. .VALID-KEYS. G5411)" # # # NIL) 16: ((METHOD CLIM:RUN-FRAME-TOP-LEVEL NIL (CLIM:APPLICATION-FRAME)) (#(20) . #()) # # #) 17: ((METHOD CLIM:RUN-FRAME-TOP-LEVEL (:AROUND) (CLIM:APPLICATION-FRAME)) (#(16 15) . #(#)) #S(PCL::FAST-METHOD-CALL :FUNCTION # :PV-CELL (# . #) :NEXT-METHOD-CALL NIL :ARG-INFO (1 . T)) # NIL) 18: (INTERACTIVE-EVAL (CLIM-USER::PRINT-DIALOG)) 19: (LISP::%TOP-LEVEL) 20: ((LABELS LISP::RESTART-LISP SAVE-LISP)) 0] Is there any way of returning control back to the McCLIM application after printing? There's probably something fundamental about spawning external processes and their I/O streams I don't understand. Paolo -- Lisp Propulsion Laboratory log - http://www.paoloamoroso.it/log From rpgoldman at sift.info Fri Jul 22 23:09:53 2005 From: rpgoldman at sift.info (Robert P. Goldman) Date: Fri, 22 Jul 2005 18:09:53 -0500 Subject: [mcclim-devel] (no subject) Message-ID: <17121.31937.986476.680997@gargle.gargle.HOWL> From rpgoldman at real-time.com Fri Jul 22 23:32:03 2005 From: rpgoldman at real-time.com (rpgoldman at real-time.com) Date: Fri, 22 Jul 2005 18:32:03 -0500 Subject: [mcclim-devel] questions about CLIM interface paradigm Message-ID: <17121.33267.276376.428740@gargle.gargle.HOWL> 1. This is a question about using the CLIM interface paradigm that I know should have an obvious answer, but I just don't understand where to find it. Consider that I have a command with some number of arguments > 1. Now, I specify a command with argument types. OK. Now where do I put the procedural information about the mechanics of choosing the arguments? I have done this in an existing application by the somewhat unpleasant expedient of having a command just have a single argument, for the "major" parameter, and then having procedural code that supplies the modifier arguments. E.g., there might be some boolean modifiers, and I provide code that tells CLIM to use a particular way of reading those modifiers. At the end of this UI code, we dispatch to the stuff that does the real work. But this seems wrong. Shouldn't I be able to specify the arguments and their types and have McCLIM Do the Right Thing about reading the additional arguments? E.g., know that a set of radiobuttons might be the right way to collect a multiple-choice modifier argument? 1.a) To get this behavior, should I be writing a bunch of accept methods? 1.b) If so, in an app that has an graphical application-pane and an interactor-pane, should I have one accept method that lets people click on the corresponding presentations (if any) and a separate one that will handle textual input? E.g., if I have objects with names, then the default accept method would allow users to supply an object of the right type by just clicking on it; could I add a second method, specialized for interactor panes, that would allow users to type the name and which would cause CLIM to somehow lookup the corresponding data strucure? And will a simple call to accept allow either form of input? Seems like it would, based on my experience, but I wasn't sure... Does CLIM just invoke ACCEPT on the application frame, and have that call to accept simply invoke ACCEPT on the associated panes? [P.S. shouldn't there be a default method for reading yes or no and returning T or NIL, as with y-or-n-p?] 2. Is it good style to use the Enabled method? Is there a standard for how CLIM should graphically present disablement? My understanding of the current UI dogma is that users should be presented with *all* the alternatives, both enabled and disabled, and that the disabled ones should be presented in some form that makes it clear they are disabled (e.g., pulldown menus with greyed-out alternatives). Not showing the disabled ones leaves the user confused about the facilities offered by the application, and showing the disabled ones w/o cues, leaves the user frustrated. Previously I had a lot of code that would handle inappropriate methods and produce a helpful string or two, but this is repetitive, a blemish on the design, and can be very large if there is a relatively complex condition for enablement.... 3. Is there any reasonable way to handle CERROR in code running inside a CLIM UI? Seems like there should be, but I don't really know how one would go about doing it.... Many thanks, Robert From amoroso at mclink.it Sat Jul 23 09:47:23 2005 From: amoroso at mclink.it (Paolo Amoroso) Date: Sat, 23 Jul 2005 11:47:23 +0200 Subject: [mcclim-devel] questions about CLIM interface paradigm References: <17121.33267.276376.428740@gargle.gargle.HOWL> Message-ID: <87ek9p26s4.fsf@plato.moon.paoloamoroso.it> rpgoldman at real-time.com writes: > 1. This is a question about using the CLIM interface paradigm that I know > should have an obvious answer, but I just don't understand where to > find it. Possibly likewise ;-) > Consider that I have a command with some number of arguments > 1. > Now, I specify a command with argument types. OK. Now where do I put > the procedural information about the mechanics of choosing the > arguments? Doesn't CLIM already take care of this in interactor panes, e.g. with required and keyword arguments? See for example the CLIM Listener commands Show Class Subclasses/Superclasses. > But this seems wrong. Shouldn't I be able to specify the arguments > and their types and have McCLIM Do the Right Thing about reading the > additional arguments? E.g., know that a set of radiobuttons might be > the right way to collect a multiple-choice modifier argument? > > 1.a) To get this behavior, should I be writing a bunch of accept > methods? When you have many inputs of different types, you may use accepting-values. > 1.b) If so, in an app that has an graphical application-pane and an > interactor-pane, should I have one accept method that lets people > click on the corresponding presentations (if any) and a separate one > that will handle textual input? E.g., if I have objects with names, What about views? > 2. Is it good style to use the Enabled method? Is there a standard I think this is a general UI issue, not necessarily a CLIM one. > Previously I had a lot of code that would handle inappropriate methods > and produce a helpful string or two, but this is repetitive, a blemish > on the design, and can be very large if there is a relatively complex > condition for enablement.... You may keep a table associating commands with lists of commands to enable/disable. Each entry could also include tests for checking whether to actually disable/enable commands. The commands that need to do that could access the appropriate entry, run the tests, and enable/disable the relevant commands. It should be possible to hide much of this complexity to a CLIM application. Paolo -- Lisp Propulsion Laboratory log - http://www.paoloamoroso.it/log From duncan at robotcat.demon.co.uk Sat Jul 23 11:59:42 2005 From: duncan at robotcat.demon.co.uk (Duncan Rose) Date: Sat, 23 Jul 2005 12:59:42 +0100 Subject: [mcclim-devel] question about CLIM spec [format-graph-from-roots] In-Reply-To: <42DFE1A3.3040103@real-time.com> Message-ID: <40C8B518-FB71-11D9-A8C2-000A9577B8A2@robotcat.demon.co.uk> On Thursday, July 21, 2005, at 06:55 pm, Robert P. Goldman wrote: > Duncan Rose wrote: > >> On Monday, July 18, 2005, at 08:13 pm, rpgoldman at real-time.com wrote: >>> >>> AFAICT, the CLIM spec doesn't say anything about what are the >>> acceptable values for the DUPLICATE-TEST argument to >>> FORMAT-GRAPH-FROM-ROOTS (although it does specify that the default >>> value is EQL). This seems very unfortunate. If we were to be >>> limited >> It says that it is a '...function of two arguments that is used to >> compare two objects...' >> I think any function of two arguments that can be used to compare two >> objects should be ok. >>> to the conventional equality test values, then we would be able to >>> use >>> a hash-table to store the graph nodes. However, if the spec permits >>> arbitrary functions of two arguments, that seems to make use of >>> hash-tables impossible. Am I overlooking something? Is this a >>> deficiency in the spec? >> I don't understand what the benefit of using hashtables to store the >> nodes is. That said, it seems to me that whilst the spec permits >> arbitrary functions of two arguments, any specific node type will >> need a particular DUPLICATE-TEST function... or am I missing the >> point of the question totally? > > Given the fact that there might be a very large number of nodes, and > that we have to check for duplicates, a hash-table providing constant > time lookup seems pretty desirable, so one could do > > (make-hash-table :test duplicate-test) > (gethash (funcall duplicate-key node) hash-table) > > to find duplicates. > > Perhaps /I'm/ missing the point --- do you have a different vision of > how the duplicate detection could be performed? > I'm with you now; I agree that duplicate node checking would be easier and likely quicker using hashtables. I suspect that the original intention was that the graph be traversed to find duplicates; as you say, a linear search (perhaps something better could be achieved for ordered trees, but I'm not sure that the function arguments allow such possibilities). > I haven't thought long and hard about it, but in the worst case, given > an arbitrary duplicate-test function, isn't it the case that we can't > do better than linear search to check for duplicates? I.e., we have > no idea how to thin the field of possible match candidates, so we > can't do better than check EVERY existing graph node to see if a > newly-added one matches. That seems unacceptable. Or am I > overlooking somethign? > I think that the original intention was not to provide a built in function for drawing arbitrarily complex graphs of many nodes. Perhaps it was (again, it would be nice if one of the CLIM designers were subscribed to this list to answer such questions ;-). In the CLIM mailing list archive (ftp://ftp.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/lisp/gui/ clim/mail/) a lot of these kinds of questions seem to me to have been answered with 'if the supplied functionality doesn't meet your specific requirement, write something that does'. I think this may be one of those cases where the generalised CLIM functionality fails under specific usage, that either was not anticipated (in which case there's an argument for changing FORMAT-GRAPH-FROM-ROOTS) or that was anticipated and purposefully left unsupported - of course, we are only left to guess at which of these is correct. > Note also that since you have the freedom to define the DUPLICATE-KEY > funciton yourself, restricting the DUPLICATE-TEST values to one of the > hash-table-acceptable alternatives doesn't seem like a profound > sacrifice. > > Can anyone think of a case where it would be a substantial sacrifice? In as far as it departs from the specification, it's a sacrifice I think (though probably not a substantial sacrifice). To retread old ground, I would personally still prefer to see these kinds of changes implemented outside the main codebase of McCLIM (or at least, external to the implementation of the functionality that is present in the specification). At least that way, somebody could take the McCLIM 'extension' and drop it into LW or ACL, and have their application work. That said, it's not like the vendors (especially Franz) have adhered to the spec anyway (Franz likes to reorder argument lists which kind of makes more sense, and improves consistency I guess, but does (IMO) damage portability. I can only conclude that portability between CLIM implementations wasn't Franz' main concern with regards to CLIM. Is it ours?) so maybe this is an irrelevance. In summary: I think your argument has merit, I just feel that changing FORMAT-GRAPH-FROM-ROOTS isn't the place to rectify the problem(s). Without further user feedback I fear it will be difficult to resolve this issue to everyones satisfaction. -Duncan > > Best, > > Robert > > > P.S. This discussion has reminded me to add an annotation to the CLIM > spec to this effect. > From moore at bricoworks.com Sat Jul 23 13:51:00 2005 From: moore at bricoworks.com (Timothy Moore) Date: Sat, 23 Jul 2005 15:51:00 +0200 Subject: [mcclim-devel] questions about CLIM interface paradigm In-Reply-To: <17121.33267.276376.428740@gargle.gargle.HOWL> References: <17121.33267.276376.428740@gargle.gargle.HOWL> Message-ID: <6b995ea535b1a32640b5678f60726fc9@bricoworks.com> On Jul 23, 2005, at 1:32 AM, rpgoldman at real-time.com wrote: > > 1. This is a question about using the CLIM interface paradigm that I > know > should have an obvious answer, but I just don't understand where to > find it. > > Consider that I have a command with some number of arguments > 1. > Now, I specify a command with argument types. OK. Now where do I put > the procedural information about the mechanics of choosing the > arguments? If you're talking about a fixed number of arguments in a fixed order, then this is a property of the presentation type of each argument. There are also keyword command arguments that allow optional arguments in any order. > I have done this in an existing application by the somewhat unpleasant > expedient of having a command just have a single argument, for the > "major" parameter, and then having procedural code that supplies the > modifier arguments. E.g., there might be some boolean modifiers, and > I provide code that tells CLIM to use a particular way of reading > those modifiers. At the end of this UI code, we dispatch to the stuff > that does the real work. > I'm not sure what you mean by "modifier," but I'll try to muddle through anyway. > But this seems wrong. Shouldn't I be able to specify the arguments > and their types and have McCLIM Do the Right Thing about reading the > additional arguments? E.g., know that a set of radiobuttons might be > the right way to collect a multiple-choice modifier argument? Well, yes :) But we haven't yet written the accept methods that are specialized for "multiple choice" (called completion presentation types) in dialog views. We do have one experimental accept method for multiple choices that uses a pop-up menu. Look at accepting-values.lisp in Examples. > > 1.a) To get this behavior, should I be writing a bunch of accept > methods? > You should be defining a bunch of presentation types and, where necessary, write accept methods for them. Ideally you would inherit from existing presentation types and use their accept methods. > 1.b) If so, in an app that has an graphical application-pane and an > interactor-pane, should I have one accept method that lets people > click on the corresponding presentations (if any) and a separate one > that will handle textual input? E.g., if I have objects with names, > then the default accept method would allow users to supply an object > of the right type by just clicking on it; could I add a second method, > specialized for interactor panes, that would allow users to type the > name and which would cause CLIM to somehow lookup the corresponding > data strucure? No. When you call accept, explicitly or implicitly when reading command arguments, the user can always click on matching presentations in the graphical display. If the input stream is compatible with a text-view -- which is the case for an interactor pane -- the user can enter textual input at the same time. Typically, the only accept methods that you need to write deal with parsing complex input from a text stream. > And will a simple call to accept allow either form of > input? Seems like it would, based on my experience, but I wasn't > sure... Does CLIM just invoke ACCEPT on the application frame, and > have that call to accept simply invoke ACCEPT on the associated panes? > Accept is called with a stream (by default a pane that is frame-standard-input) and a view, usually a text view. > [P.S. shouldn't there be a default method for reading yes or no and > returning T or NIL, as with y-or-n-p?] > Yes; the boolean presentation type works like that. > 2. Is it good style to use the Enabled method? Is there a standard > for how CLIM should graphically present disablement? My understanding > of the current UI dogma is that users should be presented with *all* > the alternatives, both enabled and disabled, and that the disabled > ones should be presented in some form that makes it clear they are > disabled (e.g., pulldown menus with greyed-out alternatives). Not > showing the disabled ones leaves the user confused about the > facilities offered by the application, and showing the disabled ones > w/o cues, leaves the user frustrated. You have a good point; it is confusing to have commands appear and disappear. McCLIM does what CLIM did which is not ideal from a UI point of view. > > Previously I had a lot of code that would handle inappropriate methods > and produce a helpful string or two, but this is repetitive, a blemish > on the design, and can be very large if there is a relatively complex > condition for enablement.... > > 3. Is there any reasonable way to handle CERROR in code running > inside a CLIM UI? Seems like there should be, but I don't really know > how one would go about doing it.... > Some clever handler established with handler-bind, left as an exercise for the reader :) Tim From duncan at robotcat.demon.co.uk Sun Jul 24 12:49:45 2005 From: duncan at robotcat.demon.co.uk (Duncan Rose) Date: Sun, 24 Jul 2005 13:49:45 +0100 Subject: [mcclim-devel] Layers and packages Message-ID: <6936B18E-FC41-11D9-8F3F-000A9577B8A2@robotcat.demon.co.uk> > More as an exercise to improve my understanding of CLIM than anything > else, I've drawn an overview of the different CLIM layers with the > general groupings for functionality found in the specification. > > I thought I'd post it here because > > a) others might find it interesting / useful > b) I have a couple of questions about it ;-) > > In the diagram the stone coloured part contains 'system' level > functionality. Things that aren't part of CL, but aren't really part > of CLIM either. > > The blue parts are areas that the back ends need to worry about. > The orange parts are things that I'm not sure are in the right layer. > The layers go from most specific / closest to the hardware at the top > to the most abstract at the bottom. > The 'arrows' (does anybody know how to draw real arrows in > Appleworks?) show the relationships between the packages; more > abstract packages use functionality defined in less abstract packages. > > Questions: > > 1. Can anybody think of instances where less abstract layers (in the > diagram) have dependencies on 'more' abstract layers? Specifically I'm > thinking that input editing might have a dependency on command > processing. > > 2. Which layers should the orange bits be in? I'm particularly unhappy > with the position of the menu, dialog and pane functionality. I'm > think that the first two should perhaps be in the ADAPTIVE layer, and > the last one maybe should be in the 'McSILICA' layer. > > -Duncan -------------- next part -------------- A non-text attachment was scrubbed... Name: clim-packages-2 Type: application/octet-stream Size: 27045 bytes Desc: not available URL: -------------- next part -------------- From amoroso at mclink.it Sun Jul 24 16:23:16 2005 From: amoroso at mclink.it (Paolo Amoroso) Date: Sun, 24 Jul 2005 18:23:16 +0200 Subject: [mcclim-devel] Poor man's CLIM text style selection dialog Message-ID: <87k6jgnpfv.fsf@plato.moon.paoloamoroso.it> As a CLIM learning exercise, and as a way of finally contributing back something even remotely useful to the CLIM community, I have written the attached text style selection dialog. I have tested it with McCLIM and CMUCL under Linux, but it should work with little or no modification with other CLIM implementations. The dialog provides the same features of the CLIM text style model. See for example make-text-style in the specification. To invoke the dialog, just compile the file and evaluate: (clim-user::select-text-style) from a listener, or call it from your own code. When you click on "OK", the function returns a CLIM text style object, nil otherwise. The usage of the dialog should be straightforward, except for a couple of issues. When you enter a numeric text size in the appropriate field (try 2 or 60 for fun), hit ENTER to update the sample text. The family/face/size gadgets are not updated. See the code for additional information or issues. The main problem is that I shouldn't have written it in the first place: CLIM provides the powerful accepting-values macro for creating dialogs. McCLIM, however, supports only part of its functionality and can be used in simple cases. The bottom line is that my code is an example of how to fake a value-returning modal dialog, provides an interim solution, and gives an appreciation of the power and complexity of accepting-values. Paolo -- Lisp Propulsion Laboratory log - http://www.paoloamoroso.it/log -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: select-text-style.lisp URL: From amoroso at mclink.it Sun Jul 24 17:55:28 2005 From: amoroso at mclink.it (Paolo Amoroso) Date: Sun, 24 Jul 2005 19:55:28 +0200 Subject: [mcclim-devel] Layers and packages In-Reply-To: <6936B18E-FC41-11D9-8F3F-000A9577B8A2@robotcat.demon.co.uk> (Duncan Rose's message of "Sun, 24 Jul 2005 13:49:45 +0100") References: <6936B18E-FC41-11D9-8F3F-000A9577B8A2@robotcat.demon.co.uk> Message-ID: <87oe8sf5rj.fsf@plato.moon.paoloamoroso.it> Duncan Rose writes: >> anything else, I've drawn an overview of the different CLIM layers >> with the general groupings for functionality found in the >> specification. [...] >> 2. Which layers should the orange bits be in? I'm particularly >> unhappy with the position of the menu, dialog and pane >> functionality. I'm think that the first two should perhaps be in the >> ADAPTIVE layer, and the last one maybe should be in the 'McSILICA' >> layer. I would move dialogue and menu facilities to application building. Thanks for the useful resource. Paolo -- Lisp Propulsion Laboratory log - http://www.paoloamoroso.it/log From amoroso at mclink.it Mon Jul 25 20:45:21 2005 From: amoroso at mclink.it (Paolo Amoroso) Date: Mon, 25 Jul 2005 22:45:21 +0200 Subject: [mcclim-devel] Re: [Sbcl-devel] Would McCLIM benefit from this pretty printer patch for CMUCL/SBCL? In-Reply-To: (Matthias Koeppe's message of "Mon, 25 Jul 2005 22:57:39 +0200") References: Message-ID: <87sly262e6.fsf@plato.moon.paoloamoroso.it> Matthias Koeppe writes: > I have worked on a patch for the pretty printer of CMUCL and SBCL that > helps support the new presentation-like features of SLIME in > conjunction with the pretty printer. I probably can't comment on your patch. But you might be interested in some experimental work Gilbert Baumann did on the CMUCL pretty printer and McCLIM. Here are some screen shots: http://bauhh.dyndns.org:8000/mcclim/screenshots/pprint-1.png http://bauhh.dyndns.org:8000/mcclim/screenshots/pprint-2.png I think the code is here: http://bauhh.dyndns.org:8000/mcclim/pprint/ Paolo -- Lisp Propulsion Laboratory log - http://www.paoloamoroso.it/log From rpgoldman at real-time.com Mon Jul 25 23:09:14 2005 From: rpgoldman at real-time.com (Robert P. Goldman) Date: Mon, 25 Jul 2005 18:09:14 -0500 Subject: [mcclim-devel] questions about CLIM interface paradigm In-Reply-To: <87ek9p26s4.fsf@plato.moon.paoloamoroso.it> References: <17121.33267.276376.428740@gargle.gargle.HOWL> <87ek9p26s4.fsf@plato.moon.paoloamoroso.it> Message-ID: <42E5711A.8010705@real-time.com> Paolo Amoroso wrote: > rpgoldman at real-time.com writes: > > [...snip...] >>2. Is it good style to use the Enabled method? Is there a standard > > > I think this is a general UI issue, not necessarily a CLIM one. I actually think that this /is/ a specifically CLIM issue --- if CLIM simply removes disabled interactions from the UI, instead of leaving them there in a visibly-disabled form, then one may feel compelled to modify the way disablement is handled, or reimplement a custom disablement behavior. Down below, I will mention another reason why this might be a CLIM issue. > > > >>Previously I had a lot of code that would handle inappropriate methods >>and produce a helpful string or two, but this is repetitive, a blemish >>on the design, and can be very large if there is a relatively complex >>condition for enablement.... > > > You may keep a table associating commands with lists of commands to > enable/disable. Each entry could also include tests for checking > whether to actually disable/enable commands. The commands that need > to do that could access the appropriate entry, run the tests, and > enable/disable the relevant commands. It should be possible to hide > much of this complexity to a CLIM application. I see I wasn't clear enough in my first email. Actually, writing methods for "enabled" is really easy, and substantially cleans up my code for the commands themselves. On the other hand, I really don't want my user interface to be playing "hide-and-seek" with command names. Instead, if there's something about the current situation that makes a command be disabled, I'd like it to be displayed in some form that's effectively a "gray-out." This leads to my second point about why this might be a CLIM issue. It seems to me at least possible that there are TWO different kinds of enablement and disablement, which I will call (relatively) static and dynamic. The static case would correspond to application contexts where the command is inappropriate. E.g., if you have a browser for two different kinds of objects (say a schedule and a map), that application might have two different layouts, and some commands would be appropriate when in one layout and other commands would be appropriate in the other. That seems like a static case, and one where you might well NOT want the "gray out". Instead there would be two different contexts, with two different interfaces, and different sets of active commands. This seems like the case that the existing enablement protocol handles well. BUT, there is also another case, the "dynamic" case, where there may be short-lived situations that make a particular command inappropriate. For lack of a better example, consider some form of interface where one is editing a document. This document can be saved to a file. One might have the conventional "save" and "save as" commands. The former would be dynamically/temporarily disabled until a file name is associated with the current document, so that if you create an initial document, initially only the "Save as" is fully usable, and "Save" should be grayed out (if you open an existing document, the "Save" should be available immediately). Then, when a file name is associated with the document, the "Save" command would no longer be grayed out. IIUC, it is this latter case that is not supported by the existing CLIM protocols. Am I correct in this assumption? If so, how hard would it be to fix that problem? Presumably the command menu and menu bar objects could be subclassed to support this kind of dynamic disabling, yes? Cheers, R From rpgoldman at sift.info Mon Jul 25 15:22:12 2005 From: rpgoldman at sift.info (Robert P. Goldman) Date: Mon, 25 Jul 2005 10:22:12 -0500 Subject: [mcclim-devel] question about CLIM spec [format-graph-from-roots] In-Reply-To: <40C8B518-FB71-11D9-A8C2-000A9577B8A2@robotcat.demon.co.uk> References: <42DFE1A3.3040103@real-time.com> <40C8B518-FB71-11D9-A8C2-000A9577B8A2@robotcat.demon.co.uk> Message-ID: <17125.932.101336.964382@gargle.gargle.HOWL> >>>>> "DR" == Duncan Rose writes: DR> On Thursday, July 21, 2005, at 06:55 pm, Robert P. Goldman wrote: >> Duncan Rose wrote: >> >>> On Monday, July 18, 2005, at 08:13 pm, rpgoldman at real-time.com wrote: >>>> >>>> AFAICT, the CLIM spec doesn't say anything about what are the >>>> acceptable values for the DUPLICATE-TEST argument to >>>> FORMAT-GRAPH-FROM-ROOTS (although it does specify that the default >>>> value is EQL). This seems very unfortunate. If we were to be >>>> limited >>> It says that it is a '...function of two arguments that is used to >>> compare two objects...' >>> I think any function of two arguments that can be used to compare two >>> objects should be ok. >>>> to the conventional equality test values, then we would be able to >>>> use >>>> a hash-table to store the graph nodes. However, if the spec permits >>>> arbitrary functions of two arguments, that seems to make use of >>>> hash-tables impossible. Am I overlooking something? Is this a >>>> deficiency in the spec? >>> I don't understand what the benefit of using hashtables to store the >>> nodes is. That said, it seems to me that whilst the spec permits >>> arbitrary functions of two arguments, any specific node type will >>> need a particular DUPLICATE-TEST function... or am I missing the >>> point of the question totally? >> >> Given the fact that there might be a very large number of nodes, and >> that we have to check for duplicates, a hash-table providing constant >> time lookup seems pretty desirable, so one could do >> >> (make-hash-table :test duplicate-test) >> (gethash (funcall duplicate-key node) hash-table) >> >> to find duplicates. >> >> Perhaps /I'm/ missing the point --- do you have a different vision of >> how the duplicate detection could be performed? >> DR> I'm with you now; I agree that duplicate node checking would DR> be easier and likely quicker using hashtables. I suspect that DR> the original intention was that the graph be traversed to find DR> duplicates; as you say, a linear search (perhaps something DR> better could be achieved for ordered trees, but I'm not sure DR> that the function arguments allow such possibilities). I think I should provide an implementation that is based on the use of hash-tables, checking for the argument being EQ, EQL, EQUAL or EQUALP. I can have a mixin class providing the hash table and a find-previous-node generic function that does the lookup, and a linear search implementation of the method for the default case. [...snip...] DR> I think that the original intention was not to provide a built in DR> function for drawing arbitrarily complex graphs of many nodes. Perhaps DR> it was (again, it would be nice if one of the CLIM designers were DR> subscribed to this list to answer such questions ;-). In the CLIM DR> mailing list archive DR> (ftp://ftp.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/lisp/gui/ DR> clim/mail/) a lot of these kinds of questions seem to me to have been DR> answered with 'if the supplied functionality doesn't meet your specific DR> requirement, write something that does'. I think this may be one of DR> those cases where the generalised CLIM functionality fails under DR> specific usage, that either was not anticipated (in which case there's DR> an argument for changing FORMAT-GRAPH-FROM-ROOTS) or that was DR> anticipated and purposefully left unsupported - of course, we are only DR> left to guess at which of these is correct. This is a good point. It's clear, for example, that format-graph-from-roots is insufficient for a graph *editor*, since it doesn't provide support for being able to move the graph nodes, etc. So there's a case where one is clearly moving beyond format-graph-from-roots. But my case (simple dag display) seems acceptable. >> Note also that since you have the freedom to define the DUPLICATE-KEY >> funciton yourself, restricting the DUPLICATE-TEST values to one of the >> hash-table-acceptable alternatives doesn't seem like a profound >> sacrifice. >> >> Can anyone think of a case where it would be a substantial sacrifice? DR> In as far as it departs from the specification, it's a DR> sacrifice I think (though probably not a substantial DR> sacrifice). To retread old ground, I would personally still DR> prefer to see these kinds of changes implemented outside the DR> main codebase of McCLIM (or at least, external to the DR> implementation of the functionality that is present in the DR> specification). At least that way, somebody could take the DR> McCLIM 'extension' and drop it into LW or ACL, and have their DR> application work. This seems like a good idea, but I'm skeptical that it will work in practice. AFAICT, the documented graph-formatting interface is simply not sufficient to allow someone to create a portable extension. I am certainly finding that if I want to build onto what's there, I need to (or at least, it's a lot easier to) do it by using stuff that is not part of the specified API. For example, if one wishes to add slots to graph-output-record types, one really must take advantage of the fact that format-graph-from-roots in McCLIM passes extra arguments to the graph-output-record constructor, which is not (unless I missed something) part of the graph-formatting protocol. Similarly, the CLIM spec does not allow for additional keyword arguments to format-graph-from-roots, which IMHO would make the avowed intention of providing an extensible API well-nigh impossible. I take advantage of the fact that McCLIM has format-graph-from-roots with &allow-other-keys. DR> That said, it's not like the vendors (especially Franz) have DR> adhered to the spec anyway (Franz likes to reorder argument DR> lists which kind of makes more sense, and improves consistency DR> I guess, but does (IMO) damage portability. I can only DR> conclude that portability between CLIM implementations wasn't DR> Franz' main concern with regards to CLIM. Is it ours?) so DR> maybe this is an irrelevance. Since I don't expect ever to be able to afford a CLIM license from a vendor, this is not a huge concern of mine! That's not intended to be a slam --- CLIM seems horribly un-economical from the perspective of a Lisp vendor, combining the bad features of hugeness and expense to maintain with a vanishingly small user base. DR> In summary: I think your argument has merit, I just feel that DR> changing FORMAT-GRAPH-FROM-ROOTS isn't the place to rectify DR> the problem(s). Granted. I think that the hash-table can be supported using CLOS specialization w/o violating the spec (modulo &allow-other-keys, which seems mandatory and, in any case, isn't MY violation!), and if it can be, should be. I hope to provide a better patch RSN. OPEN QUESTION: The HASH-TABLE slot in standard-graph-output-record is not documented as to purpose. However, it seems to be used for duplicate-handling in GENERATE-GRAPH-NODES. On that basis, I'm inclined to remove it, move it into the mixin I've proposed, and provide something that's actually MORE standard-compliant than the current version. OPEN QUESTION 2: Would people who are using format-graph-from-roots be willing to send me a test case or test cases so that I can make sure any new version I create doesn't bust things? Thanks, Robert From rpgoldman at sift.info Tue Jul 26 00:49:40 2005 From: rpgoldman at sift.info (Robert P. Goldman) Date: Mon, 25 Jul 2005 19:49:40 -0500 Subject: [mcclim-devel] question about CLIM spec [format-graph-from-roots] In-Reply-To: <40C8B518-FB71-11D9-A8C2-000A9577B8A2@robotcat.demon.co.uk> References: <42DFE1A3.3040103@real-time.com> <40C8B518-FB71-11D9-A8C2-000A9577B8A2@robotcat.demon.co.uk> Message-ID: <17125.34980.531639.862128@gargle.gargle.HOWL> There is one problem with the strategy I've proposed of using mixins to handle different possible arguments to format-graph-from-roots: 1. Recall the proposed strategy: a. add a mixin class that would be something like HASHABLE-GRAPH. b. move the HASH-TABLE slot from STANDARD-GRAPH-OUTPUT-RECORD to HASHABLE-GRAPH. Add a NODE-LIST slot to STANDARD-GRAPH-OUTPUT-RECORD. c. Add a FIND-DUPLICATE-GRAPH-NODE generic function that would do linear search on NODE-LIST by default, but that would do a hash lookup if the graph-output-record were of type HASHABLE-GRAPH. d. Given a call to FORMAT-GRAPH-FROM-ROOTS, if DUPLICATE-TEST were one of the acceptable values (EQ, EQL, EQUAL or EQUALP), then make the hash-table, else use NODE-LIST. 2. Problem: as far as I can tell, this would make (if you pardon the pun) a hash of the existing McCLIM graph-type declaration process. Recall that when one defines a new graph-type, one needs to call DEFINE-GRAPH-TYPE to create a mapping from graph-type name (by convention, it seems, a keyword symbol, but could be simply a symbol) to graph-output-record subclass. But this approach isn't really compatible with the approach I've outlined in #1, above, since we'd need to be creating (at least) TWO new classes, one with the HASHABLE-GRAPH mixin, and one without, and the person defining the graph-output record would have to know about this difference. 3. Possible work-arounds: a. Avoid using CLOS and method dispatch to solve this problem. Instead, create a DUPLICATE-GRAPH-NODE function that is *not* methodified, but that simply checks the value of duplicate-test, and either uses a hash-table or not, as appropriate. Somehow this bothers me, but not as much as: b. Do something scary involving CHANGE-CLASS. The route of least resistance is clearly: c. Go ahead and violate the CLIM spec, and restrict DUPLICATE-TEST to be one of EQ, EQL, EQUAL or EQUALP. Note that *this is the status quo* --- the existing McCLIM code already makes a graph node hash table and initializes it using DUPLICATE-TEST, so I will just be continuing in the current line, not adding any new spec violations. Actually, note that if we don't want to choose this alternative, I actually need to fix existing code --- it's not enough to just add code, I actually need to fix the existing graph layout code If I had this to design ab initio (not that I would dare to design a graphic interface spec myself), I would just do 3(c), and make the spec limit DUPLICATE-TEST to one of the hash-able values, which seems far better. Anyone have a vendor CLIM license and know if they support arbitrary DUPLICATE-TEST values? Reading the Franz user's guide suggests that they do. Unless I get a strong vote for 3(c), I will follow 3(a), and try to have a patch RSN. BTW, shouldn't McCLIM cough up a hairball if you try to use format-graph-from-roots with :merge-duplicates t and :graph-type :tree? Cheers, Robert From rpgoldman at real-time.com Tue Jul 26 00:49:48 2005 From: rpgoldman at real-time.com (rpgoldman at real-time.com) Date: Mon, 25 Jul 2005 19:49:48 -0500 Subject: [mcclim-devel] question about implementation detail in format-graph-from-roots Message-ID: <17125.34988.49924.627526@gargle.gargle.HOWL> Some questions about the implementation details: 1. I was just noticing that the hash-table that is used for duplicate detection is only used in the generate-graph-nodes method, but is kept as a slot in standard-graph-output-record. Is there any reason I shouldn't turn this into a local variable in generate-graph-nodes? I'm concerned that, for large graphs, this could end up being a pretty large piece of uncollectable garbage. It might be even worse if the duplicate-test argument forces us to have a node list instead of a hash-table. I suppose if I do this, then we might get away with not keeping duplicate-test and duplicate-key in the graph-output-record objects, either... 2. Can anyone explain to me why :duplicate-test and :duplicate-key are keyword arguments to generate-graph-nodes but :merge-duplicates is not? Seems odd... 3. The existing code has the following lambda list for FORMAT-GRAPH-FROM ROOTS: (root-objects object-printer inferior-producer &rest graph-options &key stream orientation cutoff-depth merge-duplicates duplicate-key duplicate-test generation-separation within-generation-separation center-nodes (arc-drawer #'clim-internals::standard-arc-drawer) arc-drawing-options graph-type (move-cursor t) &allow-other-keys) then has the following immediately below: (setf stream (or stream *standard-output*) graph-type (or graph-type (if merge-duplicates :digraph :tree)) duplicate-key (or duplicate-key #'identity) duplicate-test (or duplicate-test #'eql) ) Could we have duplicate-key and duplicate-test simply get their defaults in the lambda-list? Or is this not feasible because of the GRAPH-OPTIONS &rest argument? Thanks, Robert From rpgoldman at sift.info Tue Jul 26 02:40:15 2005 From: rpgoldman at sift.info (Robert P. Goldman) Date: Mon, 25 Jul 2005 21:40:15 -0500 Subject: [mcclim-devel] New version of :dag graph-formatting patch Message-ID: <17125.41615.695781.558707@gargle.gargle.HOWL> This new version provides a layout-graph-nodes method for the :dag graph type. It also makes the code conform better to the specification of :duplicate-test and :duplicate-key argument handling in the CLIM spec. I would be very grateful if people would play with my dag formatting, but realize that it's a long shot if anyone else needs it right now[1], since it only handles merge-duplicates for the acyclic case. Maybe I'll tackle :digraph if I can get this working and accepted! But even if you don't need the :dag graph type, I'd be grateful if you were to test the code to make sure it doesn't break any of your existing uses of the tree type, and I know there must be SOME people using that... Cheers, R Footnotes: [1] Although I think if you were to browse a CLOS inheritance hierarchy, you might want to be able to merge duplicates, and I think that would have to be acyclic... Index: graph-formatting.lisp =================================================================== RCS file: /project/mcclim/cvsroot/mcclim/graph-formatting.lisp,v retrieving revision 1.15 diff -u -F^(def -r1.15 graph-formatting.lisp --- graph-formatting.lisp 13 May 2005 03:00:25 -0000 1.15 +++ graph-formatting.lisp 26 Jul 2005 02:34:20 -0000 @@ -163,8 +163,10 @@ (defun format-graph-from-roots (root-obj #'cont (find-graph-type graph-type) nil - :hash-table (make-hash-table :test duplicate-test) - graph-options)))) + ;; moved to local variable... [2005/07/25:rpg] + ;; :hash-table (make-hash-table :test duplicate-test) + graph-options + )))) (setf (output-record-position graph-output-record) (values cursor-old-x cursor-old-y)) (with-output-recording-options (stream :draw t :record nil) @@ -182,35 +184,40 @@ (defun format-graph-from-root (root &res (defclass standard-graph-output-record (graph-output-record standard-sequence-output-record) - ((orientation - :initarg :orientation - :initform :horizontal) - (center-nodes - :initarg :center-nodes - :initform nil) - (cutoff-depth - :initarg :cutoff-depth - :initform nil) - (merge-duplicates - :initarg :merge-duplicates - :initform nil) - (generation-separation - :initarg :generation-separation - :initform '(4 :character)) - (within-generation-separation - :initarg :within-generation-separation - :initform '(1/2 :line)) - (hash-table - :initarg :hash-table - :initform nil) - (root-nodes - :accessor graph-root-nodes) )) + ((orientation + :initarg :orientation + :initform :horizontal) + (center-nodes + :initarg :center-nodes + :initform nil) + (cutoff-depth + :initarg :cutoff-depth + :initform nil) + (merge-duplicates + :initarg :merge-duplicates + :initform nil) + (generation-separation + :initarg :generation-separation + :initform '(4 :character)) + (within-generation-separation + :initarg :within-generation-separation + :initform '(1/2 :line)) + ;; removed HASH-TABLE slot and stuffed it into + ;; GENERATE-GRAPH-NODES method definition [2005/07/25:rpg] + (root-nodes + :accessor graph-root-nodes) + )) (defclass tree-graph-output-record (standard-graph-output-record) - ()) + ()) + +(defmethod initialize-instance :after ((obj tree-graph-output-record) &key merge-duplicates) + (when merge-duplicates + (error "Cannot use a TREE layout for graphs while merging duplicates."))) (defclass dag-graph-output-record (standard-graph-output-record) - ()) + ( + )) (defclass digraph-graph-output-record (standard-graph-output-record) ()) @@ -242,41 +249,57 @@ (defmethod generate-graph-nodes ((graph- stream root-objects object-printer inferior-producer &key duplicate-key duplicate-test) - (declare (ignore duplicate-test)) - (with-slots (cutoff-depth merge-duplicates hash-table) graph-output-record - (labels - ((traverse-objects (node objects depth) - (unless (and cutoff-depth (>= depth cutoff-depth)) - (remove nil - (map 'list - (lambda (child) - (let* ((key (funcall duplicate-key child)) - (child-node (and merge-duplicates - (gethash key hash-table)))) - (cond (child-node - (when node - (push node (graph-node-parents child-node))) - child-node) - (t - (let ((child-node - (with-output-to-output-record - (stream 'standard-graph-node-output-record new-node - :object child) - (funcall object-printer child stream)))) - (when merge-duplicates - (setf (gethash key hash-table) child-node)) - (when node - (push node (graph-node-parents child-node))) - (setf (graph-node-children child-node) - (traverse-objects child-node - (funcall inferior-producer child) - (+ depth 1))) - child-node))))) - objects))))) - ;; - (setf (graph-root-nodes graph-output-record) - (traverse-objects nil root-objects 0)) - (values)))) + (with-slots (cutoff-depth merge-duplicates) graph-output-record + (let* ((hash-table (when (and merge-duplicates (member duplicate-test (list #'eq #'eql #'equal #'equalp))) + (make-hash-table :test duplicate-test))) + node-list + (hashed hash-table)) + (labels + ((previous-node (obj) + ;; is there a previous node for obj? if so, return it. + (when merge-duplicates + (if hashed + (locally (declare (type hash-table hash-table)) + (gethash obj hash-table)) + (cdr (assoc obj node-list :test duplicate-test))))) + ((setf previous-node) (val obj) + (if hashed + (locally (declare (type hash-table hash-table)) + (setf (gethash obj hash-table) val)) + (setf node-list (push (cons obj val) node-list)))) + (traverse-objects (node objects depth) + (unless (and cutoff-depth (>= depth cutoff-depth)) + (remove nil + (map 'list + (lambda (child) + (let* ((key (funcall duplicate-key child)) + (child-node (previous-node key))) + (cond (child-node + (when node + (push node (graph-node-parents child-node))) + child-node) + (t + (let ((child-node + (with-output-to-output-record + (stream 'standard-graph-node-output-record new-node + :object child) + (funcall object-printer child stream)))) + (when merge-duplicates + (setf (previous-node key) child-node) + ;; (setf (gethash key hash-table) child-node) + ) + (when node + (push node (graph-node-parents child-node))) + (setf (graph-node-children child-node) + (traverse-objects child-node + (funcall inferior-producer child) + (+ depth 1))) + child-node))))) + objects))))) + ;; + (setf (graph-root-nodes graph-output-record) + (traverse-objects nil root-objects 0)) + (values))))) (defun traverse-graph-nodes (graph continuation) ;; continuation: node x children x cont -> some value @@ -300,6 +323,8 @@ (defmethod layout-graph-nodes ((graph-ou (:horizontal :vertical) (:vertical :horizontal)))) (generation-separation (parse-space stream generation-separation orientation))) + ;; generation sizes is an adjustable array that tracks the major + ;; dimension of each of the generations [2005/07/18:rpg] (let ((generation-sizes (make-array 10 :adjustable t :initial-element 0))) (labels ((node-major-dimension (node) (if (eq orientation :vertical) @@ -309,6 +334,9 @@ (defmethod layout-graph-nodes ((graph-ou (if (eq orientation :vertical) (bounding-rectangle-width node) (bounding-rectangle-height node))) + ;; WALK returns a node minor dimension for the node, + ;; AFAICT, allowing space for that node's children + ;; along the minor dimension. [2005/07/18:rpg] (walk (node depth) (unless (graph-node-minor-size node) (when (>= depth (length generation-sizes)) @@ -367,6 +395,121 @@ (defmethod layout-graph-nodes ((graph-ou (unless (null rest) (incf v within-generation-separation))) (graph-root-nodes graph-output-record))))))))))) + + +(defmethod layout-graph-nodes ((graph-output-record dag-graph-output-record) + stream arc-drawer arc-drawing-options) + "This is a first shot at a DAG layout. First does a TOPO sort that associates +each node with a depth, then lays out by depth. Tries to reuse a maximum of the +tree graph layout code. +PRECONDITION: This code assumes that we have generated only nodes up to the +cutoff-depth. GENERATE-GRAPH-NODES seems to obey this precondition." + (declare (ignore arc-drawer arc-drawing-options)) + (with-slots (orientation center-nodes generation-separation within-generation-separation root-nodes + merge-duplicates) graph-output-record + ;; this code is snarly enough, handling merge-duplicates. If + ;; you're not merging duplicates, you're out of luck, at least for + ;; now... [2005/07/18:rpg] + (unless merge-duplicates + (cerror "Set to T and continue?" "DAG graph-layout type only supports merge-duplicates to be T") + (setf merge-duplicates t)) + + (check-type orientation (member :horizontal :vertical)) ;xxx move to init.-inst. + + ;; here major dimension is the dimension in which we grow the + ;; tree. + (let ((within-generation-separation (parse-space stream within-generation-separation + (case orientation + (:horizontal :vertical) + (:vertical :horizontal)))) + (generation-separation (parse-space stream generation-separation orientation))) + ;; generation sizes is an adjustable array that tracks the major + ;; dimension of each of the generations [2005/07/18:rpg] + (let ((generation-sizes (make-array 10 :adjustable t :initial-element 0)) + (visited (make-hash-table :test #'eq)) + (parent-hash (make-hash-table :test #'eq))) + (labels ((node-major-dimension (node) + (if (eq orientation :vertical) + (bounding-rectangle-height node) + (bounding-rectangle-width node))) + (node-minor-dimension (node) + (if (eq orientation :vertical) + (bounding-rectangle-width node) + (bounding-rectangle-height node))) + ;; WALK returns a node minor dimension for the node, + ;; AFAICT, allowing space for that node's children + ;; along the minor dimension. [2005/07/18:rpg] + (walk (node depth &optional parent) + (unless (gethash node visited) + (setf (gethash node visited) depth) + (when parent + (setf (gethash node parent-hash) parent)) + (unless (graph-node-minor-size node) + (when (>= depth (length generation-sizes)) + (setf generation-sizes (adjust-array generation-sizes (ceiling (* depth 1.2)) + :initial-element 0))) + (setf (aref generation-sizes depth) + (max (aref generation-sizes depth) (node-major-dimension node))) + (setf (graph-node-minor-size node) 0) + (max (node-minor-dimension node) + (setf (graph-node-minor-size node) + (let ((sum 0) (n 0)) + (map nil (lambda (child) + (let ((x (walk child (+ depth 1) node))) + (when x + (incf sum x) + (incf n)))) + (graph-node-children node)) + (+ sum + (* (max 0 (- n 1)) within-generation-separation))))))))) + (map nil #'(lambda (x) (walk x 0)) root-nodes) + (let ((hash (make-hash-table :test #'eq))) + (labels ((foo (node majors u0 v0) + (cond ((gethash node hash) + v0) + (t + (setf (gethash node hash) t) + (let ((d (- (node-minor-dimension node) + (graph-node-minor-size node)))) + (let ((v (+ v0 (/ (min 0 d) -2)))) + (setf (output-record-position node) + (if (eq orientation :vertical) + (transform-position (medium-transformation stream) v u0) + (transform-position (medium-transformation stream) u0 v))) + (add-output-record node graph-output-record)) + ;; + (let ((u (+ u0 (car majors))) + (v (+ v0 (max 0 (/ d 2)))) + (firstp t)) + (map nil (lambda (q) + (unless (gethash q hash) + (if firstp + (setf firstp nil) + (incf v within-generation-separation)) + (setf v (foo q (cdr majors) + u v)))) + ;; when computing the sizes, to + ;; make the tree-style layout + ;; work, we have to have each + ;; node have a unique + ;; parent. [2005/07/18:rpg] + (remove-if-not #'(lambda (x) (eq (gethash x parent-hash) node)) + (graph-node-children node)))) + ;; + (+ v0 (max (node-minor-dimension node) + (graph-node-minor-size node)))))))) + ;; + (let ((majors (mapcar (lambda (x) (+ x generation-separation)) + (coerce generation-sizes 'list)))) + (let ((u (+ 0 (car majors))) + (v 0)) + (maplist (lambda (rest) + (setf v (foo (car rest) majors u v)) + (unless (null rest) + (incf v within-generation-separation))) + (graph-root-nodes graph-output-record))))))))))) + + #+ignore (defmethod layout-graph-edges ((graph-output-record standard-graph-output-record) From rpgoldman at real-time.com Tue Jul 26 02:50:16 2005 From: rpgoldman at real-time.com (rpgoldman at real-time.com) Date: Mon, 25 Jul 2005 21:50:16 -0500 Subject: [mcclim-devel] questions about CLIM interface paradigm In-Reply-To: <42E5711A.8010705@real-time.com> References: <17121.33267.276376.428740@gargle.gargle.HOWL> <87ek9p26s4.fsf@plato.moon.paoloamoroso.it> <42E5711A.8010705@real-time.com> Message-ID: <17125.42216.897622.288695@gargle.gargle.HOWL> >>>>> "Me" == Robert P Goldman writes: Me> Paolo Amoroso wrote: >> rpgoldman at real-time.com writes: >> >> Me> [...snip...] >>> 2. Is it good style to use the Enabled method? Is there a standard >> >> >> I think this is a general UI issue, not necessarily a CLIM one. Me> I actually think that this /is/ a specifically CLIM issue --- if CLIM Me> simply removes disabled interactions from the UI, instead of leaving Me> them there in a visibly-disabled form, then one may feel compelled to Me> modify the way disablement is handled, or reimplement a custom Me> disablement behavior. OK, I see that this was unclear, mirroring my own confusion, but somewhat reinforced by what feels like inconsistency in McCLIM. When I make a menubar, then disabling is handled in the "dynamic" style I referred to in my email, and disabled items are grayed out. This seems fine. *But* when I just use the right menu button, then *all* commands are shown, whether they are enabled or disabled. Question: should we be modifying McCLIM to make the right button menu act like the menu bar, or vice versa? I favor the former, but defer to wiser heads.... Cheers, R From rpgoldman at real-time.com Tue Jul 26 18:49:32 2005 From: rpgoldman at real-time.com (rpgoldman at real-time.com) Date: Tue, 26 Jul 2005 13:49:32 -0500 Subject: [mcclim-devel] questions about CLIM interface paradigm In-Reply-To: <6b995ea535b1a32640b5678f60726fc9@bricoworks.com> References: <17121.33267.276376.428740@gargle.gargle.HOWL> <6b995ea535b1a32640b5678f60726fc9@bricoworks.com> Message-ID: <17126.34236.453233.606035@gargle.gargle.HOWL> >>>>> "TM" == Timothy Moore writes: TM> On Jul 23, 2005, at 1:32 AM, rpgoldman at real-time.com wrote: >> 3. Is there any reasonable way to handle CERROR in code running >> inside a CLIM UI? Seems like there should be, but I don't really know >> how one would go about doing it.... >> TM> Some clever handler established with handler-bind, left as an exercise TM> for the reader :) I think the problem is that the CERROR recovery code can do something pretty arbitrary, and I don't believe there's any obvious way to introspect in the handler to guess what presentation type you need to accept to do what the CERROR handler wants.... R From amoroso at mclink.it Wed Jul 27 19:03:38 2005 From: amoroso at mclink.it (Paolo Amoroso) Date: Wed, 27 Jul 2005 21:03:38 +0200 Subject: [mcclim-devel] Changing items list in list-pane/option-pane Message-ID: <87oe8o1379.fsf@plato.moon.paoloamoroso.it> Is there a portable way of changing the list of items in a list-pane or option-pane after it has been created? I mean after it has given an initial value via the :items initarg. Paolo -- Lisp Propulsion Laboratory log - http://www.paoloamoroso.it/log From csr21 at cam.ac.uk Thu Jul 28 11:28:41 2005 From: csr21 at cam.ac.uk (Christophe Rhodes) Date: Thu, 28 Jul 2005 12:28:41 +0100 Subject: [mcclim-devel] postscript streams Message-ID: Hi, Does it make sense to consider a request to include updating-output-stream-mixin in the superclasses of postscript-stream (in Backends/Postscript/class.lisp)? We have an application which uses updating-output for incremental redisplay, but we would also like to be able to use the same drawing functions (and the same drawing logic, more importantly) without having to implement it twice, once for updating-output streams and once for postscript streams. The below makes sense in our use case. (The alternative of creating a graphical output record and then replaying it on the postscript stream is unattractive because we have a use case with no graphical output at all) Cheers, Christophe Index: Backends/PostScript/class.lisp =================================================================== RCS file: /project/mcclim/cvsroot/mcclim/Backends/PostScript/class.lisp,v retrieving revision 1.6 diff -u -r1.6 class.lisp --- Backends/PostScript/class.lisp 4 Jul 2002 06:57:43 -0000 1.6 +++ Backends/PostScript/class.lisp 28 Jul 2005 11:31:01 -0000 @@ -60,6 +60,7 @@ permanent-medium-sheet-output-mixin sheet-mute-repainting-mixin mirrored-sheet-mixin ; ? + climi::updating-output-stream-mixin ; ?? standard-extended-output-stream standard-output-recording-stream) ((file-stream :initarg :file-stream :reader postscript-stream-file-stream) From csr21 at cam.ac.uk Thu Jul 28 14:30:16 2005 From: csr21 at cam.ac.uk (Christophe Rhodes) Date: Thu, 28 Jul 2005 15:30:16 +0100 Subject: [mcclim-devel] postscript streams In-Reply-To: (Christophe Rhodes's message of "Thu, 28 Jul 2005 12:28:41 +0100") References: Message-ID: Christophe Rhodes writes: > Does it make sense to consider a request to include > updating-output-stream-mixin in the superclasses of postscript-stream > (in Backends/Postscript/class.lisp)? We have an application which > uses updating-output for incremental redisplay, but we would also like > to be able to use the same drawing functions (and the same drawing > logic, more importantly) without having to implement it twice, once > for updating-output streams and once for postscript streams. Maybe a better implementation would make updating-output-stream-mixin a superclass of output-recording-stream? The clim spec says for UPDATING-OUTPUT The stream argument is not evaluated, and must be a symbol that is bound to an output recording stream. but at the moment postscript-stream is an output-recording-stream but errors when I call updating-output on it. Cheers, Christophe From pw at snoopy.mv.com Thu Jul 28 16:24:23 2005 From: pw at snoopy.mv.com (Paul Werkowski) Date: Thu, 28 Jul 2005 12:24:23 -0400 Subject: [mcclim-devel] Changing items list in list-pane/option-pane References: <87oe8o1379.fsf@plato.moon.paoloamoroso.it> Message-ID: <000f01c59390$d2a130c0$0201a8c0@moby> | Is there a portable way of changing the list of items in a list-pane | or option-pane after it has been created? I mean after it has given | an initial value via the :items initarg. (setf (gadget-value list-object) new-list) should work. Paul From amoroso at mclink.it Thu Jul 28 16:40:20 2005 From: amoroso at mclink.it (Paolo Amoroso) Date: Thu, 28 Jul 2005 18:40:20 +0200 Subject: [mcclim-devel] Changing items list in list-pane/option-pane In-Reply-To: <000f01c59390$d2a130c0$0201a8c0@moby> (Paul Werkowski's message of "Thu, 28 Jul 2005 12:24:23 -0400") References: <87oe8o1379.fsf@plato.moon.paoloamoroso.it> <000f01c59390$d2a130c0$0201a8c0@moby> Message-ID: <87u0ie7ukr.fsf@plato.moon.paoloamoroso.it> "Paul Werkowski" writes: > | Is there a portable way of changing the list of items in a list-pane > | or option-pane after it has been created? I mean after it has given > | an initial value via the :items initarg. > > (setf (gadget-value list-object) new-list) should work. Doesn't this change only the selected item? Paolo -- Lisp Propulsion Laboratory log - http://www.paoloamoroso.it/log From pw at snoopy.mv.com Thu Jul 28 18:01:59 2005 From: pw at snoopy.mv.com (Paul Werkowski) Date: Thu, 28 Jul 2005 14:01:59 -0400 Subject: [mcclim-devel] Changing items list in list-pane/option-pane References: <87oe8o1379.fsf@plato.moon.paoloamoroso.it><000f01c59390$d2a130c0$0201a8c0@moby> <87u0ie7ukr.fsf@plato.moon.paoloamoroso.it> Message-ID: <001301c5939e$734ed830$0201a8c0@moby> | > | > (setf (gadget-value list-object) new-list) should work. | | Doesn't this change only the selected item? Oops, yes. Sorry for not checking first :( Paul From raymond.toy at ericsson.com Thu Jul 28 16:49:21 2005 From: raymond.toy at ericsson.com (Raymond Toy) Date: Thu, 28 Jul 2005 12:49:21 -0400 Subject: [mcclim-devel] Re: [Sbcl-devel] Would McCLIM benefit from this pretty printer patch for CMUCL/SBCL? In-Reply-To: (Matthias Koeppe's message of "Mon, 25 Jul 2005 22:57:39 +0200") References: Message-ID: >>>>> "Matthias" == Matthias Koeppe writes: Matthias> Dear McCLIM list, Matthias> I have worked on a patch for the pretty printer of CMUCL and SBCL that Matthias> helps support the new presentation-like features of SLIME in Matthias> conjunction with the pretty printer. I'm not familiar with slime's presentations, but I was going to look into incorporating your patch. However, in light of Paolo's comments, I'm holding off until this is resolved. Ray From ahefner at gmail.com Thu Jul 28 17:37:52 2005 From: ahefner at gmail.com (Andy Hefner) Date: Thu, 28 Jul 2005 13:37:52 -0400 Subject: [mcclim-devel] Changing items list in list-pane/option-pane In-Reply-To: <87oe8o1379.fsf@plato.moon.paoloamoroso.it> References: <87oe8o1379.fsf@plato.moon.paoloamoroso.it> Message-ID: <31ffd3c40507281037624fa00e@mail.gmail.com> I don't think there's a portable way to do this. Looking through the Franz guide, it doesn't seem like they have a way to do this at all. I guess we should add this to the TODO list. Also, relating to what Paul Werkowski suggests, I don't think (setf (gadget-value ...) ...) works right in our current implementation - the slot value may change, but the graphic display won't be updated, and strange things may happen relating to multiple selection. Another TODO. On 7/27/05, Paolo Amoroso wrote: > Is there a portable way of changing the list of items in a list-pane > or option-pane after it has been created? I mean after it has given > an initial value via the :items initarg. From rpgoldman at sift.info Thu Jul 28 22:49:09 2005 From: rpgoldman at sift.info (Robert P. Goldman) Date: Thu, 28 Jul 2005 17:49:09 -0500 Subject: [mcclim-devel] Changing items list in list-pane/option-pane In-Reply-To: <31ffd3c40507281037624fa00e@mail.gmail.com> References: <87oe8o1379.fsf@plato.moon.paoloamoroso.it> <31ffd3c40507281037624fa00e@mail.gmail.com> Message-ID: <17129.24805.739659.734391@gargle.gargle.HOWL> Would you mind adding these to Paolo's bug list? Might be handy to have them lying around in case someone has a few spare hacking cycles... R From mkoeppe+slime at mail.math.uni-magdeburg.de Fri Jul 29 10:12:06 2005 From: mkoeppe+slime at mail.math.uni-magdeburg.de (Matthias Koeppe) Date: Fri, 29 Jul 2005 12:12:06 +0200 Subject: [mcclim-devel] Re: [Sbcl-devel] Would McCLIM benefit from this pretty printer patch for CMUCL/SBCL? Message-ID: Raymond Toy writes: >>>>>> "Matthias" == Matthias Koeppe writes: > Matthias> Dear McCLIM list, > Matthias> I have worked on a patch for the pretty printer of CMUCL and SBCL that > Matthias> helps support the new presentation-like features of SLIME in > Matthias> conjunction with the pretty printer. > > I'm not familiar with slime's presentations, but I was going to look > into incorporating your patch. > > However, in light of Paolo's comments, I'm holding off until this is > resolved. I have now taken a look at Gilbert Baumann's pretty printer patch (http://bauhh.dyndns.org:8000/mcclim/pprint/) that Paolo pointed out. As far as I can see, the changes there could be easily re-implemented in terms of the more general "annotations" feature implemented in my patch. The only thing that is not covered by my patch is the hooks into START-LOGICAL-BLOCK, END-LOGICAL-BLOCK, PPRINT-LOGICAL-BLOCK. These changes are independent of my patch. (I am including below a new version of the patch for CMUCL that includes docstrings.) Cheers, -- Matthias Koeppe -- http://www.math.uni-magdeburg.de/~mkoeppe diff -u /home/mkoeppe/s/slime/cmucl/pprint.lisp.orig /home/mkoeppe/s/slime/cmucl/pprint.lisp --- /home/mkoeppe/s/slime/cmucl/pprint.lisp.orig 2005-07-21 22:30:50.000000000 +0200 +++ /home/mkoeppe/s/slime/cmucl/pprint.lisp 2005-07-29 00:11:19.000000000 +0200 @@ -109,6 +109,10 @@ ;; ;; Block-start queue entries in effect at the queue head. (pending-blocks nil :type list) + ;; + ;; Queue of annotations to the buffer + (annotations-tail nil :type list) + (annotations-head nil :type list) ) (defun %print-pretty-stream (pstream stream depth) @@ -381,6 +385,10 @@ (enqueue stream tab :sectionp sectionp :relativep relativep :colnum colnum :colinc colinc))) +(defstruct (annotation (:include queued-op)) + (handler (constantly nil) :type function) + (record)) + ;;;; Tab support. @@ -472,6 +480,91 @@ (replace new-buffer buffer :end1 end :end2 end)))))) +;;;; Annotations support. + +(defun enqueue-annotation (stream handler record) + "Insert an annotation into the pretty-printing stream STREAM. +HANDLER is a function, and RECORD is an arbitrary datum. The +pretty-printing stream conceptionally queues annotations in sequence +with the characters that are printed to the stream, until the stream +has decided on the concrete layout. When the characters are forwarded +to the target stream, annotations are invoked at the right position. +An annotation is invoked by calling the function HANDLER with the +three arguments RECORD, TARGET-STREAM, and TRUNCATEP. The argument +TRUNCATEP is true if the text surrounding the annotation is suppressed +due to line abbreviation (see *PRINT-LINES*). +If STREAM is not a pretty-printing stream, simply call HANDLER +with the arguments RECORD, STREAM and nil." + (enqueue stream annotation :handler handler + :record record)) + +(defun re-enqueue-annotation (stream annotation) + "Insert ANNOTATION into the queue of annotations in STREAM." + (let* ((annotation-cons (list annotation)) + (head (pretty-stream-annotations-head stream))) + (if head + (setf (cdr head) annotation-cons) + (setf (pretty-stream-annotations-tail stream) annotation-cons)) + (setf (pretty-stream-annotations-head stream) annotation-cons))) + +(defun re-enqueue-annotations (stream end) + "Insert all annotations in STREAM from the queue of pending +operations into the queue of annotations. When END is non-nil, +stop before reaching the queued-op END." + (loop for tail = (pretty-stream-queue-tail stream) then (cdr tail) + while (and tail (not (eql (car tail) end))) + when (annotation-p (car tail)) + do (re-enqueue-annotation stream (car tail)))) + +(defun dequeue-annotation (stream &key end-posn) + "Dequeue the next annotation from the queue of annotations of STREAM +and return it. Return nil if there are no more annotations. When +:END-POSN is given and the next annotation has a posn greater than +this, also return nil." + (let ((next-annotation (car (pretty-stream-annotations-tail stream)))) + (when next-annotation + (when (or (not end-posn) + (<= (annotation-posn next-annotation) end-posn)) + (pop (pretty-stream-annotations-tail stream)) + (unless (pretty-stream-annotations-tail stream) + (setf (pretty-stream-annotations-head stream) nil)) + next-annotation)))) + +(defun invoke-annotation (stream annotation truncatep) + (let ((target (pretty-stream-target stream))) + (funcall (annotation-handler annotation) + (annotation-record annotation) + target + truncatep))) + +(defun output-buffer-with-annotations (stream end) + "Output the buffer of STREAM up to (excluding) the buffer index END. +When annotations are present, invoke them at the right positions." + (let ((target (pretty-stream-target stream)) + (buffer (pretty-stream-buffer stream)) + (end-posn (index-posn end stream)) + (start 0)) + (loop + for annotation = (dequeue-annotation stream :end-posn end-posn) + while annotation + do + (let ((annotation-index (posn-index (annotation-posn annotation) + stream))) + (write-string buffer target :start start + :end annotation-index) + (invoke-annotation stream annotation nil) + (setf start annotation-index))) + (write-string buffer target :start start :end end))) + +(defun flush-annotations (stream end truncatep) + "Invoke all annotations in STREAM up to (including) the buffer index END." + (let ((end-posn (index-posn end stream))) + (loop + for annotation = (dequeue-annotation stream :end-posn end-posn) + while annotation + do (invoke-annotation stream annotation truncatep)))) + + ;;;; Stuff to do the actual outputting. (defun assure-space-in-buffer (stream want) @@ -541,9 +634,10 @@ force-newlines-p) ((t) ;; Just nuke the whole logical block and make it look like one - ;; nice long literal. + ;; nice long literal. (But don't nuke annotations.) (let ((end (block-start-block-end next))) (expand-tabs stream end) + (re-enqueue-annotations stream end) (setf tail (cdr (member end tail))))) ((nil) (really-start-logical-block @@ -556,7 +650,9 @@ (block-end (really-end-logical-block stream)) (tab - (expand-tabs stream next)))) + (expand-tabs stream next)) + (annotation + (re-enqueue-annotation stream next)))) (setf (pretty-stream-queue-tail stream) tail)) output-anything)) @@ -600,12 +696,16 @@ (if last-non-blank (1+ last-non-blank) 0))))) - (write-string buffer target :end amount-to-print) + (output-buffer-with-annotations stream amount-to-print) + (flush-annotations stream amount-to-consume nil) (let ((line-number (pretty-stream-line-number stream))) (incf line-number) (when (and (not *print-readably*) *print-lines* (>= line-number *print-lines*)) (write-string " .." target) + (flush-annotations stream + (pretty-stream-buffer-fill-pointer stream) + t) (let ((suffix-length (logical-block-suffix-length (car (pretty-stream-blocks stream))))) (unless (zerop suffix-length) @@ -657,8 +757,7 @@ (buffer (pretty-stream-buffer stream))) (when (zerop count) (error "Output-partial-line called when nothing can be output.")) - (write-string buffer (pretty-stream-target stream) - :start 0 :end count) + (output-buffer-with-annotations stream count) (incf (pretty-stream-buffer-start-column stream) count) (replace buffer buffer :end1 new-fill-ptr :start2 count :end2 fill-ptr) (setf (pretty-stream-buffer-fill-pointer stream) new-fill-ptr) @@ -667,9 +766,9 @@ (defun force-pretty-output (stream) (maybe-output stream nil) (expand-tabs stream nil) - (write-string (pretty-stream-buffer stream) - (pretty-stream-target stream) - :end (pretty-stream-buffer-fill-pointer stream))) + (re-enqueue-annotations stream nil) + (output-buffer-with-annotations stream + (pretty-stream-buffer-fill-pointer stream))) ;;;; Utilities. From csr21 at cam.ac.uk Fri Jul 29 15:18:39 2005 From: csr21 at cam.ac.uk (Christophe Rhodes) Date: Fri, 29 Jul 2005 16:18:39 +0100 Subject: [mcclim-devel] Postscript partial ellipses Message-ID: Hi, I believe the Postscript backend needs this patch. Is anyone other than us using it at the moment who could check my reasoning? Cheers, Christophe Index: Backends/PostScript/graphics.lisp =================================================================== RCS file: /project/mcclim/cvsroot/mcclim/Backends/PostScript/graphics.lisp,v retrieving revision 1.12 diff -u -r1.12 graphics.lisp --- Backends/PostScript/graphics.lisp 3 Dec 2004 11:42:43 -0000 1.12 +++ Backends/PostScript/graphics.lisp 29 Jul 2005 15:20:39 -0000 @@ -147,8 +147,11 @@ (cy (point-y center)) (tr (make-transformation ndx2 ndx1 ndy2 ndy1 cx cy)) (circle (untransform-region tr ellipse)) - (start-angle (or (ellipse-start-angle circle) 0)) - (end-angle (or (ellipse-end-angle circle) (* 2 pi)))) + ;; we need an extra minus sign because the rotation + ;; convention for Postscript differs in chirality from the + ;; abstract CLIM convention. -- CSR, 2005-07-29 + (start-angle (or (- (ellipse-end-angle circle)) 0)) + (end-angle (or (- (ellipse-start-angle circle)) (* 2 pi)))) (write-string (if filled "true " "false ") stream) (write-angle stream (if (< end-angle start-angle) (+ end-angle (* 2 pi)) From rpgoldman at real-time.com Fri Jul 29 23:44:28 2005 From: rpgoldman at real-time.com (rpgoldman at real-time.com) Date: Fri, 29 Jul 2005 18:44:28 -0500 Subject: [mcclim-devel] question about presentation method for pathnames Message-ID: <17130.48988.90889.757573@gargle.gargle.HOWL> The presentation method for pathnames is defined this way: (define-presentation-method present (object (type pathname) stream (view textual-view) &key acceptably for-context-type) (declare (ignore acceptably for-context-type)) (princ object stream)) At least in one app I've been working on, when I make the default for accepting be a LISP pathname, the system displays the s-expression form, rather than a more intuitive "real" Unix pathname. So I was wondering if it mightn't be better to use namestring than princ. But this won't work in some situations, because the pathname might not be complete (e.g., it's a default, so it has only a directory and a type and no filename). I'm afraid I don't understand pathnames (I only know enough to know that they almost never do what I want!) well enough to understand what would be a good substitute for princ... Thanks, r From mkoeppe at mail.math.uni-magdeburg.de Mon Jul 25 20:57:39 2005 From: mkoeppe at mail.math.uni-magdeburg.de (Matthias Koeppe) Date: Mon, 25 Jul 2005 22:57:39 +0200 Subject: [mcclim-devel] Would McCLIM benefit from this pretty printer patch for CMUCL/SBCL? Message-ID: Dear McCLIM list, I have worked on a patch for the pretty printer of CMUCL and SBCL that helps support the new presentation-like features of SLIME in conjunction with the pretty printer. I'd like to ask if someone of you could comment on whether this patch would be useful for McCLIM as well. If so, this would be another reason for including it at as a core extension into SBCL and CMUCL. The patch implements a feature of the Allegro CL pretty printer, which is called SCHEDULE-ANNOTATION there. This function queues arbitrary functions ("annotations") in the pretty-printing stream in sequence with the characters that are printed to the stream, until the stream has decided on the concrete layout. When the characters are forwarded to the target stream, the annotations are invoked at the right position. In my changes to SLIME, I use the function as follows. I believe (without knowing much about CLIM) that it should be possible to use similar code to send CLIM presentation data (output records?), font changes, etc. cleanly through pretty-printing streams. ;; If we are printing to an XP (pretty printing) stream, printing the ;; escape sequences directly would mess up the layout because column ;; counting is disturbed. Use "annotations" instead. #+allegro (defun write-annotation (stream function arg) (if (typep stream 'excl:xp-simple-stream) (excl::schedule-annotation stream function arg) (funcall function arg stream nil))) #+cmu (defun write-annotation (stream function arg) (if (typep stream 'pp:pretty-stream) (pp::enqueue-annotation stream function arg) (funcall function arg stream nil))) #+sbcl (defun write-annotation (stream function arg) (if (typep stream 'sb-pretty::pretty-stream) (sb-pretty::enqueue-annotation stream function arg) (funcall function arg stream nil))) #-(or allegro cmu sbcl) (defun write-annotation (stream function arg) (funcall function arg stream nil)) (defstruct presentation-record (id) (printed-p)) (defun presentation-start (record stream truncatep) (unless truncatep ;; Don't start new presentations when nothing is going to be ;; printed due to *print-lines*. (let ((pid (presentation-record-id record))) (cond (*use-dedicated-output-stream* (write-string "<" stream) (prin1 pid stream) (write-string "" stream)) (t (force-output stream) (send-to-emacs `(:presentation-start ,pid))))) (setf (presentation-record-printed-p record) t))) (defun presentation-end (record stream truncatep) (declare (ignore truncatep)) ;; Always end old presentations that were started. (when (presentation-record-printed-p record) (let ((pid (presentation-record-id record))) (cond (*use-dedicated-output-stream* (write-string ">" stream) (prin1 pid stream) (write-string "" stream)) (t (force-output stream) (send-to-emacs `(:presentation-end ,pid))))))) (defun presenting-object-1 (object stream continue) "Uses the bridge mechanism with two messages >id and annotation-index start) + (write-string buffer target :start start + :end annotation-index) + (setf start annotation-index)) + (invoke-annotation stream annotation nil))) + (when (> end start) + (write-string buffer target :start start :end end)))) + +(defun flush-annotations (stream end truncatep) + #+sb-doc + "Invoke all annotations in STREAM up to (including) the buffer index END." + (let ((end-posn (index-posn end stream))) + (loop + for annotation = (dequeue-annotation stream :end-posn end-posn) + while annotation + do (invoke-annotation stream annotation truncatep)))) + + ;;;; stuff to do the actual outputting (defun ensure-space-in-buffer (stream want) @@ -520,10 +625,11 @@ (ecase (fits-on-line-p stream (block-start-section-end next) force-newlines-p) ((t) - ;; Just nuke the whole logical block and make it look - ;; like one nice long literal. + ;; Just nuke the whole logical block and make it look like one + ;; nice long literal. (But don't nuke annotations.) (let ((end (block-start-block-end next))) (expand-tabs stream end) + (re-enqueue-annotations stream end) (setf tail (cdr (member end tail))))) ((nil) (really-start-logical-block @@ -536,7 +642,9 @@ (block-end (really-end-logical-block stream)) (tab - (expand-tabs stream next)))) + (expand-tabs stream next)) + (annotation + (re-enqueue-annotation stream next)))) (setf (pretty-stream-queue-tail stream) tail)) output-anything)) @@ -582,13 +690,17 @@ (if last-non-blank (1+ last-non-blank) 0))))) - (write-string buffer target :end amount-to-print) + (output-buffer-with-annotations stream amount-to-print) + (flush-annotations stream amount-to-consume nil) (let ((line-number (pretty-stream-line-number stream))) (incf line-number) (when (and (not *print-readably*) (pretty-stream-print-lines stream) (>= line-number (pretty-stream-print-lines stream))) (write-string " .." target) + (flush-annotations stream + (pretty-stream-buffer-fill-pointer stream) + t) (let ((suffix-length (logical-block-suffix-length (car (pretty-stream-blocks stream))))) (unless (zerop suffix-length) @@ -640,8 +752,7 @@ (buffer (pretty-stream-buffer stream))) (when (zerop count) (error "Output-partial-line called when nothing can be output.")) - (write-string buffer (pretty-stream-target stream) - :start 0 :end count) + (output-buffer-with-annotations stream count) (incf (pretty-stream-buffer-start-column stream) count) (replace buffer buffer :end1 new-fill-ptr :start2 count :end2 fill-ptr) (setf (pretty-stream-buffer-fill-pointer stream) new-fill-ptr) @@ -650,9 +761,10 @@ (defun force-pretty-output (stream) (maybe-output stream nil) (expand-tabs stream nil) - (write-string (pretty-stream-buffer stream) - (pretty-stream-target stream) - :end (pretty-stream-buffer-fill-pointer stream))) + (re-enqueue-annotations stream nil) + (output-buffer-with-annotations stream + (pretty-stream-buffer-fill-pointer stream))) + ;;;; user interface to the pretty printer From jeremy.rayman at gmail.com Tue Jul 26 05:31:41 2005 From: jeremy.rayman at gmail.com (Jeremy Rayman) Date: Mon, 25 Jul 2005 22:31:41 -0700 Subject: [mcclim-devel] drawing a transformed-design using draw-rectangle* says not yet implemented Message-ID: <2fc9a7750507252231708de925@mail.gmail.com> Hi there. I'm trying to resize an image and show it to the screen. The image loads OK, and before the image is transformed I can use (draw-rectangle* pane 0 0 50 50 :ink image) to show it fine (just clips off the rest). After scaling it, when I try to change the :ink to the new transformed-design, it gives an error "Sorry, not yet implemented". Here's what I'm doing: ;; xpm.lisp is loaded by clim-listener.asd (let* ((image (clim-internals::xpm-parse-file #p"image.xpm")) (transformed-bg (transform-region (make-scaling-transformation 5 5) image))) (draw-rectangle* pane 0 0 50 50 :ink transformed-bg)) ;; if it's :ink image, it shows, just not scaled Sorry, not yet implemented. [Condition of type SIMPLE-ERROR] That error is coming from clim-clx::medium-gcontext (backtrace below). >From mcclim/Backends/CLX/medium.lisp, that func is set up for translation-transformations, but not scaling-transformations. If it's anything but a translation-transformation, it gives an error saying not yet implemented. Here's that function, only the first 5 lines matter: (defmethod medium-gcontext ((medium clx-medium) (ink climi::transformed-design)) (let ((transformation (climi::transformed-design-transformation ink)) (design (climi::transformed-design-design ink))) (unless (translation-transformation-p transformation) (error "Sorry, not yet implemented.")) ;; Bah! (typecase design ((or climi::indexed-pattern climi::rectangular-tile) (multiple-value-bind (tx ty) (transform-position transformation 0 0) (let ((gc-x (round-coordinate tx)) (gc-y (round-coordinate ty)) (gc (design-gcontext medium design))) (setf (xlib:gcontext-ts-x gc) gc-x (xlib:gcontext-ts-y gc) gc-y (xlib:gcontext-clip-x gc) gc-x (xlib:gcontext-clip-y gc) gc-y) gc))) (t (error "You lost, we not yet implemented transforming an ~S." (type-of ink)))))) Since I don't know how to make it handle scaling-transformations, I guess this ends up being a feature request. I asked on irc about it and was told to mail here. If there's a better way to draw my scaled image, I'd love to know. Thanks guys! - Jeremy Rayman jeremy.rayman at gmail.com ----------- if anyone needs it: Backtrace: 0: ((SB-PCL::FAST-METHOD CLIM-CLX::MEDIUM-GCONTEXT (CLIM-CLX::CLX-MEDIUM CLIM-INTERNALS::TRANSFORMED-DESIGN)) # # # #) 1: ((LAMBDA (SB-PCL::.PV-CELL. SB-PCL::.NEXT-METHOD-CALL. SB-PCL::.ARG0. SB-PCL::.ARG1.)) # # # #) 2: ((SB-PCL::FAST-METHOD CLIM-CLX::MEDIUM-DRAW-RECTANGLE-USING-INK* (CLIM-CLX::CLX-MEDIUM T T T T T T)) # # # # 0 0 40 40 T) 3: ((SB-PCL::FAST-METHOD CLIM-INTERNALS::DO-GRAPHICS-WITH-OPTIONS-INTERNAL (MEDIUM T T)) # # # # # (:INK #)) 4: ((FLET #:FN21) #) 5: ((LAMBDA (SB-PCL::.PV-CELL. SB-PCL::.NEXT-METHOD-CALL. SB-PCL::.ARG0. SB-PCL::.ARG1. SB-PCL::.ARG2.)) # # # # #:GADGET10920) 6: ((LAMBDA (SB-PCL::.PV-CELL. SB-PCL::.NEXT-METHOD-CALL. SB-PCL::.ARG0. SB-PCL::.ARG1.)) # # # #) ... From adm at cpmg.tv Wed Jul 27 14:36:17 2005 From: adm at cpmg.tv (David Murray) Date: Wed, 27 Jul 2005 15:36:17 +0100 Subject: [mcclim-devel] One char patch to presentation-defs Message-ID: <42E79BE1.6010606@cpmg.tv> [resent to the correct address - I hope...] I was wondering why the :highlight option to :single-box wasn't working as I thought it should. Turns out there was a wee typo. Patch attached. JQS -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: presentation-defs.patch URL: From mkoeppe at mail.math.uni-magdeburg.de Thu Jul 28 22:12:27 2005 From: mkoeppe at mail.math.uni-magdeburg.de (Matthias Koeppe) Date: Fri, 29 Jul 2005 00:12:27 +0200 Subject: [mcclim-devel] Re: [Sbcl-devel] Would McCLIM benefit from this pretty printer patch for CMUCL/SBCL? In-Reply-To: (Raymond Toy's message of "Thu, 28 Jul 2005 12:49:21 -0400") References: Message-ID: Raymond Toy writes: >>>>>> "Matthias" == Matthias Koeppe writes: > Matthias> Dear McCLIM list, > Matthias> I have worked on a patch for the pretty printer of CMUCL and SBCL that > Matthias> helps support the new presentation-like features of SLIME in > Matthias> conjunction with the pretty printer. > > I'm not familiar with slime's presentations, but I was going to look > into incorporating your patch. > > However, in light of Paolo's comments, I'm holding off until this is > resolved. I have now taken a look at Gilbert Baumann's pretty printer patch (http://bauhh.dyndns.org:8000/mcclim/pprint/) that Paolo pointed out. As far as I can see, the changes there could be easily re-implemented in terms of the more general "annotations" feature implemented in my patch. The only thing that is not covered by my patch is the hooks into START-LOGICAL-BLOCK, END-LOGICAL-BLOCK, PPRINT-LOGICAL-BLOCK. These changes are independent of my patch. (I am including below a new version of the patch for CMUCL that includes docstrings.) Cheers, -- Matthias Koeppe -- http://www.math.uni-magdeburg.de/~mkoeppe diff -u /home/mkoeppe/s/slime/cmucl/pprint.lisp.orig /home/mkoeppe/s/slime/cmucl/pprint.lisp --- /home/mkoeppe/s/slime/cmucl/pprint.lisp.orig 2005-07-21 22:30:50.000000000 +0200 +++ /home/mkoeppe/s/slime/cmucl/pprint.lisp 2005-07-29 00:11:19.000000000 +0200 @@ -109,6 +109,10 @@ ;; ;; Block-start queue entries in effect at the queue head. (pending-blocks nil :type list) + ;; + ;; Queue of annotations to the buffer + (annotations-tail nil :type list) + (annotations-head nil :type list) ) (defun %print-pretty-stream (pstream stream depth) @@ -381,6 +385,10 @@ (enqueue stream tab :sectionp sectionp :relativep relativep :colnum colnum :colinc colinc))) +(defstruct (annotation (:include queued-op)) + (handler (constantly nil) :type function) + (record)) + ;;;; Tab support. @@ -472,6 +480,91 @@ (replace new-buffer buffer :end1 end :end2 end)))))) +;;;; Annotations support. + +(defun enqueue-annotation (stream handler record) + "Insert an annotation into the pretty-printing stream STREAM. +HANDLER is a function, and RECORD is an arbitrary datum. The +pretty-printing stream conceptionally queues annotations in sequence +with the characters that are printed to the stream, until the stream +has decided on the concrete layout. When the characters are forwarded +to the target stream, annotations are invoked at the right position. +An annotation is invoked by calling the function HANDLER with the +three arguments RECORD, TARGET-STREAM, and TRUNCATEP. The argument +TRUNCATEP is true if the text surrounding the annotation is suppressed +due to line abbreviation (see *PRINT-LINES*). +If STREAM is not a pretty-printing stream, simply call HANDLER +with the arguments RECORD, STREAM and nil." + (enqueue stream annotation :handler handler + :record record)) + +(defun re-enqueue-annotation (stream annotation) + "Insert ANNOTATION into the queue of annotations in STREAM." + (let* ((annotation-cons (list annotation)) + (head (pretty-stream-annotations-head stream))) + (if head + (setf (cdr head) annotation-cons) + (setf (pretty-stream-annotations-tail stream) annotation-cons)) + (setf (pretty-stream-annotations-head stream) annotation-cons))) + +(defun re-enqueue-annotations (stream end) + "Insert all annotations in STREAM from the queue of pending +operations into the queue of annotations. When END is non-nil, +stop before reaching the queued-op END." + (loop for tail = (pretty-stream-queue-tail stream) then (cdr tail) + while (and tail (not (eql (car tail) end))) + when (annotation-p (car tail)) + do (re-enqueue-annotation stream (car tail)))) + +(defun dequeue-annotation (stream &key end-posn) + "Dequeue the next annotation from the queue of annotations of STREAM +and return it. Return nil if there are no more annotations. When +:END-POSN is given and the next annotation has a posn greater than +this, also return nil." + (let ((next-annotation (car (pretty-stream-annotations-tail stream)))) + (when next-annotation + (when (or (not end-posn) + (<= (annotation-posn next-annotation) end-posn)) + (pop (pretty-stream-annotations-tail stream)) + (unless (pretty-stream-annotations-tail stream) + (setf (pretty-stream-annotations-head stream) nil)) + next-annotation)))) + +(defun invoke-annotation (stream annotation truncatep) + (let ((target (pretty-stream-target stream))) + (funcall (annotation-handler annotation) + (annotation-record annotation) + target + truncatep))) + +(defun output-buffer-with-annotations (stream end) + "Output the buffer of STREAM up to (excluding) the buffer index END. +When annotations are present, invoke them at the right positions." + (let ((target (pretty-stream-target stream)) + (buffer (pretty-stream-buffer stream)) + (end-posn (index-posn end stream)) + (start 0)) + (loop + for annotation = (dequeue-annotation stream :end-posn end-posn) + while annotation + do + (let ((annotation-index (posn-index (annotation-posn annotation) + stream))) + (write-string buffer target :start start + :end annotation-index) + (invoke-annotation stream annotation nil) + (setf start annotation-index))) + (write-string buffer target :start start :end end))) + +(defun flush-annotations (stream end truncatep) + "Invoke all annotations in STREAM up to (including) the buffer index END." + (let ((end-posn (index-posn end stream))) + (loop + for annotation = (dequeue-annotation stream :end-posn end-posn) + while annotation + do (invoke-annotation stream annotation truncatep)))) + + ;;;; Stuff to do the actual outputting. (defun assure-space-in-buffer (stream want) @@ -541,9 +634,10 @@ force-newlines-p) ((t) ;; Just nuke the whole logical block and make it look like one - ;; nice long literal. + ;; nice long literal. (But don't nuke annotations.) (let ((end (block-start-block-end next))) (expand-tabs stream end) + (re-enqueue-annotations stream end) (setf tail (cdr (member end tail))))) ((nil) (really-start-logical-block @@ -556,7 +650,9 @@ (block-end (really-end-logical-block stream)) (tab - (expand-tabs stream next)))) + (expand-tabs stream next)) + (annotation + (re-enqueue-annotation stream next)))) (setf (pretty-stream-queue-tail stream) tail)) output-anything)) @@ -600,12 +696,16 @@ (if last-non-blank (1+ last-non-blank) 0))))) - (write-string buffer target :end amount-to-print) + (output-buffer-with-annotations stream amount-to-print) + (flush-annotations stream amount-to-consume nil) (let ((line-number (pretty-stream-line-number stream))) (incf line-number) (when (and (not *print-readably*) *print-lines* (>= line-number *print-lines*)) (write-string " .." target) + (flush-annotations stream + (pretty-stream-buffer-fill-pointer stream) + t) (let ((suffix-length (logical-block-suffix-length (car (pretty-stream-blocks stream))))) (unless (zerop suffix-length) @@ -657,8 +757,7 @@ (buffer (pretty-stream-buffer stream))) (when (zerop count) (error "Output-partial-line called when nothing can be output.")) - (write-string buffer (pretty-stream-target stream) - :start 0 :end count) + (output-buffer-with-annotations stream count) (incf (pretty-stream-buffer-start-column stream) count) (replace buffer buffer :end1 new-fill-ptr :start2 count :end2 fill-ptr) (setf (pretty-stream-buffer-fill-pointer stream) new-fill-ptr) @@ -667,9 +766,9 @@ (defun force-pretty-output (stream) (maybe-output stream nil) (expand-tabs stream nil) - (write-string (pretty-stream-buffer stream) - (pretty-stream-target stream) - :end (pretty-stream-buffer-fill-pointer stream))) + (re-enqueue-annotations stream nil) + (output-buffer-with-annotations stream + (pretty-stream-buffer-fill-pointer stream))) ;;;; Utilities.