;;;-*- Mode:LISP; Package: Palette; Base: 8-*- ;;; This program defines the PALETTE flavor. The general idea for ;;; this program is in AI: DLW; PALETT >. ;;; Windows herein only work on color screens. Doesn't it suck. ;;; How to use: ;;; (palette) creates a palette of six color that you can edit. ;;; (edit-colors) just creates a color editor with no palette. (DEFMACRO WITH-MOUSE-SHEET ((SHEET) &BODY BODY) (LET ((OLD-SHEET (GENSYM))) `(LET ((,OLD-SHEET TV:MOUSE-SHEET)) (UNWIND-PROTECT (PROGN (TV:MOUSE-SET-SHEET ,SHEET) . ,BODY) (TV:MOUSE-SET-SHEET ,OLD-SHEET))))) (DEFMACRO WITH-MOUSE-CAPTURED ((SHEET) &BODY BODY) (LET ((OLD-SHEET (GENSYM))) `(LET ((,OLD-SHEET TV:MOUSE-SHEET)) (TV:WITH-MOUSE-GRABBED (UNWIND-PROTECT (PROGN (TV:MOUSE-SET-SHEET ,SHEET) (FUNCALL TV:MOUSE-BLINKER ':SET-VISIBILITY NIL) . ,BODY) (FUNCALL TV:MOUSE-BLINKER ':SET-VISIBILITY T) (TV:MOUSE-SET-SHEET ,OLD-SHEET)))))) ;;; Primitive to draw rectangles in color onto SELF. (DEFMACRO DRAW-COLOR-RECTANGLE (COLOR X1 Y1 X2 Y2) `(COLOR:COLOR-BITBLT (+ ,X1 TV:LEFT-MARGIN-SIZE) (+ ,Y1 TV:TOP-MARGIN-SIZE) (+ ,X2 TV:LEFT-MARGIN-SIZE) (+ ,Y2 TV:TOP-MARGIN-SIZE) ,COLOR TV:ALU-SETA SELF)) ;(DEFFLAVOR TV:WINDOW-PANE () (TV:PANE-MIXIN TV:WINDOW)) (DEFCONST  3.14159s0) ;;; This is a try at a standard mixin for all color windows to have. ;;; COLOR-MAP's first dimension is by location in the color map ;;; Its second dimension has red, green, and blue elements in 0, 1, and 2, ;;; and 3 is zero if this entry is "not used" and non-zero if it is used. ;;; A not-used entry does not get written into the hardware color map. ;;; SCREEN is the screen of this window. ;;; Note: the criterion used for calling WRITE-COLOR-MAP herein is essentially ;;; bogus. First of all, just because you're exposed does not mean you are ;;; visible. Secondly, the window that controls the color map really ought ;;; to have some more distinguishing characteristic than simply being the ;;; latest one exposed; "selected" is not quite right, but something along ;;; those lines is probably called for. (DEFFLAVOR COLOR-MIXIN (COLOR-MAP SCREEN) () (:INCLUDED-FLAVORS TV:SHEET)) (DEFMETHOD (COLOR-MIXIN :BEFORE :INIT) (&REST IGNORE) (SETQ COLOR-MAP (MAKE-ARRAY '(400 4) ':TYPE 'ART-8B)) (SETQ SCREEN (TV:SHEET-GET-SCREEN SELF))) ;;; Copy the "used" elements of our map into the hardware. (DEFUN WRITE-MAP (COLOR-MAP SCREEN) (LET ((HARDWARE-COLOR-MAP (GET (LOCF (TV:SCREEN-PROPERTY-LIST SCREEN)) ':HARDWARE-COLOR-MAP))) (DOTIMES (LOCATION 400) (COND ((ZEROP (AREF COLOR-MAP LOCATION 3)) (ASET (AREF HARDWARE-COLOR-MAP LOCATION 0) COLOR-MAP LOCATION 0) (ASET (AREF HARDWARE-COLOR-MAP LOCATION 1) COLOR-MAP LOCATION 1) (ASET (AREF HARDWARE-COLOR-MAP LOCATION 2) COLOR-MAP LOCATION 2))))) (COLOR:BLT-COLOR-MAP COLOR-MAP SCREEN)) (DEFMETHOD (COLOR-MIXIN :BEFORE :EXPOSE) (&REST IGNORE) (WRITE-MAP COLOR-MAP SCREEN)) ;;; Write given R, G, B values into given location of the color map. (DEFMETHOD (COLOR-MIXIN :SET-COLOR-MAP) (LOCATION RED GREEN BLUE) (ASET (LOGAND 377 (FIX RED)) COLOR-MAP LOCATION 0) (ASET (LOGAND 377 (FIX GREEN)) COLOR-MAP LOCATION 1) (ASET (LOGAND 377 (FIX BLUE)) COLOR-MAP LOCATION 2) (ASET 1 COLOR-MAP LOCATION 3) ; This location is used. (IF TV:EXPOSED-P (WRITE-MAP COLOR-MAP SCREEN))) ;;; Make a location unused. (DEFMETHOD (COLOR-MIXIN :UNSET-COLOR-MAP) (LOCATION) (ASET 0 COLOR-MAP LOCATION 3)) ;;; Read given location of the color map. (DEFMETHOD (COLOR-MIXIN :READ-COLOR-MAP) (LOCATION) (VALUES (AREF COLOR-MAP LOCATION 0) (AREF COLOR-MAP LOCATION 1) (AREF COLOR-MAP LOCATION 2) (AREF COLOR-MAP LOCATION 3))) ;;; Write given R, G, B values into all elements of the color map. (DEFMETHOD (COLOR-MIXIN :FILL-COLOR-MAP) (RED GREEN BLUE) (LET ((R (LOGAND 377 (FIX RED))) (G (LOGAND 377 (FIX GREEN))) (B (LOGAND 377 (FIX BLUE)))) (DOTIMES (I 400) (ASET R COLOR-MAP I 0) (ASET G COLOR-MAP I 1) (ASET B COLOR-MAP I 2) (ASET 1 COLOR-MAP I 3))) (IF TV:EXPOSED-P (WRITE-MAP COLOR-MAP SCREEN))) ;;; Set my color map from someone else's. (DEFMETHOD (COLOR-MIXIN :SET-WHOLE-COLOR-MAP) (MAP) (DOTIMES (LOCATION 400) (DOTIMES (I 4) (ASET COLOR-MAP (AREF MAP LOCATION I) LOCATION I))) (IF TV:EXPOSED-P (WRITE-MAP COLOR-MAP SCREEN))) (DEFMETHOD (COLOR-MIXIN :COLORFUL-MAP) () (DO ((I 1 (1+ I)) (OFFSET 0 (COND ((= I 177) 1) ((= I 376) 2) (T OFFSET)))) (( I 377)) (FUNCALL-SELF ':SET-COLOR-MAP I (* (LDB 0001 (+ I OFFSET)) 255.) (* (LDB 0101 (+ I OFFSET)) 255.) (* (LDB 0201 (+ I OFFSET)) 255.))) (FUNCALL-SELF ':SET-COLOR-MAP 0 0 0 0) (FUNCALL-SELF ':SET-COLOR-MAP 377 255. 255. 255.) (IF TV:EXPOSED-P (WRITE-MAP COLOR-MAP SCREEN))) ;;; This flavor displays an array of rectangles showing colors. It figures ;;; out the number of rows and columns when its size is set or changed. ;;; COLOR-ALIST associates character string names with slots in the color ;;; map, and the Nth rectangle will correspond to the color-map slot cited ;;; by the Nth element in COLOR-ALIST. BORDER-FRACTION says how much room ;;; to leave between the rectangles. (DEFFLAVOR COLOR-CHOICE-WINDOW (COLOR-ALIST BORDER-FRACTION N-ROWS N-COLUMNS PIXELS-PER-ROW PIXELS-PER-COLUMN) (TV:LIST-MOUSE-BUTTONS-MIXIN COLOR-MIXIN TV:WINDOW) (:GETTABLE-INSTANCE-VARIABLES COLOR-ALIST BORDER-FRACTION) (:INITABLE-INSTANCE-VARIABLES COLOR-ALIST BORDER-FRACTION) (:DEFAULT-INIT-PLIST :BORDER-FRACTION .1s0)) ;;; Color zero is used for the space between rectangles. (DEFCONST *COLOR-CHOICE-BORDER-COLOR* 0) (DEFMETHOD (COLOR-CHOICE-WINDOW :WHO-LINE-DOCUMENTATION-STRING) () "Click left to edit this color. Click middle to quit.") ;;; Computation of the number of rows and columns, and their sizes. (DEFMETHOD (COLOR-CHOICE-WINDOW :AFTER :INIT) (&OPTIONAL IGNORE) (COLOR-CHOICE-RECOMPUTE)) (DEFMETHOD (COLOR-CHOICE-WINDOW :AFTER :CHANGE-OF-SIZE-OR-MARGINS) (&REST IGNORE) (COLOR-CHOICE-RECOMPUTE)) (DEFUN COLOR-CHOICE-RECOMPUTE () (DECLARE-FLAVOR-INSTANCE-VARIABLES (COLOR-CHOICE-WINDOW) (MULTIPLE-VALUE-BIND (INSIDE-WIDTH INSIDE-HEIGHT) (FUNCALL-SELF ':INSIDE-SIZE) (LET ((N-COLORS (LENGTH COLOR-ALIST))) ;; Figure out the best value for N-COLUMNS by trying all possible ;; values and seeing which is closest to a square. (DO ((NC 1 (1+ NC)) (BEST-RATIO 1.0S5)) ((> NC N-COLORS) (SETQ PIXELS-PER-COLUMN (// INSIDE-WIDTH N-COLUMNS) PIXELS-PER-ROW (// INSIDE-HEIGHT N-ROWS))) ;; Given that NC is the number of columns, how square would the boxes be? (LET* ((NR (// (+ N-COLORS NC -1) NC)) (RATIO (// (SMALL-FLOAT (// INSIDE-WIDTH NC)) (SMALL-FLOAT (// INSIDE-HEIGHT NR)))) (RATIO* (IF (> RATIO 1.0s0) RATIO (// 1.0s0 RATIO)))) (IF (< RATIO* BEST-RATIO) (SETQ BEST-RATIO RATIO* N-COLUMNS NC N-ROWS NR)))))))) ;;; Redisplay. (DEFMETHOD (COLOR-CHOICE-WINDOW :AFTER :REFRESH) (&OPTIONAL TYPE) (AND (OR (NOT TV:RESTORED-BITS-P) (EQ TYPE ':SIZE-CHANGED)) (TV:SHEET-FORCE-ACCESS (SELF) ;; Redraw the window contents. (MULTIPLE-VALUE-BIND (INSIDE-WIDTH INSIDE-HEIGHT) (FUNCALL-SELF ':INSIDE-SIZE) (LET* ((COLUMN-BORDER (FIX (* PIXELS-PER-COLUMN BORDER-FRACTION))) (ROW-BORDER (FIX (* PIXELS-PER-ROW BORDER-FRACTION))) (ALIST COLOR-ALIST)) (DO ((ROW 0 (1+ ROW)) (ROW-START-PIXEL 0 ROW-END-PIXEL) (ROW-END-PIXEL PIXELS-PER-ROW (+ ROW-END-PIXEL PIXELS-PER-ROW))) (( ROW N-ROWS)) (IF (= ROW (1- N-ROWS)) ;; Last time, fill up the window. (SETQ ROW-END-PIXEL INSIDE-HEIGHT)) (DO ((COLUMN 0 (1+ COLUMN)) (COLUMN-START-PIXEL 0 COLUMN-END-PIXEL) (COLUMN-END-PIXEL PIXELS-PER-COLUMN (+ COLUMN-END-PIXEL PIXELS-PER-COLUMN))) (( COLUMN N-COLUMNS)) (IF (= COLUMN (1- N-COLUMNS)) ;; Last time, fill up the window. (SETQ COLUMN-END-PIXEL INSIDE-WIDTH)) (IF (NULL ALIST) ;; No more elements, just filling up unused spaces. (DRAW-COLOR-RECTANGLE *COLOR-CHOICE-BORDER-COLOR* (- COLUMN-END-PIXEL COLUMN-START-PIXEL) (- ROW-END-PIXEL ROW-START-PIXEL) COLUMN-START-PIXEL ROW-START-PIXEL) ;; Real elements. (LET* ((COLOR-ELEMENT (CAR ALIST)) (INSIDE-ROW-START-PIXEL (+ ROW-START-PIXEL ROW-BORDER)) (INSIDE-ROW-END-PIXEL (- ROW-END-PIXEL ROW-BORDER)) (INSIDE-COLUMN-START-PIXEL (+ COLUMN-START-PIXEL COLUMN-BORDER)) (INSIDE-COLUMN-END-PIXEL (- COLUMN-END-PIXEL COLUMN-BORDER))) ;; Put in the border, in four pieces. (DRAW-COLOR-RECTANGLE *COLOR-CHOICE-BORDER-COLOR* ; left edge COLUMN-START-PIXEL ROW-START-PIXEL INSIDE-COLUMN-START-PIXEL ROW-END-PIXEL) (DRAW-COLOR-RECTANGLE *COLOR-CHOICE-BORDER-COLOR* ; right edge INSIDE-COLUMN-END-PIXEL ROW-START-PIXEL COLUMN-END-PIXEL ROW-END-PIXEL) (DRAW-COLOR-RECTANGLE *COLOR-CHOICE-BORDER-COLOR* ; top edge INSIDE-COLUMN-START-PIXEL ROW-START-PIXEL INSIDE-COLUMN-END-PIXEL INSIDE-ROW-START-PIXEL) (DRAW-COLOR-RECTANGLE *COLOR-CHOICE-BORDER-COLOR* ; bottom edge INSIDE-COLUMN-START-PIXEL INSIDE-ROW-END-PIXEL INSIDE-COLUMN-END-PIXEL ROW-END-PIXEL) ;; Put in the color itself. (DRAW-COLOR-RECTANGLE (CDR COLOR-ELEMENT) INSIDE-COLUMN-START-PIXEL INSIDE-ROW-START-PIXEL INSIDE-COLUMN-END-PIXEL INSIDE-ROW-END-PIXEL) (POP ALIST)))))))))) ;;; In tidy mode, don't show user the color editor until it is all set up. (DEFVAR *TIDY-MODE* T) ;;; This is the editor command loop. Do single-click-left over the palette ;;; element you want to edit. Single-click-middle or any kbd character exits. (DEFMETHOD (COLOR-CHOICE-WINDOW :EDIT) () (DO () (()) (LET ((BLIP (FUNCALL-SELF ':ANY-TYI))) (COND ((LISTP BLIP) (LET ((BUTTON (SECOND BLIP)) (X (FOURTH BLIP)) (Y (FIFTH BLIP))) (COND ((= BUTTON #\MOUSE-L-1) ;; Single-click left on a palette edits this color. (LET* ((ROW (MIN N-ROWS (MAX 0 (// Y PIXELS-PER-ROW)))) (COLUMN (MIN N-COLUMNS (MAX 0 (// X PIXELS-PER-COLUMN)))) (COLOR-ELEMENT (NTH (+ COLUMN (* ROW N-COLUMNS)) COLOR-ALIST))) (COND ((NULL COLOR-ELEMENT) (FUNCALL-SELF ':BEEP)) (T (LET ((COLOR-NAME (CAR COLOR-ELEMENT)) (SLOT-NUMBER (CDR COLOR-ELEMENT))) (USING-RESOURCE (FRAME COLOR-EDITORS) (UNWIND-PROTECT (PROGN (IF (NOT *TIDY-MODE*) (FUNCALL FRAME ':EXPOSE)) (MULTIPLE-VALUE-BIND (OLD-RED OLD-GREEN OLD-BLUE) (FUNCALL-SELF ':READ-COLOR-MAP SLOT-NUMBER) (MULTIPLE-VALUE-BIND (NEW-RED NEW-GREEN NEW-BLUE) (FUNCALL FRAME ':EDIT (STRING-APPEND "Editing " COLOR-NAME) OLD-RED OLD-GREEN OLD-BLUE) (FUNCALL FRAME ':DEACTIVATE) (IF (NOT (NULL NEW-RED)) (FUNCALL-SELF ':SET-COLOR-MAP SLOT-NUMBER NEW-RED NEW-GREEN NEW-BLUE) (FUNCALL-SELF ':BEEP))))) (FUNCALL FRAME ':DEACTIVATE)))))))) ((= BUTTON #\MOUSE-M-1) ;; Single-click middle means quit. (RETURN)) (T (FUNCALL-SELF ':BEEP))))) (T ; not a blip (RETURN)))))) (DEFVAR W) (DEFUN PALETTE () (WITH-MOUSE-SHEET (COLOR:COLOR-SCREEN) (LET ((OLD-SEL TV:SELECTED-WINDOW)) (UNWIND-PROTECT (PROGN (SETQ W (MAKE-INSTANCE 'COLOR-CHOICE-WINDOW ':COLOR-ALIST '(("a" . 1) ("b" . 2) ("c" . 3) ("d" . 4) ("e" . 5) ("f" . 6)) ':SUPERIOR COLOR:COLOR-SCREEN ':BOTTOM 100 ':BLINKER-P NIL ':LABEL NIL)) (FUNCALL W ':COLORFUL-MAP) (FUNCALL W ':EXPOSE) (FUNCALL W ':EDIT)) (FUNCALL W ':DEACTIVATE) (FUNCALL OLD-SEL ':SELECT)))) W) ;;; Init-options for slider windows: ;;; :FORMAT-CONTROL is format control string for printing out the current value. ;;; :LABEL-STRING is the name to appear before the number. ;;; :MIN is the minimum value of the variable. ;;; :MAX is the maximum value of the variable. ;;; :VALUE is the current value. ;;; Meanings of other instance variables: ;;; LINE-Y is the Y-coord of the line; LINE-DY is the half-length of the ;;; little vertical lines at the end. LINE-X1 snd LINE-X2 are the X-coords ;;; of the ends of the line (and of the little vertical lines.) ;;; KNOB-HEIGHT and KNOB-WIDTH are the size of the knob in pixels. ;;; KNOB-X is the X-position of the knob, or NIL if no knob is being displayed. ;;; KNOB-BITS is an image of the knob, to be BLTed onto the window. ;;; KNOB-SAVE-BITS is another array of the same size used to save what is ;;; underneath the knob. ;;; LABEL-END-X is where the label ends and the number begins. (DEFFLAVOR SLIDER-WINDOW (FORMAT-CONTROL LABEL-STRING MIN MAX VALUE LINE-Y LINE-DY LINE-X1 LINE-X2 KNOB-X KNOB-HEIGHT KNOB-WIDTH KNOB-BITS KNOB-SAVE-BITS LABEL-END-X) (TV:LINE-TRUNCATING-MIXIN TV:WINDOW) (:DEFAULT-INIT-PLIST :FORMAT-CONTROL "~4,2F" :BLINKER-P NIL :LABEL NIL) (:INITABLE-INSTANCE-VARIABLES FORMAT-CONTROL LABEL-STRING MIN MAX VALUE) (:GETTABLE-INSTANCE-VARIABLES FORMAT-CONTROL LABEL-STRING MIN MAX VALUE)) (DEFMETHOD (SLIDER-WINDOW :AFTER :INIT) (&REST IGNORE) (CHECK-ARG FORMAT-CONTROL STRINGP "a string") (CHECK-ARG MIN NUMBERP "a number") (CHECK-ARG MAX NUMBERP "a number") (SETQ VALUE (MIN MAX (MAX MIN VALUE))) (SETQ KNOB-X NIL) (SLIDER-RECOMPUTE)) (DEFMETHOD (SLIDER-WINDOW :AFTER :CHANGE-OF-SIZE-OR-MARGINS) (&REST IGNORE) (SLIDER-RECOMPUTE)) (DEFCONST *SLIDER-SPACE-1* 0) ; Between top of window and label-string. (DEFCONST *SLIDER-SPACE-2* 0) ; Between bottom of label-string and center. (DEFCONST *SLIDER-SIDE-SPACE* 0.05s0) ; Between end of line and left (right) edge. (DEFCONST *SLIDER-LINE-COLOR* 377) ; Color for the line. (DEFCONST *SLIDER-KNOB-HEIGHT* 2.0s0) ; Function of LINE-DY. (DEFCONST *SLIDER-KNOB-WIDTH* 0.05s0) ; Function of (- LINE-x2 LINE-x1). (DEFUN SLIDER-RECOMPUTE () (DECLARE-FLAVOR-INSTANCE-VARIABLES (SLIDER-WINDOW) (MULTIPLE-VALUE-BIND (INSIDE-WIDTH INSIDE-HEIGHT) (FUNCALL-SELF ':INSIDE-SIZE) (LET ((TOP-PART-HEIGHT (+ *SLIDER-SPACE-1* (FONT-RASTER-HEIGHT TV:CURRENT-FONT) *SLIDER-SPACE-2*))) (SETQ LINE-Y (// (+ INSIDE-HEIGHT TOP-PART-HEIGHT) 2) LINE-DY (// (- INSIDE-HEIGHT TOP-PART-HEIGHT) 4) LINE-X1 (FIX (* *SLIDER-SIDE-SPACE* INSIDE-WIDTH)) LINE-X2 (- INSIDE-WIDTH LINE-X1) KNOB-HEIGHT (1+ (* 2 (// (1+ (FIX (* *SLIDER-KNOB-HEIGHT* LINE-DY))) 2))) KNOB-WIDTH (1+ (* 2 (// (1+ (FIX (* *SLIDER-KNOB-WIDTH* (- LINE-X2 LINE-X1)))) 2)))) (SETQ KNOB-BITS (TV:MAKE-SHEET-BIT-ARRAY SELF KNOB-WIDTH KNOB-HEIGHT) KNOB-SAVE-BITS (TV:MAKE-SHEET-BIT-ARRAY SELF KNOB-WIDTH KNOB-HEIGHT)) ;; Draw the image of the knob. (DOTIMES (I KNOB-HEIGHT) (ASET *SLIDER-LINE-COLOR* KNOB-BITS 0 I) (ASET *SLIDER-LINE-COLOR* KNOB-BITS (1- KNOB-WIDTH) I)) (DOTIMES (I KNOB-WIDTH) (ASET *SLIDER-LINE-COLOR* KNOB-BITS I 0) (ASET *SLIDER-LINE-COLOR* KNOB-BITS I (1- KNOB-HEIGHT))))))) (DEFMETHOD (SLIDER-WINDOW :AFTER :REFRESH) (&OPTIONAL TYPE) (COND ((OR (NOT TV:RESTORED-BITS-P) (EQ TYPE ':SIZE-CHANGED)) ;; Write the text at the top. (FUNCALL-SELF ':SET-CURSORPOS 0 *SLIDER-SPACE-1*) (FUNCALL-SELF ':CLEAR-EOL) (FUNCALL-SELF ':STRING-OUT LABEL-STRING) (FUNCALL-SELF ':STRING-OUT ": ") (SETQ LABEL-END-X (FUNCALL-SELF ':READ-CURSORPOS)) (FORMAT SELF FORMAT-CONTROL (FLOAT VALUE)) ;kludge!!! ;; Draw the line. (FUNCALL-SELF ':DRAW-LINE LINE-X1 LINE-Y LINE-X2 LINE-Y) (FUNCALL-SELF ':DRAW-LINE LINE-X1 (- LINE-Y LINE-DY) LINE-X1 (+ LINE-Y LINE-DY)) (FUNCALL-SELF ':DRAW-LINE LINE-X2 (- LINE-Y LINE-DY) LINE-X2 (+ LINE-Y LINE-DY)) ;; Draw the knob. (SETQ KNOB-X NIL) (SLIDER-DRAW-KNOB) ))) (DEFUN SLIDER-DRAW-KNOB () (DECLARE-FLAVOR-INSTANCE-VARIABLES (SLIDER-WINDOW) (IF (NOT (NULL KNOB-X)) (SLIDER-UNDRAW-KNOB)) (SETQ KNOB-X (+ LINE-X1 (FIX (* (- LINE-X2 LINE-X1) (// (- (SMALL-FLOAT VALUE) MIN) (- MAX MIN)))))) (FUNCALL-SELF ':BITBLT-FROM-SHEET TV:ALU-SETA KNOB-WIDTH KNOB-HEIGHT (- KNOB-X (1+ (// KNOB-WIDTH 2))) (- LINE-Y LINE-DY) KNOB-SAVE-BITS 0 0) (FUNCALL-SELF ':BITBLT TV:ALU-SETA KNOB-WIDTH KNOB-HEIGHT KNOB-BITS 0 0 (- KNOB-X (1+ (// KNOB-WIDTH 2))) (- LINE-Y LINE-DY)))) (DEFUN SLIDER-UNDRAW-KNOB () (DECLARE-FLAVOR-INSTANCE-VARIABLES (SLIDER-WINDOW) (COND ((NOT (NULL KNOB-X)) (FUNCALL-SELF ':BITBLT TV:ALU-SETA KNOB-WIDTH KNOB-HEIGHT KNOB-SAVE-BITS 0 0 (- KNOB-X (1+ (// KNOB-WIDTH 2))) (- LINE-Y LINE-DY)))) (SETQ KNOB-X NIL))) (DEFMETHOD (SLIDER-WINDOW :SET-VALUE) (NEW-VALUE) (SETQ VALUE (MIN MAX (MAX MIN NEW-VALUE))) (SLIDER-UNDRAW-KNOB) (FUNCALL-SELF ':SET-CURSORPOS LABEL-END-X *SLIDER-SPACE-1*) (FUNCALL-SELF ':CLEAR-EOL) (FORMAT SELF FORMAT-CONTROL (FLOAT VALUE)) ;kludge!!! (SLIDER-DRAW-KNOB)) (DEFMETHOD (SLIDER-WINDOW :MOUSE-BUTTONS) (IGNORE IGNORE IGNORE) (FUNCALL-SELF ':FORCE-KBD-INPUT (LIST 'SLIDER-BUTTONS SELF))) ;;; Track the mouse. This runs in the calling process. ;;; When mouse-tracking sets the value of the window, it uses a :MOUSE-SET-VALUE ;;; message. This is what you should add daemons to if you want to get ;;; extra effects during mouse tracking (rather than putting daemons onto :SET-VALUE). ;;; The basic method just turns into a :SET-VALUE. (DEFMETHOD (SLIDER-WINDOW :TRACK-MOUSE) () (WITH-MOUSE-CAPTURED (SELF) (DO () (()) (IF (ZEROP TV:MOUSE-LAST-BUTTONS) (RETURN)) (FUNCALL-SELF ':MOUSE-SET-VALUE (+ (* (// (SMALL-FLOAT (- TV:MOUSE-X LINE-X1)) (- LINE-X2 LINE-X1)) (- MAX MIN)) MIN)) (TV:MOUSE-WAIT)))) (DEFMETHOD (SLIDER-WINDOW :MOUSE-SET-VALUE) (NEW-VALUE) (FUNCALL-SELF ':SET-VALUE NEW-VALUE)) ;;; Color editor. ;;; This kind of pane is used to show the user the color you are editing. (DEFFLAVOR EDITOR-DISPLAY-PANE () (TV:WINDOW-PANE) (:DEFAULT-INIT-PLIST :BLINKER-P NIL :LABEL NIL :BORDER-MARGIN-WIDTH 0)) (DEFCONST *SAMPLE-LOC* 376) (DEFMETHOD (EDITOR-DISPLAY-PANE :AFTER :REFRESH) (&OPTIONAL TYPE) (AND (OR (NOT TV:RESTORED-BITS-P) (EQ TYPE ':SIZE-CHANGED)) (MULTIPLE-VALUE-BIND (WIDTH HEIGHT) (FUNCALL-SELF ':INSIDE-SIZE) (DRAW-COLOR-RECTANGLE *SAMPLE-LOC* 0 0 WIDTH HEIGHT)))) (DEFMETHOD (EDITOR-DISPLAY-PANE :WHO-LINE-DOCUMENTATION-STRING) () "Use the mouse on the sliders to edit this color, or on the menu to finish editing.") ;;; This kind of pane is basically a slider that tells its superior ;;; whenever anything happens to its value because of the mouse. (DEFFLAVOR EDITOR-SLIDER-PANE (WHOAMI) (TV:PANE-MIXIN SLIDER-WINDOW) (:DEFAULT-INIT-PLIST :MIN 0.0s0 :MAX 1.0s0 :VALUE 0.0s0 :WHOAMI NIL :FONT-MAP '(FONTS:TVFONT)) (:SETTABLE-INSTANCE-VARIABLES WHOAMI)) ;;; If any inferior gets his value change by the mouse, let the superior know. (DEFMETHOD (EDITOR-SLIDER-PANE :AFTER :MOUSE-SET-VALUE) (IGNORE) (FUNCALL TV:SUPERIOR ':INFERIOR-MOUSE-SET-VALUE WHOAMI VALUE)) (DEFMETHOD (EDITOR-SLIDER-PANE :WHO-LINE-DOCUMENTATION-STRING) () "Click left to move the slider to this point. Hold down to continuously move the slider.") ;;; This is a color-editor frame. (DEFFLAVOR COLOR-EDITOR (RED-PANE GREEN-PANE BLUE-PANE INTENSITY-PANE HUE-PANE SATURATION-PANE) (TV:BORDERS-MIXIN TV:TOP-LABEL-MIXIN COLOR-MIXIN TV:BORDERED-CONSTRAINT-FRAME-WITH-SHARED-IO-BUFFER)) (DEFRESOURCE COLOR-EDITORS () :CONSTRUCTOR (TV:WINDOW-CREATE 'COLOR-EDITOR ':SAVE-BITS T ':SUPERIOR COLOR:COLOR-SCREEN ':PANES `((RED-BAR EDITOR-SLIDER-PANE :LABEL-STRING "Red" :WHOAMI RED) (GREEN-BAR EDITOR-SLIDER-PANE :LABEL-STRING "Green" :WHOAMI GREEN) (BLUE-BAR EDITOR-SLIDER-PANE :LABEL-STRING "Blue" :WHOAMI BLUE) (INTENSITY-BAR EDITOR-SLIDER-PANE :LABEL-STRING "Intensity" :WHOAMI INTENSITY :MIN 0.0s0 :MAX ,(SMALL-FLOAT (SQRT 3.0))) (HUE-BAR EDITOR-SLIDER-PANE :LABEL-STRING "Hue" :WHOAMI HUE :MIN ,(- ) :MAX ,) (SATURATION-BAR EDITOR-SLIDER-PANE :LABEL-STRING "Saturation" :WHOAMI SATURATION) (DISPLAY EDITOR-DISPLAY-PANE) (MENU TV:COMMAND-MENU-PANE :ITEM-LIST (("Done" :VALUE DONE :DOCUMENTATION "Finish editing this color.") ("Abort" :VALUE ABORT :DOCUMENTATION "Abort editing this color."))) ) ':CONSTRAINTS '((MAIN . ((DISPLAY MENU RED-BAR GREEN-BAR BLUE-BAR INTENSITY-BAR HUE-BAR SATURATION-BAR) ((DISPLAY 0.5s0)) ((MENU :ASK :PANE-SIZE)) ((RED-BAR :EVEN) (GREEN-BAR :EVEN) (BLUE-BAR :EVEN) (INTENSITY-BAR :EVEN) (HUE-BAR :EVEN) (SATURATION-BAR :EVEN))))))) ;;; Initialization for COLOR-EDITOR frames. Just get all the panes ;;; into instance variables, and set up some color map elements. (DEFMETHOD (COLOR-EDITOR :AFTER :INIT) (&REST IGNORE) (FUNCALL-SELF ':SET-COLOR-MAP 0 0 0 0) (FUNCALL-SELF ':SET-COLOR-MAP *SLIDER-LINE-COLOR* 255. 255. 255.) (SETQ RED-PANE (FUNCALL-SELF ':GET-PANE 'RED-BAR) GREEN-PANE (FUNCALL-SELF ':GET-PANE 'GREEN-BAR) BLUE-PANE (FUNCALL-SELF ':GET-PANE 'BLUE-BAR) INTENSITY-PANE (FUNCALL-SELF ':GET-PANE 'INTENSITY-BAR) HUE-PANE (FUNCALL-SELF ':GET-PANE 'HUE-BAR) SATURATION-PANE (FUNCALL-SELF ':GET-PANE 'SATURATION-BAR))) ;;; This is for demos of the color editor. (DEFUN EDIT-COLORS () (WITH-MOUSE-SHEET (COLOR:COLOR-SCREEN) (LET (VAL1 VAL2 VAL3) (USING-RESOURCE (FRAME COLOR-EDITORS) (FUNCALL FRAME ':EXPOSE) (MULTIPLE-VALUE (VAL1 VAL2 VAL3) (FUNCALL FRAME ':EDIT "Demonstration of the color editor." 0 0 0)) (FUNCALL FRAME ':DEACTIVATE)) (VALUES VAL1 VAL2 VAL3)))) ;;; Takes three values, representing RED, GREEN, and BLUE levels expressed ;;; as fixnums. Edits them and returns three new values as fixnums, or NIL ;;; if the user aborts. COLOR-NAME is displayed in the label as the ;;; name of the color being edited. (DEFMETHOD (COLOR-EDITOR :EDIT) (LABEL RED GREEN BLUE) (TV:SHEET-FORCE-ACCESS (SELF) (FUNCALL-SELF ':SET-LABEL LABEL) (FUNCALL RED-PANE ':MOUSE-SET-VALUE (// RED 255.0s0)) (FUNCALL GREEN-PANE ':MOUSE-SET-VALUE (// GREEN 255.0s0)) (FUNCALL BLUE-PANE ':MOUSE-SET-VALUE (// BLUE 255.0s0)) (FUNCALL RED-PANE ':CLEAR-INPUT)) (IF (NOT TV:EXPOSED-P) (FUNCALL-SELF ':EXPOSE)) (DO () (()) (LET ((CHAR (FUNCALL RED-PANE ':ANY-TYI))) (COND ((AND (LISTP CHAR) (SELECTQ (FIRST CHAR) (SLIDER-BUTTONS (FUNCALL (SECOND CHAR) ':TRACK-MOUSE)) (:MENU (SELECTQ (FUNCALL (FOURTH CHAR) ':EXECUTE (SECOND CHAR)) (DONE (RETURN (FUNCALL-SELF ':READ-COLOR-MAP *SAMPLE-LOC*))) (ABORT (RETURN NIL))))))))))) ;;; Magic constants for the IHSRGB conversion algorithm. (DEFCONST C1 (SQRT (// 6.0))) (DEFCONST C2 (SQRT (// 2.0))) (DEFCONST C3 (SQRT (// 3.0))) ;;; This is called when one of the inferiors got its value changed by the ;;; mouse; it is forwarded to us by a daemon on the :MOUSE-SET-VALUE ;;; message. This method must update the other three sliders, and change ;;; the color map itself. (DEFMETHOD (COLOR-EDITOR :INFERIOR-MOUSE-SET-VALUE) (WHOISIT IGNORE) (LET ((R) (G) (B)) (COND ((MEMQ WHOISIT '(RED GREEN BLUE)) (SETQ R (FUNCALL RED-PANE ':VALUE) G (FUNCALL GREEN-PANE ':VALUE) B (FUNCALL BLUE-PANE ':VALUE)) ;; Do :SET-VALUEs on the other three. (LET* ((X (* C1 (- (+ R R) B G))) (Y (* C2 (- G B))) (Z (* C3 (+ R G B))) (Q (+ (* X X) (* Y Y))) (I (SMALL-FLOAT (SQRT (+ Q (* Z Z)))))) (FUNCALL INTENSITY-PANE ':SET-VALUE I) (COND ((ZEROP Q) (FUNCALL HUE-PANE ':SET-VALUE 0.0s0) (FUNCALL SATURATION-PANE ':SET-VALUE 0.0s0)) (T (FUNCALL HUE-PANE ':SET-VALUE (SMALL-FLOAT (ATAN2 Y X))) (FUNCALL SATURATION-PANE ':SET-VALUE (LET ((F (// Z I))) ;; RG claims this MAX is needed. I can't make the problem ;; happen, but might as well leave it in. -- DLW 6/20/81 (SMALL-FLOAT (ATAN2 (SQRT (MAX 0.0s0 (- 1.0 (* F F)))) F)))))) )) (T (LET ((I (FUNCALL INTENSITY-PANE ':VALUE)) (H (FUNCALL HUE-PANE ':VALUE)) (S (FUNCALL SATURATION-PANE ':VALUE))) ;; Figure out R, G, and B. (LET* ((S3 (SIN S)) (Z (* C3 (COS S) I)) (X (* C1 S3 (COS H) I)) (Y (* C2 S3 (SIN H) I))) (SETQ R (+ X X Z) G (+ Y Z (- X)) B (- Z X Y))) (FUNCALL RED-PANE ':SET-VALUE R) (FUNCALL GREEN-PANE ':SET-VALUE G) (FUNCALL BLUE-PANE ':SET-VALUE B) (SETQ R (FUNCALL RED-PANE ':VALUE) G (FUNCALL GREEN-PANE ':VALUE) B (FUNCALL BLUE-PANE ':VALUE))))) (FUNCALL-SELF ':SET-COLOR-MAP *SAMPLE-LOC* (FIX (* R 255.0s0)) (FIX (* G 255.0s0)) (FIX (* B 255.0s0))))) ;;; ;;; Magic functions from Seth Steinberg, Oct 19 1976. ;;; (DEFUN RGB-TO-IHS (R G B) ;;; (LET* ((X (* C1 (- (+ R R) B G))) ;;; (Y (* C2 (- G B))) ;;; (Z (* C3 (+ R G B))) ;;; (Q (+ (* X X) (* Y Y))) ;;; (I (SQRT (+ Q (* Z Z))))) ;;; (IF (ZEROP Q) ;;; (VALUES I 0.0 0.0) ;;; (VALUES I (ATAN2 Y X) ;;; ;;(ATAN2 Q Z) Seth's formula does not work. ;;; (LET ((F (// Z I))) ;;; (ATAN2 (SQRT (- 1.0 (* F F))) F)))))) ;;; ;;; (DEFUN IHS-TO-RGB (I H S) ;;; (LET* ((S3 (SIN S)) ;;; (Z (* C3 (COS S) I)) ;;; (X (* C1 S3 (COS H) I)) ;;; (Y (* C2 S3 (SIN H) I))) ;;; (VALUES (+ X X Z) ;;; (+ Y Z (- X)) ;;; (- Z X Y)))) (COMPILE-FLAVOR-METHODS COLOR-CHOICE-WINDOW COLOR-EDITOR EDITOR-SLIDER-PANE EDITOR-DISPLAY-PANE)