From 8c7632c025213fc71ae3d74ebe441b82f6af8bd9 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Tue, 23 Apr 2013 12:22:22 -0600 Subject: [PATCH] pkg/gui: first cut at a GUI package manager For now, run it with `racket -l pkg/gui/main'. The expectation is that DrRacket will start using this GUI. --- collects/meta/dist-specs.rkt | 6 +- collects/meta/props | 2 + collects/pkg/gui/by-installed.rkt | 168 ++++++ collects/pkg/gui/by-list.rkt | 497 ++++++++++++++++++ collects/pkg/gui/by-source.rkt | 395 ++++++++++++++ collects/pkg/gui/common.rkt | 25 + collects/pkg/gui/main.rkt | 58 ++ .../private/english-string-constants.rkt | 34 +- 8 files changed, 1181 insertions(+), 4 deletions(-) create mode 100644 collects/pkg/gui/by-installed.rkt create mode 100644 collects/pkg/gui/by-list.rkt create mode 100644 collects/pkg/gui/by-source.rkt create mode 100644 collects/pkg/gui/common.rkt create mode 100644 collects/pkg/gui/main.rkt diff --git a/collects/meta/dist-specs.rkt b/collects/meta/dist-specs.rkt index 92497fb586..7ebecf4b83 100644 --- a/collects/meta/dist-specs.rkt +++ b/collects/meta/dist-specs.rkt @@ -556,7 +556,11 @@ plt-extras :+= (package: "slatex") ;; -------------------- planet mz-extras :+= (package: "planet") -mz-extras :+= (package: "pkg") + +;; -------------------- pkg +mz-extras :+= (- (package: "pkg") + (collects: "pkg/gui/")) +dr-extras :+= (collects: "pkg/gui/") ;; -------------------- mrlib mr-extras :+= (+ (- (package: "mrlib/") diff --git a/collects/meta/props b/collects/meta/props index 709b1e792f..8f761dd593 100755 --- a/collects/meta/props +++ b/collects/meta/props @@ -859,6 +859,8 @@ path/s is either such a string or a list of them. "collects/parser-tools/private-lex/error-tests.rkt" drdr:command-line #f "collects/picturing-programs" responsible (sbloch) "collects/pkg" responsible (jay) +"collects/pkg/gui" responsible (mflatt) +"collects/pkg/gui/main.rkt" drdr:command-line (mzc *) "collects/plai" responsible (jay) "collects/planet" responsible (robby) "collects/plot" responsible (ntoronto) diff --git a/collects/pkg/gui/by-installed.rkt b/collects/pkg/gui/by-installed.rkt new file mode 100644 index 0000000000..7805415746 --- /dev/null +++ b/collects/pkg/gui/by-installed.rkt @@ -0,0 +1,168 @@ +#lang racket/base +(require racket/class + racket/gui/base + racket/format + pkg/lib + (prefix-in pkg: pkg) + string-constants + "common.rkt") + +(provide by-installed-panel%) + +(struct ipkg (name scope auto? checksum source)) + +(define (scopevector l)) + (send pkg-list set + (for/list ([i (in-list l)]) + (~a (if (ipkg-auto? i) "*" "") + (ipkg-scope i))) + (for/list ([i (in-list l)]) + (format "~a" (ipkg-name i))) + (for/list ([i (in-list l)]) + (or (ipkg-checksum i) "")) + (for/list ([i (in-list l)]) + (define s (ipkg-source i)) + (format "~a: ~a" + (case (car s) + [(catalog) "Catalog"] + [(url) "URL"] + [(link) "Link"]) + (cadr s)))) + (adjust-buttons!)))) diff --git a/collects/pkg/gui/by-list.rkt b/collects/pkg/gui/by-list.rkt new file mode 100644 index 0000000000..ea1b052675 --- /dev/null +++ b/collects/pkg/gui/by-list.rkt @@ -0,0 +1,497 @@ +#lang racket/base +(require racket/class + racket/gui/base + racket/port + racket/set + racket/format + string-constants + net/url + pkg/lib + (prefix-in pkg: pkg) + (prefix-in db: pkg/db) + "common.rkt") + +(provide by-list-panel%) + +(define sc-pkg-update-package-list (string-constant install-pkg-update-package-list)) +(define sc-pkg-stop-update (string-constant install-pkg-stop-update)) + +(define check-mark "✓") + +(define default-status + (~a check-mark ": installed" + " " + "*: auto-installed" + " " + "!: not default scope" + " " + "=: installed as link" + " " + "@: installed from URL")) + +(define by-list-panel% + (class vertical-panel% + (init-field [in-terminal in-terminal]) + + (super-new) + + (inherit get-top-level-window) + + (define tool-panel + (new horizontal-panel% + [parent this] + [alignment '(left center)] + [stretchable-height #f])) + + (define keep-rx #rx"") + (define/private (list-pkg-keep? a) + (or (regexp-match? keep-rx (db:pkg-name a)) + (regexp-match? keep-rx (db:pkg-author a)) + (regexp-match? keep-rx (db:pkg-desc a)) + (regexp-match? keep-rx (format-tags (pkg-tags a))) + (regexp-match? keep-rx (db:pkg-source a)) + (regexp-match? keep-rx (db:pkg-catalog a)))) + + (define filter-text + (new text-field% + [label (~a (string-constant install-pkg-filter) ":")] + [parent tool-panel] + [font small-control-font] + [stretchable-width #t] + [callback (lambda (tf e) + (define s (send tf get-value)) + (define terms (filter (lambda (s) (not (string=? s ""))) + (regexp-split #rx"[, \t\r\n]" s))) + (define rx + (regexp (apply ~a + #:separator "|" + (for/list ([term terms]) + (~a "(?i:" (regexp-quote term) ")"))))) + (unless (equal? rx keep-rx) + (set! keep-rx rx) + (sort-pkg-list!)))])) + + (define filter-result + (new message% + [label "9999/9999 match"] + [parent tool-panel] + [font small-control-font])) + (send filter-result set-label "") + + (define updating? #f) + + (define update-button + (new button% + [label sc-pkg-update-package-list] + [parent tool-panel] + [font small-control-font] + [callback + (lambda (b e) + (if updating? + (interrupt-task!) + (update-db-package-list)))])) + + (define status-text + (new message% + [parent this] + [label default-status] + [font small-control-font] + [stretchable-width #t])) + + (define sort-column -1) + (define list-pkgset db-catalogs) + (list->set user-catalogs)) + (when (= 1 (message-box/custom "Package Catalogs" + (~a + (string-constant install-pkg-update-catalogs?) + "\n\n" + (string-constant install-pkg-currently-configured-are) ":\n" + (apply ~a + (for/list ([url user-catalogs]) + (~a " " url "\n"))) + "\n" + (string-constant install-pkg-database-recorded-are) ":\n" + (apply ~a + (for/list ([url db-catalogs]) + (~a " " url "\n")))) + (string-constant install-pkg-update-catalogs) + (string-constant install-pkg-do-not-update-catalogs) + #f + this + '(caution default=1))) + (db:set-catalogs! user-catalogs) + (update-db-package-list)))) + + (define/private (check-init) + (unless (file-exists? (db:current-pkg-catalog-file)) + (db:set-catalogs! (pkg-config-catalogs)) + (update-db-package-list))) + + (define task #f) + (define finalize void) + + (define/private (task! thunk given-finalize) + (define finalized? #f) + (set! finalize (lambda (ok?) + (unless finalized? + (set! finalized? #t) + (given-finalize ok?)))) + (set! task (thread + (lambda () + (with-handlers ([exn:break? void]) + (thunk) + (let ([f finalize]) + (queue-callback/wait (lambda () (f #t))))))))) + + (define/private (queue-callback/wait thunk) + (define s (make-semaphore)) + (define ok? #t) + (queue-callback (lambda () + (when ok? + (thunk) + (semaphore-post s)))) + (dynamic-wind + void + (lambda () (semaphore-wait s)) + (lambda () (set! ok? #f)))) + + (define/private (interrupt-task!) + (when task + (break-thread task) + (sync task) + (finalize #f)) + (set! task #f) + (send status-text set-label default-status)) + + (define/private (update-db-package-list) + (interrupt-task!) + (set! updating? #t) + (send update-button set-label sc-pkg-stop-update) + (send status-text set-label "Updating package list...") + (task! + (lambda () + (define db-catalogs (db:get-catalogs)) + (for ([catalog (in-list db-catalogs)]) + (queue-callback/wait + (lambda () + (send status-text set-label (format "Updating from ~a..." catalog)))) + (define details + (parameterize ([current-pkg-catalogs (list (string->url catalog))]) + (get-all-pkg-details-from-catalogs))) + (db:set-pkgs! catalog (for/list ([(name ht) (in-hash details)]) + (db:pkg name + catalog + (hash-ref ht 'author "") + (hash-ref ht 'source "") + (hash-ref ht 'checksum "") + (hash-ref ht 'description "")))) + (for/list ([(name ht) (in-hash details)]) + (db:set-pkg-tags! name catalog (hash-ref ht 'tags '()))))) + (lambda (finished?) + (send status-text set-label default-status) + (set! updating? #f) + (send update-button set-label sc-pkg-update-package-list) + (refresh-pkg-list!)))) + + (define/private (background-pkg-details) + (task! + (lambda () + (define catalog-ht (make-hash)) + (for ([a-pkg (in-vector pkgs)] + [pos (in-naturals)]) + (define name (db:pkg-name a-pkg)) + (define catalog (db:pkg-catalog a-pkg)) + (send status-text set-label + (~a "Getting details for " name " from " catalog "...")) + (define all-ht + (or (hash-ref catalog-ht catalog #f) + (let ([all-ht (parameterize ([current-pkg-catalogs (list (string->url catalog))]) + (get-all-pkg-details-from-catalogs))]) + (hash-set! catalog-ht catalog all-ht) + all-ht))) + (define ht (hash-ref all-ht name)) + (define author (hash-ref ht 'author "")) + (define source (hash-ref ht 'source "")) + (define checksum (hash-ref ht 'checksum "")) + (define desc (hash-ref ht 'description "")) + (define tags (hash-ref ht 'tags '())) + (define new-pkg (db:pkg name catalog author source checksum desc)) + (unless (and (equal? new-pkg a-pkg) + (equal? tagss (pkg-tags a-pkg))) + (db:set-pkg! name catalog author source checksum desc) + (db:set-pkg-tags! name catalog tags) + (queue-callback/wait + (lambda () + (vector-set! pkgs pos new-pkg) + (set! tagss (hash-set (hash-remove tagss a-pkg) + new-pkg + tags)) + (define lpos (vector-ref posns pos)) + (when lpos + (send pkg-list set-string lpos (->label-string author) 2) + (send pkg-list set-string lpos (->label-string desc) 3) + (send pkg-list set-string lpos (->label-string (format-tags tags)) 4) + (send pkg-list set-string lpos (->label-string checksum) 5) + (send pkg-list set-string lpos (->label-string source) 6))))))) + (lambda (ok?) + (send status-text set-label default-status)))) + + (define/private (->label-string s) + (substring s 0 (min 200 (string-length s)))) + + (define pkgs '#()) + (define tagss #(hash)) + (define posns '#()) + (define installed '#()) + (define default-scope 'user) + + (define/private (refresh-pkg-list!) + (define pkg-list (db:get-pkgs)) + (define tags-list (map (lambda (p) + (db:get-pkg-tags (db:pkg-name p) + (db:pkg-catalog p))) + pkg-list)) + (set! pkgs (list->vector pkg-list)) + (set! tagss (for/hash ([p (in-list pkg-list)] + [t (in-list tags-list)]) + (values p t))) + (set! default-scope (default-pkg-scope)) + (refresh-installed-list! #:always? #t)) + + (define/private (refresh-installed-list! #:always? [always? #f]) + (define new-installed + (for*/hash ([scope (in-list '(installation user shared))] + [(k v) (in-hash (installed-pkg-table #:scope scope))]) + (values k (cons scope v)))) + (when (or always? + (not (equal? installed new-installed))) + (set! installed new-installed) + (sort-pkg-list!))) + + (define/private (pkg-tags p) + (hash-ref tagss p '())) + + (define/private (sort-pkg-list!) + (define sels (for/list ([i (in-list (send pkg-list get-selections))]) + (define p (send pkg-list get-data i)) + (cons (db:pkg-name p) (db:pkg-catalog p)))) + (set! posns (make-vector (vector-length pkgs) #f)) + (define list-pkg+poses + (sort + (filter + (lambda (p) (list-pkg-keep? (car p))) + (for/list ([p pkgs] + [i (in-naturals)]) + (cons p i))) + list-pkglabel-string (db:pkg-name p))) + (for/list ([p list-pkgs]) (->label-string (db:pkg-author p))) + (for/list ([p list-pkgs]) (->label-string (db:pkg-desc p))) + (for/list ([p list-pkgs]) (->label-string (format-tags (pkg-tags p)))) + (for/list ([p list-pkgs]) (->label-string (db:pkg-checksum p))) + (for/list ([p list-pkgs]) (->label-string (db:pkg-source p))) + (for/list ([p list-pkgs]) (->label-string (db:pkg-catalog p)))) + (let ([ht (for/hash ([p list-pkgs] + [i (in-naturals)]) + (send pkg-list set-data i p) + (values (cons (db:pkg-name p) (db:pkg-catalog p)) + i))]) + (for/fold ([did? #f]) ([sel (in-list sels)]) + (define i (hash-ref ht sel #f)) + (when i + (if did? + (send pkg-list select i) + (begin + (send pkg-list set-selection i) + (let ([f (send pkg-list get-first-visible-item)] + [n (send pkg-list number-of-visible-items)]) + (unless (<= f i (+ f n -1)) + (send pkg-list set-first-visible-item + (if (i . < . f) + i + (max 0 (add1 (- i n)))))))))) + (or did? i))) + (adjust-buttons!)) + + (define/private (format-tags tags) + (apply ~a #:separator ", " tags)) + + (define prepared? #f) + + (define/override (on-superwindow-show on?) + (if on? + (cond + [prepared? (refresh-installed-list!)] + [else + (check-init) + (refresh-pkg-list!) + (check-catalogs) + (set! prepared? #t)]) + (interrupt-task!))))) diff --git a/collects/pkg/gui/by-source.rkt b/collects/pkg/gui/by-source.rkt new file mode 100644 index 0000000000..c02e0c304a --- /dev/null +++ b/collects/pkg/gui/by-source.rkt @@ -0,0 +1,395 @@ +#lang racket/base + +(require racket/gui/base + racket/class + racket/format + string-constants + pkg/name + pkg/lib + pkg + racket/list + framework + net/url) + +(provide by-source-panel%) + +(define sc-install-pkg-dialog-title (string-constant install-pkg-dialog-title)) +(define sc-install-pkg-source-label (string-constant install-pkg-source-label)) +(define sc-install-pkg-type-label (string-constant install-pkg-type-label)) +(define sc-install-pkg-infer (string-constant install-pkg-infer)) +(define sc-install-pkg-file (string-constant install-pkg-file)) +(define sc-install-pkg-dir (string-constant install-pkg-dir)) +(define sc-install-pkg-dir-url (string-constant install-pkg-dir-url)) +(define sc-install-pkg-file-url (string-constant install-pkg-file-url)) +(define sc-install-pkg-github (string-constant install-pkg-github)) +(define sc-install-pkg-name (string-constant install-pkg-name)) +(define sc-install-pkg-inferred-as (string-constant install-pkg-inferred-as)) +(define sc-install-pkg-force? (string-constant install-pkg-force?)) +(define sc-install-pkg-command-line (string-constant install-pkg-command-line)) + +(define sc-install-pkg-action-label (string-constant install-pkg-action-label)) +(define sc-install-pkg-install (string-constant install-pkg-install)) +(define sc-install-pkg-update (string-constant install-pkg-update)) +(define sc-action-inferred-to-be-update (string-constant install-pkg-action-inferred-to-be-update)) +(define sc-action-inferred-to-be-install (string-constant install-pkg-action-inferred-to-be-install)) + +(define sc-install-pkg-default (string-constant install-pkg-default)) +(define sc-install-pkg-scope-label (string-constant install-pkg-scope-label)) +(define sc-install-pkg-installation (string-constant install-pkg-installation)) +(define sc-install-pkg-user (string-constant install-pkg-user)) +(define sc-install-pkg-shared (string-constant install-pkg-shared)) +(define sc-install-pkg-set-as-default (string-constant install-pkg-set-as-default)) +(define sc-install-pkg-scope-is (string-constant install-pkg-scope-is)) + +(define sc-install-pkg-browse (string-constant browse...)) + +(preferences:set-default 'drracket:gui-installer-pkg-source "" string?) + +(define by-source-panel% + (class vertical-panel% + (init-field [in-terminal in-terminal]) + + (super-new) + + (inherit get-top-level-window) + + (define source-panel (new horizontal-panel% + [parent this] + [stretchable-height #f])) + + (define tf (new text-field% + [parent source-panel] + [min-width 600] + [label (~a sc-install-pkg-source-label ":")] + [callback (λ (_1 _2) + (preferences:set 'drracket:gui-installer-pkg-source (send tf get-value)) + (adjust-all))])) + (send tf set-value (preferences:get 'drracket:gui-installer-pkg-source)) + + (define browse-button (new button% + [parent source-panel] + [label (string-constant browse...)] + [font small-control-font] + [callback (lambda (b e) + (define mode (send choice get-string-selection)) + (define dir? (or (equal? mode sc-install-pkg-dir) + (equal? mode sc-install-pkg-dir-url))) + (define f + (cond + [dir? + (get-directory (string-constant install-pkg-select-package-directory) + (get-top-level-window))] + [else + (parameterize ([finder:default-filters + '(("Package" "*.zip;*.plt;*.tgz;*.tar") + ("Any" "*.*"))]) + (finder:get-file #f (string-constant install-pkg-select-package-file) + #f "bad" + (get-top-level-window)))])) + (when f + (send tf set-value + (url->string (path->url (if dir? + (path->directory-path f) + f)))) + (adjust-all)))])) + + (define button-panel (new horizontal-panel% + [parent this] + [stretchable-height #f])) + (define details-parent (new vertical-panel% [parent this])) + (define details-panel (new group-box-panel% + [label (string-constant autosave-details)] + [parent details-parent] + [alignment '(left center)] + [stretchable-height #f])) + + (define ok-button + (new button% + [label sc-install-pkg-install] + [parent button-panel] + [style '(border)] + [callback (lambda (b e) + (define res (compute-cmd-line)) + (in-terminal + (case (car res) + [(install) (string-constant install-pkg-abort-install)] + [(update) (string-constant install-pkg-abort-update)]) + (lambda () + (define action (case (car res) + [(install) install] + [(update) update])) + (apply action (cdr res)))) + (reset-installed-pkgs!))])) + + (define/private (reset-installed-pkgs!) + (set! currently-installed-pkgs (installed-pkg-names)) + (adjust-all)) + + (new horizontal-panel% [parent button-panel]) + (define details-shown? #f) + (define details-button (new button% + [label (string-constant show-details-button-label)] + [parent button-panel] + [callback + (λ (a b) + (set! details-shown? (not details-shown?)) + (adjust-all))])) + + (send details-parent change-children (λ (l) '())) + (define choice (new choice% + [label (~a sc-install-pkg-type-label ":")] + [parent details-panel] + [stretchable-width #t] + [callback (λ (x y) (adjust-all))] + [choices (list sc-install-pkg-infer + sc-install-pkg-file + sc-install-pkg-dir + sc-install-pkg-file-url + sc-install-pkg-dir-url + sc-install-pkg-github + sc-install-pkg-name)])) + + (define inferred-msg-parent (new horizontal-panel% + [parent details-panel] + [stretchable-height #f] + [alignment '(right center)])) + (define inferred-msg (new message% [label ""] [parent inferred-msg-parent] [auto-resize #t])) + + (define action-choice (new choice% + [label (~a sc-install-pkg-action-label ":")] + [parent details-panel] + [stretchable-width #t] + [callback (λ (x y) (adjust-all))] + [choices (list sc-install-pkg-infer + sc-install-pkg-install + sc-install-pkg-update)])) + (define inferred-action-msg-parent (new horizontal-panel% + [parent details-panel] + [stretchable-height #f] + [alignment '(right center)])) + (define inferred-action-msg (new message% [label ""] [parent inferred-action-msg-parent] [auto-resize #t])) + + + (define scope-panel (new horizontal-panel% + [parent details-panel] + [stretchable-height #f])) + (define scope-choice (new choice% + [label (~a sc-install-pkg-scope-label ":")] + [parent scope-panel] + [stretchable-width #t] + [callback (λ (x y) (adjust-all))] + [choices (list sc-install-pkg-default + sc-install-pkg-installation + sc-install-pkg-user + sc-install-pkg-shared)])) + (define/private (selected-scope) (case (send scope-choice get-selection) + [(0) (default-pkg-scope)] + [(1) 'installation] + [(2) 'user] + [(3) 'shared])) + (define scope-default-button (new button% + [label sc-install-pkg-set-as-default] + [font small-control-font] + [parent scope-panel] + [callback (lambda (b e) + (in-terminal + (lambda () + (define scope (selected-scope)) + (config #:scope 'installation #:set #t "default-scope" (~a scope)) + (printf "Default scope successfully changed to ~a" scope))) + (adjust-all))])) + (define inferred-scope-msg-parent (new horizontal-panel% + [parent details-panel] + [stretchable-height #f] + [alignment '(right center)])) + (define scope-msg (new message% [label ""] [parent inferred-scope-msg-parent] [auto-resize #t])) + + (define cb (new check-box% + [label sc-install-pkg-force?] + [parent details-panel] + [callback (λ (a b) (adjust-all))])) + + (new message% [parent details-panel] [label " "]) ; a spacer + + (new message% [parent details-panel] [label sc-install-pkg-command-line]) + (define cmdline-panel (new horizontal-panel% [parent details-panel] [stretchable-height #f])) + (new horizontal-panel% [parent cmdline-panel] [min-width 12] [stretchable-width #f]) + (define cmdline-msg (new message% + [parent cmdline-panel] + [stretchable-width #t] + [label ""] + [font (send (send (editor:get-standard-style-list) + find-named-style + "Standard") + get-font)])) + + (define/private (selected-type) + (case (send choice get-selection) + [(0) #f] + [(1) 'file] + [(2) 'dir] + [(3) 'file-url] + [(4) 'dir-url] + [(5) 'github] + [(6) 'name])) + + (define/private (type->str type) + (case type + [(file) sc-install-pkg-file] + [(name) sc-install-pkg-name] + [(dir) sc-install-pkg-dir] + [(github) sc-install-pkg-github] + [(file-url) sc-install-pkg-file-url] + [(dir-url) sc-install-pkg-dir-url] + [else (error 'type->str "unknown type ~s\n" type)])) + + (define currently-installed-pkgs (installed-pkg-names)) + (define/private (get-current-action) + (case (send action-choice get-selection) + [(0) + (define current-name (package-source->name (send tf get-value))) + (cond + [(and current-name (member current-name currently-installed-pkgs)) + 'update] + [else + 'install])] + [(1) 'install] + [(2) 'update])) + + + (define/private (adjust-all) + (adjust-inferred) + (adjust-inferred-action) + (adjust-checkbox) + (adjust-cmd-line) + (adjust-details-shown) + (adjust-browse) + (adjust-scope) + (adjust-ok)) + + (define/private (adjust-checkbox) + (send cb enable (equal? 'install (get-current-action)))) + + (define/private (adjust-inferred-action) + (define action (get-current-action)) + (define new-lab + (cond + [(equal? 0 (send action-choice get-selection)) + (case (get-current-action) + [(install) sc-action-inferred-to-be-install] + [(update) sc-action-inferred-to-be-update])] + [else ""])) + (send inferred-action-msg set-label new-lab)) + + (define/private (adjust-ok) + (send ok-button set-label (case (get-current-action) + [(install) sc-install-pkg-install] + [(update) sc-install-pkg-update])) + (send ok-button enable (compute-cmd-line))) + + (define/private (adjust-browse) + (define mode (send choice get-string-selection)) + (define show? (not (or (equal? mode sc-install-pkg-github) + (equal? mode sc-install-pkg-name)))) + (define shown? (member browse-button (send source-panel get-children))) + (unless (eq? (and show? #t) (and shown? #t)) + (if show? + (send source-panel add-child browse-button) + (send source-panel delete-child browse-button)))) + + (define/private (adjust-details-shown) + (define current-details-shown-state? + (and (member details-panel (send details-parent get-children)) + #t)) + (unless (equal? current-details-shown-state? + details-shown?) + (cond + [details-shown? + (send details-button set-label (string-constant hide-details-button-label)) + (send details-parent change-children + (λ (l) (list details-panel)))] + [else + (send details-button set-label (string-constant show-details-button-label)) + (send details-parent change-children + (λ (l) '()))]))) + + (define/private (adjust-inferred) + (define new-lab + (and (equal? #f (selected-type)) + (let-values ([(_ actual-type) + (package-source->name+type (send tf get-value) #f)]) + (and actual-type + (format sc-install-pkg-inferred-as (type->str actual-type)))))) + (send inferred-msg set-label (or new-lab ""))) + + (define/private (adjust-scope) + (send scope-msg set-label (format sc-install-pkg-scope-is + (case (selected-scope) + [(installation) sc-install-pkg-installation] + [(user) sc-install-pkg-user] + [(shared) sc-install-pkg-shared]))) + (define is-default? (let ([v (send scope-choice get-selection)]) + (or (zero? v) + (= v (case (default-pkg-scope) + [(installation) 1] + [(user) 2] + [(shared) 3]))))) + (define deleted? (not (member scope-default-button (send scope-panel get-children)))) + (unless (equal? is-default? deleted?) + (if is-default? + (send scope-panel delete-child scope-default-button) + (send scope-panel add-child scope-default-button)))) + + (define/private (adjust-cmd-line) + (define (convert-to-string s) + (cond + [(string? s) + (if (regexp-match #rx" " s) + (string-append "\"" s "\"") + s)] + [(keyword? s) (regexp-replace #rx"^#:" (format "~a" s) "--")] + [(symbol? s) (symbol->string s)] + [(boolean? s) #f] + [else (error 'convert-to-string "unk ~s" s)])) + (define cmd-line (compute-cmd-line)) + (send cmdline-msg set-label + (if cmd-line + (string-append + (if (eq? (system-type) 'windows) + "raco.exe" + "raco") + " pkg " + (apply string-append + (add-between + (filter values (map convert-to-string cmd-line)) + " "))) + ""))) + + (define/private (compute-cmd-line) + (define the-pkg + (cond + [(and (equal? 'update (get-current-action)) + (package-source->name (send tf get-value))) + => + values] + [else (send tf get-value)])) + (and (not (equal? the-pkg "")) + (cons (get-current-action) + (append + (if (send cb get-value) + '(#:force #t) + '()) + (if (selected-type) + (list '#:type (selected-type)) + '()) + (let ([scope (selected-scope)]) + (if (equal? scope (default-pkg-scope)) + '() + (list '#:scope scope))) + (list the-pkg))))) + + (define/override (on-superwindow-show on?) + (when on? + (reset-installed-pkgs!))) + + (adjust-all))) diff --git a/collects/pkg/gui/common.rkt b/collects/pkg/gui/common.rkt new file mode 100644 index 0000000000..a0c5249a43 --- /dev/null +++ b/collects/pkg/gui/common.rkt @@ -0,0 +1,25 @@ +#lang racket/base +(require racket/class + racket/gui/base + string-constants + racket/format) + +(provide really-remove? + sc-install-pkg-remove) + +(define sc-install-pkg-remove (string-constant install-pkg-remove)) +(define really-uninstall?-msg (string-constant install-pkg-really-remove?)) + +(define (really-remove? #:parent [parent #f] names) + (equal? 1 + (message-box/custom sc-install-pkg-remove + (apply ~a + really-uninstall?-msg + (for/list ([n (in-list names)]) + (~a "\n " n))) + sc-install-pkg-remove + (string-constant cancel) + #f + parent + '(caution default=1)))) + diff --git a/collects/pkg/gui/main.rkt b/collects/pkg/gui/main.rkt new file mode 100644 index 0000000000..4b3392f430 --- /dev/null +++ b/collects/pkg/gui/main.rkt @@ -0,0 +1,58 @@ +#lang racket/base +(require racket/class + racket/gui/base + framework + "by-source.rkt" + "by-list.rkt" + "by-installed.rkt" + mrlib/terminal + string-constants) + +(define frame + (new frame:basic% + [label "Package Manager"] + [width 800] + [height 600])) + +(define sel-tab + (new tab-panel% + [parent (send frame get-area-container)] + [choices (list (string-constant install-pkg-install-by-source) + (string-constant install-pkg-install-from-list) + (string-constant install-pkg-install-installed))] + [callback (lambda (t e) + (define old (send sel-panel active-child)) + (define new (list-ref panels (send t get-selection))) + (unless (eq? new old) + (send sel-panel active-child new)))])) + +(define sel-panel + (new panel:single% + [parent sel-tab])) + +(define terminal #f) +(define (in-terminal-panel abort-label thunk) + (when terminal + (send terminal close)) + (define t (in-terminal + #:abort-label abort-label + #:container (send frame get-area-container) + (λ (cust parent) (thunk)))) + (set! terminal t) + (send sel-tab enable #f) + (yield (send t can-close-evt)) + (send sel-tab enable #t)) + +(define panels + (list + (new by-source-panel% + [parent sel-panel] + [in-terminal in-terminal-panel]) + (new by-list-panel% + [parent sel-panel] + [in-terminal in-terminal-panel]) + (new by-installed-panel% + [parent sel-panel] + [in-terminal in-terminal-panel]))) + +(send frame show #t) diff --git a/collects/string-constants/private/english-string-constants.rkt b/collects/string-constants/private/english-string-constants.rkt index 66f058a167..4e165f9158 100644 --- a/collects/string-constants/private/english-string-constants.rkt +++ b/collects/string-constants/private/english-string-constants.rkt @@ -1773,6 +1773,9 @@ please adhere to these guidelines: (spell-program-wrote-to-stderr-on-startup "The spell program (~a) printed an error message:") ;; GUI for installing a pkg package; available via File|Install Package... + (install-pkg-install-by-source "Install by Source") ; tab label + (install-pkg-install-from-list "Install from List") ; tab label + (install-pkg-install-installed "Installed") ; tab label (install-pkg-menu-item... "Install Package...") (install-pkg-dialog-title "Install Package") (install-pkg-source-label "Package Source") @@ -1786,10 +1789,35 @@ please adhere to these guidelines: (install-pkg-name "Name (consulting resolver)") (install-pkg-inferred-as "Type inferred to be ~a") (install-pkg-force? "Overwrite Existing?") - (install-pkg-command-line "Equivalent Command Line Invocation:") + (install-pkg-command-line "Equivalent command line invocation:") (install-pkg-error-installing-title "Error Installing Package") (install-pkg-action-label "Action to Take") (install-pkg-install "Install") (install-pkg-update "Update") - (install-pkg-action-inferred-to-be-update "Action Inferred to be Update") - (install-pkg-action-inferred-to-be-install "Action Inferred to be Install")) + (install-pkg-remove "Remove") + (install-pkg-action-inferred-to-be-update "Action inferred to be Update") + (install-pkg-action-inferred-to-be-install "Action inferred to be Install") + (install-pkg-default "Default") + (install-pkg-scope-label "Package Scope") + (install-pkg-installation "Specific Racket Installation") + (install-pkg-user "Specific User and Racket Version") + (install-pkg-shared "Specific User and All Racket Versions") + (install-pkg-set-as-default "Set as Default") + (install-pkg-scope-is "Package scope is ~a") ; ~a gets install-pkg-{installation,user,shared} + (install-pkg-select-package-directory "Select Package Directory") + (install-pkg-select-package-file "Select Package File") + (install-pkg-update-package-list "Update Package List") + (install-pkg-stop-update "Stop Update") + (install-pkg-filter "Filter") + (install-pkg-update-catalogs? "Update database to match the configured set of catalogs?") + (install-pkg-currently-configured-are "The currently configured catalogs are") + (install-pkg-database-recorded-are "The database-recorded catalogs are") + (install-pkg-update-catalogs "Update") + (install-pkg-do-not-update-catalogs "Don't Update") + (install-pkg-really-remove? "Are you sure you want to remove the following selected packages?") + (install-pkg-abort-install "Abort Install") + (install-pkg-abort-update "Abort Update") + (install-pkg-abort-remove "Abort Remove") + (install-pkg-abort-generic-action "Abort Action") + + )