Module: CAPI-DUIM Synopsis: CAPI back-end Author: Andy Armstrong, Scott McKay Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// Abstract gadget handling define open abstract class () end class ; define open abstract class (, , ) end class ; define open abstract class () end class ; define open abstract class () end class ; /// Value handling define method capi-mirror-value (gadget :: ) #f end method capi-mirror-value; define method capi-new-mirror-value (gadget :: ) capi-mirror-value(gadget) end method capi-new-mirror-value; define method capi-sheet-enabled? (gadget :: ) gadget-enabled?(gadget) end method capi-sheet-enabled?; define method note-gadget-enabled (client, gadget :: ) update-capi-gadget-enabled-state(gadget, #t); next-method() end method note-gadget-enabled; define method note-gadget-disabled (client, gadget :: ) => () update-capi-gadget-enabled-state(gadget, #f); next-method() end method note-gadget-disabled; define method update-capi-gadget-enabled-state (gadget :: , state) let mirror = sheet-direct-mirror(gadget); when (instance?(mirror, )) simple-pane-enabled(mirror) := state | #() end end method update-capi-gadget-enabled-state; /// callback handling define method mirror-primary-callback (mirror) sheet-primary-callback(mirror-sheet(mirror)) end method mirror-primary-callback; define method sheet-primary-callback (sheet :: ) distribute-activate-callback(sheet) end method sheet-primary-callback; define method sheet-primary-callback (gadget :: ) let value = capi-new-mirror-value(sheet-direct-mirror(gadget)); distribute-value-changed-callback(gadget, value) end method sheet-primary-callback; // Return #t if the port generates damage events for each mirrored sheet, // in effect, handling repaint itself (this is the X model). Return #f // if only one damage event comes in for the top level sheet, meaning that // repainting of child sheets must be done manually (the Mac model). define method port-handles-repaint? (_port :: , sheet :: ) #f end method port-handles-repaint?; // CAPI "panes" are all of the gadget-y things. They repaint themselves. define method port-handles-repaint? (_port :: , sheet :: ) #t end method port-handles-repaint?; // In CAPI, we don't get a repaint event for this, so get DUIM to do it. define method port-handles-repaint? (_port :: , sheet :: ) #f end method port-handles-repaint?; define method default-foreground-setter (foreground :: , pane :: ) => (foreground :: ) next-method(); //--- change the foreground of the gadget foreground end method default-foreground-setter; define method default-background-setter (background :: , pane :: ) => (background :: ) next-method(); //--- change the background of the gadget background end method default-background-setter; define method capi-mirror-parent (sheet) sheet-mirror(sheet) end method capi-mirror-parent; define method capi-gadget-constraints (gadget :: ) let mirror = sheet-direct-mirror(gadget); if (mirror) let pane = maybe-decoration-pane(mirror, #t); let (min-width, min-height, max-width, max-height) = get-constraints(pane); local method max-space (min, max) select (max by instance?) => max; => $fill; otherwise => min; end end method; values(min-width, min-height, max-space(min-width, max-width), max-space(min-height, max-height)) else error("Attempting to query an unmirrored CAPI gadget's constraints: %=", gadget); values(100, 100, 100, 100) end end method capi-gadget-constraints; define method do-compose-space (gadget :: , #key width, height) => (space-req :: ) let (mn-w, mn-h, mx-w, mx-h) = capi-gadget-constraints(gadget); let best-width = if (width) constrain-size(width, mn-w, mx-w) else mn-w end; let best-height = if (height) constrain-size(height, mn-h, mx-h) else mn-h end; make(, width: best-width, height: best-height, min-width: mn-w, min-height: mn-h, max-width: mx-w, max-height: mx-h) end method do-compose-space; define method note-capi-gadget-value-changed (sheet :: , mirror, value) => () #f end method note-capi-gadget-value-changed; define method note-gadget-value-changed (gadget :: ) => () next-method(); note-capi-gadget-value-changed(gadget, sheet-direct-mirror(gadget), gadget-value(gadget)) end method note-gadget-value-changed; define method capi-gadget-selection (gadget :: ) => (selection) let selection = gadget-selection(gadget); select (gadget-selection-mode(gadget)) #"none" => #(); #"single" => (~empty?(selection) & selection[0]) | #(); #"multiple" => as(, selection); end end method capi-gadget-selection; define method note-gadget-selection-changed (gadget :: ) => () next-method(); let mirror = sheet-direct-mirror(gadget); when (mirror & ~instance?(gadget, )) choice-selection(mirror) := capi-gadget-selection(gadget) end end method note-gadget-selection-changed; define method gadget-label-setter (label, gadget :: ) => (label) next-method & next-method(); let mirror = sheet-direct-mirror(gadget); mirror & note-capi-gadget-label-changed(gadget, mirror, label); label end method gadget-label-setter; define method note-capi-gadget-label-changed (sheet :: , mirror, label) #f end method note-capi-gadget-label-changed; /// Buttons define open abstract class () end class ; define method allocate-space (pane :: , width :: , height :: ) end method allocate-space; define method text-or-image-from-gadget-label (gadget) => (text :: false-or(), image :: false-or(), mnemonic :: false-or(), index :: false-or()); let (label, mnemonic, index) = compute-mnemonic-from-label(gadget, gadget-label(gadget), remove-ampersand?: #t); case instance?(label, ) => values(label, #f, mnemonic, index); label => values("", #f, mnemonic, index); otherwise => values("", #f, mnemonic, index); end end method text-or-image-from-gadget-label; define method note-capi-gadget-label-changed (sheet :: , mirror, label) let (label, mnemonic, index) = compute-mnemonic-from-label(sheet, label, remove-ampersand?: #t); ignore(mnemonic, index); item-text(mirror) := label; invalidate-pane-constraints(mirror); relayout-parent(sheet) end method note-capi-gadget-label-changed; define sealed class (, , ) end class ; define method sheet-primary-callback (sheet :: ) distribute-activate-callback(sheet) end method sheet-primary-callback; define sealed class (, ) end class ; define method class-for-make-pane (framem :: , class == , #key) => (class :: , options :: false-or()); values(, #f) end method class-for-make-pane; define method do-make-mirror (_port :: , sheet :: ) let (text, image, mnemonic) = text-or-image-from-gadget-label(sheet); make-capi-mirror(_port, sheet, , interaction: no-selection:, text: text, callback-type: item:, callback: mirror-primary-callback); end method do-make-mirror; define sealed class (, , ) end class ; define method sheet-primary-callback (gadget :: ) let value = capi-new-mirror-value(sheet-direct-mirror(gadget)); distribute-value-changed-callback(gadget, value) end method sheet-primary-callback; define sealed class (, ) end class ; define method class-for-make-pane (framem :: , class == , #key) => (class :: , options :: false-or()); values(, #f) end method class-for-make-pane; define method capi-mirror-value (gadget :: ) lisp-true?(button-selected(gadget)) end method capi-mirror-value; define method capi-new-mirror-value (gadget :: ) lisp-false?(button-selected(gadget)) end method capi-new-mirror-value; define method note-capi-gadget-value-changed (sheet :: , mirror :: , value) button-selected(mirror) := value | #() end method note-capi-gadget-value-changed; define method do-make-mirror (_port :: , sheet :: ) let (text, image, mnemonic) = text-or-image-from-gadget-label(sheet); make-capi-mirror(_port, sheet, , interaction: single-selection:, text: text, selected: gadget-value(sheet) | #(), callback-type: item:, callback: mirror-primary-callback); end method do-make-mirror; define sealed class (, , ) end class ; define method sheet-primary-callback (gadget :: ) let value = capi-new-mirror-value(sheet-direct-mirror(gadget)); distribute-value-changed-callback(gadget, value) end method sheet-primary-callback; define sealed class (, ) end class ; define method class-for-make-pane (framem :: , class == , #key) => (class :: , options :: false-or()) values(, #f) end method class-for-make-pane; define method capi-mirror-value (gadget :: ) lisp-true?(button-selected(gadget)) end method capi-mirror-value; define method note-capi-gadget-value-changed (sheet :: , mirror :: , value) button-selected(mirror) := value | #() end method note-capi-gadget-value-changed; define method do-make-mirror (_port :: , sheet :: ) let (text, image, mnemonic) = text-or-image-from-gadget-label(sheet); make-capi-mirror(_port, sheet, , interaction: multiple-selection:, text: text, selected: gadget-value(sheet) | #(), callback-type: item:, callback: mirror-primary-callback, retract-callback: mirror-primary-callback) end method do-make-mirror; define sealed class (, , ) end class ; define sealed class (, ) end class ; define method class-for-make-pane (framem :: , class == , #key) => (class :: , options :: false-or()) values(, #f) end method class-for-make-pane; define method capi-selection-mode (gadget :: ) select (gadget-selection-mode(gadget)) #"none" => no-selection:; #"single" => single-selection:; #"multiple" => extended-selection:; end end method capi-selection-mode; define method capi-list-box-selection-callback (gadget :: ) => () let mirror = sheet-direct-mirror(gadget); let mirror-selection = choice-selection(mirror); let selection = if (instance?(mirror-selection, )) mirror-selection else vector(mirror-selection) end; distribute-selection-changed-callback(gadget, selection) end method capi-list-box-selection-callback; define method capi-list-box-action-callback (sheet :: ) distribute-activate-callback(sheet) end method capi-list-box-action-callback; define method capi-horizontal-scroll-value (sheet :: ) (gadget-scrolling-horizontally?(sheet) & bottom:) | #() end method capi-horizontal-scroll-value; define method capi-vertical-scroll-value (sheet :: ) (gadget-scrolling-vertically?(sheet) & right:) | #() end method capi-vertical-scroll-value; define method update-capi-mirror-items (mirror :: , items) collection-items(mirror) := as(, items) end method update-capi-mirror-items; define method note-gadget-items-changed (gadget :: ) => () next-method(); let mirror = sheet-direct-mirror(gadget); when (mirror) update-capi-mirror-items(mirror, gadget-items(gadget)) end end method note-gadget-items-changed; define method capi-collection-gadget-items (sheet :: ) as(, gadget-items(sheet)) end method capi-collection-gadget-items; define method do-make-mirror (_port :: , sheet :: ) let horizontal-bar? = gadget-scrolling-horizontally?(sheet); let vertical-bar? = gadget-scrolling-vertically?(sheet); make-capi-mirror(_port, sheet, , interaction: capi-selection-mode(sheet), // Coerce to a vector so that we can handle 's items: capi-collection-gadget-items(sheet), selection: capi-gadget-selection(sheet), print-function: method (item) collection-gadget-item-label(sheet, item) end, horizontal-scroll: capi-horizontal-scroll-value(sheet), vertical-scroll: capi-vertical-scroll-value(sheet), min-width: (~horizontal-bar? & text-width:) | #(), min-height: (~vertical-bar? & text-height:) | #(), callback-type: none:, selection-callback: method () capi-list-box-selection-callback(sheet) end, retract-callback: method () capi-list-box-selection-callback(sheet) end, extend-callback: method () capi-list-box-selection-callback(sheet) end, action-callback: method () distribute-activate-callback(sheet) end); end method do-make-mirror; define sealed class (, , ) end class ; define sealed class (, ) end class ; define method class-for-make-pane (framem :: , class == , #key) => (class :: , options :: false-or()) values(, #f) end method class-for-make-pane; define method capi-option-box-selection-callback (gadget :: ) => () let mirror = sheet-direct-mirror(gadget); let mirror-selection = choice-selection(mirror); let selection = if (instance?(mirror-selection, )) mirror-selection else vector(mirror-selection) end; distribute-selection-changed-callback(gadget, selection) end method capi-option-box-selection-callback; define method do-make-mirror (_port :: , sheet :: ) make-capi-mirror(_port, sheet, , // Coerce to a vector so that we can handle 's items: capi-collection-gadget-items(sheet), print-function: method (item) collection-gadget-item-label(sheet, item) end, selection: capi-gadget-selection(sheet), callback-type: none:, selection-callback: method () capi-option-box-selection-callback (sheet) end); end method do-make-mirror; /// Viewports define sealed class (, , , , ) end class ; define method class-for-make-pane (framem :: , class == , #key) => (class :: , options :: false-or()) values(, #f) end method class-for-make-pane; //--- Make the CAPI viewport four pixels bigger than it should be to //--- account for the border pane. define method do-compose-space (pane :: , #key width, height) => (space-req :: ) next-method() // let space // = next-method(pane, // width: width & width - 2, // height: height & height - 2); // let thickness = 4; // space-requirement+(space, pane, // width: thickness, height: thickness) end method do-compose-space; define method do-make-mirror (_port :: , sheet :: ) // next-method(); make-capi-mirror(_port, sheet, , has-motif-border: #"none") end method do-make-mirror; /// Scroll bars define sealed class (, , ) sealed slot scroll-bar-scale :: = 1000.0; end class ; define sealed class (, ) end class ; //--- This is a grubby hack to make the scroll-bars be the same size //--- as the rest of LispWorks... why is CAPI returning the wrong size? define method capi-gadget-constraints (gadget :: ) let (min-width, min-height, max-width, max-height) = next-method(); ignore(max-width, max-height); select (gadget-orientation(gadget)) #"horizontal" => values(min-width, 20, $fill, 20); #"vertical" => values(20, min-height, 20, $fill); end end method capi-gadget-constraints; define method class-for-make-pane (framem :: , class == , #key) => (class :: , options :: false-or()) values(, #f) end method class-for-make-pane; define method do-make-mirror (_port :: , sheet :: ) let orientation = select (gadget-orientation(sheet)) //--- damned emulator #"horizontal" => horizontal:; #"vertical" => vertical:; end; make-capi-mirror(_port, sheet, , callback: capi-scroll-bar-callback, orientation: orientation); end method do-make-mirror; define method initialize (mirror :: , #key) next-method(); let gadget = mirror-sheet(mirror); update-capi-scroll-bar-mirror-range(mirror, gadget-value-range(gadget)); update-capi-scroll-bar-mirror-values(mirror, slug-size: gadget-slug-size(gadget), value: gadget-value(gadget)) end method initialize; define method update-capi-scroll-bar-mirror-range (mirror :: , range :: ) let gadget = mirror-sheet(mirror); let scale :: = scroll-bar-scale(gadget); range-start(mirror) := floor(range[0] * scale); range-end(mirror) := floor(range[size(range) - 1] * scale); //---*** How do we really work out the line size and page size? //scroll-bar-line-size(mirror) := 16; //scroll-bar-page-size(mirror) := floor/(capi-min - capi-max, 10); end method update-capi-scroll-bar-mirror-range; define method capi-scroll-bar-value (gadget :: , value) let range = gadget-value-range(gadget); range[floor/(value, scroll-bar-scale(gadget))] end method capi-scroll-bar-value; define method capi-scroll-bar-callback (interface, mirror :: , how, where) let gadget = mirror-sheet(mirror); select (how) #"page" => case where >= 0 => scroll-down-page(gadget); otherwise => scroll-up-page(gadget); end; #"line" => case where >= 0 => scroll-down-line(gadget); otherwise => scroll-up-line(gadget); end; #"move" => gadget-value(gadget, do-callback?: #t) := capi-scroll-bar-value(gadget, range-slug-start(mirror)); #"drag" => gadget-value(gadget, do-callback?: #t) := capi-scroll-bar-value(gadget, where); end end method capi-scroll-bar-callback; define method note-gadget-value-range-changed (gadget :: ) => () let mirror = sheet-direct-mirror(gadget); when (mirror) update-capi-scroll-bar-mirror-range(mirror, gadget-value-range(gadget)) end end method note-gadget-value-range-changed; define method update-capi-scroll-bar-mirror-values (gadget :: , #key slug-size, value) let mirror = sheet-direct-mirror(gadget); when (mirror) update-capi-scroll-bar-mirror-values(mirror, slug-size: slug-size, value: value) end end method update-capi-scroll-bar-mirror-values; define method note-capi-gadget-value-changed (gadget :: , mirror, value) => () update-capi-scroll-bar-mirror-values(gadget, value: value) end method note-capi-gadget-value-changed; define method note-gadget-slug-size-changed (gadget :: ) => () next-method(); update-capi-scroll-bar-mirror-values(gadget, slug-size: gadget-slug-size(gadget)) end method note-gadget-slug-size-changed; define method note-scroll-bar-changed (gadget :: ) => () let mirror = sheet-direct-mirror(gadget); when (mirror) update-capi-scroll-bar-mirror-range(mirror, gadget-value-range(gadget)); update-capi-scroll-bar-mirror-values(mirror, slug-size: gadget-slug-size(gadget), value: gadget-value(gadget)) end end method note-scroll-bar-changed; define method update-capi-scroll-bar-mirror-values (mirror :: , #key slug-size, value) let gadget = mirror-sheet(mirror); let scale :: = scroll-bar-scale(gadget); let slug-start = floor(scale * (value | gadget-value(gadget))); let slug-size = floor(scale * (slug-size | gadget-slug-size(gadget))); range-slug-start(mirror) := slug-start; range-slug-end(mirror) := slug-start + slug-size; scroll-bar-page-size(mirror) := slug-size; end method update-capi-scroll-bar-mirror-values; /// Sliders define sealed class (, , ) end class ; define sealed class (, ) end class ; define method class-for-make-pane (framem :: , class == , #key) => (class :: , options :: false-or()) values(, #f) end method class-for-make-pane; define method do-make-mirror (_port :: , slider :: ) let range = gadget-value-range(slider); let value = gadget-value(slider); make-capi-mirror(_port, slider, , start: mirror-slider-value(slider, range[0]), end: mirror-slider-value(slider, range[size(range) - 1]), slug-start: mirror-slider-value(slider, value), callback-type: item:, callback: capi-slider-callback); end method do-make-mirror; define method capi-slider-callback (interface :: , mirror :: , value) let gadget = mirror-sheet(mirror); let mirror-value = mirror-slider-value(gadget, value); distribute-value-changed-callback(gadget, mirror-value) end method capi-slider-callback; define method mirror-slider-value (gadget :: , value) let range = gadget-value-range(gadget); let scale = range[1] - range[0]; floor(value * scale) end method mirror-slider-value; define method note-capi-gadget-value-changed (gadget :: , mirror :: , value) => () range-slug-start(mirror) := mirror-slider-value(gadget, value) end method note-capi-gadget-value-changed; /// "Decorations" define sealed class (,