(module log4scm-mzgtk2 mzscheme
(require (lib "mzgtk2.scm" "mzgtk2"))
(require (planet "roos.scm" ("oesterholt" "roos.plt" 1 0)))
(require (planet "sprefs.scm" ("oesterholt" "ho-utils.plt" 1 0)))
(require (planet "internat.scm" ("oesterholt" "internat.plt" 1 0)))
(require (planet "sutil.scm" ("oesterholt" "ho-utils.plt" 1 0)))
(require (planet "units.scm" ("oesterholt" "ho-utils.plt" 1 0)))
(require (planet "array.scm" ("oesterholt" "datastructs.plt" 1 0)))
(require "log4scm-cfg.scm")
(require "log4scm-reader.scm")
(require "log4scm.scm")
(provide log4scm-mzgtk2)
(def-class
(this (listview-log-files current-log-files callback-view-file))
(supers (gtk-list-model))
(private
(define log-files (vector))
)
(public
(define (refresh)
(set! log-files (vector)) (-> supers refresh)
(set! log-files (apply vector (current-log-files)))
(-> supers refresh))
(define (get-#columns) 1)
(define (get-#rows) (vector-length log-files))
(define (get-columnn-type col) "string")
(define (get-value row col) (vector-ref log-files row))
(define widget #f)
)
(constructor
(let* ((column (gtk-tree-view-column 'title (_ "log file")
'renderer (gtk-cell-renderer-text)
'column 0
'model this
'resizable #t
'clickable #t))
(treeview (gtk-tree-view 'model this
'widgets (list column)))
(scrolledw (gtk-scrolled-window 'widgets treeview
'policy '(never always)))
(frame (gtk-frame 'widgets scrolledw)))
(-> treeview connect "row-activated" (lambda (widget treepath column)
(mzgtk2-alive-gobjects)
(callback-view-file (vector-ref log-files (car treepath)))))
(-> treeview set-size-request 200 150)
(-> this destroy-with-view)
(set! widget frame)
(refresh))
)
)
(def-class
(this (log4scm-mzgtk2 add-menu add-view change-label))
(supers (gtk-list-model))
(private
(define reader #f)
(define opened #f)
(define cfg (log4scm-cfg "untitled.scfg"))
(define cell-timestamp (gtk-cell-renderer-text))
(define cell-level (gtk-cell-renderer-text))
(define cell-message (gtk-cell-renderer-text))
(define label-keep (gtk-label 'label (_ "Keep days : ")
'expand #f))
(define entry-keep (gtk-spin-button 'range (-> (-> cfg keep-range-in-days) range)
'value (-> cfg keep)
'expand #f))
(define label-level (gtk-label 'label (_ "Minimum log level : ")
'expand #f))
(define combo-level (gtk-combo-box 'items (map (lambda (obj) (-> obj level))
(-> cfg configurable-minimum-log-levels))
'default (-> cfg level)
'expand #f))
(define label-mode (gtk-label 'label (_ "Log mode : ")
'expand #f))
(define combo-mode (gtk-combo-box 'items (map (lambda (obj) (-> obj mode))
(-> cfg configurable-log-modes))
'default (-> cfg mode)
'expand #f))
(define check-tail-mode (gtk-check-button 'label (_ "_Tail mode")
'closure (lambda (w)
(-> cfg tail-mode (-> check-tail-mode check))
(-> this go-to-tail-if-tail-mode))
'init (-> cfg tail-mode)
'expand #f))
(define menu-recent (gtk-menu-recently-used-files (lambda max
(meta-apply sprefs-cond-set!-get '(log recent max) 4 max))
(lambda files
(meta-apply sprefs-cond-set!-get '(log recent files) (list) files))
'label (_ "_Recently used")
'closure (lambda (filename) (open filename))))
(define log-files (listview-log-files (lambda () (-> cfg current-log-files)) (lambda (file) (view file))))
(define block-view #f)
(define log-view #f) (define log-view-filename (gtk-label 'label ""
'expand #f))
(define log-view-filesize (gtk-label 'label ""
'expand #f))
(define log-view-num-of-lines (gtk-label 'label ""
'expand #f))
(define log-progress (gtk-progress-bar 'expand #f))
(define current-perc -1)
(define log-file-array (array))
(define log-file-lines 0)
(define log-loading #f)
(define timer-id #f)
(define kill-timer #f)
)
(public
(define (open cfg-filename)
(set! cfg (log4scm-cfg cfg-filename))
(-> combo-level activate (-> cfg level))
(-> combo-mode activate (-> cfg mode))
(-> entry-keep set (-> cfg keep))
(-> check-tail-mode set (-> cfg tail-mode))
(sprefs-set! '(log last-directory) (basedir cfg-filename))
(sprefs-set! '(log recent files)
(cons cfg-filename (sprefs-get '(log recent files) '())))
(-> menu-recent refresh)
(-> log-files refresh)
(set! opened #t)
(change-label (basename cfg-filename))
#t)
(define (save cfg-filename)
(-> cfg keep (-> entry-keep get))
(-> cfg level (-> combo-level activated))
(-> cfg mode (-> combo-mode activated))
(-> cfg commit cfg-filename)
(sprefs-set! 'last-directory (basedir cfg-filename))
(sprefs-set! '(log recent files)
(cons cfg-filename (sprefs-get '(log recent files) '())))
(-> menu-recent refresh)
(log-info (format "log file configuration has been committed to ~a" cfg-filename))
(change-label (basename cfg-filename))
)
(define (go-to-tail-if-tail-mode)
(if (not block-view)
(if (-> cfg tail-mode)
(if (> log-file-lines 0)
(begin
(-> log-view scroll-to-cell (- log-file-lines 1))
(-> log-view set-cursor (- log-file-lines 1))
(-> log-view process-pending-events))))))
(define (read-more)
(log-debug "start read-more")
(let ((read-any (-> reader read)))
(if log-loading
(begin
(log-debug "refreshing...")
(-> this refresh)
(log-debug "refreshed")))
(if read-any
(begin
(-> log-view-filename label (-> reader name))
(-> log-view-filesize label (unit-convert->rounded->string
(-> reader size) 'b '(kb mb gb tb) 2))
(-> log-view-num-of-lines label (number->string log-file-lines))
(-> this go-to-tail-if-tail-mode)))
(-> log-view process-pending-events)
(log-debug "end of read-more")
))
(define (timed-read-more)
(log-debug "start of timed-read-more")
(if (not kill-timer)
(read-more))
(if (not kill-timer)
(set! timer-id (mzgtk2-timer-add 1000 timed-read-more)))
(log-debug "end of timed-read-more")
#f)
(define (kill-timed-read-more)
(set! kill-timer #t)
(if timer-id (mzgtk2-timer-remove timer-id))
(set! kill-timer #f))
(define (get-#columns)
3)
(define (get-#rows)
(array-length log-file-array))
(define (get-value row col)
(let ((A (array-ref log-file-array row)))
(if (= col 0)
(let ((colour (-> cfg color (array-ref A 1))))
(for-each (lambda (c)
(-> c foreground colour))
(list cell-timestamp cell-level cell-message))))
(array-ref A col)))
(define (get-column-type col)
"string")
(define (add-line obj)
(array-set! log-file-array log-file-lines (array (-> obj timestamp)
(-> obj level)
(-> obj message)))
(if (not log-loading)
(-> this row-inserted log-file-lines))
(++ log-file-lines)
#t)
(define (progress percentage)
(if (not (= percentage current-perc))
(begin
(set! current-perc percentage)
(-> log-progress fraction (/ (exact->inexact percentage) 100.0))
(-> log-progress label (format "~a%" percentage))
(-> log-progress process-pending-events)))
#t)
(define (view log-filename)
(if (not block-view)
(begin
(log-info (format "Opening log file ~a" log-filename))
(set! block-view #t)
(kill-timed-read-more)
(set! log-file-array (array))
(set! log-file-lines 0)
(-> this refresh)
(if reader (-> reader close))
(set! reader (log4scm-reader cfg this))
(-> reader log-file log-filename)
(set! log-loading #t)
(read-more)
(set! log-loading #f)
(timed-read-more)
(set! block-view #f)
(-> this go-to-tail-if-tail-mode))
(log-warn (format "A log file is currently loading, view request for ~a ignored"
log-filename)))
#t)
(define (open-log-cfg parent)
(let ((dlg (gtk-file-chooser-dialog 'parent parent
'title (_ "Open log configuration file")
'filters (list
(gtk-file-filter 'filter-name (_ "Scheme Configuration File")
'patterns (list "*.scfg")))
'action 'open
'current-folder (sprefs-get '(log last-directory) "."))))
(let ((result (-> dlg run)))
(if (eq? result 'open)
(open (-> dlg filename)))
(-> dlg destroy))))
(define (save-log-cfg parent prev-opened)
(if (eq? prev-opened #f)
(let ((dlg (gtk-file-chooser-dialog 'parent parent
'title (_ "Save log configuration file")
'filters (list
(gtk-file-filter 'filter-name (_ "Scheme Configuration File")
'patterns (list "*.scfg")))
'action 'save)))
(-> dlg current-name (-> cfg filename))
(if opened (-> dlg filename (-> cfg filename)))
(let ((result (-> dlg run)))
(if (eq? result 'save)
(save (-> dlg filename)))
(-> dlg destroy)))
(save (-> cfg filename))))
(define (change-colors parent)
(let ((my-parent #f))
(define (change-color parent level)
(log-debug (format "Parent: ~a" (-> parent name)))
(log-debug "change-color")
(let* ((colour (-> cfg color level))
(dlg (gtk-color-selection-dialog 'parent parent
'color (gdk-color colour))))
(log-debug "run change-color")
(if (eq? (-> dlg run) 'ok)
(-> cfg color level (-> (-> dlg color) ->string)))
(-> dlg destroy)))
(define (color-handler obj)
(let* ((level (-> obj level))
(label (-> obj label))
(lentry (gtk-label 'label (_ label)
'expand #f))
(entry (gtk-entry 'text (_ label)
'readonly #t)))
(-> entry modify-text 'normal (gdk-color (-> cfg color level)))
(-> entry width-chars (inexact->exact (/ (string-length label) 2.0)))
(log-debug (format "label-length: ~a (request ~a) (~a) ~%" (string-length label) (-> entry width-chars) label))
(list
(gtk-button 'label (symbol->string level)
'closure (lambda (w)
(change-color my-parent level)
(-> entry modify-text 'normal (gdk-color (-> cfg color level))))
'expand #f)
entry)))
(let* (
(sizegroup (gtk-size-group 'mode 'vertical))
(vbox-labels (gtk-vbox 'expand #f))
(vbox-entries (gtk-vbox))
(hbox (gtk-hbox 'widgets (list vbox-labels vbox-entries)))
(dlg (gtk-dialog 'name 'color-dialog
'title (_ "LOG Colors")
'parent parent
'buttons (list (list 'ok (lambda () 'ok)))
'widgets hbox)))
(for-each (lambda (obj)
(apply (lambda (label entry)
(-> sizegroup add label)
(-> vbox-labels add label)
(-> sizegroup add entry)
(-> vbox-entries add entry))
(color-handler obj)))
(-> cfg configurable-log-levels))
(log-debug (format "Parent: ~a" (-> parent name)))
(set! my-parent dlg)
(-> dlg run)
(-> dlg destroy)
#t)))
(define (create-gui)
(set! log-view (gtk-tree-view 'model this
'widgets (let ((col 0))
(map
(lambda (l)
(gtk-tree-view-column 'title (_ (car l))
'column (post++ col)
'renderer (cadr l)
'model this
'resizable #t
'clickable #f))
(list
(list "Timestamp" cell-timestamp)
(list "Level" cell-level)
(list "Message" cell-message))))))
(let* ((table (gtk-table 'rows 4 'columns 2
'widgets (list
label-keep entry-keep
label-level combo-level
label-mode combo-mode
check-tail-mode )
'expand #f))
(hbox (gtk-expander 'expanded #t
'label (_ "Log configuration")
'widgets (gtk-frame 'widgets
(list
(gtk-table 'rows 1
'columns 2
'widgets (list table (-> log-files widget))
'expand #f)))
'expand #f))
(log-info (gtk-frame 'widgets (gtk-hbox 'widgets (list
(gtk-frame 'widgets
(gtk-hbox 'widgets (list
(gtk-label 'label (_ "Filename : ") 'expand #f)
log-view-filename)))
(gtk-frame 'widgets
(gtk-hbox 'widgets (list
(gtk-label 'label (_ "Filesize : ") 'expand #f)
log-view-filesize)))
(gtk-frame 'widgets
(gtk-hbox 'widgets (list
(gtk-label 'label (_ "Lines : ") 'expand #f)
log-view-num-of-lines)))))
'expand #f))
(s-log-view (gtk-scrolled-window 'widgets log-view))
(vbox (gtk-vbox 'widgets (list hbox log-info s-log-view log-progress)))
(pane vbox)
(menu (gtk-menu-submenu 'name 'log-menu
'label (_ "_Log server")
'entries (list
(gtk-menu-label 'name 'open
'label (_ "_Open")
'closure open-log-cfg)
(gtk-menu-label 'name 'save
'label (_ "_Save")
'closure (lambda (parent)
(save-log-cfg parent opened))
'accelerator (gtk-accelerator "<Ctrl>s" '(log4scm log save) (lambda (parent) #t)))
(gtk-menu-label 'name 'save-as
'label (_ "Save _As")
'closure (lambda (parent)
(save-log-cfg parent #f)))
(gtk-menu-label 'name 'colors
'label (_ "_Colors")
'closure change-colors)
(gtk-menu-separator)
menu-recent))))
(-> log-view search-column 2)
(-> this destroy-with-view)
(-> pane associate-destroy-handler
(lambda ()
(kill-timed-read-more)))
(add-menu menu)
(add-view pane)))
)
(constructor
(create-gui)
)
)
)