Module: duim-sheets-internals Synopsis: DUIM sheets Author: Scott McKay, Andy Armstrong 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 /// Pointers (aka, the mouse) define protocol <> () getter pointer-sheet (pointer :: ) => (sheet :: false-or()); getter pointer-button-state (pointer :: ) => (state :: ); function pointer-position (pointer :: , #key sheet) => (x :: , y :: ); function do-pointer-position (port :: , pointer :: , sheet :: ) => (x :: , y :: ); function set-pointer-position (pointer :: , x :: , y :: , #key sheet) => (); function do-set-pointer-position (port :: , pointer :: , sheet :: , x :: , y :: ) => (); getter pointer-cursor (pointer :: ) => (cursor :: ); setter pointer-cursor-setter (cursor :: , pointer :: ) => (cursor :: ); function do-set-pointer-cursor (port :: , pointer :: , cursor :: ) => (); function do-set-sheet-cursor (port :: , sheet :: , cursor :: ) => (); getter pointer-grabbed? (pointer :: ) => (sheet :: false-or()); getter pointer-grabbed?-setter (sheet :: false-or(), pointer :: ) => (sheet :: false-or()); function grab-pointer (port :: , pointer :: , sheet :: ) => (success? :: ); function ungrab-pointer (port :: , pointer :: ) => (success? :: ); function do-with-pointer-grabbed (port :: , sheet :: , continuation :: ) => (#rest values); end protocol <>; define sealed class () sealed slot port :: false-or() = #f, init-keyword: port:, setter: %port-setter; sealed slot pointer-sheet :: false-or() = #f; // The pointer button state gets maintained by each back-end sealed slot pointer-button-state :: = 0; sealed slot %x-position :: = 0; sealed slot %y-position :: = 0; sealed slot %position-changed? :: = #f; sealed slot pointer-cursor :: = #"default", setter: %cursor-setter; sealed slot pointer-grabbed? :: false-or() = #f, setter: %grabbed?-setter; end class ; define sealed domain make (singleton()); define sealed domain initialize (); define sealed inline method make (class == , #key port, display) => (pointer :: ) ignore(display); make(, port: port) end method make; define method display (pointer :: ) => (display :: false-or()) let sheet = pointer-sheet(pointer); sheet & display(sheet) end method display; define sealed method pointer-cursor-setter (cursor :: , pointer :: ) => (cursor :: ) unless (pointer-cursor(pointer) == cursor) do-set-pointer-cursor(port(pointer), pointer, cursor); pointer.%cursor := cursor end; cursor end method pointer-cursor-setter; define method do-set-sheet-cursor (_port :: , sheet :: , cursor :: ) => () ignore(sheet, cursor); #f end method do-set-sheet-cursor; define method update-pointer-cursor (pointer :: , #key sheet = pointer-sheet(pointer), frame = (sheet & sheet-frame(sheet))) => () pointer-cursor(pointer) := (frame & frame-cursor-override(frame)) | (sheet & sheet-cursor(sheet)) | #"default" end method update-pointer-cursor; // This is robust against ungrafted sheets and umapped frames, // because 'with-busy-cursor' often gets used in initialization code... define macro with-busy-cursor { with-busy-cursor (?frame:expression, ?cursor:expression) ?:body end } => { begin let with-busy-cursor-body = method () ?body end; do-with-busy-cursor(?frame, ?cursor, with-busy-cursor-body) end } { with-busy-cursor (?frame:expression) ?:body end } => { begin let with-busy-cursor-body = method () ?body end; do-with-busy-cursor(?frame, #"busy", with-busy-cursor-body) end } end macro with-busy-cursor; define method do-with-busy-cursor (sheet :: , cursor, continuation :: ) => (#rest values) let frame = sheet-frame(sheet); if (frame) do-with-busy-cursor(frame, cursor, continuation) else continuation() // sheet is not grafted end end method do-with-busy-cursor; define method do-with-busy-cursor (frame :: , cursor, continuation :: ) => (#rest values) let old-cursor = frame-cursor-override(frame); block () frame-cursor-override(frame) := cursor; continuation() cleanup frame-cursor-override(frame) := old-cursor end end method do-with-busy-cursor; // Returns X and Y in sheet's coordinate system define sealed method pointer-position (pointer :: , #key sheet) => (x :: , x :: ) if (sheet & ~(sheet == pointer-sheet(pointer))) // He's asking for the position w.r.t. another sheet, // so we have to consult the port directly do-pointer-position(port(pointer), pointer, sheet) else values(pointer.%x-position, pointer.%y-position) end end method pointer-position; // X and Y are in the sheet's coordinate system define sealed method set-pointer-position (pointer :: , x :: , y :: , #key sheet) => () pointer.%x-position := x; pointer.%y-position := y; pointer.%position-changed? := #t; when (sheet & ~(sheet == pointer-sheet(pointer))) do-set-pointer-position(port(pointer), pointer, sheet, x, y) end end method set-pointer-position; define method pointer-state-changed? (pointer :: , old-sheet, old-x, old-y) => (changed? :: , sheet :: , x :: , x :: ) let sheet = pointer-sheet(pointer); let (x-position, y-position) = pointer-position(pointer, sheet: sheet); values(~(sheet == old-sheet) // Compare coordinates with eql, not =, because null values can be passed in | ~(old-x == x-position) | ~(old-y == y-position), sheet, x-position, y-position) end method pointer-state-changed?; /// Pointer grabbing //---*** What should we do if one thread tries to grab the pointer //---*** when another thread already has the grab? define sealed method pointer-grabbed?-setter (sheet :: false-or(), pointer :: ) => (sheet :: false-or()) let _port = port(pointer); unless (pointer-grabbed?(pointer) == sheet) if (sheet) let new-focus = sheet; let old-focus = port-input-focus(_port); when (grab-pointer(_port, pointer, sheet)) when (new-focus ~== old-focus & port-focus-policy(_port) == #"sheet-under-pointer" & sheet-handles-keyboard?(new-focus)) port-input-focus(_port) := new-focus; end; pointer.%grabbed? := sheet end else when (ungrab-pointer(_port, pointer)) pointer.%grabbed? := #f end end end; sheet end method pointer-grabbed?-setter; define method grab-pointer (_port :: , pointer :: , sheet :: ) => (success? :: ) #t end method grab-pointer; define method ungrab-pointer (_port :: , pointer :: ) => (success? :: ) #t end method ungrab-pointer; define macro with-pointer-grabbed { with-pointer-grabbed (?sheet:name) ?:body end } => { begin let with-pointer-grabbed-body = method () ?body end; let _port = port(?sheet); do-with-pointer-grabbed(_port, ?sheet, with-pointer-grabbed-body) end } end macro with-pointer-grabbed; define sealed method do-with-pointer-grabbed (_port :: , sheet :: , continuation :: ) => (#rest values) let pointer = port-pointer(_port); block () pointer-grabbed?(pointer) := sheet; continuation() cleanup pointer-grabbed?(pointer) := #f; end end method do-with-pointer-grabbed;