rexx-things/modules/windows/oodialog/mouse/dragNdrop.rex
2025-03-12 20:50:48 +00:00

516 lines
14 KiB
Rexx
Executable File

/*----------------------------------------------------------------------------*/
/* */
/* Copyright (c) 2011-2014 Rexx Language Association. All rights reserved. */
/* */
/* This program and the accompanying materials are made available under */
/* the terms of the Common Public License v1.0 which accompanies this */
/* distribution. A copy is also available at the following address: */
/* https://www.oorexx.org/license.html */
/* */
/* Redistribution and use in source and binary forms, with or */
/* without modification, are permitted provided that the following */
/* conditions are met: */
/* */
/* Redistributions of source code must retain the above copyright */
/* notice, this list of conditions and the following disclaimer. */
/* Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in */
/* the documentation and/or other materials provided with the distribution. */
/* */
/* Neither the name of Rexx Language Association nor the names */
/* of its contributors may be used to endorse or promote products */
/* derived from this software without specific prior written permission. */
/* */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
/* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
/* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/* */
/*----------------------------------------------------------------------------*/
/**
* This example program demonstrates one approach to adding drag-and-drop to an
* ooDialog program using the .Mouse class.
*/
sd = locate()
.application~setDefaults('O', sd'dragNdrop.h', .false)
dlg = .DreamTeamDlg~new(sd'dragNdrop.rc', IDD_NFL_DLG)
if dlg~initCode == 0 then do
dlg~execute("SHOWTOP", IDI_DLG_OOREXX)
end
::requires "ooDialog.cls"
::class 'DreamTeamDlg' subclass RcDialog
::method init
expose dropOkCursor noDropCursor cursorIsNoDrop dragging
forward class (super) continue
-- This will load a cursor from a file. The file has to actually be a cursor
-- or the method will fail. The operating system will refuse to load anything
-- but an actual cursor. The return is an .Image object.
dropOkCursor = .Mouse~loadCursorFromFile(.application~srcDir"dragging.cur")
if dropOkCursor == 0 then do
say 'Error loading Drop Ok Cursor system error code:' .SystemErrorCode
self~initCode = 1
return self~initCode
end
-- This loads the system's predefined NO cursor. Again the return is a .Image
-- object.
noDropCursor = .Mouse~loadCursor("NO")
if noDropCursor == 0 then do
say 'Error loading No Drop Cursor, system error code:' .SystemErrorCode
self~initCode = 1
return self~initCode
end
cursorIsNoDrop = .false
dragging = .false
return 0
::method initDialog
expose lvNFL lvWest lvEast noDropCursor dragItem nfl2west nfl2east
lvNFL = self~newListView(IDC_LV_NFL)
self~setUpListView(lvNFL)
lvWest = self~newListView(IDC_LV_WEST)
self~setUpListView(lvWest)
lvEast = self~newListView(IDC_LV_EAST)
self~setUpListView(lvEast)
self~setMice
self~mapDropTargets
dragItem = .nil
/** mapDropTargets()
*
*/
::method mapDropTargets private
expose lvNFL lvWest lvEast nfl2west nfl2east west2nfl west2east east2nfl east2west
nfl2west = lvWest~clientRect
lvWest~mapWindowPoints(lvNFL~hwnd, nfl2west)
nfl2east = lvEast~clientRect
lvEast~mapWindowPoints(lvNFL~hwnd, nfl2east)
west2nfl = lvNFL~clientRect
lvNFL~mapWindowPoints(lvWest~hwnd, west2nfl)
west2east = lvEast~clientRect
lvEast~mapWindowPoints(lvWest~hwnd, west2east)
east2nfl = lvNFL~clientRect
lvNFL~mapWindowPoints(lvEast~hwnd, east2nfl)
east2west = lvWest~clientRect
lvWest~mapWindowPoints(lvEast~hwnd, east2west)
::method doDrag private
expose oldCursor noDropCursor cursorIsNoDrop dragging
use arg lv, index, p, mouse
mouse~capture
oldCursor = mouse~setCursor(noDropCursor)
di = .DragItem~new(lv, index, p, mouse)
cursorIsNoDrop = .true
dragging = .true
return di
::method nflOnLBdown unguarded
expose lvNFL dragItem
use arg keyState, p, mouse
index = lvNFL~hitTestInfo(p)
if keyState \== 'lButton' | index = -1 then return .false
lvNFL~assignFocus
lvNFL~focus(index)
lvNFL~select(index)
if mouse~dragDetect(p) then do
dragItem = self~doDrag(lvNFL, index, p, mouse)
return .true
end
return .false
::method nflOnLBup unguarded
expose oldCursor cursorIsNoDrop dragging lvNFL lvWest lvEast dragItem nfl2west nfl2east
use arg keyState, p, mouse
if dragging then do
okayToDrop = (cusorIsNoDrop \== .true)
dragging = .false
cursorIsNoDrop = .false
mouse~releaseCapture
mouse~setCursor(oldCursor)
if okayToDrop & dragItem \== .nil then do
if p~inRect(nfl2west) then do
dragItem~target = lvWest
end
else if p~inRect(nfl2east) then do
dragItem~target = lvEast
end
else do
-- Theoretically this can not happen, but just to be sure...
dragItem = .nil
return 0
end
-- The point p is in client coordinates of the NFL list view. We are
-- going to map that point back to the client coordinates of the list view
-- where the row is being dropped. Then the drag item can determine where
-- the insertion poing for the row should be in the target list view.
lvNFL~mapWindowPoints(dragItem~target~hwnd, p)
dragItem~drop(p)
dragItem = .nil
end
end
return 0
::method nflOnMove unguarded
expose dragging dropOkCursor noDropCursor cursorIsNoDrop nfl2west nfl2east
use arg keyState, p, mouse
if dragging then do
if p~inRect(nfl2west) then do
if cursorIsNoDrop then do
mouse~setCursor(dropOkCursor)
cursorIsNoDrop = .false
end
end
else if p~inRect(nfl2east) then do
if cursorIsNoDrop then do
mouse~setCursor(dropOkCursor)
cursorIsNoDrop = .false
end
end
else do
if \ cursorIsNoDrop then do
mouse~setCursor(noDropCursor)
cursorIsNoDrop = .true
end
end
end
return 0
::method westOnLBdown unguarded
expose lvWest dragItem
use arg keyState, p, mouse
index = lvWest~hitTestInfo(p)
if keyState \== 'lButton' | index = -1 then return .false
lvWest~assignFocus
lvWest~focus(index)
lvWest~select(index)
if mouse~dragDetect(p) then do
dragItem = self~doDrag(lvWest, index, p, mouse)
return .true
end
return .false
::method westOnLBup unguarded
expose oldCursor cursorIsNoDrop dragging lvNFL lvWest lvEast dragItem west2nfl west2east
use arg keyState, p, mouse
if dragging then do
okayToDrop = (cusorIsNoDrop \== .true)
dragging = .false
cursorIsNoDrop = .false
mouse~releaseCapture
mouse~setCursor(oldCursor)
if okayToDrop & dragItem \== .nil then do
if p~inRect(west2nfl) then do
dragItem~target = lvNFL
end
else if p~inRect(west2east) then do
dragItem~target = lvEast
end
else do
-- Theoretically this can not happen, but just to be sure...
dragItem = .nil
return 0
end
-- The point p is in client coordinates of the West list view. We are
-- going to map that point back to the client coordinates of the list view
-- where the row is being dropped. Then the drag item can determine where
-- the insertion poing for the row should be in the target list view.
lvWest~mapWindowPoints(dragItem~target~hwnd, p)
dragItem~drop(p)
dragItem = .nil
end
end
return 0
::method westOnMove unguarded
expose dragging dropOkCursor noDropCursor cursorIsNoDrop west2nfl west2east
use arg keyState, p, mouse
if dragging then do
if p~inRect(west2nfl) then do
if cursorIsNoDrop then do
mouse~setCursor(dropOkCursor)
cursorIsNoDrop = .false
end
end
else if p~inRect(west2east) then do
if cursorIsNoDrop then do
mouse~setCursor(dropOkCursor)
cursorIsNoDrop = .false
end
end
else do
if \ cursorIsNoDrop then do
mouse~setCursor(noDropCursor)
cursorIsNoDrop = .true
end
end
end
return 0
::method eastOnLBdown unguarded
expose lvEast dragItem
use arg keyState, p, mouse
index = lvEast~hitTestInfo(p)
if keyState \== 'lButton' | index = -1 then return .false
lvEast~assignFocus
lvEast~focus(index)
lvEast~select(index)
if mouse~dragDetect(p) then do
dragItem = self~doDrag(lvEast, index, p, mouse)
return .true
end
return .false
::method eastOnLBup unguarded
expose oldCursor cursorIsNoDrop dragging lvNFL lvWest lvEast dragItem east2nfl east2west
use arg keyState, p, mouse
if dragging then do
okayToDrop = (cusorIsNoDrop \== .true)
dragging = .false
cursorIsNoDrop = .false
mouse~releaseCapture
mouse~setCursor(oldCursor)
if okayToDrop & dragItem \== .nil then do
if p~inRect(east2nfl) then do
dragItem~target = lvNFL
end
else if p~inRect(east2west) then do
dragItem~target = lvWest
end
else do
-- Theoretically this can not happen, but just to be sure...
dragItem = .nil
return 0
end
-- The point p is in client coordinates of the East list view. We are
-- going to map that point back to the client coordinates of the list view
-- where the row is being dropped. Then the drag item can determine where
-- the insertion poing for the row should be in the target list view.
lvEast~mapWindowPoints(dragItem~target~hwnd, p)
dragItem~drop(p)
dragItem = .nil
end
end
return 0
::method eastOnMove unguarded
expose dragging dropOkCursor noDropCursor cursorIsNoDrop east2nfl east2west
use arg keyState, p, mouse
if dragging then do
if p~inRect(east2nfl) then do
if cursorIsNoDrop then do
mouse~setCursor(dropOkCursor)
cursorIsNoDrop = .false
end
end
else if p~inRect(east2west) then do
if cursorIsNoDrop then do
mouse~setCursor(dropOkCursor)
cursorIsNoDrop = .false
end
end
else do
if \ cursorIsNoDrop then do
mouse~setCursor(noDropCursor)
cursorIsNoDrop = .true
end
end
end
return 0
::method setMice private
expose mLVnfl mLVwest mLVeast lvNFL lvWest lvEast
mLVnfl = .Mouse~new(lvNFL)
mLVwest = .Mouse~new(lvWest)
mLVeast = .Mouse~new(lvEast)
mLVnfl~connectEvent('LBUTTONDOWN', nflOnLBdown)
mLVnfl~connectEvent('LBUTTONUP', nflOnLBup)
mLVnfl~connectEvent('MOUSEMOVE', nflOnMove)
mLVwest~connectEvent('LBUTTONDOWN', westOnLBdown)
mLVwest~connectEvent('LBUTTONUP', westOnLBup)
mLVwest~connectEvent('MOUSEMOVE', westOnMove)
mLVeast~connectEvent('LBUTTONDOWN', eastOnLBdown)
mLVeast~connectEvent('LBUTTONUP', eastOnLBup)
mLVeast~connectEvent('MOUSEMOVE', eastOnMove)
::method setUpListView private
use strict arg lv
lv~addExtendedStyle("GRIDLINES DOUBLEBUFFER FULLROWSELECT")
lv~insertColumnPX(0, 'Player', 105, 'LEFT')
lv~insertColumnPX(1, 'Team', 115, 'CENTER')
lv~insertColumnPX(2, 'Position', 65, 'CENTER')
lv~insertColumnPX(3, 'Rating', 65, 'CENTER')
if lv~id == .constDir[IDC_LV_NFL] then do
playerRows = self~getPlayers
do l over playerRows
lv~addRow( , , l~player, l~team, l~pos, l~rating)
end
end
::method getPlayers private
rows = .Array~new
fileObj = .stream~new(.application~srcDir"nflPlayers.txt")
players = fileObj~makeArray
do line over players
line = line~strip
if line~length == 0 | line~abbrev("#") | line~abbrev("/*") then iterate
parse var line name', 'team', 'p', 'rating .
r = .Directory~new
r~player = name
r~team = team
r~pos = p
r~rating = rating
rows~append(r)
end
fileObj~close
return rows
/* Class: DragItem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
Drag items are an encapsulation of the data needed to drag and drop one row
of a list view to another.
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
::class 'DragItem'
::attribute target
::method init
expose lv index row target
use arg lv, index, p, mouse
row = .Directory~new
lv~getItemInfo(index, row)
row~player = row~text
lv~getItemInfo(index, row, 1)
row~team = row~text
lv~getItemInfo(index, row, 2)
row~pos = row~text
lv~getItemInfo(index, row, 3)
row~rating = row~text
target = .nil
::method drop
expose lv index row target
use strict arg pt = 0
if target \== .nil then do
-- Drop the row at the end of the list by default.
targetIndex = target~items
if pt~isA(.Point) then do
i = target~hitTestInfo(pt)
if i <> -1 then targetIndex = i
end
target~addRow(targetIndex, , row~player, row~team, row~pos, row~rating)
lv~delete(index)
count = lv~items
if count <> 0 then do
if index == count then lv~select(index - 1)
else lv~select(index)
end
end