expr: 6.81%

#lang racket/gui
(provide project-manager%)
(require raco/invoke
         "../path/util.rkt"
         "project-templates.rkt")

(define project-manager%
  (class frame%
    (init-field on-select)
    (super-new [width 600] [height 600])

    ;;; auto setup configuration
    (define projects-file (build-path config-dir "projects"))

    (define (get-projects) (file->lines projects-file #:mode 'text))

    (define (auto-setup-configuration-env)
      ; create projects file configuration if not existed
      (unless (file-exists? projects-file)
        (display-to-file "" projects-file)))

    (define (add-project path)
      (call-with-output-file projects-file
        #:exists 'append
         (port)
          (parameterize ([current-output-port port])
            ; put path into config
            (displayln path)
            ; append into current selectable list
            (send list-box append (path->string path))))))
    (define (remove-selected-project)
      ; for current single selection list-box, this method always returns a list contains one number or a null
      (define selection* (send list-box get-selections))
      (unless (null? selection*)
        (let ([n (first selection*)])
          ; 1. remove to delete item from list-box
          (send list-box delete n)
          (call-with-output-file projects-file
            #:exists 'truncate ; 2. truncate removes all data from config
             (port)
              (parameterize ([current-output-port port])
                ; 3. now write all paths in list-box back into config
                (for ([n (range (send list-box get-number))])
                  (displayln (send list-box get-string n)))))))))

    (define list-box
      (new list-box% [parent this]
           [label "projects"]
           [choices '()]
           [style '(single)]
           [callback
             (proj-manager event)
              (define evt-type (send event get-event-type))
              (match evt-type
                ['list-box-dclick
                 (let ([str-path (send proj-manager get-string-selection)])
                   (when str-path
                      (let ([path (string->path str-path)])
                         (if (directory-exists? path)
                             (begin
                               (send this show #f)
                               (on-select path))
                             (begin
                               (message-box "Failed" "project not existed")
                               (remove-selected-project))))))]
                ['list-box
                 (void)]))]))

    (new button% [parent this]
         [label "add project"]
         [callback
           (btn event)
            (define path (get-directory #f this))
            (when (and path
                       (directory-exists? path)
                       (not (member (path->string path) (get-projects))))
              (add-project path)))])

    (new button% [parent this]
         [label "create project"]
         [callback
           (btn event)
            (define user-selected-path (get-directory "create at?"))
            (define project-name (get-text-from-user "name of project?" ""))
            (define tmp-frame (new frame% [label "template"]
                                   [height 600] [width 600]))
            (new list-box% [parent tmp-frame]
                 [label "template"]
                 [choices project-templates]
                 [callback
                   (template-selection event)
                    (define evt-type (send event get-event-type))
                    (match evt-type
                      ['list-box-dclick
                       (send tmp-frame show #f)
                       (match-define (list n)
                         (send template-selection get-selections))
                       (define path (build-path user-selected-path project-name))
                       (raco "new"
                             (send template-selection get-string n)
                             (path->string path))
                       (add-project path)]
                      ['list-box (void)]))])
            (send* tmp-frame
              [center]
              [show #t]))])

    (new button% [parent this]
         [label "remove project"]
         [callback
           (btn event)
            (remove-selected-project))])

    (define (load-projects)
      (for ([project-path (get-projects)])
        (send list-box append project-path)))

    (define/public (run)
      (auto-setup-configuration-env)
      (load-projects)
      (send* this
        [center 'both]
        [show #t]))))

(module+ main
  (define starter (new project-manager%
                       [label "select a project"]
                       [on-select  (path) (message-box "dummy" (format "~a opened" path)))]))
  (send starter run))