git.fiddlerwoaroof.com
main.lisp
0378dd50
 ;  Copyright (c) 2014 Edward Langley
 ;  All rights reserved.
 
 ;  Redistribution and use in source and binary forms, with or without
 ;  modification, are permitted provided that the following conditions
 ;  are met:
 
 ;  Redistributions of source code must retain the above copyright notice,
 ;  this list of conditions and the following disclaimer.
 
 ;  Redistributions in binary form must reproduce the above copyright
 ;  notice, this list of conditions and the following disclaimer in the
 ;  documentation and/or other materials provided with the distribution.
 
 ;  Neither the name of the project's author nor the names of its
 ;  contributors may be used to endorse or promote products derived from
 ;  this software without specific prior written permission.
 
 ;  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 ;  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 ;  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 ;  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 ;  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ;  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 ;  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 ;  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 ;  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 ;  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 ;  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cf79e684
 (require 'asdf)
 (asdf:load-system 'quicklisp)
0378dd50
 
 (asdf:load-system :cl-cffi-gtk)
cf79e684
 (ql:quickload :cl-fad)
 (ql:quickload :x.let-star)
0378dd50
 
278be5ac
 (defpackage :notes-app
0378dd50
   (:use :gtk :gdk :gdk-pixbuf :gobject
cf79e684
         :glib :gio :pango :cairo :common-lisp
         :cl-fad :x.let-star)
   (:shadowing-import-from x.let-star let*)
   (:export main))
0378dd50
 
278be5ac
 (in-package :notes-app)
0378dd50
 
 ;(macroexpand-1 '(on (clicked button) (__)
 ;                    (declare (ignore __))
 ;                    (format t "Button 1 was pressed")))
 
 (defmacro on ((sig target) bind &body body)
278be5ac
   "Bind a signal handler: note that this intentionally shadows whatever
    the 'bind' argument is"
0378dd50
   `(g-signal-connect ,target ,(string-downcase (symbol-name sig))
                      (lambda ,bind
                        ,@body)))
 
278be5ac
 
 
 (defmacro thread-signal ((sig target) bind callback)
   "Bind a signal handler: note that this intentionally shadows whatever
    the 'bind' argument is"
   `(g-signal-connect ,target ,(string-downcase (symbol-name sig)) ,callback))
 
cf79e684
 (defmacro mainloop (fname winvar title width height &body body)
   `(defun ,fname ()
      (within-main-loop
        (let ((,winvar (make-instance 'gtk-window
                                      :title ,title
                                      :type :toplevel
                                      :default-width ,width
                                      :default-height ,height)))
 
          (on (destroy ,winvar) (widget)
            (declare (ignore widget))
            (leave-gtk-main))
 
          ,@body
 
278be5ac
          (gtk-widget-show-all ,winvar)))
      (join-gtk-main)))
cf79e684
 
 ;(macroexpand-1
 ;  (quote
 ;    (declare-paned-widget (:orientation :vertical)
 ;      (declare-scroll-window () list-view)
 ;      (declare-scroll-window (:wrap-mode :word) text-view))))
 
 ;(let ((paned (make-instance 'gtk :orientation :vertical)))
 ;  (let ((scroller (make-instance 'gtk-scroll-window)))
 ;    (gtk-container-add list-view))
 ;  (let ((scroller (make-instance 'gtk-scroll-window :wrap-mode :word)))
 ;    (gtk-container-add text-view)))
 
 (defmacro declare-scroll-window (initializers child)
   (let ((sw-sym (gensym)))
     `(let ((,sw-sym (make-instance 'gtk-scrolled-window ,@initializers)))
        (gtk-container-add ,sw-sym ,child)
        ,sw-sym)))
 
 (defmacro declare-paned-widget (initializers child1 child2)
   (let ((paned-sym (gensym)))
     `(let ((,paned-sym (make-instance 'gtk-paned ,@initializers)))
        (gtk-paned-add1 ,paned-sym ,child1)
        (gtk-paned-add2 ,paned-sym ,child2)
        ,paned-sym)))
278be5ac
 (labels ((process-child (child box-sym)
            (if (listp child)
              (let* ((_initializers
                       (if (< 2 (length child)) (cddr child) ()))
                     (initializers (apply #'append _initializers))
                     (widget (second child)))
                (ecase  (first child)
                  (:start `(gtk-box-pack-start ,box-sym
                                               ,widget
                                               ,@initializers))
 
                  (:end   `(gtk-box-pack-end   ,box-sym
                                               ,widget
                                               ,@initializers)))))))
 
   (defmacro declare-box (initializers &body children)
     (let ((box-sym (gensym)))
       `(let ((,box-sym (make-instance 'gtk-box ,@initializers)))
          ,@(loop for child in children collect (process-child child box-sym))
          ,box-sym))))
 
 (defmacro declare-toolbar (initializers &body children)
   (let ((toolbar-sym (gensym)))
     `(let ((,toolbar-sym (gtk-toolbar-new)))
        ,@(loop for button in children collect
                `(gtk-toolbar-insert ,toolbar-sym
                                     (make-instance 'gtk-tool-button ,@button)
                                     -1))
        ,toolbar-sym)))
cf79e684
 
 (defun make-list-view (display-list)
   (let ((store (make-instance 'gtk-list-store :column-types '("gchararray")))
         (list-view (make-instance 'gtk-tree-view :headers-visible nil)))
 
     (gtk-tree-view-set-model list-view store)
 
     (loop for fn in display-list
           do (gtk-list-store-set store
                                  (gtk-list-store-append store)
                                  (file-namestring fn)))
 
     (let* ((renderer (make-instance 'gtk-cell-renderer-text))
            (column
              (gtk-tree-view-column-new-with-attributes "Filename" renderer "text" 0)))
       (gtk-tree-view-append-column list-view column))
     (values store list-view)))
 
 (defun make-text-view (text)
   (let* ((text-view (make-instance 'gtk-text-view :wrap-mode :word))
          (buffer (gtk-text-view-get-buffer text-view)))
     (gtk-text-buffer-set-text buffer text)
     (values buffer text-view)))
 
278be5ac
 (defun get-list-at-path (list-model path)
   (let* ((iter (gtk-tree-model-get-iter list-model path)))
     (selected (gtk-tree-model-get-value list-model iter 0))) )
 
 (defvar *note-dir* "/home/edwlan/Dropbox/Notes/")
cf79e684
 (mainloop main window "test" 200 200
278be5ac
   (let* (((:mval list-model list-view) (make-list-view
                                          (cl-fad:list-directory *note-dir*)))
cf79e684
          ((:mval text-buffer text-view) (make-text-view "")))
 
278be5ac
 
cf79e684
     (on (row-activated list-view) (tree-view path column)
278be5ac
       (let* ((selected (get-list-at-path list-model path))
cf79e684
              (file-contents
278be5ac
                (with-open-file (fd (merge-pathnames-as-file *note-dir* selected))
cf79e684
                  (let ((seq (make-string (file-length fd))))
                    (read-sequence seq fd)
                    seq))))
         (gtk-text-buffer-set-text text-buffer file-contents)))
 
     (gtk-container-add window
                        (declare-paned-widget (:orientation :vertical)
278be5ac
                                              (declare-box
                                                (:orientation :vertical)
                                                (:end
                                                  (declare-box
                                                    (:orientation :horizontal)
                                                    (:start (gtk-entry-new) (:fill t))
                                                    (:end
                                                      (make-instance 'gtk-button 
                                                                     :use-stock t
                                                                     :label "gtk-add")
                                                      (:expand nil)))
                                                  (:expand nil))
                                                (:start
                                                  (declare-scroll-window
                                                        (:height-request 400)
                                                        list-view)
                                                  (:fill t)))
                                              (declare-scroll-window
                                                (:wrap-mode :word)
                                                text-view)))))
cf79e684
 
 ;(main)
 
278be5ac
 (labels
cf79e684
   ((slot-function (descriptor)
      (ecase descriptor
        (:start 'gtk-box-pack-start)
        (:end   'gtk-box-pack-end)))
    (proc-slot (box from-object slot)
      (if (equal (first slot) 'QUOTE)
278be5ac
        `(gtk-box-pack-start ,box (slot-value ,from-object ,slot))
cf79e684
        (ecase (length slot)
          (1 `(gtk-box-pack-start ,box (slot-value ,from-object ,(first slot))))
          (2 `(,(slot-function (second slot)) ,box (slot-value ,from-object ,(first slot))))))))
 
   (defmacro renderwin (window box from-object &body slots)
     (let ((window-sym (gensym))
           (box-sym (gensym))
           (from-obj-sym (gensym)))
       `(let ((,window-sym ,window)
              (,box-sym ,box)
              (,from-obj-sym ,from-object))
          ,@(loop for slot in slots collect (proc-slot box-sym from-obj-sym slot))
          (gtk-container-add ,window-sym ,box-sym)))))
 
 (defclass mainwin nil
   ((file-view :initarg :file-view)
    (file-list :initarg :file-list)))
 
 
 (defun wrap-with-scrollwindow (widg &key (hscrpolicy :automatic) (vscrpolicy :automatic))
   (let ((scroller (make-instance 'gtk-scrolled-window
                                  :hscrollbar-policy hscrpolicy
                                  :vscrollbar-policy vscrpolicy)))
     (gtk-container-add scroller widg)
     scroller))
0378dd50