(module wxtextfield mzscheme
  (require (lib "class.ss")
	   (lib "class100.ss")
	   (prefix wx: "kernel.ss")
	   "lock.ss"
	   "const.ss"
	   "check.ss"
	   "helper.ss"
	   "gdi.ss"
	   "wx.ss"
	   "wxwindow.ss"
	   "wxitem.ss"
	   "wxcanvas.ss"
	   "wxpanel.ss"
	   "editor.ss"
	   "mrpopup.ss")

  (provide (protect wx-text-field%))

  (define text-field-text% 
    (class100 text% (cb ret-cb control set-cb-mgrs!)
      (rename [super-on-char on-char])
      (inherit get-text last-position)
      (private-field
       [return-cb ret-cb])
      (private-field
       [block-callback 1]
       [callback
	(lambda (type)
	  (when (zero? block-callback)
	    (let ([e (make-object wx:control-event% type)])
	      (as-exit (lambda ()
			 (cb control e))))))])
      (override
	[on-char
	 (entry-point
	  (lambda (e)
	    (let ([c (send e get-key-code)])
	      (unless (and (or (eq? c #\return) (eq? c #\newline))
			   return-cb
			   (return-cb (lambda () (callback 'text-field-enter) #t)))
		(as-exit (lambda () (super-on-char e)))))))])
      (augment
       [after-insert
	(lambda args
	  (as-entry
	   (lambda ()
	     (callback 'text-field))))]
       [after-delete
	(lambda args
	  (as-entry
	   (lambda ()
	     (callback 'text-field))))])
      (sequence
	(set-cb-mgrs!
	 (lambda (thunk)
	   (dynamic-wind
	       (lambda () (set! block-callback (add1 block-callback)))
	       thunk
	       (lambda () (set! block-callback (sub1 block-callback)))))
	 (lambda () 
	   (set! block-callback 0)))
	(super-init))))
  
  (define wx-text-editor-canvas% 
    (class100* wx-editor-canvas% (wx-text-editor-canvas<%>) (mred proxy control parent style)
      (sequence
	(super-init mred proxy parent -1 -1 100 30 #f style 100 #f))))

  (define wx-text-field%
    (class100 wx-horizontal-panel% (mred proxy parent fun label value style _font)
      ;; Make text field first because we'll have to exit
      ;;  for keymap initializer
      (private-field
       [func fun]
       [font (or _font normal-control-font)]
       [without-callback #f]
       [callback-ready #f]
       [e (make-object text-field-text%
		       func
		       (lambda (do-cb)
			 (if multi?
			     #f
			     (do-cb)))
		       this
		       (lambda (wc cr)
			 (set! without-callback wc)
			 (set! callback-ready cr)))])
      (sequence
	(as-exit
	 (lambda ()
	   ((current-text-keymap-initializer) (send e get-keymap)))))
      (inherit alignment stretchable-in-y area-parent
	       get-min-size set-min-width set-min-height)
      (rename [super-place-children place-children])
      (public
	[command (lambda (e)  ; No entry/exit needed
		   (check-instance '(method text-field% command) wx:control-event% 'control-event% #f e)
		   (func this e)
		   (void))]

	[get-editor (lambda () e)]
	
	[get-value (lambda () (send e get-text))] ; note: not as-entry when called
	[set-value (lambda (v) (without-callback
				(lambda () (send e insert v 0 (send e last-position)))))]

	[set-label (lambda (str) (when l (send l set-label str)))]
	[get-canvas-width (lambda ()
			    (let ([tw (box 0)])
			      (send c get-size tw (box 0))
			      (unbox tw)))])
      (override
	;; These might be called before we are fully initialized

	[set-cursor (lambda (c) (send e set-cursor c #t))]
	[set-focus (lambda () (when (object? c) (send c set-focus)))]
	
	[place-children
	 (lambda (children-info width height)
	   (if (null? children-info)
	       null
	       (let ([r (super-place-children children-info width height)])
		 (if horiz?
		     ;; Line up label right with text:
		     (cons (list* (caar r) (+ (cadar r) dy) (cddar r))
			   (cdr r))
		     r))))])
      (sequence
	(super-init #f proxy parent (if (memq 'deleted style) '(deleted) null))
	(unless (memq 'deleted style)
	  (send (area-parent) add-child this)))
      (private-field
       [multi? (memq 'multiple style)]
       [horiz? (cond
		[(memq 'vertical-label style) #f]
		[(memq 'horizontal-label style) #t]
		[else (eq? (send (send parent get-window) get-label-position) 'horizontal)])]
       [dy 0]
       [p (if horiz?
	      this
	      (let ([p (make-object wx-vertical-pane% #f proxy this null)])
		(send (send p area-parent) add-child p)
		p))])
      (sequence
	(alignment 'left 'top)
	(unless horiz? (send p alignment 'left 'top))
	(unless multi? (stretchable-in-y #f))
	;; For Windows:
	(wx:set-combo-box-font font))
      (private-field
       [l (and label
	       (make-object wx-message% #f proxy p label -1 -1 null font))]
       [c (make-object wx-text-editor-canvas% #f proxy this p
		       (append
			'(control-border)
			(if (memq 'combo style)
			    '(combo)
			    null)
			(if multi?
			    (if (memq 'hscroll style)
				null
				'(hide-hscroll))
			    '(hide-vscroll hide-hscroll))))])
      (sequence
	(when l
	  (send l x-margin 0))
	(send c set-x-margin 2)
	(send c set-y-margin 2)
	(send e set-line-spacing 0)
	(send e set-paste-text-only #t)
	(send e auto-wrap (and multi? (not (memq 'hscroll style))))
	(let ([f font]
	      [s (send (send e get-style-list) find-named-style "Standard")])
	  (send s set-delta (let ([d (font->delta f)])
			      (if (memq 'password style)
				  (begin
				    (send d set-face #f)
				    (send d set-family 'modern)
				    (send d set-delta-foreground "darkgray")
				    (send d set-delta-background "darkgray"))
				  d))))
	(send c set-editor e)
	(send c set-line-count (if multi? 3 1))
	(unless multi? (send c set-single-line))

	(when (and l horiz?)
	  ;; Minimize vertical space around the label:
	  (send l y-margin 0)
	  ;; Find amount to drop label down to line up the baselines:
	  (let ([wbox (box 0)]
		[hbox (box 0)]
		[ybox (box 0)]
		[abox (box 0)])
	    ;; To bottom of first line
	    (send (send e get-admin) get-dc #f ybox)
	    (set! dy (+ (abs (unbox ybox)) (send e line-location 0 #f)))
	    
	    ;; Add diff for client size
	    (send c get-client-size wbox hbox)
	    (let ([d (- (send c get-height) (unbox hbox))])
	      (set! dy (+ dy (quotient d 2))))
	    
	    ;; Subtract descent of canvas-drawn text
	    (let ([font (send (send (send e get-style-list) find-named-style "Standard") get-font)])
	      (send c get-text-extent "hi" wbox hbox ybox #f font)
	      (set! dy (- dy (unbox ybox))))
	    
	    ;; Subtract ascent of label
	    (send l get-text-extent "hi" wbox hbox ybox abox)
	    (set! dy (- dy (- (unbox hbox) (unbox ybox))))
	    
	    ;; Subtract space above label
	    (set! dy (- dy (quotient (- (send l get-height) (unbox hbox)) 2)))

	    ;; Exact
	    (set! dy (inexact->exact dy))))
	
	(when value
	  (set-value value)
	  (unless (string=? value "")
	    (let* ([ew (box 0)]
		   [cw (box 0)]
		   [tw (box 0)])
	      (send e get-extent ew #f)
	      (send (send e get-admin) get-view #f #f cw #f)
	      (send c get-size tw (box 0))
	      (let ([new-min-width (+ (unbox ew) (- (unbox tw) (unbox cw)))])
		(send c set-min-width (inexact->exact new-min-width))))))
	(let ([min-size (get-min-size)])
	  (set-min-width (car min-size))
	  (set-min-height (cadr min-size)))
	(callback-ready)))))
