#lang racket/base (module install-pkg racket/base (require racket/gui/base racket/class string-constants planet2/name racket/list framework) (provide install-pkg) (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 (install-pkg parent) (define dlg (new dialog% [parent parent] [label sc-install-pkg-dialog-title] [alignment '(right center)])) (define tf (new text-field% [parent dlg] [min-width 600] [label sc-install-pkg-source-label] [callback (λ (_1 _2) (adjust-all))])) (define details-parent (new vertical-panel% [parent dlg])) (define details-panel (new group-box-panel% [label (string-constant autosave-details)] [parent details-parent] [alignment '(left center)])) (define button-panel (new horizontal-panel% [parent dlg] [stretchable-height #f] [alignment '(right center)])) (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))])) (new horizontal-panel% [parent button-panel]) (define-values (ok-button cancel-button) (gui-utils:ok/cancel-buttons button-panel (λ (_1 _2) (set! ok? #t) (send dlg show #f)) (λ (_1 _2) (send dlg show #f)))) (send details-parent change-children (λ (l) '())) (define choice (new choice% [label 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 cb (new check-box% [label sc-install-pkg-force?] [parent details-panel] [callback (λ (a b) (adjust-all))])) (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 (selected-type) (case (send choice get-selection) [(0) #f] [(1) 'file] [(2) 'dir] [(3) 'file-url] [(4) 'dir-url] [(5) 'github] [(6) 'name])) (define (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 (adjust-all) (adjust-inferred) (adjust-cmd-line) (adjust-details-shown) (adjust-ok/cancel)) (define (adjust-ok/cancel) (send ok-button enable (compute-cmd-line))) (define (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 (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 (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 install " (apply string-append (add-between (filter values (map convert-to-string cmd-line)) " "))) ""))) (define (compute-cmd-line) (define the-pkg (send tf get-value)) (and (not (equal? the-pkg "")) (append (if (send cb get-value) '(#:force #t) '()) (if (selected-type) (list '#:type (selected-type)) '()) (list the-pkg)))) (adjust-all) (define ok? #f) (send dlg show #t) (and ok? (compute-cmd-line)))) (module main racket (require (submod ".." install-pkg)) (install-pkg #f)) (require string-constants racket/match racket/class racket/string racket/file racket/math racket/unit "drsig.rkt" racket/gui/base framework net/url net/head setup/plt-installer help/bug-report setup/unpack planet2 (submod "." install-pkg)) (provide frame@) (define-unit frame@ (import [prefix drracket:unit: drracket:unit^] [prefix drracket:app: drracket:app^] [prefix help: drracket:help-desk^] [prefix drracket:multi-file-search: drracket:multi-file-search^] [prefix drracket:init: drracket:init^] [prefix drracket: drracket:interface^]) (export (rename drracket:frame^ [-mixin mixin])) (define last-keybindings-planet-attempt "") (define basics-mixin (mixin (frame:standard-menus<%>) (drracket:frame:basics<%>) (define/override (on-subwindow-focus win on?) (when the-keybindings-frame (when on? (send the-keybindings-frame set-bindings (if (can-show-keybindings?) (get-keybindings-to-show) '()))))) (define/override (on-subwindow-char receiver event) (let ([user-key? (send (keymap:get-user) handle-key-event (if (is-a? receiver editor-canvas%) (send receiver get-editor) receiver) event)]) ;; (printf "user-key? ~s\n" user-key?) returns #t for key release events -- is this a problem? (we'll find out!) (or user-key? (super on-subwindow-char receiver event)))) (inherit get-edit-target-window get-edit-target-object get-menu-bar) (define/private (get-menu-bindings) (let ([name-ht (make-hasheq)]) (let loop ([menu-container (get-menu-bar)]) (for-each (λ (item) (when (is-a? item selectable-menu-item<%>) (let ([short-cut (send item get-shortcut)]) (when short-cut (let ([keyname (string->symbol (keymap:canonicalize-keybinding-string (string-append (menu-item->prefix-string item) (case short-cut [(#\;) "semicolon"] [(#\:) "colon"] [(#\space) "space"] [else (cond [(symbol? short-cut) (symbol->string short-cut)] [(char? short-cut) (string short-cut)])]))))]) (hash-set! name-ht keyname (send item get-plain-label)))))) (when (is-a? item menu-item-container<%>) (loop item))) (send menu-container get-items))) (when (eq? (system-type) 'windows) (for-each (λ (top-level-menu) (when (is-a? top-level-menu menu%) (let ([amp-key (let loop ([str (send top-level-menu get-label)]) (cond [(regexp-match #rx"[^&]*[&](.)(.*)" str) => (λ (m) (let ([this-amp (list-ref m 1)] [rest (list-ref m 2)]) (cond [(equal? this-amp "&") (loop rest)] [else (string-downcase this-amp)])))] [else #f]))]) (when amp-key (hash-set! name-ht (format "m:~a" amp-key) (format "~a menu" (send top-level-menu get-plain-label))) (hash-set! name-ht (format "m:s:~a" amp-key) (format "~a menu" (send top-level-menu get-plain-label))))))) (send (get-menu-bar) get-items))) name-ht)) (define/private (menu-item->prefix-string item) (apply string-append (map (λ (prefix) (case prefix [(alt) (if (eq? (system-type) 'windows) "m:" "a:")] [(cmd) "d:"] [(meta) "m:"] [(ctl) "c:"] [(shift) "s:"] [(opt option) "a:"] [else (error 'menu-item->prefix-string "unknown prefix ~s\n" prefix)])) (send item get-shortcut-prefix)))) (define/private (copy-hash-table ht) (let ([res (make-hasheq)]) (hash-for-each ht (λ (x y) (hash-set! res x y))) res)) (define/private (can-show-keybindings?) (let ([edit-object (get-edit-target-object)]) (and edit-object (is-a? edit-object editor<%>) (let ([keymap (send edit-object get-keymap)]) (is-a? keymap keymap:aug-keymap<%>))))) ;; pre: (can-show-keybindings?) = #t (define/private (get-keybindings-to-show) (define edit-object (get-edit-target-object)) (define keymap (send edit-object get-keymap)) (define menu-names (get-menu-bindings)) (define table (send keymap get-map-function-table)) (define bindings (hash-map table list)) (define w/menus (append (hash-map menu-names list) (filter (λ (binding) (not (bound-by-menu? binding menu-names))) bindings))) (sort w/menus (λ (x y) (string-ci<=? (cadr x) (cadr y))))) (define/private (show-keybindings) (if (can-show-keybindings?) (show-keybindings-to-user (get-keybindings-to-show) this) (bell))) (define/private (bound-by-menu? binding menu-table) (ormap (λ (constituent) (hash-ref menu-table (string->symbol constituent) (λ () #f))) (regexp-split #rx";" (symbol->string (car binding))))) (define/override (help-menu:before-about help-menu) (make-help-desk-menu-item help-menu)) (define/override (help-menu:about-callback item evt) (drracket:app:about-drscheme)) (define/override (help-menu:about-string) (string-constant about-drscheme)) (define/override (help-menu:create-about?) #t) (define/public (get-additional-important-urls) '()) (define/override (help-menu:after-about menu) (drracket-help-menu:after-about menu this)) (define/override (file-menu:new-string) (string-constant new-menu-item)) (define/override (file-menu:open-string) (string-constant open-menu-item)) (define/override (file-menu:between-open-and-revert file-menu) (new menu-item% [label (string-constant install-plt-file-menu-item...)] [parent file-menu] [callback (λ (item evt) (install-plt-file this))]) (new menu-item% [label (string-constant install-pkg-menu-item...)] [parent file-menu] [callback (λ (item evt) (define res (install-pkg this)) (when res (with-handlers ((exn:fail? (λ (x) (define sp (open-output-string)) (parameterize ([current-error-port sp]) (drracket:init:original-error-display-handler (exn-message x) x)) (message-box (string-constant install-pkg-error-installing-title) (get-output-string sp))))) (apply install res))))]) (super file-menu:between-open-and-revert file-menu)) (define/override (file-menu:between-print-and-close menu) (super file-menu:between-print-and-close menu) (instantiate menu-item% () (label (string-constant mfs-multi-file-search-menu-item)) (parent menu) (callback (λ (_1 _2) (drracket:multi-file-search:multi-file-search)))) (new separator-menu-item% (parent menu))) (define/override (edit-menu:between-find-and-preferences menu) (super edit-menu:between-find-and-preferences menu) (when (current-eventspace-has-standard-menus?) (new separator-menu-item% [parent menu])) (let ([keybindings-on-demand (λ (menu-item) (let ([last-edit-object (get-edit-target-window)]) (send menu-item enable (can-show-keybindings?))))]) (instantiate menu% () (label (string-constant keybindings-menu-item)) (parent menu) (demand-callback (λ (keybindings-menu) (for-each (λ (old) (send old delete)) (send keybindings-menu get-items)) (new menu-item% (parent keybindings-menu) (label (string-constant keybindings-show-active)) (callback (λ (x y) (show-keybindings))) (help-string (string-constant keybindings-info)) (demand-callback keybindings-on-demand)) (new menu-item% (parent keybindings-menu) (label (string-constant keybindings-add-user-defined-keybindings)) (callback (λ (x y) (with-handlers ([exn? (λ (x) (printf "~a\n" (exn-message x)))]) (let ([filename (finder:get-file #f (string-constant keybindings-choose-user-defined-file) #f "" this)]) (when filename (add-keybindings-item/update-prefs filename))))))) (new menu-item% (parent keybindings-menu) (label (string-constant keybindings-add-user-defined-keybindings/planet)) (callback (λ (x y) (let ([planet-spec (get-text-from-user (string-constant drscheme) (string-constant keybindings-type-planet-spec) this last-keybindings-planet-attempt)]) (when planet-spec (set! last-keybindings-planet-attempt planet-spec) (cond [(planet-string-spec? planet-spec) => (λ (planet-sexp-spec) (add-keybindings-item/update-prefs planet-sexp-spec))] [else (message-box (string-constant drscheme) (format (string-constant keybindings-planet-malformed-spec) planet-spec) #:dialog-mixin frame:focus-table-mixin)])))))) (let ([ud (preferences:get 'drracket:user-defined-keybindings)]) (unless (null? ud) (new separator-menu-item% (parent keybindings-menu)) (for-each (λ (item) (new menu-item% (label (format (string-constant keybindings-menu-remove) (if (path? item) (path->string item) (format "~s" item)))) (parent keybindings-menu) (callback (λ (x y) (remove-keybindings-item item))))) ud))))))) (unless (current-eventspace-has-standard-menus?) (make-object separator-menu-item% menu))) (super-new))) (define (add-keybindings-item/update-prefs item) (when (add-keybindings-item item) (preferences:set 'drracket:user-defined-keybindings (cons item (preferences:get 'drracket:user-defined-keybindings))))) (define (planet-string-spec? p) (let ([sexp (with-handlers ([exn:fail:read? (λ (x) #f)]) (read (open-input-string p)))]) (and sexp (planet-spec? sexp) sexp))) (define (planet-spec? p) (match p [`(planet ,(? string?) (,(? string?) ,(? string?) ,(? number?))) #t] [`(planet ,(? string?) (,(? string?) ,(? string?) ,(? number?) ,(? number?))) #t] [else #f])) ;; add-keybindings-item : keybindings-item[path or planet spec] -> boolean ;; boolean indicates if the addition happened sucessfully (define (add-keybindings-item item) (with-handlers ([exn:fail? (λ (x) (message-box (string-constant drscheme) (format (string-constant keybindings-error-installing-file) (if (path? item) (path->string item) (format "~s" item)) (exn-message x)) #:dialog-mixin frame:focus-table-mixin) #f)]) (keymap:add-user-keybindings-file item) #t)) (define (remove-keybindings-item item) (keymap:remove-user-keybindings-file item) (preferences:set 'drracket:user-defined-keybindings (remove item (preferences:get 'drracket:user-defined-keybindings)))) ;; install-plt-file : (union #f dialog% frame%) -> void ;; asks the user for a .plt file, either from the web or from ;; a file on the disk and installs it. (define (install-plt-file parent) (define pref (preferences:get 'drracket:install-plt-dialog)) (define dialog (new dialog% [parent parent] [label (string-constant install-plt-file-dialog-title)] [alignment '(left center)])) (define tab-panel (new tab-panel% [parent dialog] [callback (λ (x y) (update-panels))] [choices (list (string-constant install-plt-web-tab) (string-constant install-plt-file-tab))])) (define outer-swapping-panel (new horizontal-panel% [parent tab-panel] [stretchable-height #f])) (define spacing-panel (new horizontal-panel% [parent outer-swapping-panel] [stretchable-width #f] [min-width 20])) (define swapping-panel (new panel:single% [parent outer-swapping-panel] [alignment '(left center)] [stretchable-width #t] [stretchable-height #f])) (define file-panel (new horizontal-panel% [parent swapping-panel] [stretchable-width #t] [stretchable-height #f])) (define url-panel (new horizontal-panel% [parent swapping-panel] [stretchable-height #f])) (define button-panel (new horizontal-panel% [parent dialog] [stretchable-height #f] [alignment '(right center)])) (define file-text-field (new text-field% [parent file-panel] [callback void] [min-width 300] [stretchable-width #t] [init-value (caddr pref)] [label (string-constant install-plt-filename)])) (define file-button (new button% [parent file-panel] [callback (λ (x y) (browse))] [label (string-constant browse...)])) (define url-text-field (new text-field% [parent url-panel] [min-width 300] [stretchable-width #t] [callback void] [init-value (cadr pref)] [label (string-constant install-plt-url)])) (define-values (ok-button cancel-button) (gui-utils:ok/cancel-buttons button-panel (λ (x y) (set! cancel? #f) (send dialog show #f)) (λ (x y) (send dialog show #f)))) ;; browse : -> void ;; gets the name of a file from the user and updates file-text-field (define (browse) (let ([filename (parameterize ([finder:default-extension "plt"] [finder:default-filters (if (eq? (system-type) 'macosx) (finder:default-filters) '(("PLT Files" "*.plt") ("Any" "*.*")))]) (finder:get-file #f "" #f "" dialog))]) (when filename (send file-text-field set-value (path->string filename))))) ;; from-web? : -> boolean ;; returns #t if the user has selected a web address (define (from-web?) (zero? (send tab-panel get-selection))) (define cancel? #t) (define (update-panels) (define w? (from-web?)) (define t (if w? url-text-field file-text-field)) (send swapping-panel active-child (if w? url-panel file-panel)) (send t focus) (send (send t get-editor) set-position 0 (string-length (send t get-value)))) ;; initialize (send tab-panel set-selection (if (car pref) 0 1)) (update-panels) (send dialog show #t) (preferences:set 'drracket:install-plt-dialog (list (from-web?) (send url-text-field get-value) (send file-text-field get-value))) (cond [cancel? (void)] [(from-web?) (install-plt-from-url (let* ([url (send url-text-field get-value)] ;; trim whitespaces [url (regexp-replace #rx"^ +" url "")] [url (regexp-replace #rx" +$" url "")]) (if (regexp-match? #rx"^(?:[^/:]*://|$)" url) url (string-append "http://" url))) parent)] [else (parameterize ([error-display-handler drracket:init:original-error-display-handler]) (run-installer (string->path (send file-text-field get-value))))])) ;; install-plt-from-url : string (union #f dialog%) -> void ;; downloads and installs a .plt file from the given url (define (install-plt-from-url s-url parent) (define-values (port size) (let-values ([(port header) (get-pure-port/headers (string->url s-url) #:redirections 5)]) (define size (let* ([content-header (extract-field "content-length" header)] [m (and content-header (regexp-match "[0-9]+" content-header))]) (and m (string->number (car m))))) (values port size))) (let* ([tmp-filename (make-temporary-file "tmp~a.plt")] [header (purify-port port)] [d (make-object dialog% (string-constant downloading) parent)] [message (make-object message% (string-constant downloading-file...) d)] [gauge (if size (make-object gauge% #f 100 d) #f)] [exn #f] ; Semaphores to avoid race conditions: [wait-to-start (make-semaphore 0)] [wait-to-break (make-semaphore 0)] ; Thread to perform the download: [t (thread (λ () (semaphore-wait wait-to-start) (with-handlers ([exn:fail? (λ (x) (set! exn x))] [exn:break? ; throw away break exceptions void]) (semaphore-post wait-to-break) (with-output-to-file tmp-filename (λ () (let loop ([total 0]) (when gauge (send gauge set-value (inexact->exact (floor (* 100 (/ total size)))))) (let ([s (read-string 1024 port)]) (unless (eof-object? s) (unless (eof-object? s) (display s) (loop (+ total (string-length s)))))))) #:mode 'binary #:exists 'truncate)) (send d show #f)))]) (send d center) (make-object button% (string-constant &stop) d (λ (b e) (semaphore-wait wait-to-break) (set! tmp-filename #f) (send d show #f) (break-thread t))) ; Let thread run only after the dialog is shown (queue-callback (λ () (semaphore-post wait-to-start))) (send d show #t) (when exn (raise exn)) (define unpack-err (open-output-string)) (cond [(with-handlers ((exn:fail? values)) (parameterize ([error-display-handler drracket:init:original-error-display-handler] [current-error-port unpack-err]) (fold-plt-archive tmp-filename void void void void void)) #f) => (λ (exn) (delete-file tmp-filename) (message-box (string-constant drscheme) (string-append (string-constant install-plt-error-header) "\n\n" (exn-message exn) "\n\n" (get-output-string unpack-err)) #:dialog-mixin frame:focus-table-mixin))] [else (parameterize ([error-display-handler drracket:init:original-error-display-handler]) (run-installer tmp-filename (λ () (delete-file tmp-filename))))]))) (define keybindings-frame% (class frame% (init-field bindings) (define/override (on-size w h) (preferences:set 'drracket:keybindings-window-size (cons w h)) (super on-size w h)) (super-new) (define/public (set-bindings _bindings) (set! bindings _bindings) (update-bindings)) (define bp (make-object horizontal-panel% this)) (define search-field (new text-field% [parent this] [label (string-constant mfs-search-string)] [callback (λ (a b) (update-bindings))])) (define b-name (new button% [label (string-constant keybindings-sort-by-name)] [parent bp] [callback (λ x (set! by-key? #f) (update-bindings))])) (define b-key (new button% [label (string-constant keybindings-sort-by-key)] [parent bp] [callback (λ x (set! by-key? #t) (update-bindings))])) (define lb (new list-box% [parent this] [label #f] [choices '()])) (define by-key? #f) (define bp2 (make-object horizontal-panel% this)) (define cancel (make-object button% (string-constant close) bp2 (λ x (send this show #f)))) (define/private (update-bindings) (let ([format-binding/name (λ (b) (format "~a (~a)" (cadr b) (car b)))] [format-binding/key (λ (b) (format "~a (~a)" (car b) (cadr b)))] [predicate/key (λ (a b) (string-ci<=? (format "~a" (car a)) (format "~a" (car b))))] [predicate/name (λ (a b) (string-ci<=? (cadr a) (cadr b)))]) (send lb set (if by-key? (map format-binding/key (sort (filter-search bindings) predicate/key)) (map format-binding/name (sort (filter-search bindings) predicate/name)))))) (define/private (filter-search bindings) (let ([str (send search-field get-value)]) (if (equal? str "") bindings (let ([reg (regexp (regexp-quote str #f))]) (filter (λ (x) (or (regexp-match reg (cadr x)) (regexp-match reg (format "~a" (car x))))) bindings))))) (send search-field focus) (send bp stretchable-height #f) (send bp set-alignment 'center 'center) (send bp2 stretchable-height #f) (send bp2 set-alignment 'right 'center) (update-bindings))) (define the-keybindings-frame #f) (define (show-keybindings-to-user bindings frame) (unless the-keybindings-frame (set! the-keybindings-frame (new keybindings-frame% [label (string-constant keybindings-frame-title)] [width (car (preferences:get 'drracket:keybindings-window-size))] [height (cdr (preferences:get 'drracket:keybindings-window-size))] [bindings bindings]))) (send the-keybindings-frame show #t)) (define -mixin (mixin (frame:editor<%> frame:text-info<%> drracket:frame:basics<%>) (drracket:frame:<%>) (inherit get-editor get-menu% get-menu-bar) (define show-menu #f) (define/public get-show-menu (λ () show-menu)) (define/public update-shown (λ () (void))) (define/public (add-show-menu-items show-menu) (void)) (define sort-menu-sort-keys (make-hasheq)) (define/public (set-show-menu-sort-key item val) (cond [sort-menu-sort-keys (for ([(k v) (in-hash sort-menu-sort-keys)]) (when (eq? k item) (error 'set-show-menu-sort-key "set menu item ~s twice, to ~s and ~s" (send item get-label) v val)) (when (= v val) (error 'set-show-menu-sort-key "two menu items have the same val: ~s and ~s" (send k get-label) (send item get-label)))) (hash-set! sort-menu-sort-keys item val)] [else (error 'set-show-menu-sort-key "the sort menu has already been created and its order has been set")])) (super-new) (set! show-menu (make-object (get-menu%) (string-constant view-menu-label) (get-menu-bar))) (add-show-menu-items show-menu) (sort-show-menu-items show-menu sort-menu-sort-keys) (set! sort-menu-sort-keys #f))) (define (sort-show-menu-items show-menu show-menu-sort-keys) (define items (send show-menu get-items)) (for ([itm (in-list items)]) (send itm delete)) (define (get-key item) (hash-ref show-menu-sort-keys item (λ () (define lab (cond [(is-a? item labelled-menu-item<%>) (send item get-label)] [else ""])) (cond [(regexp-match #rx"^Show (.*)$" lab) => (λ (x) (list-ref x 1))] [(regexp-match #rx"^Hide (.*)$" lab) => (λ (x) (list-ref x 1))] [else lab])))) (define (cmp item-x item-y) (define x (get-key item-x)) (define y (get-key item-y)) (cond [(and (number? x) (number? y)) (< x y)] [(and (string? x) (string? y)) (string<=? x y)] [(and (number? x) (string? y)) #t] [(and (string? x) (number? y)) #f])) (define sorted-items (sort items cmp)) (define (different-slots? item-key next-item-key) (or (not (= (quotient item-key 100) (quotient next-item-key 100))) (not (= (sgn item-key) (sgn next-item-key))))) (for ([item (in-list sorted-items)] [next-item (in-list (append (cdr sorted-items) (list #f)))]) (define item-key (get-key item)) (define next-item-key (and next-item (get-key next-item))) (define add-sep? (cond [(and (number? item-key) (number? next-item-key)) (different-slots? item-key next-item-key)] [(or (and (string? item-key) (string? next-item-key)) (not next-item-key)) #f] [else #t])) (send item restore) (when add-sep? (new separator-menu-item% [parent show-menu])))) (define (create-root-menubar) (define mb (new menu-bar% (parent 'root))) (define file-menu (new menu% (label (string-constant file-menu)) (parent mb))) (define help-menu (new menu% (label (string-constant help-menu)) (parent mb))) (new menu-item% (label (string-constant new-menu-item)) (parent file-menu) (shortcut #\n) (callback (λ (x y) (handler:edit-file #f) #t))) (new menu-item% (label (string-constant open-menu-item)) (parent file-menu) (shortcut #\o) (callback (λ (x y) (handler:open-file) #t))) (new menu% (label (string-constant open-recent-menu-item)) (parent file-menu) (demand-callback (λ (menu) (handler:install-recent-items menu)))) (new menu-item% [label (string-constant mfs-multi-file-search-menu-item)] [parent file-menu] [callback (λ (_1 _2) (drracket:multi-file-search:multi-file-search))]) (unless (current-eventspace-has-standard-menus?) (new separator-menu-item% (parent file-menu)) (new menu-item% (label (string-constant quit-menu-item-others)) (parent file-menu) (shortcut #\q) (callback (λ (x y) (when (exit:user-oks-exit) (exit:exit)) #t)))) (make-help-desk-menu-item help-menu) (drracket-help-menu:after-about help-menu #f)) (define (make-help-desk-menu-item help-menu) (define (docs-menu-item label) (new menu-item% [label label] [parent help-menu] [callback (λ (item evt) (help:help-desk) #t)])) (docs-menu-item (string-constant racket-documentation)) (new separator-menu-item% [parent help-menu]) (docs-menu-item (string-constant help-desk))) (define (drracket-help-menu:after-about menu dlg-parent) (drracket:app:add-important-urls-to-help-menu menu '()) (new menu-item% [label (string-constant bug-report-submit-menu-item)] [parent menu] [callback (λ (x y) (define saved (saved-bug-report-titles/ids)) (cond [(null? saved) (help-desk:report-bug #f #:frame-mixin basics-mixin)] [else (define which #f) (define (done the-one) (set! which the-one) (send dlg show #f)) (define dlg (new dialog% [label (string-constant drscheme)] [parent dlg-parent])) (define btn1 (new button% [parent dlg] [label (string-constant new-bug-report)] [callback (λ (x y) (done #f))])) (new message% [parent dlg] [label (string-constant saved-unsubmitted-bug-reports)]) (define btns (cons btn1 (for/list ([a-brinfo (in-list saved)]) (new button% [parent dlg] [label (brinfo-title a-brinfo)] [callback (λ (x y) (done (brinfo-id a-brinfo)))])))) (define width (apply max (map (λ (x) (let-values ([(w h) (send x get-client-size)]) w)) btns))) (for ([x (in-list btns)]) (send x min-width width)) (send btn1 focus) (send dlg show #t) (help-desk:report-bug which #:frame-mixin basics-mixin)]))]) (new menu% [label (string-constant saved-bug-reports-menu-item)] [parent menu] [demand-callback (let ([last-time (gensym)]) ;; a unique thing to guarantee the menu is built the first time (λ (saved-bug-reports-menu) (define this-time (saved-bug-report-titles/ids)) (unless (equal? last-time this-time) (set! last-time this-time) (for ([x (in-list (send saved-bug-reports-menu get-items))]) (send x delete)) (cond [(null? this-time) (send (new menu-item% [parent saved-bug-reports-menu] [label (string-constant no-saved-bug-reports)] [callback void]) enable #f)] [else (for ([a-brinfo (in-list this-time)]) (new menu-item% [parent saved-bug-reports-menu] [label (brinfo-title a-brinfo)] [callback (λ (x y) (help-desk:report-bug (brinfo-id a-brinfo) #:frame-mixin basics-mixin))])) (new separator-menu-item% [parent saved-bug-reports-menu]) (new menu-item% [parent saved-bug-reports-menu] [label (string-constant disacard-all-saved-bug-reports)] [callback (λ (x y) (discard-all-saved-bug-reports))])]))))]) (drracket:app:add-language-items-to-help-menu menu)))