guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: glade + guile?


From: thi
Subject: Re: glade + guile?
Date: Thu, 19 Oct 2000 14:25:18 -0700

   From: Neil Jerram <address@hidden>
   Date: 19 Oct 2000 21:55:49 +0100

   Yes, that's what I would really like too - sorry if that wasn't clear
   from my last post.  But I don't think I understand your "spacewise"
   requirements.  Do you mean that you are happy to involve a shared lib
   (i.e. expat, via mixp) at translation time, but don't want to use
   (another) one (i.e. libglade, via gnome-guile) at run time?

yes exactly.  although libglade is only 80kb, you can write a lot of
(high-level) apps  w/ 80kb.... [insert timex sinclair nostalgia here]

btw, for the curious, below is some mad hacking that actually produces
runnable (but very buggy) guile-gtk code from a .glade file.  i'm
working on bugfixes now and then later, getting it generalized (perhaps
by grokking guile-gtk's gtk-1.2.defs file).  sad to say, still haven't
used grover yet, but that will come into play when generalizing, i
think.  even further below is the input .glade file and even further is
the generated output, prepended w/ some hand-written stubs.

this is very much a work in progress!  some of the (ttn ...)  modules
are not yet released -- mail me privately if you want a snapshot.  also,
i'm no expert on anything gui (having resisted running X until this
year), and so am interested in an educated review of the code, most
specifically the general form produced by the `make-code' proc -- is
that something i can live with forever, or will there be some gotcha
down the line requiring redesign?

thi


__________________________
;;; glade2scm --- translate .glade files to guile-gtk code

;; $State$:$Name$
;;
;; Copyright (C) 2000 Thien-Thi Nguyen
;; This file is part of ttn's personal scheme library, released under GNU
;; GPL with ABSOLUTELY NO WARRANTY.  See the file COPYING for details.

;;; Commentary:

;;; Code:

(let ((slib-parent-dir '/home/ttn/codebits/scheme))
  (or (member slib-parent-dir %load-path)
      (set! %load-path (cons slib-parent-dir %load-path))))

(use-modules (ice-9 format))
(define-module (ice-9 format) :use-module (ice-9 slib))
(define-module (guile) :use-module (ice-9 format))


(use-modules (ttn echo))
(use-modules (ttn mixp))
(use-modules (ice-9 regex))

(define all-whitespace-rx (make-regexp "^[ \t\n]+$"))

(define (aset! a idx val) (array-set! a val idx))
(define (aref  a idx)     (array-ref  a     idx))

(define (make-suda)                     ; standard user-data array
  (vector 0                             ; 0: level
          0                             ; 1: previous level
          '()                           ; 2: tree
          ))

(define (roll-level suda) (aset! suda 1 (aref suda 0)))
(define (level+ suda) (aset! suda 0 (1+ (aref suda 0))))
(define (level- suda) (aset! suda 0 (1- (aref suda 0))))

(if (getenv "DEBUG")
    (begin
      (define decho  echo)
      (define dechow echow))
    (begin
      (define (decho  . args) #t)
      (define (dechow . args) #t)))

(define all-upcase-and-underscore-rx (make-regexp "^[A-Z_]+$"))
(define all-upcase-rx (make-regexp "^[A-Z]+$"))
(define all-numeric-rx (make-regexp "^[0-9.]+$"))
(define all-numeric-and-comma-rx (make-regexp "^[0-9.,]+$"))

(use-modules (ice-9 string-fun))
(define (interpret-atom s)              ; heuristical, bfd
  (cond ((string=? "True"  s) #t)
        ((string=? "False" s) #f)
        ((and (< 4 (string-length s))
              (string=? "GTK_" (substring s 0 4))
              (regexp-exec all-upcase-and-underscore-rx s))
         (string->symbol s))
        ((and (< 4 (string-length s))
              (string=? "Gtk" (substring s 0 3))
              (regexp-exec all-upcase-rx (substring s 3 4)))
         (string->symbol s))
        ((regexp-exec all-numeric-rx s)
         (string->number s))
        ((regexp-exec all-numeric-and-comma-rx s)
         (map interpret-atom (separate-fields-discarding-char #\, s list)))
        (else s)))

(define (make-glade-parser suda)
  (make-fully-specified-expat-parser
   suda
   (vector
    (lambda (suda name attributes)      ; 0: element start
      (level+ suda)
      (let ((all (aref suda 2)))
        (set! all (append
                   (cons (list name)
                         (make-list (- (aref suda 0) (length all))
                                    '()))
                   all))
        (aset! suda 2 all))
      (roll-level suda)
      (decho 'e+ name attributes))
    (lambda (suda name)                 ; 1: element end
      (level- suda)
      (let* ((all (aref suda 2))
             (chunk (let ((rev (reverse (car all))))
                      (cons (string->symbol (car rev))
                            (let ((cr (cdr rev)))
                              (cond ((null? cr) cr)
                                    ((and (= 1 (length cr))
                                          (string? (car cr)))
                                     (interpret-atom (car cr)))
                                    (else cr)))))))
        (set-car! (cdr all) (cons chunk (cadr all)))
        (set! all (cdr all))
        (aset! suda 2 all))
      (roll-level suda)
      (decho 'e- name))
    (lambda (suda value)                ; 2: character data
      (or (regexp-exec all-whitespace-rx value)
          (let ((all (aref suda 2)))
            (set-car! all (cons value (car all)))
            (aset! suda 2 all)
            (decho 'c value))))
    (lambda (suda name pi-data)         ; 3: processing instruction
      (decho 'pi name pi-data))
    (lambda (suda comment-data)         ; 4: comment
      (dechow 'comment comment-data))
    (lambda (suda)                      ; 5: cdata start
      (decho 'cd+))
    (lambda (suda)                      ; 6: cdata end
      (decho 'cd-))
    (lambda (suda string)               ; 7: default
      (dechow 'default string))
    (lambda (suda entity-name           ; 8: unparsed entity decl
                       base system-id
                       public-id
                       notation-name)
      (decho 'unparsed-entity-decl
            entity-name base system-id public-id notation-name))
    (lambda (suda prefix uri)           ; 9: namespace decl start
      (decho 'namespace+ prefix uri))
    (lambda (suda prefix uri)           ; 10: namespace decl end
      (decho 'namespace- prefix uri))
    (lambda (suda)                      ; 11: not-standalone
      (decho 'not-standalone))
    (lambda (suda context base          ; 12: external entity ref
                       system-id public-id)
      (decho 'ext-ent-ref context base system-id public-id)
      (open-file system-id "r"))
    (lambda (encoding-handler-data      ; 13: uknown encoding
             name info)
      (decho 'unknown-encoding encoding-handler-data name info))
    )))

(define (glade->project+widget-trees filename)
  (let ((suda (make-suda)))
    (parse-xml (make-glade-parser suda) (open-file filename "r"))
    (let ((p+w-tree (caar (aref suda 2))))
      (or (false-if-exception (and (eq? 'GTK-Interface (car p+w-tree))
                                   (eq? 'project (caadr p+w-tree))))
          (error "ill formed glade file" filename))
      (values (cadr p+w-tree)
              (cddr p+w-tree)))))

(use-modules (ice-9 common-list))
(define (make-init-code class props)    ; todo: hook in w/ gtk-1.2.defs
  ;;(pk 'props props)
  (case class
    ((GtkWindow)
     `(gtk-window-new ',(case (cdr (assq 'type props))
                          ((GTK_WINDOW_TOPLEVEL) 'toplevel)
                          ((GTK_WINDOW_DIALOG) 'dialog)
                          ((GTK_WINDOW_POPUP) 'popup))))
    ((GtkButton)
     (let ((stock (assq 'stock_button props)))
       `(gtk-button-new-with-label ,(if stock
                                        (cdr stock) ; for now
                                        (cdr (assq 'label props))))))
    ((GtkPixmap)
     `(gtk-pixmap-new ,(cdr (assq 'filename props)) w))
    ((GtkLabel)
     `(gtk-label-new ,(cdr (assq 'label props))))
    ((GtkCTree)
     `(gtk-ctree-new ,(cdr (assq 'columns props)) 0))
    ((GtkScrolledWindow)
     '(gtk-scrolled-window-new))
    ((GtkVBox)
     `(gtk-vbox-new ,(cdr (assq 'homogeneous props))
                    ,(cdr (assq 'spacing props))))
    ((GtkCList)
     `(gtk-clist-new ,(cdr (assq 'columns props))))
    ;; Add new init code mappings here.
    (else (begin
            (echo "WARNING: Class" class "not supported -- faking it!")
            `(,(symbol-append 'gtk- class '-new))))))

(define (make-pack-code name class kids)
  (let ((count -1))
    (map (lambda (kid)
           (set! count (1+ count))
           (let ((kid-name (object-property kid 'name)))
             (case class
               ((GtkWindow GtkScrolledWindow GtkCTree)
                `(gtk-container-add w (pk ',kid-name ,kid-name)))
               ((GtkVBox GtkHBox)
                `(gtk-box-pack-start w (pk ',kid-name ,kid-name) #f #f 0)) ; 
for now
               ((GtkCList)
                `(gtk-clist-set-column-widget w ,count (pk ',kid-name 
,kid-name)))
               ;; Add new parent packing mappings here.
               (else
                (begin (echo "WARNING: Class" class
                             "packing not supported -- faking it!")
                       `(gtk-container-add w (pk ',kid-name ,kid-name)))))))
         kids)))

(define (make-code kids sigs props)
  ;;(pk 'kids kids "\n;;; sigs" sigs "\n;;; props" props)
  (let* ((name (string->symbol (cdr (assq 'name props))))
         (class (cdr (assq 'class props)))
         (init (make-init-code class props)))
    `(define ,name
       (let ((w ,init))
         ,@kids
         ,@(make-pack-code name class kids)
         ,@(map (lambda (ev-handler)
                  `(gtk-signal-connect w
                                       ,(symbol->string (car ev-handler))
                                       ,(cdr ev-handler)))
                sigs)
         w))))

(define (widget-tree->code tree)
  (or (eq? 'widget (car tree))
      (error "ill formed widget tree" tree))
  (let* ((kids '()) (sigs '())          ; fill in
         (props (remove-if
                 (lambda (elem)
                   (and (list? elem)
                        (case (car elem)
                          ((widget)
                           (set! kids (cons (widget-tree->code elem) kids))
                           #t)
                          ((signal)
                           (set! sigs (cons
                                       (cons (string->symbol (cdadr elem))
                                             (string->symbol (cdaddr elem)))
                                       sigs))
                           #t)
                          (else #f))))
                 (cdr tree))))
    (let ((code (make-code kids sigs props)))
      (set-object-property! code 'name (string->symbol
                                        (cdr (assq 'name (cdr tree)))))
      (set-object-property! code 'props props)
      code)))

(define (glade2scm filename)
  (and filename
       (file-exists? filename)
       (call-with-values (lambda () (glade->project+widget-trees filename))
         (lambda (project top-level-widget-trees)
           (let* ((w-code (map widget-tree->code top-level-widget-trees))
                  (w-ret (map (lambda (w) (object-property w 'name)) w-code)))
             (for-each (lambda (form)
                         (format #t "~Y~%" form))
                       `((use-modules (gtk gtk) (gtk gdk))
                         (define (make-gtk-gui)
                           ,@w-code
                           (list ,@w-ret))
                         ;(export make-gtk-gui)
                         (map gtk-widget-show-all (make-gtk-gui))
                         (gtk-main)
                         )))))))

;; do it!

(glade2scm (cadr (command-line)))

;;; glade2scm ends here
____________________________________
<?xml version="1.0"?>
<GTK-Interface>

<project>
  <name>Project1</name>
  <program_name>project1</program_name>
  <directory></directory>
  <source_directory>src</source_directory>
  <pixmaps_directory>pixmaps</pixmaps_directory>
  <language>C</language>
  <gnome_support>False</gnome_support>
  <gettext_support>True</gettext_support>
  <use_widget_names>False</use_widget_names>
  <output_main_file>True</output_main_file>
  <output_support_files>True</output_support_files>
  <output_build_files>True</output_build_files>
  <backup_source_files>True</backup_source_files>
  <main_source_file>interface.c</main_source_file>
  <main_header_file>interface.h</main_header_file>
  <handler_source_file>callbacks.c</handler_source_file>
  <handler_header_file>callbacks.h</handler_header_file>
  <support_source_file>support.c</support_source_file>
  <support_header_file>support.h</support_header_file>
  <translatable_strings_file></translatable_strings_file>
</project>

<widget>
  <class>GtkWindow</class>
  <name>window2</name>
  <title>window2</title>
  <type>GTK_WINDOW_TOPLEVEL</type>
  <position>GTK_WIN_POS_NONE</position>
  <modal>False</modal>
  <allow_shrink>False</allow_shrink>
  <allow_grow>True</allow_grow>
  <auto_shrink>False</auto_shrink>

  <widget>
    <class>GtkPixmap</class>
    <name>pixmap1</name>
    <filename>new.xpm</filename>
    <xalign>0.5</xalign>
    <yalign>0.5</yalign>
    <xpad>0</xpad>
    <ypad>0</ypad>
    <build_insensitive>True</build_insensitive>
  </widget>
</widget>

<widget>
  <class>GtkWindow</class>
  <name>window3</name>
  <title>window3</title>
  <type>GTK_WINDOW_TOPLEVEL</type>
  <position>GTK_WIN_POS_NONE</position>
  <modal>False</modal>
  <allow_shrink>False</allow_shrink>
  <allow_grow>True</allow_grow>
  <auto_shrink>False</auto_shrink>

  <widget>
    <class>GtkVBox</class>
    <name>vbox1</name>
    <homogeneous>False</homogeneous>
    <spacing>0</spacing>

    <widget>
      <class>GtkButton</class>
      <name>button4</name>
      <can_focus>True</can_focus>
      <signal>
        <name>clicked</name>
        <handler>refresh-clist</handler>
        <last_modification_time>Thu, 19 Oct 2000 19:11:25 
GMT</last_modification_time>
      </signal>
      <label>button4</label>
      <child>
        <padding>0</padding>
        <expand>False</expand>
        <fill>False</fill>
      </child>
    </widget>

    <widget>
      <class>GtkScrolledWindow</class>
      <name>scrolledwindow1</name>
      <hscrollbar_policy>GTK_POLICY_ALWAYS</hscrollbar_policy>
      <vscrollbar_policy>GTK_POLICY_ALWAYS</vscrollbar_policy>
      <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
      <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
      <child>
        <padding>0</padding>
        <expand>True</expand>
        <fill>True</fill>
      </child>

      <widget>
        <class>GtkCList</class>
        <name>clist1</name>
        <can_focus>True</can_focus>
        <columns>3</columns>
        <column_widths>80,80,80</column_widths>
        <selection_mode>GTK_SELECTION_SINGLE</selection_mode>
        <show_titles>True</show_titles>
        <shadow_type>GTK_SHADOW_IN</shadow_type>

        <widget>
          <class>GtkLabel</class>
          <child_name>CList:title</child_name>
          <name>label9</name>
          <signal>
            <name>button_press_event</name>
            <handler>sort-by-filename</handler>
            <last_modification_time>Thu, 19 Oct 2000 19:12:39 
GMT</last_modification_time>
          </signal>
          <label>filename</label>
          <justify>GTK_JUSTIFY_CENTER</justify>
          <wrap>False</wrap>
          <xalign>0.5</xalign>
          <yalign>0.5</yalign>
          <xpad>0</xpad>
          <ypad>0</ypad>
        </widget>

        <widget>
          <class>GtkLabel</class>
          <child_name>CList:title</child_name>
          <name>label10</name>
          <signal>
            <name>button_press_event</name>
            <handler>sort-by-size</handler>
            <last_modification_time>Thu, 19 Oct 2000 19:13:02 
GMT</last_modification_time>
          </signal>
          <label>size</label>
          <justify>GTK_JUSTIFY_CENTER</justify>
          <wrap>False</wrap>
          <xalign>0.5</xalign>
          <yalign>0.5</yalign>
          <xpad>0</xpad>
          <ypad>0</ypad>
        </widget>

        <widget>
          <class>GtkLabel</class>
          <child_name>CList:title</child_name>
          <name>label11</name>
          <signal>
            <name>button_press_event</name>
            <handler>sort-by-i/d/t-size</handler>
            <last_modification_time>Thu, 19 Oct 2000 19:13:42 
GMT</last_modification_time>
          </signal>
          <label>i/d/t-size</label>
          <justify>GTK_JUSTIFY_CENTER</justify>
          <wrap>False</wrap>
          <xalign>0.5</xalign>
          <yalign>0.5</yalign>
          <xpad>0</xpad>
          <ypad>0</ypad>
        </widget>
      </widget>
    </widget>

    <widget>
      <class>GtkButton</class>
      <name>button5</name>
      <can_focus>True</can_focus>
      <stock_button>GNOME_STOCK_BUTTON_CLOSE</stock_button>
      <child>
        <padding>0</padding>
        <expand>False</expand>
        <fill>False</fill>
      </child>
    </widget>
  </widget>
</widget>

</GTK-Interface>
___________________________________
(define (sort-by-i/d/t-size)
  (write-line "sort-by-i/d/t-size"))

(define (sort-by-size)
  (write-line "sort-by-size"))

(define (sort-by-filename)
  (write-line "sort-by-filename"))

(define (refresh-clist)
  (write-line "refresh-clist"))
(use-modules (gtk gtk) (gtk gdk))

(define (make-gtk-gui)
  (define window2
    (let ((w (gtk-window-new 'toplevel)))
      (define pixmap1
        (let ((w (gtk-pixmap-new "new.xpm" w))) w))
      (gtk-container-add w (pk 'pixmap1 pixmap1))
      w))
  (define window3
    (let ((w (gtk-window-new 'toplevel)))
      (define vbox1
        (let ((w (gtk-vbox-new #f 0)))
          (define button5
            (let ((w (gtk-button-new-with-label
                       "GNOME_STOCK_BUTTON_CLOSE")))
              w))
          (define scrolledwindow1
            (let ((w (gtk-scrolled-window-new)))
              (define clist1
                (let ((w (gtk-clist-new 3)))
                  (define label11
                    (let ((w (gtk-label-new "i/d/t-size")))
                      (gtk-signal-connect
                        w
                        "button_press_event"
                        sort-by-i/d/t-size)
                      w))
                  (define label10
                    (let ((w (gtk-label-new "size")))
                      (gtk-signal-connect
                        w
                        "button_press_event"
                        sort-by-size)
                      w))
                  (define label9
                    (let ((w (gtk-label-new "filename")))
                      (gtk-signal-connect
                        w
                        "button_press_event"
                        sort-by-filename)
                      w))
                  (gtk-clist-set-column-widget
                    w
                    0
                    (pk 'label11 label11))
                  (gtk-clist-set-column-widget
                    w
                    1
                    (pk 'label10 label10))
                  (gtk-clist-set-column-widget
                    w
                    2
                    (pk 'label9 label9))
                  w))
              (gtk-container-add w (pk 'clist1 clist1))
              w))
          (define button4
            (let ((w (gtk-button-new-with-label "button4")))
              (gtk-signal-connect w "clicked" refresh-clist)
              w))
          (gtk-box-pack-start
            w
            (pk 'button5 button5)
            #f
            #f
            0)
          (gtk-box-pack-start
            w
            (pk 'scrolledwindow1 scrolledwindow1)
            #f
            #f
            0)
          (gtk-box-pack-start
            w
            (pk 'button4 button4)
            #f
            #f
            0)
          w))
      (gtk-container-add w (pk 'vbox1 vbox1))
      w))
  (list window2 window3))

(map gtk-widget-show-all (make-gtk-gui))

(gtk-main)

_______________________________
[that's all!]



reply via email to

[Prev in Thread] Current Thread [Next in Thread]