I'm trying to use GtkTextIter and GtkTargetEntry structs with
lambda-GTK. The problem is that GTK doesn't provide any means of
creating them (AFAIK), so I need to allocate them.
I think I could be able to do that using something like alien:make-alien
but I can't even find the name of the structs in lambda-GTK. This must
be pretty obvious, but I've looked everywhere and found nothing. When I
list symbols using do-external-symbols, the structs aren't there (no
"rectangle" for instance).
Has anybody done something similar, or has any clue about how to find
the desired symbols?
On Sat, 2005-07-02 at 11:29 +0200, Philippe Lorin wrote:
> I'm trying to use GtkTextIter and GtkTargetEntry structs with
> lambda-GTK. The problem is that GTK doesn't provide any means of
> creating them (AFAIK), so I need to allocate them.
Here's a bit of code I struggled with a year ago or so trying to get the
actual text from a text buffer:
(defun get-text (buf)
(let ((end (gtk:gtk-alloc GtkTextIter))
(start (gtk:gtk-alloc GtkTextIter)))
(gtk:gtk-text-buffer-get-start-iter buf start)
(gtk:gtk-text-buffer-get-end-iter buf end)
(let ((text (gtk:gtk-text-buffer-get-text buf start end nil)))
(let ((text-str (cvt-string text)))
(gtk:gtk-free start)
(gtk:gtk-free end)
text-str))))
Alas, no proper exception handling, but then I abandoned the project.
I'm guessing gtk-alloc is what you want. (Hoping.)
The c-to-list-string function was on the to do list for lambda-gtk when
I looked at it, so I had to write my own. The following at least
worked.
(defun cvt-string (ptr)
(let ((ptr* (sb-sys:int-sap ptr))
(index 0)
(result ()))
(loop
(let ((val (sb-sys:sap-ref-8 ptr* index)))
(if (= val 0)
(return)
(progn
(push (code-char val) result)
(incf index)))))
(coerce (nreverse result) 'string)))
Finally, I was interested in doing something with the text every time
the user hit a key, so:
(define-alien-function cb-keypress ((* t) (widget (* t)) (ev (* t)))
(let ((buf (gtk:gtk-text-view-get-buffer widget)))
(let ((text (get-text buf)))
(format t "text=[~a] (len=~a)~%" text (length text)))))
This was using a patch to SBCL enabling callbacks. I believe the patch
no longer works with recent versions of SBCL, but that callbacks will be
in the next release.
> I think I could be able to do that using something like alien:make-alien
> but I can't even find the name of the structs in lambda-GTK. This must
> be pretty obvious, but I've looked everywhere and found nothing. When I
> list symbols using do-external-symbols, the structs aren't there (no
> "rectangle" for instance).
Is the above: (let ((end (gtk:gtk-alloc GtkTextIter)))) what you're
talking about?
> Has anybody done something similar, or has any clue about how to find
> the desired symbols?
I've had luck finding things by going through the C documentation (and
I've never written anything significant in C.) Ubuntu (debian?) has a
Devhelp package which provides a nice viewer. I'd look stuff up there,
then grep through the lambda-gtk source.
Hope this helps!
Keith
Philippe Lorin wrote:
> I'm trying to use GtkTextIter and GtkTargetEntry structs with
> lambda-GTK. The problem is that GTK doesn't provide any means of
> creating them (AFAIK), so I need to allocate them.
>
> I think I could be able to do that using something like alien:make-alien
> but I can't even find the name of the structs in lambda-GTK. This must
> be pretty obvious, but I've looked everywhere and found nothing. When I
> list symbols using do-external-symbols, the structs aren't there (no
> "rectangle" for instance).
>
> Has anybody done something similar, or has any clue about how to find
> the desired symbols?
The relevant form here is: (gtk::struct-alloc gtktextiter)
(gtk:define-signal-handler delete-event :int (widget event data)
widget event data
(gtk:main-quit)
gtk:+false+)
(gtk:define-signal-handler clicked-signal :void (view)
(let ((iter (gtk::struct-alloc gtktextiter)) ; ***
(buffer (gtk::text-view-get-buffer view)))
(gtk::text-buffer-get-iter-at-line-index buffer iter 5 0)
(gtk::text-view-scroll-to-iter view iter 0.0 gtk:+false+ 0.0 0.0)))
(defun doublethink ()
(gtk:init-ensure)
(let* ((window (gtk:window-new gtk::window-toplevel))
(view (gtk:text-view-new))
(buffer (gtk:text-view-get-buffer view))
(text "War is Peace
Freedom is Slavery
Ignorance is Strength
2 + 2 = 5")
(vbox (gtk:vbox-new gtk:+true+ 10))
(button (gtk:button-new-with-label "Scroll")))
(g:signal-connect window "delete_event" (g:callback delete-event)
(g:nullptr))
(g:signal-connect-swapped button "clicked"
(g:callback clicked-signal) view)
(gtk::window-set-title window "DoubleThink")
(gtk::container-set-border-width window 15)
(gtk:container-add window vbox)
(gtk:widget-show vbox)
(gtk::text-buffer-set-text buffer text (length text))
(gtk::widget-set-size-request view 150 60)
(gtk:widget-show view)
(gtk:box-pack-start vbox view gtk:+false+ gtk:+false+ 0)
(gtk:box-pack-start vbox button gtk:+false+ gtk:+false+ 0)
(gtk:widget-show button)
(gtk:widget-show window)
(gtk:main)))
(doublethink)
Regards,
Nineteen84
(Caveat Emptor: this message has almost certainly been reviewed
and possibly modified by the Thought Police and related agencies.
Use at your own discretion.)
Thanks to Keith Irwin and Nineteen84, I was able to use GtkTextIter and
GtkTargetEntry. While the former was just a matter of knowing about
gtk:struct-alloc and using Keith's cstring->string, GtkTargetEntry was
not as easy. I could not find its definition in lambda-GTK. I tried
adding the following line to gtk.api, and processing this file as
indicated on the lambda-GTK homepage, but while gtktargetentry got
defined, I couldn't access its fields.
(:struct "GtkTargetEntry" (:accessors "target" "flags" "info"))
So I explored the other way, less satisfying but which worked: using the
alien package. In case somebody is interested, I paste below a simple
example of drag'n'drop (written for CMUCL). It just creates two windows
and one button. You can drag the button to any droppable place that
accepts text/plain data, as well as drag text/plain data from anywhere
into the empty window. These actions will print stuff to the console,
not modify any widget.
Any comments welcome! Note that I'm a Lisp newbie, so the style is
probably not very good.
(load "../lambda-gtk-0.1/gtkffi-cmusbcl") ; adjust to your system
(gtk:init-ensure)
;;; Function by Keith Irwin on comp.lang.lisp; I changed "sb-sys" into "system" for CMUCL.
(defun cstring->string (ptr)
(let ((ptr* (system:int-sap ptr))
(index 0)
(result ()))
(loop
(let ((val (system:sap-ref-8 ptr* index)))
(if (= val 0)
(return)
(progn
(push (code-char val) result)
(incf index)))))
(coerce (nreverse result) 'string)))
(defmacro dynamic-callback (&rest body)
(let ((callback-name (gensym)))
`(progn
(gtk:define-signal-handler ,callback-name ,@body)
(g:callback ,callback-name))))
;; Connects signal to widget with body as the callback.
(defmacro dynamic-signal-connect (widget signal &rest body)
`(g:signal-connect ,widget ,signal
(dynamic-callback ,@body)
(g:nullptr)))
(defconstant GDK-BUTTON1-MASK 256)
(defconstant GDK-ACTION-COPY 2)
(defconstant GTK-DEST-DEFAULT-ALL 7)
(defconstant GDK-SELECTION-TYPE-STRING (gdk:atom-intern "GDK_SELECTION_TYPE_STRING" 0))
(defconstant text-to-send "This is the text sent!")
(alien:def-alien-type nil
(alien:struct GtkTargetEntry
(target c-call:c-string)
(flags alien:signed 32)
(info alien:signed 32)))
(alien:with-alien ((target-entry (alien:struct GtkTargetEntry)))
(setf (alien:slot target-entry 'target) "text/plain")
(setf (alien:slot target-entry 'flags) 0)
(setf (alien:slot target-entry 'info) 0)
;; Build source window
(let ((win-source (gtk:window-new 0))
(box (gtk:vbox-new 0 0))
(dragable (gtk:button-new-with-label "Drag this Button")))
(gtk:container-add win-source box)
(gtk:container-add box dragable)
(gtk:window-set-title win-source "Source")
(dynamic-signal-connect win-source "delete-event" :void (widget event data)
(gtk:main-quit)
(quit))
(gtk:drag-source-set dragable GDK-BUTTON1-MASK (alien:addr target-entry) 1 GDK-ACTION-COPY)
(dynamic-signal-connect dragable "drag-data-get" :void (widget context data info time user-data)
;; I don't know why the line below doesn't work
;; (gtk:selection-data-set data GDK-SELECTION-TYPE-STRING 8 text-to-send (length text-to-send))
(gtk:selection-data-set-text data text-to-send (length text-to-send))
(format t "Text sent!~%"))
(gtk:widget-show-all win-source))
;; Build destination window
(let ((win-dest (gtk:window-new 0)))
(gtk:window-set-title win-dest "Dest")
(dynamic-signal-connect win-dest "delete-event" :void (widget event data)
(gtk:main-quit)
(quit))
(gtk:drag-dest-set win-dest GTK-DEST-DEFAULT-ALL (alien:addr target-entry) 1 GDK-ACTION-COPY)
(dynamic-signal-connect win-dest "drag-data-received" :void (widget context x y data info time user-data)
(format t "Text received: \"~A\"~%" (cstring->string (gtk:selection-data-get-text data))))
(gtk:widget-show-all win-dest))
(gtk:main))
Philippe Lorin wrote:
> Thanks to Keith Irwin and Nineteen84, I was able to use GtkTextIter and
> GtkTargetEntry. While the former was just a matter of knowing about
> gtk:struct-alloc and using Keith's cstring->string, GtkTargetEntry was
> not as easy. I could not find its definition in lambda-GTK. I tried
> adding the following line to gtk.api, and processing this file as
> indicated on the lambda-GTK homepage, but while gtktargetentry got
> defined, I couldn't access its fields.
> (:struct "GtkTargetEntry" (:accessors "target" "flags" "info"))
Philippe,
Thanks for posting the drag-and-drop example. That will come in handy.
I just tried adding (:struct "GtkTargetEntry" (:accessors "target"
"flags" "info")) to gtk.api and rebuilding the system; it does work
as advertised.
Did you then use: (gtk::TargetEntry.target entry)?
On several occassions I have supplemented gtk.api and then assumed
that the accessor would be gtk::GtkTargetEntry.target instead of
gtk::TargetEntry.target. That's a bit confusing, but I suppose
Rick is trying to conserve some keystrokes.
In any event, you might give it another try.
Regards,
Nineteen84
··········@bellsouth.net wrote:
> I just tried adding (:struct "GtkTargetEntry" (:accessors "target"
> "flags" "info")) to gtk.api and rebuilding the system; it does work
> as advertised.
You're right, it does work. I was mislead by two things.
The first is that when using gtk:struct-alloc, the struct seems to be
called GtkTargetEntry, while when accessing fields, it is called
gtk:TargetEntry. I find this confusing; it took a while for me to figure
out. Is there a rationale behind this?
The second is the difference between "::" and ":". I assumed there was
no difference between the two, and I was wrongly using ":" exclusively
-- now I think I understand that "::" is needed to access internal
symbols. Sorry for this newbish mistake.
> On several occassions I have supplemented gtk.api and then assumed
> that the accessor would be gtk::GtkTargetEntry.target instead of
> gtk::TargetEntry.target. That's a bit confusing, but I suppose
> Rick is trying to conserve some keystrokes.
I fall in the same trap about every time I look up some function name in
the GTK docs and copy it into my code; I just add "gtk:" at the
beginning and shortly after wish for a pair programmer to watch me
typing ;) .
Now I am able to read a GtkTargetEntry with something like:
(gtk:TargetEntry.info entry)
But this does not seem to be setfable; if I do:
(setf (gtk:TargetEntry.info target-entry) 0)
I get:
the function (SETF GTK:TARGETENTRY.INFO) is undefined.
still I can use:
(setf (alien:slot target-entry 'gtk::info) 0)
but it's less satisfying. Did I miss something?
Finally, I'm struggling with yet another probably obvious thing: I can't
find out how to setf a cstring field; I would write for instance:
(setf (alien:slot target-entry 'gtk::target)
(gtk:string->cstring "text/plain"))
But then when I try to read the field, with Keith's cstring->string, I
get an error:
#<Alien (* T) at #x080AAF40> is not of type (UNSIGNED-BYTE 32)
and without it, I get an alien:
#<Alien (* T) at #x080AAF40>
For the record, I also tried not using gtk:string->cstring in the first
place:
(setf (alien:slot target-entry 'gtk::target) "text/plain")
but I get:
"text/plain" fell through ETYPECASE expression.
Wanted one of ((ALIEN:ALIEN #<ALIEN::ALIEN-POINTER-TYPE (* T)>)
SYSTEM:SYSTEM-AREA-POINTER NULL).
What should I do to set the target field properly?
Philippe Lorin wrote:
> Now I am able to read a GtkTargetEntry with something like:
> (gtk:TargetEntry.info entry)
> But this does not seem to be setfable; if I do:
> (setf (gtk:TargetEntry.info target-entry) 0)
> I get:
> the function (SETF GTK:TARGETENTRY.INFO) is undefined.
> still I can use:
> (setf (alien:slot target-entry 'gtk::info) 0)
> but it's less satisfying. Did I miss something?
I would prefer the (setf ) syntax myself, but currently you
set a slot like this:
(gtk:TargetEntry.info target-entry 0)
> Finally, I'm struggling with yet another probably obvious thing: I can't
> find out how to setf a cstring field; I would write for instance:
> (setf (alien:slot target-entry 'gtk::target)
> (gtk:string->cstring "text/plain"))
> But then when I try to read the field, with Keith's cstring->string, I
> get an error:
> #<Alien (* T) at #x080AAF40> is not of type (UNSIGNED-BYTE 32)
> and without it, I get an alien:
> #<Alien (* T) at #x080AAF40>
;; this is slightly different from Kevin's function
;; it expects a (* (unsigned 8))
(defun cstring->string (ptr)
(do* (result
(idx 0 (1+ idx))
(char (deref ptr idx) (deref ptr idx)))
((= char 0) (coerce (nreverse result) 'string))
(push (code-char char) result)))
(defun test ()
(let ((entry (gtk::struct-alloc gtktargetentry))
(str "Cabbages and Kings"))
;; set the value:
(gtk::TargetEntry.target entry (gtk::string->cstring str))
;; now, fetch the value and display:
(format t "~%recycled string: ~a~%"
(cstring->string
(cast (gtk::TargetEntry.target entry) (* (unsigned
8)))))))
(test)
Hope this helps,
Nineteen84