Instantly share code, notes, and snippets.
Last active
November 7, 2017 16:15
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save trevordevore/2f0e33ec3fa3f6df9a0dbc7beb4cf45c to your computer and use it in GitHub Desktop.
Fixes issues where mobileScroller is not recreated when returning to card with DataGrid on it
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local sDataArray -- Multi-dimensional array | |
local sIndexSequencing -- sDataArray indexes in order they should appear | |
local sInit -- Has control been opened once before? | |
local sControlIsOpen | |
constant kDefaultDimmedHiliteColor = "212,212,212" | |
constant kRowColor = "255,255,255" | |
constant kHeaderBkgrndStartColor = "219,219,219" | |
constant kHeaderBkgrndEndColor = "188,188,188" | |
constant kHeaderBkgrndHiliteStartColor = "194,207,221" | |
constant kHeaderBkgrndHiliteEndColor = "125,147,148" | |
constant kHeaderDividerColor = "168,168,168" | |
constant kHeaderDividerThreeDColor = "227,227,227" | |
constant kAlternateRowColor = "230,237,247" | |
constant kDefaultTableColWidth = 100 | |
constant kDefaultRowHeight = 21 | |
constant kDefaultRowColor = "255,255,255" | |
constant kDefaultCornerColor = "232,232,232" | |
constant kAlternatingRowModValue = 0 -- 0 to have first color be first, 1 to have first color be alternating color | |
constant kSortTypes = "text,international,numeric,datetime,system datetime" | |
constant kSBWidthWin = 17 | |
constant kSBWidthMac = 15 | |
constant kSBWidthLinux = 16 | |
constant kErrInvalidArray = 422 | |
constant kErrInvalidBoolean = 452 | |
constant kErrInvalidNumber = 453 | |
constant kErrPropDoesntExist = 456 | |
constant kErrInvalidInteger = 354 | |
constant kErrInvalidPoint = 355 | |
constant kErrInvalidRect = 356 | |
constant kErrInvalidColor = 343 | |
constant kErrReadOnlyProp = 449 | |
constant kErrInvalidProperty = 348 | |
constant kErrCantFindObject = 619 | |
constant kErrRenameErrorInDestination = 487 | |
constant kFieldEditorName = "DataGridFieldEditor" | |
local sFieldEditor | |
local sControlOfIndexA -- Links an index to a particular control. Used when dgProps["cache controls"] = true and drawing is not done in real time. | |
local sControlHeights -- if uFixedLineHeight is true then the height of a single contorl. Otherwise an array keyed by INDEX of sDataArray. | |
local sFormattedHeight | |
local sFormattedWidth | |
local sControlsRequiredToFillSpace | |
local sHilitedIndexes -- comma delimited list | |
local sFirstIndexClickedWithShiftKeyDown -- When shift clicking we need to store first index clicked on for reference | |
local sPendingMsgsA | |
local sRunningActionsA | |
local sIsAnimating = false | |
local sDeselectOnMouseUp | |
local sSystemA -- stores system specific settings | |
local sFocusLeftMe | |
local sDefaultDropIndicatorRect | |
local sDropStructure | |
local sLockDrawing -- todo: add set/get. When locked we don't redraw when data is created, deleted, reordered or updated. | |
## for tables | |
local sTableObjectsA | |
local sScrollerId | |
local sPollID | |
--> Messages (engine) | |
before preopenControl | |
if the target is not me then pass preopencontrol | |
local isOpen | |
put sControlIsOpen into isOpen | |
try | |
_Initialize | |
put true into sControlIsOpen | |
if not isOpen then | |
_DrawAlternatingRows | |
end if | |
catch e | |
put e | |
end try | |
pass preopenControl | |
end preopenControl | |
before closeControl | |
if the target is not me then pass closeControl | |
DeleteFieldEditor | |
put false into sControlIsOpen | |
put true into sFocusLeftMe | |
if the environment is "mobile" then | |
mobileControlDelete sScrollerId | |
put empty into sScrollerId | |
end if | |
cancel sPollID | |
pass closeControl | |
end closeControl | |
before newGroup | |
if the target is not me then pass newGroup | |
_Initialize | |
pass newGroup | |
end newGroup | |
on __PollVisibility | |
cancel sPollID | |
if __HasMobileScroller() then | |
send "__PollVisibility" to me in 300 milliseconds | |
put the result into sPollID | |
mobileControlSet sScrollerId, "visible", the effective visible of me | |
end if | |
end __PollVisibility | |
private command _Initialize | |
if not sInit then | |
set the wholeMatches to true | |
local theMasterRect | |
put the rect of me into theMasterRect | |
switch the platform | |
case "Win32" | |
if "registryRead" is among the items of the securityPermissions then | |
put queryRegistry("HKEY_CURRENT_USER\Control Panel\Colors\HilightText") into sSystemA["hilited text color"] | |
if sSystemA["hilited text color"] is not empty then | |
replace space with comma in sSystemA["hilited text color"] | |
end if | |
local theValue | |
put queryRegistry("HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics\ScrollWidth") into theValue | |
if theValue is an integer then | |
put abs(theValue/15) into sSystemA["scrollbarWidth"] | |
end if | |
end if | |
break | |
end switch | |
## Set behaviors as needed | |
## These behaviors weren't set in original template | |
set the behavior of group "dgHeaderMask" of me to the long ID of button "Header Mask" of group "Behaviors" of stack _ResourceStack() | |
set the behavior of group "dgList" of me to the long ID of button "dgList Message Catcher" of group "Behaviors" of stack _ResourceStack() | |
## Developer could have set data before control opened | |
if the keys of sDataArray is empty then | |
_DeleteControls | |
_RestorePersistentData | |
end if | |
if the environment is not "mobile" then | |
set the traversalOn of scrollbar "dgScrollbar" of me to false ## take out of tabbing | |
set the traversalOn of scrollbar "dgHScrollbar" of me to false ## take out of tabbing | |
set the visible of scrollbar "dgScrollbar" of me to the dgProps["show vscrollbar"] of me is not false | |
if _ControlType() is "table" then | |
set the visible of group "dgHorizontalComponents" of me to the dgProps["show hscrollbar"] of me is not false | |
end if | |
set the borderWidth of scrollbar "dgScrollbar" of me to 0 ## For Windows 2000 | |
set the borderWidth of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me to 0 ## For Windows 2000 | |
_SetScrollbarWidth | |
else | |
_CreateMobileScroller | |
end if | |
## Set props which didn't come with original Data Grid | |
if the dgProps["header divider color"] of me is not a color then | |
set the dgProps["header divider color"] of me to kHeaderDividerColor | |
end if | |
if the dgProps["header divider threeD color"] of me is not a color then | |
set the dgProps["header divider threeD color"] of me to kHeaderDividerThreeDColor | |
end if | |
if _ControlType() is "table" then | |
_table.CreateColumns | |
end if | |
_ResetTemplateFieldEditor | |
## Make sure rect is as it started | |
lock messages | |
set the rect of me to theMasterRect | |
unlock messages | |
put true into sInit | |
else | |
_CreateMobileScroller | |
end if | |
ResizeToFit | |
end _Initialize | |
private command _CreateMobileScroller | |
set the visible of scrollbar "dgScrollbar" of me to false | |
set the visible of scrollbar "dgHScrollbar" of me to false | |
if the dgProps["show vscrollbar"] of me is not false or \ | |
(_ControlType() is "table" and \ | |
the dgProps["show hscrollbar"] of me is not false) then | |
if sScrollerId is empty or sScrollerId is not among the lines of mobileControls() then | |
mobileControlCreate "scroller" | |
put the result into sScrollerId | |
end if | |
mobileControlSet sScrollerId, "canBounce", "true" | |
mobileControlSet sScrollerId, "pagingEnabled", "false" | |
mobileControlSet sScrollerId, "canScrollToTop", "true" | |
mobileControlSet sScrollerId, "delayTouches", "true" | |
mobileControlSet sScrollerId, "canCancelTouches", "true" | |
__PollVisibility | |
end if | |
end _CreateMobileScroller | |
command _SetScrollbarWidth pWidth | |
lock screen | |
if pWidth is not an integer then | |
if the dgProps["scrollbar width"] of me is an integer then | |
put the dgProps["scrollbar width"] of me into pWidth | |
else | |
if sSystemA["scrollbarWidth"] is an integer then | |
put sSystemA["scrollbarWidth"] into pWidth | |
else | |
if the platform is "MacOS" then | |
put kSBWidthMac into pWidth | |
else if the platform is "win32" then | |
put kSBWidthWin into pWidth | |
else | |
put kSBWidthLinux into pWidth | |
end if | |
end if | |
end if | |
end if | |
local theRectV, theRectH | |
put the rect of scrollbar "dgScrollbar" of me into theRectV | |
put the rect of scrollbar "dgHScrollbar" of me into theRectH | |
put item 3 of theRectV - pWidth into item 1 of theRectV | |
put item 4 of theRectH - pWidth into item 2 of theRectH | |
set the rect of scrollbar "dgScrollbar" of me to theRectV | |
set the rect of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me to theRectH | |
unlock screen | |
end _SetScrollbarWidth | |
before deleteGroup | |
if the target is not me then pass deleteGroup | |
end deleteGroup | |
before resizeControl | |
if the target is not me then return empty | |
ResizeToFit | |
pass resizeControl | |
end resizeControl | |
before focusIn | |
## update the hilite when focus enters control | |
if sFocusLeftMe is not false then | |
_UpdateHiliteColor | |
end if | |
put false into sFocusLeftMe | |
pass focusIn | |
end focusIn | |
before openField | |
if sFocusLeftMe is not false then | |
_UpdateHiliteColor | |
end if | |
pass openField | |
end openField | |
before focusOut | |
## As of Rev 3.5 we don't know whether or not focus is leaving the data grid in focusOut | |
## We send a message in time and check the focusedobject in order to work around this. | |
## This is not ideal and leads to other issues as focus shifts around some UIs but it is all we have. | |
send "_CheckForFocusLeavingMe" to me in 0 milliseconds | |
pass focusOut | |
end focusOut | |
before closeField | |
send "_CheckForFocusLeavingMe" to me in 0 seconds | |
pass closeField | |
end closeField | |
before exitField | |
send "_CheckForFocusLeavingMe" to me in 0 seconds | |
pass exitField | |
end exitField | |
command _CheckForFocusLeavingMe | |
if not sControlIsOpen then return empty | |
if the long ID of me is not in the long ID of the focusedObject then | |
## focus left me | |
put true into sFocusLeftMe | |
_UpdateHiliteColor | |
end if | |
end _CheckForFocusLeavingMe | |
## Store data that will be sent when user reorders row | |
setprop dgDragReorderData pValue | |
put pValue into sDropStructure["drag drop data"] | |
end dgDragReorderData | |
getprop dgDragReorderData | |
return sDropStructure["drag drop data"] | |
end dgDragReorderData | |
setprop dgDragImageIndex pIndex | |
if _ControlType() is "form" then | |
local theControl | |
put the dgDataControlOfIndex[pIndex] of me into theControl | |
if theControl is not empty then | |
_CreateDragImageFromControl theControl | |
end if | |
else | |
_CreateDragImageFromIndex pIndex | |
end if | |
end dgDragImageIndex | |
setprop dgDragImageLine pLine | |
if _ControlType() is "form" then | |
local theControl | |
put the dgDataControlOfLine[pLine] of me into theControl | |
if theControl is not empty then | |
_CreateDragImageFromControl theControl | |
end if | |
else | |
_CreateDragImageFromIndex the dgIndexOfLine[pLine] of me | |
end if | |
end dgDragImageLine | |
private command _CreateDragImageFromControl pControl | |
if there is not a image "dgDragImage" of me then | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
reset the templateImage | |
create image "dgDragImage" in me | |
set the lockMessages to msgsAreLocked | |
set the visible of it to false | |
unlock screen | |
end if | |
export snapshot from pControl to image "dgDragImage" of me as PNG | |
local theImageOffset | |
set the dragImage to the ID of image "dgDragImage" of me | |
put the clickH - the left of pControl & comma & \ | |
the clickV - the top of pControl into theImageOffset | |
set the dragImageOffset to theImageOffset | |
end _CreateDragImageFromControl | |
private command _CreateDragImageFromIndex pIndex | |
local theControls, theRect, theVisibleRect, msgsAreLocked | |
put sTableObjectsA["columns"][ line 1 of the keys of sTableObjectsA["columns"] ]["row controls"] into theControls | |
repeat for each line theControl in theControls | |
if the dgIndex of theControl is pIndex then | |
put the rect of theControl into theRect | |
put _VisibleDataRect() into theVisibleRect | |
put item 1 of theVisibleRect into item 1 of theRect | |
put item 3 of theVisibleRect into item 3 of theRect | |
if there is not a image "dgDragImage" of me then | |
lock screen | |
put the lockMessages into msgsAreLocked | |
lock messages | |
reset the templateImage | |
create image "dgDragImage" in me | |
set the lockMessages to msgsAreLocked | |
set the visible of it to false | |
unlock screen | |
end if | |
export snapshot from rect theRect of this card to image "dgDragImage" of me as PNG | |
set the dragImage to the ID of image "dgDragImage" of me | |
local theImageOffset | |
put the clickH - item 1 of theRect & comma & \ | |
the clickV - item 2 of theRect into theImageOffset | |
set the dragImageOffset to theImageOffset | |
exit repeat | |
end if | |
end repeat | |
end _CreateDragImageFromIndex | |
## Set to true to turn on drag indicator and announce | |
## if a successful drop occurs | |
setprop dgTrackDragReorder [pOriginatingIndex] pValue | |
if there is not a group "dgDropIndicator" of me then throw "Group Drag Indicator does not exist in list group:" && the long ID of me | |
if pValue is not sDropStructure["tracking"] then | |
if pValue then | |
put pOriginatingIndex into sDropStructure["originating index"] | |
set the topLeft of group "dgDropIndicator" of me to the topLeft of group "dgList" of me | |
dispatch "resizeControl" to group "dgDropIndicator" of me | |
put the rect of group "dgDropIndicator" of me into sDefaultDropIndicatorRect | |
insert script of button "dgTrackDragDrop" of me into front | |
set the dgTargetControl of group "dgDropIndicator" of me to the long ID of me | |
## initialize index mouse is over | |
dgDragMove the mouseH, the mouseV | |
else | |
remove script of button "dgTrackDragDrop" of me from front | |
set the visible of group "dgDropIndicator" of me to false | |
put empty into sDropStructure | |
if there is a image "dgDragImage" of me then | |
delete image "dgDragImage" of me | |
end if | |
end if | |
put pValue is true into sDropStructure["tracking"] | |
end if | |
end dgTrackDragReorder | |
on dgDragEnd | |
set the dgTrackDragReorder of me to false | |
end dgDragEnd | |
on dgDragDrop | |
## Cache before we clean up | |
local processTheDrop | |
put the visible of group "dgDropIndicator" of me into processTheDrop | |
local theDropStructure | |
put sDropStructure into theDropStructure | |
## If some other control displays a dialog during dragDrop, dragMove will still be sent | |
## if the mouse moves within the dialog. We don't want to process those any longer at this point. | |
set the dgTrackDragReorder of me to false | |
if processTheDrop then | |
local theStartLine, theDroppedAfterLine, theDroppedOnLine | |
put the dgLineOfIndex[ theDropStructure["originating index"] ] of me into theStartLine | |
put max(0, the dgLineOfIndex[ theDropStructure["dropped after index"] ] of me) into theDroppedAfterLine | |
put theDroppedAfterLine into theDroppedOnLine | |
if theDroppedAfterLine < theStartLine then add 1 to theDroppedOnLine | |
dispatch "DragReorderDrop" with theDropStructure["originating index"], \ | |
theStartLine, theDroppedOnLine | |
end if | |
end dgDragDrop | |
## Sent from TrackDragDrop | |
on dgDragMove pMouseH,pMouseV | |
## Throttle calls to this | |
if sRunningActionsA["drag move"] then return empty | |
## Make sure data grid control will accept drop | |
if the dragSource contains the long ID of me and (the mouseControl is not empty and the long id of the mouseControl contains the long ID of me) then | |
set the dragAction to "move" | |
end if | |
put true into sRunningActionsA["drag move"] | |
try | |
_PositionDropIndicator pMouseH,pMouseV | |
## We can't send messages so hang out until mouseloc changes | |
repeat until (the mouseLoc is not pMouseH,pMouseV) or (mouse(1) is "up" and mouse(3) is "up") | |
## Throttle calls to redrawing | |
if not sRunningActionsA["v scroll"] then | |
_ListGroupDragReorderAutoScroll pMouseH,pMouseV | |
_PositionDropIndicator pMouseH, pMouseV | |
end if | |
end repeat | |
catch e | |
end try | |
put false into sRunningActionsA["drag move"] | |
end dgDragMove | |
private function _VisibleDataRect | |
local theRect | |
put the rect of group "dgListMask" of me into theRect | |
if _ControlType() is "table" then | |
if the visible of scrollbar "dgScrollbar" of me then | |
put the left of scrollbar "dgScrollbar" of me into item 3 of theRect | |
else | |
put the right of group "dgHeaderComponents" of me into item 3 of theRect | |
end if | |
end if | |
return theRect | |
end _VisibleDataRect | |
private command _PositionDropIndicator pMouseH, pMouseV | |
----- | |
local i,theX,theY | |
local index, theIndex | |
local showDropIndicator, theDropIndicatorOffset | |
local theControl, theControlIndex | |
local theControlsToCheck | |
local theCorralRect | |
local theEndControl | |
local theHeight | |
local theSequence | |
local theStartControl | |
local theTargetControl | |
----- | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
set the wholeMatches to true | |
put false into showDropIndicator | |
if pMouseH,pMouseV is within _VisibleDataRect() then --the rect of group "dgListMask" of me then | |
put true into showDropIndicator | |
if the mouseControl is not empty then | |
if not (the long ID of the mouseControl contains the long ID of me) then | |
put false into showDropIndicator ## control over the list but not part of the list | |
else | |
put the dgDataControl of the mouseControl into theTargetControl | |
## Check for drags in empty columns of tables. | |
if theTargetControl is empty then | |
if the target is not the name of scrollbar "dgScrollbar" of me then | |
## Perhaps no column in clicked area or no mouse receiving controls in column | |
repeat for each line thePotentialControl in _ListOfVisibleControls() | |
local theRect | |
put the rect of thePotentialControl into theRect | |
if pMouseV >= item 2 of theRect and pMouseV <= item 4 of theRect then | |
put the dgIndex of thePotentialControl into theIndex | |
put thePotentialControl into theTargetControl | |
exit repeat | |
end if | |
end repeat | |
end if | |
## Still no control | |
## Assume we are below the last control | |
if theTargetControl is empty then | |
put the last line of _ListOfVisibleControls() into theTargetControl ## explicitly draw on only visible controls | |
end if | |
end if | |
if theTargetControl is not empty then | |
-- put true into showDropIndicator | |
## Determine offset of control in list | |
put _LineNoOfControlInListControls(theTargetControl, the dgColumn of theTargetControl) into theControlIndex | |
-- put "ControlIndex:" && theControlIndex | |
## Look for next/prev control that is not filtered out | |
## Direction we look depends on position of mouse | |
## relative to Y of control | |
put item 2 of the loc of theTargetControl into theY | |
## Look for start/end controls in our drop | |
if the dgAcceptsDrop of theTargetControl is false then | |
## Look for next control that accepts a drop | |
put line theControlIndex to -1 of sTableObjectsA["visible row controls"] into theControlsToCheck | |
repeat for each line theControl in theControlsToCheck | |
if the dgAcceptsDrop of theControl is false then next repeat | |
else | |
put theControl into theEndControl | |
exit repeat | |
end if | |
end repeat | |
## Look for preceding control that accepts a drop | |
repeat with i = theControlIndex down to 1 | |
put line i of sTableObjectsA["visible row controls"] into theControl | |
if the dgAcceptsDrop of theControl is false then next repeat | |
else | |
put theControl into theStartControl | |
exit repeat | |
end if | |
end repeat | |
else | |
put theTargetControl into theStartControl | |
put theTargetControl into theEndControl | |
end if | |
## What is height of drop area as defined by start/end controls? | |
if theStartControl is empty and theEndControl is empty then | |
## No drop. Hide the drop indicator | |
put false into showDropIndicator | |
else | |
if theStartControl is empty then put theEndControl into theStartControl | |
else if theEndControl is empty then put theStartControl into theEndControl | |
put the bottom of theEndControl - the top of theStartControl into theHeight | |
if pMouseV < (the top of theStartControl + round(theHeight / 2)) then | |
## if start/end control are same OR the mouse is above theStartControl then use | |
## the top of control as reference | |
if theStartControl is theEndControl or pMouseV < the top of theStartControl then | |
put the top of theStartControl into theY | |
put the dgIndex of theStartControl into theIndex | |
put itemOffset(theIndex, sIndexSequencing) into theSequence | |
put max(item (theSequence - 1) of sIndexSequencing, 0) into sDropStructure["dropped after index"] | |
else | |
put the bottom of theStartControl into theY | |
put the dgIndex of theStartControl into sDropStructure["dropped after index"] | |
end if | |
else | |
## If start/end control are same OR the mouse below theEndControl then use | |
## bottom of control as reference | |
if theEndControl is theStartControl or pMouseV > the bottom of theEndControl then | |
put the bottom of theEndControl into theY | |
put the dgIndex of theEndControl into sDropStructure["dropped after index"] | |
else | |
put the top of theEndControl into theY | |
put the dgIndex of theEndControl into theIndex | |
put itemOffset(theIndex, sIndexSequencing) into theSequence | |
put max(item (theSequence - 1) of sIndexSequencing, 0) into sDropStructure["dropped after index"] | |
end if | |
end if | |
## Position drop indicator | |
put _VisibleDataRect() into theCorralRect | |
put (item 4 of sDefaultDropIndicatorRect - item 2 of sDefaultDropIndicatorRect) / 2 div 1 into theDropIndicatorOffset ## div 1 = floor | |
lock screen | |
lock messages | |
put item 1 of sDefaultDropIndicatorRect into theX | |
put min(item 4 of theCorralRect, max(item 2 of theCorralRect - theDropIndicatorOffset, theY)) into theY ## don't let outside of the List group | |
put theY - theDropIndicatorOffset into theY | |
set the topLeft of group "dgDropIndicator" of me to theX, theY | |
set the visible of group "dgDropIndicator" of me to true | |
unlock messages | |
dispatch "PositionDropIndicator" to group "dgDropIndicator" of me | |
unlock screen | |
end if | |
end if | |
end if ## mousecontrol is part of list group control | |
else | |
## mousecontrol is empty | |
end if | |
end if ## within rect of list | |
set the lockMessages to msgsAreLocked | |
if not showDropIndicator then | |
set the visible of group "dgDropIndicator" of me to false | |
end if | |
end _PositionDropIndicator | |
private function _LineNoOfControlInListControls pControl, pColumn | |
----- | |
local theLineNo = 0 | |
local theListControl | |
local i | |
----- | |
if _ControlType() is "form" then | |
repeat for each line theListControl in sTableObjectsA["visible row controls"] | |
add 1 to i | |
## We use this technique so that the developer can have uListConrol return a child of the actual list control | |
## Useful when a record can have more than one possible view. | |
if pControl ends with theListControl then | |
put i into theLineNo | |
exit repeat | |
end if | |
end repeat | |
else | |
local theShortID | |
put the short ID of pControl into theShortID | |
repeat for each line theListControl in sTableObjectsA["columns"][ pColumn ]["row controls"] | |
add 1 to i | |
## We use this technique so that the developer can have uListConrol return a child of the actual list control | |
## Useful when a record can have more than one possible view. | |
if word 3 of theListControl is theShortID then | |
put i into theLineNo | |
exit repeat | |
end if | |
end repeat | |
end if | |
return theLineNo | |
end _LineNoOfControlInListControls | |
private command _ListGroupDragReorderAutoScroll pMouseH, pMouseV | |
local theMaskRect | |
put the rect of group "dgListMask" of me into theMaskRect | |
## Check that mouse is within vertical space of group | |
if pMouseH > item 1 of theMaskRect and pMouseH < item 3 of theMaskRect then | |
local theAvailHeight, theScroll, theHotZone, theDiff | |
put item 4 of theMaskRect - item 2 of theMaskRect into theAvailHeight | |
put round(theAvailHeight * .1) into theScroll | |
## Decide the zone where auto scrolling will occur | |
if the dgProps["fixed row height"] of me then | |
put round(sControlHeights / 2) into theHotZone | |
else | |
put 20 into theHotZone ## 20 is arbitrary number | |
end if | |
## First check above | |
put item 2 of theMaskRect - pMouseV into theDiff | |
if theDiff > 0 and theDiff <= theHotZone then | |
put theDiff * -1 into theDiff | |
-- put (theHotZone - theDiff) * -1 into theDiff ## This speeds up scrolling too much | |
else | |
put pMouseV - item 4 of theMaskRect into theDiff | |
-- put item 4 of theMaskRect - pMouseV into theDiff | |
if theDiff > 0 and theDiff <= theHotZone then | |
-- put (theHotZone - theDiff) into theDiff | |
else | |
put 0 into theDiff | |
end if | |
end if | |
if abs(theDiff) > 0 and abs(theDiff) <= theHotZone then | |
local theCurrentScroll | |
## Mouse is in a hot zone; auto scroll. | |
put round((theDiff * 5) / 100 * theScroll) into theScroll | |
put the dgVScroll of me into theCurrentScroll | |
put theCurrentScroll + theScroll into theScroll | |
_SetVScroll theScroll | |
end if | |
end if | |
end _ListGroupDragReorderAutoScroll | |
on scrollbarDrag pScrollValue | |
if the long ID of the target is the long ID of scrollbar "dgScrollbar" of me then | |
dgScrollbarDragV pScrollValue | |
else if the long ID of the target is the long ID of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me then | |
dgScrollbarDragH pScrollValue | |
else | |
pass "scrollbarDrag" | |
end if | |
end scrollbarDrag | |
command dgScrollbarDragV pScrollValue | |
_ScrollListV pScrollValue | |
end dgScrollbarDragV | |
command dgScrollbarDragH pScrollValue | |
_ScrollListH pScrollValue | |
end dgScrollbarDragH | |
before arrowKey pDirection | |
dgArrowKey pDirection | |
pass arrowkey | |
-- if the result then pass arrowkey | |
end arrowKey | |
command dgArrowKey pDirection | |
## Look for cases where we pass arrowkey | |
if the selectedField is not empty and not the lockText of the selectedField then return true | |
if the dgProps["auto hilite"] of me is false then return true | |
## Don't let queue of key repeats build up or list might keep scrolling after user releases arrow key | |
## Mac needs 'autokey'. Windows needs 'keyDown'. | |
get flushEvents("autokey") | |
get flushEvents("keyDown") | |
local theSelectionChanged | |
put false into theSelectionChanged | |
lock screen | |
if sHilitedIndexes is not empty then | |
set the wholeMatches to true | |
local thePreviouslyHilitedIndexes | |
put sHilitedIndexes into thePreviouslyHilitedIndexes | |
switch pDirection | |
case "up" | |
local theItemNo | |
put itemOffset(item 1 of sHilitedIndexes, sIndexSequencing) into theItemNo | |
if _IsThisModifierSetActive("shift") and the dgProps["multiple lines"] of me then | |
if theItemNo > 1 then | |
local theNewIndex | |
put max(1, item (theItemNo - 1) of sIndexSequencing) into theNewIndex | |
put itemOffset(theNewIndex, thePreviouslyHilitedIndexes) into theItemNo | |
if theItemNo is 0 then | |
put comma & theNewIndex after thePreviouslyHilitedIndexes | |
set the dgHilitedIndexes of me to thePreviouslyHilitedIndexes | |
put true into theSelectionChanged | |
end if | |
end if | |
else | |
put max(1, item (theItemNo - 1) of sIndexSequencing) into theNewIndex | |
if theItemNo > 1 then | |
set the dgHilitedIndexes of me to theNewIndex | |
put true into theSelectionChanged | |
else if theItemNo is 1 and sHilitedIndexes is not theNewIndex then | |
## Case for multiple selections going all the way to top | |
set the dgHilitedIndexes of me to item 1 of sIndexSequencing | |
put true into theSelectionChanged | |
end if | |
end if | |
break | |
case "down" | |
put itemOffset(the last item of sHilitedIndexes, sIndexSequencing) into theItemNo | |
if _IsThisModifierSetActive("shift") and the dgProps["multiple lines"] of me then | |
if theItemNo > 0 and theItemNo < the number of items of sIndexSequencing then | |
put item (theItemNo + 1) of sIndexSequencing into theNewIndex | |
put itemOffset(theNewIndex, thePreviouslyHilitedIndexes) into theItemNo | |
if theItemNo is 0 then | |
put comma & theNewIndex after thePreviouslyHilitedIndexes | |
put _SortIndexesSequentially(thePreviouslyHilitedIndexes) into thePreviouslyHilitedIndexes | |
put thePreviouslyHilitedIndexes into sHilitedIndexes | |
ScrollIndexIntoView theNewIndex | |
_HiliteIndexesInVisibleControls | |
put true into theSelectionChanged | |
end if | |
end if | |
else | |
local theIndexCount | |
put the number of items of sIndexSequencing into theIndexCount | |
put item (theItemNo + 1) of sIndexSequencing into theNewIndex | |
if theItemNo > 0 and theItemNo < theIndexCount then | |
set the dgHilitedIndexes of me to theNewIndex | |
put true into theSelectionChanged | |
else if theItemNo is theIndexCount and sHilitedIndexes is not theNewIndex then | |
## Case for multiple selections going all the way to bottom | |
set the dgHilitedIndexes of me to the last item of sIndexSequencing | |
put true into theSelectionChanged | |
end if | |
end if | |
break | |
end switch | |
else if the dgNumberOfRecords of me > 0 then | |
set the dgHilitedIndexes of me to 1 | |
end if | |
if theSelectionChanged then _SelectionChanged thePreviouslyHilitedIndexes | |
unlock screen | |
return false | |
end dgArrowKey | |
## Scrolls the group vertically when using the scroll wheel | |
before rawKeyDown pKeyNum | |
## exit immediately if an editable field is the target and action isn't scroll wheel. | |
set the wholeMatches to true | |
if word 1 of the target is "field" and not the lockText of the target and pKeyNum is not among the items of "65308,65309,65310,65311" then pass rawkeydown | |
-- 65308 - Mouse wheel down | |
-- 65309 - Mouse wheel up | |
-- 65310 - Mouse wheel right | |
-- 65311 - Mouse wheel left | |
-- 65365 - page up | |
-- 65366 - page down | |
-- 65360 - home | |
-- 65367 - end | |
## Note: Messages are locked when setting thumbpostion and we call scrolling code directly. | |
## This is done so that scrolling works when scrollbars are hidden. | |
switch pKeyNum | |
case "65360" | |
## home | |
if the visible of scrollbar "dgScrollbar" of me or the dgProps["scroll when vScrollbar is hidden"] of me then | |
_SetVScroll 0 | |
end if | |
break | |
case "65367" | |
## end | |
if the visible of scrollbar "dgScrollbar" of me or the dgProps["scroll when vScrollbar is hidden"] of me then | |
_SetVScroll the endValue of scrollbar "dgScrollbar" of me | |
end if | |
break | |
case "65365" | |
## scroll page up | |
if the visible of scrollbar "dgScrollbar" of me or the dgProps["scroll when vScrollbar is hidden"] of me then | |
_SetVScroll the thumbPosition of scrollbar "dgScrollbar" of me - \ | |
the pageIncrement of scrollbar "dgScrollbar" of me | |
end if | |
break | |
case "65366" | |
## scroll page down | |
if the visible of scrollbar "dgScrollbar" of me or the dgProps["scroll when vScrollbar is hidden"] of me then | |
_SetVScroll the thumbPosition of scrollbar "dgScrollbar" of me + \ | |
the pageIncrement of scrollbar "dgScrollbar" of me | |
end if | |
break | |
case "65309" # scroll up | |
## mouse wheel up | |
if the visible of scrollbar "dgScrollbar" of me or the dgProps["scroll when vScrollbar is hidden"] of me then | |
local theAvailHeight | |
put the height of group "dgListMask" of me into theAvailHeight | |
local theScroll | |
put round(theAvailHeight * .1) into theScroll | |
_SetVScroll round(the thumbPosition of scrollbar "dgScrollbar" of me) - theScroll | |
end if | |
break | |
case "65308" # scroll down | |
## mouse wheel down | |
if the visible of scrollbar "dgScrollbar" of me or the dgProps["scroll when vScrollbar is hidden"] of me then | |
put the height of group "dgListMask" of me into theAvailHeight | |
put round(theAvailHeight * .1) into theScroll | |
_SetVScroll round(the thumbPosition of scrollbar "dgScrollbar" of me) + theScroll | |
end if | |
break | |
case "65310" | |
## mouse wheel right | |
if _ControlType() is "table" and (the visible of group "dgHorizontalComponents" of me or the dgProps["scroll when hScrollbar is hidden"] of me) then | |
local theAvailWidth | |
put the width of group "dgList" of me into theAvailWidth | |
put round(theAvailWidth * .1) into theScroll | |
_SetHScroll round(the thumbPosition of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me) + theScroll | |
end if | |
break | |
case "65311" | |
## mouse wheel left | |
if _ControlType() is "table" and (the visible of group "dgHorizontalComponents" of me or the dgProps["scroll when hScrollbar is hidden"] of me) then | |
put the width of group "dgList" of me into theAvailWidth | |
put round(theAvailWidth * .1) into theScroll | |
_SetHScroll round(the thumbPosition of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me) - theScroll | |
end if | |
break | |
default | |
pass rawKeyDown | |
end SWITCH | |
end rawKeyDown | |
before mouseDown pMouseBtnNum | |
// Mikey-2015-05-19: [[ Bug 15387 ]] Don't send dgMouseDown on mobile on mouseUp | |
if the environment is "mobile" then exit mouseDown | |
dgMouseDown pMouseBtnNum | |
pass mousedown | |
end mouseDown | |
// Mikey-2015-05-19: [[ Bug 15387 ]] Use touchStart for mobile | |
before touchStart | |
dgMouseDown 1 | |
pass touchStart | |
end touchStart | |
getprop dgClickLine | |
local theIndex | |
put the dgClickIndex of me into theIndex | |
if theIndex > 0 then return the dgLineOfIndex[theIndex] of me | |
else return empty | |
end dgClickLine | |
getprop dgClickIndex | |
## figure out the index clicked on | |
local theIndex, theControl | |
put empty into theIndex | |
put _RowControlClickedOn() into theControl | |
if theControl is not empty then | |
put the dgIndex of theControl into theIndex | |
end if | |
return theIndex | |
end dgClickIndex | |
getprop dgClickedInDataView | |
## Note that Rev doesn't report scrollbars as mousecontrol if thumb is not | |
## showing. Hence the checks for visibility and click not within of vertical scrollbar | |
## (dglist overlaps with rect of vertical scrollbar) | |
local theClickLoc | |
put the clickLoc into theClickLoc | |
return theClickLoc is within the rect of group "dgListMask" of me \ | |
and (not the visible of scrollbar "dgScrollbar" of me \ | |
or (the visible of scrollbar "dgScrollbar" of me and theClickLoc is not within the rect of scrollbar "dgScrollbar" of me)) | |
end dgClickedInDataView | |
private function _RowControlClickedOn | |
local theControl | |
## figure out the index clicked on | |
put the dgDataControl of the target into theControl | |
local theY, theRect | |
## Make sure user clicked in valid area. | |
if theControl is empty and the dgClickedInDataView of me then | |
## Perhaps no column in clicked area or no mouse receiving controls in column | |
put the clickV into theY | |
repeat for each line thePotentialControl in _ListOfVisibleControls() | |
put the rect of thePotentialControl into theRect | |
if theY >= item 2 of theRect and theY <= item 4 of theRect then | |
put thePotentialControl into theControl | |
exit repeat | |
end if | |
end repeat | |
end if | |
return theControl | |
end _RowControlClickedOn | |
command dgMouseDown pMouseBtnNum | |
## figure out the index clicked on | |
local theIndex, theControl | |
put empty into theIndex | |
put _RowControlClickedOn() into theControl | |
if theControl is not empty then | |
put the dgIndex of theControl into theIndex | |
end if | |
## Bring focus into control. Do this before any possible exit points. | |
if theIndex is not empty then | |
## Clicked on a control. Bring focus into control but away from scrollbar. | |
if the long ID of me is not in the long ID of the focusedObject or word 1 of the target is "scrollbar" or \ | |
(sFieldEditor is not empty and sFieldEditor is the long ID of the focusedObject) then | |
focus on graphic "dgBackground" of me | |
end if | |
else | |
## clicked in space but no on list control | |
focus on graphic "dgBackground" of me | |
end if | |
if the dgProps["auto hilite"] of me is false then return empty | |
local theSelectionChanged | |
put false into theSelectionChanged | |
local unlockTheScreen | |
put true into unlockTheScreen | |
lock screen | |
set the wholeMatches to true | |
## Close field editor. Note: I don't think the target can ever be sFieldEditor | |
if sFieldEditor is not empty and the long ID of the target is not sFieldEditor then | |
DeleteFieldEditor true | |
end if | |
## No do what we need to do | |
if theIndex is not empty then | |
local theHilitedIndexes, thePreviouslyHilitedIndexes | |
put sHilitedIndexes into theHilitedIndexes | |
put sHilitedIndexes into thePreviouslyHilitedIndexes | |
if pMouseBtnNum is 1 then | |
// Mikey-2015-05-19: [[ Bug 15240 ]] for mobile, if "multiple lines", then every tap selects/deselects a line | |
if (_IsThisModifierSetActive("command") or the environment is "mobile") and the dgProps["multiple lines"] of me then | |
## Add or remove hilite | |
local theItemNo | |
put itemOffset(theIndex, theHilitedIndexes) into theItemNo | |
if theItemNo > 0 then | |
delete item theItemNo of theHilitedIndexes | |
else | |
put theIndex into item (the number of items of theHilitedIndexes + 1) of theHilitedIndexes | |
ScrollIndexIntoView theIndex | |
end if | |
put _SortIndexesSequentially(theHilitedIndexes) into theHilitedIndexes | |
## At this point we want to hilite controls but not scroll them | |
put theHilitedIndexes into sHilitedIndexes | |
_HiliteIndexesInVisibleControls | |
put true into theSelectionChanged | |
## Update reference for shift clicking | |
if theHilitedIndexes is empty then | |
put empty into sFirstIndexClickedWithShiftKeyDown | |
else if the number of items of theHilitedIndexes is 1 then | |
put theIndex into sFirstIndexClickedWithShiftKeyDown | |
end if | |
else if _IsThisModifierSetActive("shift") and the dgProps["multiple lines"] of me then | |
if theHilitedIndexes is empty then put theIndex into theHilitedIndexes | |
if sFirstIndexClickedWithShiftKeyDown is empty then | |
## If no first index has been logged then index becomes first index | |
put theIndex into sFirstIndexClickedWithShiftKeyDown | |
end if | |
local theIndexItemNo, theShiftKeyIndexItemNo | |
put itemOffset(theIndex, sIndexSequencing) into theIndexItemNo | |
put itemOffset(sFirstIndexClickedWithShiftKeyDown, sIndexSequencing) into theShiftKeyIndexItemNo | |
if theIndexItemNo < theShiftKeyIndexItemNo then | |
put item theIndexItemNo to theShiftKeyIndexItemNo of sIndexSequencing into theHilitedIndexes | |
else if theIndexItemNo > theShiftKeyIndexItemNo then | |
put item theShiftKeyIndexItemNo to theIndexItemNo of sIndexSequencing into theHilitedIndexes | |
else | |
put theIndex into theHilitedIndexes | |
end if | |
put _SortIndexesSequentially(theHilitedIndexes) into theHilitedIndexes | |
put theHilitedIndexes into sHilitedIndexes | |
_HiliteIndexesInVisibleControls | |
put true into theSelectionChanged | |
else | |
## If clicking on a selected control then delay deselecting until mouseUp. | |
## This allows for drag/drop of multi controls. | |
## Only applies if not modifier is down. | |
if the dgProps["multiple lines"] of me and theIndex is among the items of sHilitedIndexes and _IsThisModifierSetActive("") then | |
put true into sDeselectOnMouseUp | |
else | |
unlock screen ## selectionChanged will be sent. Unlock so UI can update. | |
_SelectTargetControl theControl ## pass in since tables might not have control clicked on | |
put false into unlockTheScreen ## Don't unlock screen at end of handler. | |
end if | |
end if | |
else if pMouseBtnNum is 3 then | |
## right-click only changes selection if clicking on index that isn't highlighted | |
if theIndex is not among the items of theHilitedIndexes then | |
## Same behavior as mouseclick 1 | |
## single click always inserts index into first index var | |
put theIndex into sFirstIndexClickedWithShiftKeyDown | |
put theIndex into sHilitedIndexes | |
_HiliteIndexesInVisibleControls | |
put true into theSelectionChanged | |
end if | |
end if | |
## moved to top in 1.0.0 b4 | |
-- ## Clicked on a control. Bring focus into control but away from scrollbar. | |
-- if the long id of me is not in the long id of the focusedobject or word 1 of the target is "scrollbar" or \ | |
-- (sFieldEditor is not empty and sFieldEditor is the long id of the focusedObject) then | |
-- focus on graphic "dgBackground" of me | |
-- end if | |
else | |
## moved to top in 1.0.0 b4 | |
-- ## clicked in space but no on list control | |
-- focus on graphic "dgBackground" of me | |
end if | |
if unlockTheScreen then unlock screen | |
if theSelectionChanged then _SelectionChanged thePreviouslyHilitedIndexes | |
end dgMouseDown | |
before mouseUp pMouseBtnNum | |
// Mikey-2015-05-19: [[ Bug 15387 ]] Don't send dgMouseUp on mobile before mouseUp | |
if the environment is "mobile" then exit mouseUp | |
dgMouseUp pMouseBtnNum | |
pass mouseUp | |
end mouseUp | |
// Mikey-2015-05-19: [[ Bug 15387 ]] Use touchEnd on mobile | |
before touchEnd | |
dgMouseUp 1 | |
pass touchEnd | |
end touchEnd | |
on dgMouseUp pMouseBtnNum | |
if sDeselectOnMouseUp then | |
_SelectTargetControl | |
put false into sDeselectOnMouseUp | |
end if | |
end dgMouseUp | |
before mouseRelease pMouseBtnNum | |
dgMouseRelease pMouseBtnNum | |
pass mouseRelease | |
end mouseRelease | |
on dgMouseRelease pMouseBtnNum | |
put false into sDeselectOnMouseUp | |
end dgMouseRelease | |
--> Commands (Data Manipulation) | |
command SelectAll | |
local thePreviouslyHilitedIndexes, theOrigValue, theSelectionWasChanged | |
put sHilitedIndexes into thePreviouslyHilitedIndexes | |
put the dgProps["scroll selections into view"] of me is not false into theOrigValue | |
set the dgProps["scroll selections into view"] of me to false | |
try | |
local theError | |
set the dgHilitedIndexes of me to sIndexSequencing | |
put the result into theSelectionWasChanged | |
catch e | |
put e into theError | |
end try | |
set the dgProps["scroll selections into view"] of me to theOrigValue | |
if theError is not empty then throw theError | |
if theSelectionWasChanged then | |
_SelectionChanged thePreviouslyHilitedIndexes | |
end if | |
end SelectAll | |
function GetDataOfIndex pIndex, pKey | |
if pKey is not empty or pKey is an array then | |
return sDataArray[pIndex][pKey] | |
else | |
return sDataArray[pIndex] | |
end if | |
end GetDataOfIndex | |
command SetDataOfIndex pIndex, pKey, pValue | |
repeat with i = 2 to the paramcount | |
put param(i) into pKey | |
if pKey is not empty or pKey is an array then | |
put param(i+1) into sDataArray[pIndex][pKey] | |
else | |
put param(i+1) into sDataArray[pIndex] | |
end if | |
add 1 to i | |
end repeat | |
_StorePersistentData | |
return empty | |
end SetDataOfIndex | |
function GetDataOfLine pLine, pKey | |
set the wholeMatches to true | |
return GetDataOfIndex(item pLine of sIndexSequencing, pKey) | |
end GetDataOfLine | |
command SetDataOfLine pLine, pKey, pValue | |
set the wholematches to true | |
local theIndex | |
put item pLine of sIndexSequencing into theIndex | |
repeat with i = 2 to the paramcount | |
put param(i) into pKey | |
if pKey is not empty or pKey is an array then | |
put param(i+1) into sDataArray[theIndex][pKey] | |
else | |
put param(i+1) into sDataArray[theIndex] | |
end if | |
add 1 to i | |
end repeat | |
_StorePersistentData | |
return empty | |
end SetDataOfLine | |
private command _DataCanBeRepresentedAsText pBoolean | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
set the dgProps["data can be represented as text"] of me to pBoolean is true | |
set the lockMessages to msgsAreLocked | |
end _DataCanBeRepresentedAsText | |
command EditCell pColumn, pLineNo | |
local theIndex | |
put the dgIndexOfLine[pLineNo] of me into theIndex | |
EditCellOfIndex pColumn, theIndex | |
if the result is not empty then | |
return "no control exists for line column" | |
else | |
return empty | |
end if | |
end EditCell | |
command EditCellOfIndex pColumn, pIndex | |
lock screen | |
ScrollIndexIntoView pIndex | |
local theControl | |
put ColumnControlOfIndex(pColumn, pIndex) into theControl | |
if _ControlType() is "table" then _table.ScrollColumnIntoView pColumn | |
if theControl is not empty then | |
dispatch "EditValue" to theControl with pColumn | |
unlock screen | |
return empty | |
else | |
unlock screen | |
return "no control exists for index column" | |
end if | |
end EditCellOfIndex | |
command EditKey pKey, pLineNo | |
local theIndex | |
put the dgIndexOfLine[pLineNo] of me into theIndex | |
EditKeyOfIndex pKey, theIndex | |
if the result is not empty then | |
return "no control exists for line" | |
else | |
return empty | |
end if | |
end EditKey | |
command EditKeyOfIndex pKey, pIndex | |
lock screen | |
ScrollIndexIntoView pIndex | |
local theControl | |
put the dgDataControlOfIndex[pIndex] of me into theControl | |
if theControl is not empty then | |
dispatch "EditValue" to theControl with pKey | |
unlock screen | |
return empty | |
else | |
unlock screen | |
return "no control exists for index" | |
end if | |
end EditKeyOfIndex | |
## Use to add data to the data array | |
command AddData pDataArray, pSequence | |
local theError | |
local theIndex | |
set the wholeMatches to true | |
_DataCanBeRepresentedAsText false | |
put item 2 of the extents of sDataArray + 1 into theIndex | |
put pDataArray into sDataArray[theIndex] | |
## Add to list or setting sequence won't work well | |
put theIndex into item (the number of items of sIndexSequencing + 1) of sIndexSequencing | |
if pSequence is not empty then | |
_SetSequenceOfIndex theIndex, pSequence | |
put the result into theError | |
end if | |
if theError is empty then | |
_StorePersistentData | |
_ProcessNewIndexData theIndex | |
_RedrawList | |
else | |
## Reset | |
delete item (itemOffset(theIndex, sIndexSequencing)) of sIndexSequencing | |
delete local sDataArray[theIndex] | |
end if | |
if theError is empty then | |
return theIndex | |
else | |
return theError | |
end if | |
end AddData | |
command AddLine pText, pColumns, pLineNo | |
----- | |
local theResult | |
----- | |
switch _ControlType() | |
case "table" | |
_table.AddLine pText, pColumns, pLineNo | |
break | |
case "form" | |
default | |
_list.AddLine pText, pColumns, pLineNo | |
end switch | |
put the result into theResult | |
if theResult is an integer then | |
## Return line number | |
set the wholeMatches to true | |
return itemOffset(theResult, sIndexSequencing) | |
else | |
return theResult | |
end if | |
end AddLine | |
command DeleteLine pLine | |
DeleteLines pLine | |
return the result | |
end DeleteLine | |
command DeleteLines pLines | |
----- | |
local theIndexes | |
local theLine | |
----- | |
repeat for each item theLine in pLines | |
put the dgIndexOfLine [theLine] of me & comma after theIndexes | |
end repeat | |
delete the last char of theIndexes | |
DeleteIndexes theIndexes | |
return the result | |
end DeleteLines | |
command DeleteIndex pIndex | |
DeleteIndexes pIndex | |
return the result | |
end DeleteIndex | |
command DeleteIndexes pIndexes | |
----- | |
local theIndex | |
local theItemNo | |
local theLineNo | |
----- | |
lock screen | |
set the wholeMatches to true | |
## Clear out any instances of this index | |
repeat for each item theIndex in pIndexes | |
put itemOffset(theIndex, sHilitedIndexes) into theItemNo | |
if theItemNo > 0 then | |
delete item theItemNo of sHilitedIndexes | |
end if | |
## Clear controls when cached | |
if the dgProps["cache controls"] of me then | |
if there is a sControlOfIndexA[theIndex] then | |
## So that all updates happen on refresh | |
delete sControlOfIndexA[theIndex] | |
put lineOffset(sControlOfIndexA[theIndex], sTableObjectsA["all row controls"]) into theLineNo | |
if theLineNo > 0 then | |
delete line theLineNo of sTableObjectsA["all row controls"] | |
subtract 1 from sTableObjectsA["row control count"] | |
end if | |
put lineOffset(sControlOfIndexA[theIndex], sTableObjectsA["visible row controls"]) into theLineNo | |
if theLineNo > 0 then | |
delete line theLineNo of sTableObjectsA["visible row controls"] | |
end if | |
end if | |
end if | |
put itemOffset(theIndex, sIndexSequencing) into theItemNo | |
if theItemNo > 0 then | |
delete item theItemNo of sIndexSequencing | |
## Update Formatted Height and UI if already calculated | |
if sFormattedHeight is not empty then | |
if the dgProps["fixed row height"] of me then | |
subtract sControlHeights from sFormattedHeight | |
else | |
subtract sControlHeights[theIndex] from sFormattedHeight | |
end if | |
end if | |
end if | |
delete local sDataArray[theIndex] | |
end repeat | |
_StorePersistentData | |
if sFormattedHeight is not empty then | |
_ConfigureScrollbar | |
_AutoHideVScrollbar ## only works for fixed height | |
end if | |
## Count and position has changed. Update alternating rows. | |
_DrawAlternatingRows | |
if the keys of sDataArray is not empty then | |
_RedrawList | |
else | |
_ResetData | |
end if | |
unlock screen | |
return empty | |
end DeleteIndexes | |
--> Commands (Sorting) | |
command SortDataByKey pKey, pType, pDirection, pCaseSensitive | |
----- | |
local msgsAreLocked | |
local theIndex | |
local theData | |
local theDo | |
local theLine | |
----- | |
if pType is not among the items of kSortTypes then put "text" into pType | |
if pDirection is not "descending" then put "ascending" into pDirection | |
if pKey is empty then | |
sort items of sIndexSequencing numeric ascending | |
else | |
## so we get a stable sort | |
repeat for each item theIndex in sIndexSequencing | |
-- repeat for each key theIndex in sDataArray | |
put theIndex & tab & line 1 of sDataArray[theIndex][pKey] & cr after theData | |
end repeat | |
delete the last char of theData | |
set the itemDelimiter to tab | |
set the caseSensitive to pCaseSensitive is true | |
if pType is "system datetime" then | |
set the useSystemDate to true | |
delete word 1 of pType | |
end if | |
put "sort lines of theData" && pType && pDirection && "by item 2 to -1 of each" into theDo | |
do theDo | |
put empty into sIndexSequencing | |
repeat for each line theLine in theData | |
put item 1 of theLine & comma after sIndexSequencing | |
end repeat | |
delete the last char of sIndexSequencing | |
end if | |
_StorePersistentSequence | |
lock screen | |
_ResetIndexesOnControls | |
_RedrawList | |
unlock screen | |
return empty | |
end SortDataByKey | |
## Reverses order of current sequence of data | |
command ReverseSort pColumn | |
lock screen | |
if sIndexSequencing is not empty then | |
local theNewSequence | |
repeat for each item theIndex in sIndexSequencing | |
put comma & theIndex before theNewSequence | |
end repeat | |
delete the first char of theNewSequence | |
put theNewSequence into sIndexSequencing | |
_ResetIndexesOnControls | |
_RedrawList | |
end if | |
if pColumn is not empty then | |
HiliteAndStoreSortByColumn pColumn | |
end if | |
unlock screen | |
return empty | |
end ReverseSort | |
command HiliteAndStoreSortByColumn pColumn | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
unlock messages | |
lock screen | |
local theOldSortKey | |
put the dgProps["sort by column"] of me into theOldSortKey | |
if theOldSortKey is not empty and there is a sTableObjectsA["columns"][ theOldSortKey ]["header"]["group"] then | |
set the dgHilite of sTableObjectsA["columns"][ theOldSortKey ]["header"]["group"] to false | |
dispatch "LayoutControl" to sTableObjectsA["columns"][ theOldSortKey ]["header"]["group"] \ | |
with the rect of sTableObjectsA["columns"][ theOldSortKey ]["header"]["group"] | |
end if | |
if pColumn is not empty and there is a sTableObjectsA["columns"][ pColumn ]["header"]["group"] then | |
set the dgHilite of sTableObjectsA["columns"][ pColumn ]["header"]["group"] to true | |
dispatch "LayoutControl" to sTableObjectsA["columns"][ pColumn ]["header"]["group"] \ | |
with the rect of sTableObjectsA["columns"][ pColumn ]["header"]["group"] | |
end if | |
lock messages | |
set the dgProps["sort by column"] of me to pColumn | |
unlock screen | |
set the lockMessages to msgsAreLocked | |
end HiliteAndStoreSortByColumn | |
## Used internally to dispatch the "SortDataGridColumn" message | |
private command _SortByColumn pColumn | |
lock screen | |
dispatch "SortDataGridColumn" to me with pColumn | |
if it is not "handled" then | |
SortByColumn pColumn | |
end if | |
unlock screen | |
end _SortByColumn | |
command SortByColumn pColumn | |
if _ControlType() is not "table" then _ThrowError kErrPropDoesntExist, "only tables can be sorted by columns" | |
local theColPropsA | |
put the dgProps["column properties"] of me into theColPropsA | |
if pColumn is not empty and pColumn is not among the keys of theColPropsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
lock screen | |
## sort and hilite | |
SortDataByKey pColumn, theColPropsA[pColumn]["sort type"], \ | |
theColPropsA[pColumn]["sort direction"], theColPropsA[pColumn]["sort is case sensitive"] | |
HiliteAndStoreSortByColumn pColumn | |
unlock screen | |
return empty | |
end SortByColumn | |
--> Commands (Control) | |
local sResizingToFit | |
local sResendResizeToFit | |
command ResizeToFit | |
----- | |
local theError | |
local theLockLoc | |
local theOffset | |
local theRect | |
----- | |
## Throttle just in case developer tries to call this from different timers | |
if sResizingToFit then | |
put true into sResendResizeToFit | |
return empty | |
end if | |
put true into sResizingToFit | |
lock screen | |
put the lockloc of me into theLockLoc | |
set the lockloc of me to true | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
set the lockMessages to true | |
try ## Try so we don't leave group locked | |
## Clear cache | |
put empty into sControlsRequiredToFillSpace | |
put _WorkingGroupRect(the long ID of me) into theRect | |
set the rect of button "dgEventCatcher" of me to theRect | |
set the rect of graphic "dgBackground" of me to theRect | |
## Account for table elements | |
if _ControlType() is "table" then | |
_table.LayoutDataArea | |
else | |
if the environment is not "mobile" then | |
if the visible of scrollbar "dgScrollbar" of me then | |
subtract the width of scrollbar "dgScrollbar" of me from item 3 of theRect | |
end if | |
if the visible of scrollbar "dgScrollbar" of me then | |
set the rect of scrollbar "dgScrollbar" of me to item 3 of theRect, \ | |
item 2 of theRect, item 3 of theRect + the width of scrollbar "dgScrollbar" of me, \ | |
item 4 of theRect - the dgProps["scrollbar corner offset"] of me | |
else | |
set the rect of scrollbar "dgScrollbar" of me to item 3 of theRect - the width of scrollbar "dgScrollbar" of me, \ | |
item 2 of theRect, item 3 of theRect, \ | |
item 4 of theRect - the dgProps["scrollbar corner offset"] of me | |
end if | |
else if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "rect", theRect | |
end if | |
set the rect of group "dgListMask" of me to theRect | |
set the rect of group "dgAlternatingRowsMask" of me to theRect | |
set the topLeft of group "dgList" of me to item 1 to 2 of theRect ## Shift all children of group along with group | |
set the rect of group "dgList" of me to theRect | |
end if | |
## Redo calculations | |
if the dgProps["cache controls"] of me then | |
_ResizeCachedControls | |
else if sFormattedHeight is empty or not the dgProps["fixed row height"] of me then | |
# Only recalculate if not fixed height or formatted height hasn't been calculated | |
# since teal-time drawing adjusts rect every time anyway. | |
_CalculateFormattedHeight | |
else | |
_AutoHideVScrollbar ## only works for fixed height | |
end if | |
_ConfigureScrollbar | |
_ConfigureHScrollbar | |
_RedrawList | |
catch e | |
put e into theError | |
-- put e | |
finally | |
set the lockloc of me to theLockLoc | |
end try | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
put false into sResizingToFit | |
if theError is not empty then throw theError | |
if sResendResizeToFit then | |
send "ResizeToFit" to me in 0 seconds | |
put false into sResendResizeToFit | |
end if | |
end ResizeToFit | |
private command _DrawList | |
lock screen | |
local weHaveSomethingToDraw | |
put the keys of sDataArray is not empty into weHaveSomethingToDraw | |
if weHaveSomethingToDraw and sFormattedHeight is empty then | |
if the dgProps["cache controls"] of me then | |
_CacheControls | |
else | |
_CalculateFormattedHeight | |
end if | |
end if | |
## Update column control cache each time drawing is specifically called | |
if _ControlType() is "table" then | |
_table.CacheCustomTemplateUsage | |
end if | |
if weHaveSomethingToDraw then | |
_DrawListWithProperties empty, empty, true | |
_DrawAlternatingRows | |
_ShowAlternatingRows | |
end if | |
unlock screen | |
end _DrawList | |
command ResetList | |
dgResetList | |
return empty | |
end ResetList | |
/** | |
* \brief Allows developer to intercept ResetList and manually call code to reset. | |
* | |
* \return empty | |
*/ | |
command dgResetList | |
lock screen | |
## For cases where user copied/pasted | |
if not sInit then _Initialize | |
local theOrigVScroll, theOrigHScroll | |
put _GetVScrollPercent() into theOrigVScroll | |
put _GetHScrollPercent() into theOrigHScroll | |
_ResetScrollsToZero | |
_DeleteControls | |
_ResetInternalCaches | |
if _ControlType() is "table" then | |
_table.CreateColumns | |
end if | |
ResizeToFit | |
_DrawAlternatingRows | |
_ShowAlternatingRows | |
_SetHScrollPercent theOrigHScroll | |
_SetVScrollPercent theOrigVScroll | |
unlock screen | |
return empty | |
end dgResetList | |
command RefreshList | |
if the keys of sDataArray is not empty then | |
if the dgProps["cache controls"] of me then | |
lock screen | |
_LayoutCachedControls | |
_RedrawList | |
unlock screen | |
else | |
_ResetIndexesOnControls | |
_RedrawList | |
end if | |
_HiliteIndexesInVisibleControls | |
else | |
_ResetData | |
end if | |
return empty | |
end RefreshList | |
on RefreshIndex pIndex | |
_RefreshIndexes pIndex | |
end RefreshIndex | |
command RefreshLine pLine | |
local theIndex | |
put the dgIndexOfLine [pLine] of me into theIndex | |
if theIndex > 0 then _RefreshIndexes theIndex | |
end RefreshLine | |
private command _ResetIndexesOnControls | |
switch _ControlType() | |
case "table" | |
lock messages | |
repeat for each line theColumn in the dgProps["columns"] of me | |
repeat for each line theControl in sTableObjectsA["columns"][theColumn]["row controls"] | |
set the dgIndex of theControl to empty | |
end repeat | |
end repeat | |
unlock messages | |
break | |
case "form" | |
default | |
if not the dgProps["cache controls"] of me then | |
lock messages | |
repeat for each line theControl in sTableObjectsA["all row controls"] | |
set the dgIndex of theControl to empty | |
end repeat | |
unlock messages | |
end if | |
end switch | |
end _ResetIndexesOnControls | |
private command _ResetControlsOfIndex pIndex | |
switch _ControlType() | |
case "table" | |
lock messages | |
repeat for each line theColumn in the dgProps["columns"] of me | |
repeat for each line theControl in sTableObjectsA["columns"][theColumn]["row controls"] | |
if the dgIndex of theControl is pIndex then | |
set the dgIndex of theControl to empty | |
end if | |
end repeat | |
end repeat | |
unlock messages | |
break | |
case "form" | |
default | |
## Cached controls aren't drawing new data into the same controls so | |
## we don't reset indexes on them. | |
if not the dgProps["cache controls"] of me then | |
lock messages | |
repeat for each line theControl in sTableObjectsA["all row controls"] | |
if the dgIndex of theControl is pIndex then | |
set the dgIndex of theControl to empty | |
end if | |
end repeat | |
unlock messages | |
end if | |
end switch | |
end _ResetControlsOfIndex | |
private command _RedrawList pVScrollPercent | |
----- | |
local thePercent | |
----- | |
if the keys of sDataArray is not empty then | |
if pVScrollPercent is empty then | |
put _GetVScrollPercent() into pVScrollPercent | |
end if | |
local theVScroll | |
put round(pVScrollPercent * (the endValue of scrollbar "dgScrollbar" of me - the thumbSize of scrollbar "dgScrollbar" of me)) into theVScroll | |
if sFormattedHeight is empty then | |
if the dgProps["cache controls"] of me then | |
_CacheControls | |
else | |
_CalculateFormattedHeight | |
end if | |
end if | |
_ScrollListV theVScroll, true | |
_ShowAlternatingRows | |
else | |
_ShowAlternatingRows | |
end if | |
return empty | |
end _RedrawList | |
command ScrollLineIntoView pLine | |
set the wholeMatches to true | |
ScrollIndexIntoView item pLine of sIndexSequencing | |
end ScrollLineIntoView | |
command ScrollIndexIntoView pIndex | |
local theError | |
## Work off of a 0 based rect, not actual position on card | |
local theMaskHeight, theOffset, theMaskRect | |
put the height of group "dgListMask" of me into theMaskHeight | |
put the dgVScroll of me into theOffset -- round needed for engine bug | |
put "0," & theOffset & ",0," & theMaskHeight + theOffset into theMaskRect | |
local theSequence | |
put the dgLineOfIndex[pIndex] of me into theSequence | |
if theSequence is 0 then put "index not found" into theError | |
local theTopOfControl, theBottomOfControl, theFormattedHeight | |
if theError is empty then | |
## Determine top and bottom of control | |
if the dgProps["fixed row height"] of me then | |
put (theSequence - 1) * sControlHeights into theTopOfControl | |
put theSequence * sControlHeights into theBottomOfControl | |
else | |
## Random sized Controls | |
repeat for each item theIndex in sIndexSequencing | |
add sControlHeights[theIndex] to theFormattedHeight | |
if pIndex is theIndex then | |
put theFormattedHeight - sControlHeights[theIndex] into theTopOfControl | |
put theFormattedHeight into theBottomOfControl | |
exit repeat | |
end if | |
end repeat | |
end if | |
## If index is off the top then scroll to top of group | |
## else if index is off the bottom then scroll to the bottom | |
## We lock messages so no scrollbarDrag message is sent. scrollbarDrag may use a slight delay when | |
## drawing so that fast scrolling doesn't cue up redraws. We bypass the message | |
## and call _ScrollListV directly so that screen is updated immediately regardless of how | |
## scrollbarDrag is finally implemented. | |
-- ====================== | |
local scrollTheList, theNewVScroll, theControlIsAtLeastAsTallAsMask | |
put false into scrollTheList | |
if theBottomOfControl <= item 2 of theMaskRect or theTopOfControl < item 2 of theMaskRect then | |
## control is off the top of view OR top edge is clipped off | |
put theTopOfControl into theNewVScroll | |
put true into scrollTheList | |
else if theTopOfControl >= item 4 of theMaskRect or theBottomOfControl > item 4 of theMaskRect then | |
## control is off the bottom of view or bottom edge is clipped off | |
put theBottomOfControl - theMaskHeight into theNewVScroll | |
put true into scrollTheList | |
end if | |
if scrollTheList then | |
## If as tall as mask group then scroll top into view. | |
## Otherwise follow regular rules | |
put theBottomOfControl - theTopOfControl >= theMaskHeight into theControlIsAtLeastAsTallAsMask | |
if theControlIsAtLeastAsTallAsMask then | |
put theTopOfControl into theNewVScroll | |
end if | |
if the dgProps["animate selections"] of me and sPendingMsgsA["UpdateScrollAnimation"] is empty then | |
StartScrollAnimation theNewVScroll | |
else | |
cancel sPendingMsgsA["UpdateScrollAnimation"] | |
put empty into sPendingMsgsA["UpdateScrollAnimation"] | |
put false into sIsAnimating | |
_SetVScroll theNewVScroll | |
end if | |
end if | |
end if | |
return theError | |
end ScrollIndexIntoView | |
## See dgRectOfIndex in order to get rect of a control in the data grid. | |
command ScrollRectIntoView pRect, pPadding | |
----- | |
local theItemNo | |
local theMaskRect | |
local theScrollToOffset, theVScroll | |
----- | |
if pPadding is an integer then | |
put pPadding & comma & pPadding & comma & pPadding into item 2 of pPadding | |
else if pPadding is not empty and pPadding is not a rect then | |
_ThrowError kErrInvalidRect, pPadding && "is not a valid margins value" | |
else if pPadding is empty then | |
put 0,0,0,0 into pPadding | |
end if | |
## Mask rect is rect of this group with vscroll taken into account | |
put the rect of group "dgListMask" of me into theMaskRect | |
local theCurrentVScroll | |
put the dgVScroll of me into theCurrentVScroll | |
## left,right are ignored right now | |
add item 1 of pPadding to item 1 of theMaskRect | |
add item 2 of pPadding to item 2 of theMaskRect | |
subtract item 3 of pPadding from item 3 of theMaskRect | |
subtract item 4 of pPadding from item 4 of theMaskRect | |
## If mask height is less than rect height than we focus on top of pRect. | |
## Otherwise we focus on bottom of pRect. | |
if item 4 of theMaskRect - item 2 of theMaskRect < item 4 of pRect - item 2 of pRect then | |
put 2 into theItemNo | |
put item 4 of theMaskRect - item 2 of theMaskRect into theScrollToOffset | |
else | |
put 4 into theItemNo | |
put 0 into theScrollToOffset | |
end if | |
-- Debugging | |
-- put "theMaskRect:" && theMaskRect & cr & \ | |
-- "pRect:" && pRect & cr & \ | |
-- "theItemNo:" && theItemNo & cr & \ | |
-- "theScrollToOffset:" && theScrollToOffset & cr & \ | |
-- "theCurrentVScroll:" && theCurrentVScroll | |
if _RectIsAtLeastAsTallAsMask(pRect, theMaskRect) then | |
if _RectCoversMask(pRect, theMaskRect) then | |
## Rect covers entire visible area. Do nothing. | |
else | |
## If rect top or bottom is visible then do nothing. Otherwise scroll top in. | |
if _TopIsVisible(pRect, theMaskRect) or _BottomIsVisible(pRect, theMaskRect) then | |
## Nothing | |
else | |
## Scroll up to top of control | |
put theCurrentVScroll - abs(item 2 of theMaskRect - item 2 of pRect) into theVScroll | |
end if | |
end if | |
else if _TopIsClipped(pRect, theMaskRect) then | |
## Scroll up to top of control | |
put theCurrentVScroll - abs(item 2 of theMaskRect - item 2 of pRect) into theVScroll | |
else if _BottomIsClipped(pRect, theMaskRect) then | |
## scroll down to bottom of control | |
put theCurrentVScroll - (item 4 of theMaskRect - item theItemNo of pRect) into theVScroll | |
add theScrollToOffset to theVScroll | |
else if _TopAndBottomAreClipped(pRect, theMaskRect) then | |
if item 2 of pRect >= item 4 of theMaskRect then | |
## scroll down so bottom to bottom of control | |
put theCurrentVScroll - (item 4 of theMaskRect - item theItemNo of pRect) into theVScroll | |
add theScrollToOffset to theVScroll | |
else | |
## scroll up to top of control | |
put theCurrentVScroll - abs(item 2 of theMaskRect - item 2 of pRect) into theVScroll | |
end if | |
end if | |
if theVScroll is not empty then | |
if the dgProps["animate selections"] of me then | |
StartScrollAnimation theVScroll | |
else | |
set the dgVScroll of me to theVScroll | |
end if | |
end if | |
end ScrollRectIntoView | |
command ResetControl | |
lock screen | |
try | |
## So we don't confuse the engine | |
if the long ID of me is in the long ID of the focusedObject then focus on graphic "dgBackground" of me | |
_DeleteControls | |
_ResetData | |
if _ControlType() is "table" then | |
_table.RegenerateColumns | |
end if | |
catch e | |
put e | |
end try | |
unlock screen | |
return empty | |
end ResetControl | |
-- Resets all controls in the data grid | |
private command _ResetAllControls | |
lock screen | |
local theControls | |
if _ControlType() is "table" then | |
repeat for each key theColumn in sTableObjectsA["columns"] | |
if sTableObjectsA["columns"][theColumn]["row controls"] is not empty then | |
put sTableObjectsA["columns"][theColumn]["row controls"] & cr after theControls | |
end if | |
end repeat | |
delete the last char of theControls | |
else | |
put sTableObjectsA["all row controls"] into theControls | |
end if | |
local theCleansedControls | |
## Make sure controls exist | |
repeat for each line theControl in theControls | |
if there is a theControl then | |
put theControl & cr after theCleansedControls | |
end if | |
end repeat | |
_ResetControls theCleansedControls | |
put empty into sTableObjectsA["current"] | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "vScroll", 0 | |
end if | |
set the thumbPosition of scrollbar "dgScrollbar" of me to 0 | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
end _ResetAllControls | |
private command _ResetScrollbarsAndRows | |
## This will call scrollbardrag. We set all structures before this happens. | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "vScroll", 0 | |
end if | |
set the thumbPosition of scrollbar "dgScrollbar" of me to 0 | |
if _ControlType() is "table" then | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "hScroll", 0 | |
end if | |
set the thumbPosition of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me to 0 | |
end if | |
_ConfigureScrollbar | |
_ConfigureHScrollbar | |
if the dgProps["fixed row height"] of me then | |
_AutoHideVScrollbar ## only works for fixed height | |
end if | |
_DrawAlternatingRows | |
_ShowAlternatingRows | |
end _ResetScrollbarsAndRows | |
-- Send ResetControl message to any controls that aren't visible. | |
-- Reset dgIndex | |
private command _ResetControls pControls | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
## Hide any controls in "all row controls" that aren't part of "visible list controls" | |
repeat for each line theControl in pControls | |
if the visible of theControl then | |
dispatch "ResetData" to theControl | |
set the visible of theControl to false | |
set the dgIndex of theControl to empty | |
end if | |
end repeat | |
set the lockMessages to msgsAreLocked | |
end _ResetControls | |
private command _ResetData | |
## In case developer redraws while editor is open | |
DeleteFieldEditor true | |
lock screen | |
## So we don't confuse the engine | |
if the long ID of me is in the long ID of the focusedObject then focus on graphic "dgBackground" of me | |
## Time intensive start | |
-- _ResetAllControls | |
_DeleteDataControls ## Replaced with above in 1.0.2 build 6. No more errors when deleting control running script! | |
put empty into sDataArray | |
## time intensive end | |
put empty into sIndexSequencing | |
_StorePersistentData | |
_ResetInternalCaches | |
_DataCanBeRepresentedAsText true | |
## This will call scrollbardrag. We set all structures before this happens. | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "vScroll", 0 | |
end if | |
set the thumbPosition of scrollbar "dgScrollbar" of me to 0 | |
if _ControlType() is "table" then | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "hScroll", 0 | |
end if | |
set the thumbPosition of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me to 0 | |
end if | |
_ConfigureScrollbar | |
_ConfigureHScrollbar | |
if the dgProps["fixed row height"] of me then | |
_AutoHideVScrollbar ## only works for fixed height | |
end if | |
_DrawAlternatingRows | |
_ShowAlternatingRows | |
unlock screen | |
end _ResetData | |
private command _ResetInternalCaches | |
put empty into sControlOfIndexA | |
put empty into sControlHeights | |
put empty into sFormattedHeight | |
put empty into sHilitedIndexes | |
put empty into sControlsRequiredToFillSpace | |
put empty into sFirstIndexClickedWithShiftKeyDown | |
put empty into sDeselectOnMouseUp | |
end _ResetInternalCaches | |
--> Commands (Find) | |
## pKey can be a string or an array keyed index. | |
function GetKeyValuesOfIndexes pIndexes, pDelimiter, pKey --, ... | |
local theIndex,theValues | |
if pDelimiter is empty then put comma into pDelimiter | |
local theParamCount, theDelimLength | |
put the paramCount into theParamCount | |
put length(pDelimiter) into theDelimLength | |
repeat for each item theIndex in pIndexes | |
repeat with i = 3 to theParamCount | |
put sDataArray[theIndex][param(i)] & pDelimiter after theValues | |
end repeat | |
delete char -theDelimLength to -1 of theValues | |
put cr after theValues | |
end repeat | |
delete the last char of theValues | |
return theValues | |
end GetKeyValuesOfIndexes | |
## pSearchA is array-valued index for accessing sDataArray | |
## pSearchA[1] = key_1 | |
## pSearchA[2] = key_2 | |
command FindIndex pKeyIndexA, pSearchString --, ... | |
----- | |
local foundAMatch, theFoundIndex | |
local i | |
local theIndex | |
----- | |
repeat for each key theIndex in sDataArray | |
## Developer can pass in multiple search strings to perform an AND search | |
repeat with i = 1 to the paramCount step 2 | |
if sDataArray[theIndex][param(i)] is param(i+1) then | |
put true into foundAMatch | |
else | |
put false into foundAMatch | |
end if | |
## AND search didn't pan out. Move on to next index. | |
if not foundAMatch then exit repeat | |
end repeat | |
if foundAMatch then | |
put theIndex into theFoundIndex | |
exit repeat | |
end if | |
end repeat | |
return max(0, theFoundIndex) | |
end FindIndex | |
command FindLine pKeyIndexA, pSearchString | |
----- | |
local theIndex | |
----- | |
FindIndex pKeyIndexA, pSearchString | |
put the result into theIndex | |
if theIndex > 0 then | |
return the dgLineOfIndex[theIndex] of me | |
else | |
return 0 | |
end if | |
end FindLine | |
--> Type: Template Handlers | |
private command _ToggleVScrollBarVisibility pBoolean | |
lock screen | |
set the visible of scrollbar "dgScrollbar" of me to pBoolean | |
switch _ControlType() | |
case "table" | |
local theOrigHScroll | |
put _GetHScrollPercent() into theOrigHScroll | |
_ResetHScrollToZero | |
_AutoHideHScrollbar | |
_table.LayoutDataArea | |
_table.RepositionHeaders | |
_ConfigureHScrollbar | |
RefreshList | |
_SetHScrollPercent theOrigHScroll | |
break | |
case "form" | |
default | |
local theRect | |
put the rect of group "dgList" of me into theRect | |
if the visible of scrollbar "dgScrollbar" of me then | |
put the left of scrollbar "dgScrollbar" of me into item 3 of theRect | |
else | |
put the right of scrollbar "dgScrollbar" of me into item 3 of theRect | |
end if | |
set the rect of group "dgListMask" of me to theRect | |
set the rect of group "dgList" of me to theRect | |
set the rect of group "dgAlternatingRowsMask" of me to theRect | |
if not the dgProps["fixed row height"] of me then | |
_CalculateFormattedHeight | |
end if | |
RefreshList | |
end switch | |
unlock screen | |
end _ToggleVScrollBarVisibility | |
private command _ToggleHScrollBarVisibility pBoolean | |
lock screen | |
## todo: can we only perform actions if visible of scrollbar is different than pBoolean? | |
set the visible of group "dgHorizontalComponents" of me to pBoolean | |
switch _ControlType() | |
case "table" | |
local theOrigVScroll | |
put _GetVScrollPercent() into theOrigVScroll | |
_ResetVScrollToZero | |
_table.LayoutDataArea | |
local theSetting | |
put the visible of scrollbar "dgScrollbar" of me into theSetting | |
_AutoHideVScrollbar | |
if the visible of scrollbar "dgScrollbar" of me is not theSetting then | |
_table.LayoutDataArea | |
end if | |
_ShowAlternatingRows | |
_ConfigureScrollbar | |
RefreshList | |
_SetVScrollPercent theOrigVScroll | |
break | |
case "form" | |
default | |
end switch | |
unlock screen | |
end _ToggleHScrollBarVisibility | |
private command _DrawListWithProperties pStartingSequence, pSetVScrollTo, pForceRefresh | |
lock screen | |
## For cases where user copied/pasted | |
if not sInit then _Initialize | |
DeleteFieldEditor | |
local theControl | |
put the long ID of the focusedObject into theControl | |
local msgsAreLocked, theStart | |
put the lockMessages into msgsAreLocked | |
put the milliseconds into theStart | |
if pStartingSequence is not an integer or pStartingSequence < 1 then put 1 into pStartingSequence | |
if pSetVScrollTo is not an integer then put 0 into pSetVScrollTo | |
## Store sequence that visible controls start drawing from | |
put pStartingSequence into sTableObjectsA["base sequence for visible controls"] | |
switch _ControlType() | |
case "table" | |
_table.DrawWithProperties pSetVScrollTo, pForceRefresh | |
break | |
case "form" | |
default | |
_list.DrawWithProperties pSetVScrollTo, pForceRefresh | |
end switch | |
## Make sure focus stays with us | |
if the long ID of me is in theControl then | |
lock messages | |
if there is not a theControl then | |
focus on graphic "dgBackground" of me | |
else | |
focus on theControl | |
end if | |
unlock messages | |
end if | |
-- put "time spent in" && param(0) & ":" && the milliseconds - theStart & cr after msg | |
unlock screen | |
set the lockMessages to msgsAreLocked | |
-- put "time spent in" && param(0) & ":" && the milliseconds - theStart & cr after msg | |
end _DrawListWithProperties | |
private command _DeleteControls | |
local i,theControl,msgsAreLocked | |
lock screen | |
switch _ControlType() | |
case "table" | |
_table.DeleteColumnControls | |
break | |
case "form" | |
default | |
end switch | |
_DeleteDataControls | |
_ResetScrollsToZero | |
## In case one gets left around | |
if there is a field kFieldEditorName of group "dgListMask" of me then | |
put the lockMessages into msgsAreLocked | |
lock messages | |
delete field kFieldEditorName of group "dgListMask" of me | |
set the lockMessages to msgsAreLocked | |
end if | |
unlock screen | |
end _DeleteControls | |
private command _DeleteDataControls | |
lock screen | |
switch _ControlType() | |
case "table" | |
_table.DeleteDataControls | |
break | |
case "form" | |
default | |
_list.DeleteControls | |
end switch | |
put empty into sTableObjectsA["current"] | |
put 0 into sTableObjectsA["row control count"] | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "vScroll", 0 | |
end if | |
set the thumbPosition of scrollbar "dgScrollbar" of me to 0 | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
end _DeleteDataControls | |
private command _CalculateFormattedHeight | |
put empty into sControlHeights | |
put 0 into sFormattedHeight | |
if item 2 of line 1 of the extents of sDataArray < 1 then | |
put kDefaultRowHeight into sControlHeights | |
return empty | |
end if | |
lock screen | |
switch _ControlType() | |
case "table" | |
_table.CalculateFormattedHeight | |
break | |
case "form" | |
default | |
_list.CalculateFormattedHeight | |
end switch | |
_ConfigureScrollbar | |
_AutoHideVScrollbar ## only works for fixed height | |
unlock screen | |
end _CalculateFormattedHeight | |
## Makes sure that enough controls exist to fill up the visible space | |
## Returns true if the number of controls required to display all records increased. | |
private command _FillListWithJustEnoughControls | |
lock screen | |
switch _ControlType() | |
case "table" | |
_table.FillListWithJustEnoughControls | |
break | |
case "form" | |
default | |
_list.FillListWithJustEnoughControls | |
end switch | |
local theControlCountChanged | |
put the result into theControlCountChanged | |
unlock screen | |
return theControlCountChanged | |
end _FillListWithJustEnoughControls | |
private command _HiliteIndexesInVisibleControls | |
switch _ControlType() | |
case "table" | |
_table.HiliteIndexesInVisibleControls | |
break | |
case "form" | |
default | |
_list.HiliteIndexesInVisibleControls | |
end switch | |
end _HiliteIndexesInVisibleControls | |
private command _UpdateHiliteColor | |
lock screen | |
if _ControlType() is "table" then | |
_table.LayoutRowHilites ## changes colors | |
end if | |
_HiliteIndexesInVisibleControls | |
unlock screen | |
end _UpdateHiliteColor | |
--> Type: List | |
private function _list.GetText pIncludeKeys | |
----- | |
local theIndex | |
local theKey | |
local theKeys | |
local theText | |
----- | |
put the keys of sDataArray[item 1 of sIndexSequencing] into theKeys | |
sort lines of theKeys | |
if pIncludeKeys then | |
put theKeys into theText | |
replace cr with tab in theText | |
put cr after theText | |
end if | |
repeat for each item theIndex in sIndexSequencing | |
repeat for each line theKey in theKeys | |
put sDataArray[theIndex][theKey] & tab after theText | |
end repeat | |
put cr into the last char of theText | |
end repeat | |
delete the last char of theText | |
return theText | |
end _list.GetText | |
private command _list.SetText pText, pKeys | |
----- | |
local theColNum | |
local theDataA | |
local theItem | |
local theLine | |
local theSequencing | |
----- | |
lock screen | |
set the itemDelimiter to tab | |
if pKeys is not empty then | |
## Developer specified item to key mapping | |
local theColCount, theColLookupA | |
put the number of lines of pKeys into theColCount | |
put 0 into theColNum | |
repeat for each line theColumn in pKeys | |
add 1 to theColNum | |
put theColumn into theColLookupA[theColNum] | |
end repeat | |
local theRow | |
repeat for each line theLine in pText | |
add 1 to theRow | |
put 0 into theColNum | |
put theRow & comma after theSequencing | |
if theLine is empty then | |
## Fill in empty records for empty lines | |
repeat for each line theColumn in pKeys | |
put empty into theDataA[theRow][theColumn] | |
end repeat | |
else | |
repeat for each item theItem in theLine | |
add 1 to theColNum | |
put theItem into theDataA[theRow][ theColLookupA[theColNum] ] | |
if theColNum is theColCount then exit repeat | |
end repeat | |
end if | |
end repeat | |
else | |
## No columns provided. We just create columns named "Label COL_NUM" | |
repeat for each line theLine in pText | |
add 1 to theRow | |
put 0 into theColNum | |
put theRow & comma after theSequencing | |
if theLine is empty then | |
## Fill in empty value for one key | |
put empty into theDataA[theRow][ "Label 1"] | |
else | |
repeat for each item theItem in theLine | |
add 1 to theColNum | |
put theItem into theDataA[theRow][ "Label" && theColNum ] | |
end repeat | |
end if | |
end repeat | |
end if | |
delete the last char of theSequencing | |
set the dgData [theSequencing] of me to theDataA | |
unlock screen | |
return empty | |
end _list.SetText | |
private command _list.AddLine pText, pKeys, pLineNo | |
----- | |
local theColNum | |
local theDataA | |
local theItem | |
local theLine,theResult | |
----- | |
lock screen | |
set the itemDelimiter to tab | |
if pKeys is not empty then | |
## Developer specified mapping of data to keys | |
local theColCount, theColLookupA | |
put the number of lines of pKeys into theColCount | |
put 0 into theColNum | |
repeat for each line theColumn in pKeys | |
add 1 to theColNum | |
put theColumn into theColLookupA[theColNum] | |
end repeat | |
repeat for each line theLine in pText | |
put 0 into theColNum | |
repeat for each item theItem in theLine | |
add 1 to theColNum | |
put theItem into theDataA[ theColLookupA[theColNum] ] | |
if theColNum is theColCount then exit repeat | |
end repeat | |
AddData theDataA, pLineNo | |
put the result into theResult | |
if pLineNo is not empty then add 1 to pLineNo | |
end repeat | |
else | |
## No columns provided. We just create columns named "Label COL_NUM" | |
repeat for each line theLine in pText | |
put 0 into theColNum | |
repeat for each item theItem in theLine | |
add 1 to theColNum | |
put theItem into theDataA[ "Label" && theColNum ] | |
end repeat | |
AddData theDataA, pLineNo | |
put the result into theResult | |
if pLineNo is not empty then add 1 to pLineNo | |
end repeat | |
end if | |
unlock screen | |
return theResult | |
end _list.AddLine | |
private command _list.DrawWithProperties pSetVScrollTo, pForceRefresh | |
----- | |
local theError | |
----- | |
lock messages | |
set the vScroll of group "dgList" of me to 0 | |
unlock messages | |
## Easier to read code | |
if the dgProps["cache controls"] of me then | |
_list.DrawCachedControlList pForceRefresh | |
else | |
_list.DrawControlsInRealTime pForceRefresh | |
end if | |
# Update VScroll every time | |
lock messages | |
set the vScroll of group "dgList" of me to pSetVScrollTo | |
unlock messages | |
return theError | |
end _list.DrawWithProperties | |
private command _list.DeleteControls | |
----- | |
local i | |
local msgsAreLocked | |
local theControl | |
----- | |
put the lockMessages into msgsAreLocked | |
lock messages ## for speed | |
repeat for each line theControl in sTableObjectsA["all row controls"] | |
if there is a control theControl then | |
delete theControl | |
end if | |
end repeat | |
## Any extra cleanup | |
repeat until there is not a (group 1 of group "dgList" of me) | |
delete group 1 of group "dgList" of me | |
end repeat | |
repeat until there is not a control 1 of group "dgList" of me | |
delete control 1 of group "dgList" of me | |
end repeat | |
put empty into sTableObjectsA["all row controls"] | |
put empty into sTableObjectsA["visible row controls"] | |
set the lockMessages to msgsAreLocked | |
end _list.DeleteControls | |
private command _list.FillListWithJustEnoughControls | |
local controlCountChanged,i | |
local theControl,theCurrentControlCount,theMsgsAreLocked,theRequiredControlCount | |
local theTemplateGroup | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
put false into controlCountChanged | |
put the dgProps["row template"] of me into theTemplateGroup | |
## Determine required control count | |
put _ControlsRequiredToFillSpace() into theRequiredControlCount | |
## Make sure we have enough controls to fill visible space | |
put sTableObjectsA["row control count"] into theCurrentControlCount | |
if theRequiredControlCount > theCurrentControlCount then | |
if sTableObjectsA["all row controls"] is not empty then put cr after sTableObjectsA["all row controls"] | |
## Create enough controls | |
lock messages ## for speed | |
repeat with i = theCurrentControlCount + 1 to theRequiredControlCount | |
copy theTemplateGroup to group "dgList" of me | |
put it into theControl | |
set the name of theControl to the short name of theControl && format("%04d", i) | |
put "control id" && word 3 of theControl && "of me" & cr after sTableObjectsA["all row controls"] | |
## take over geometry | |
set the lockloc of theControl to true | |
end repeat | |
unlock messages | |
delete the last char of sTableObjectsA["all row controls"] | |
put the number of lines of sTableObjectsA["all row controls"] into sTableObjectsA["row control count"] | |
put true into controlCountChanged | |
else if theRequiredControlCount < theCurrentControlCount then | |
## To many controls. That is okay. They just hang around invisible. | |
else | |
## We have just enough controls | |
end if | |
set the lockMessages to msgsAreLocked | |
return controlCountChanged | |
end _list.FillListWithJustEnoughControls | |
private command _list.UpdateAlternatingRowColors | |
lock screen | |
local theRow1Color, theRow2Color, theControl, theIndex | |
put _GetEffectiveColor("row color") into theRow1Color | |
put _GetEffectiveColor("alternate row color") into theRow2Color | |
set the wholeMatches to true | |
repeat for each line theControl in sTableObjectsA["visible row controls"] | |
put the dgDataControl of theControl into theControl | |
if there is a graphic "Background" of theControl then | |
put the dgIndex of theControl into theIndex | |
## don't color hilited lines | |
if theIndex is among the items of sHilitedIndexes then next repeat | |
local theLine | |
put itemOffset(theIndex, sIndexSequencing) into theLine | |
if theLine mod 2 is kAlternatingRowModValue then | |
set the backgroundColor of graphic "Background" of theControl to theRow2Color | |
else | |
set the backgroundColor of graphic "Background" of theControl to theRow1Color | |
end if | |
end if | |
end repeat | |
unlock screen | |
return empty | |
end _list.UpdateAlternatingRowColors | |
-- pControls is "all list controls" for a form or column row controls for a table | |
private function _GenerateReorderedControlList pControls, pIndexes | |
local theControl,theControlIndexesA,theControlsWithoutAHome,theIndex,theList | |
set the wholeMatches to true | |
## Separate controls based on whether or not they have an index | |
repeat for each line theControl in pControls | |
put the dgIndex of theControl into theIndex | |
if theIndex is among the items of pIndexes then | |
put theControl into theControlIndexesA[theIndex] | |
else | |
put theControl & cr after theControlsWithoutAHome | |
end if | |
end repeat | |
delete the last char of theControlsWithoutAHome | |
## Now order everything | |
repeat for each item theIndex in pIndexes | |
if theControlIndexesA[theIndex] is not empty then | |
put theControlIndexesA[theIndex] & cr after theList | |
else | |
put line 1 of theControlsWithoutAHome & cr after theList | |
delete line 1 of theControlsWithoutAHome | |
end if | |
end repeat | |
put theControlsWithoutAHome after theList | |
if the last char of theList is cr then delete the last char of theList | |
return theList | |
end _GenerateReorderedControlList | |
private command _list.DrawControlsInRealTime pForceRefresh | |
----- | |
local controlCountWasModified | |
local i | |
local msgsAreLocked | |
local noRedrawNeeded | |
local theControl, theControlIndex | |
local theEffectiveControl | |
local theIndex | |
local theIndexesInSequence | |
local theListGroupRect | |
local theMaxControlNumber | |
local theRect | |
local theRowColor | |
local theSequence | |
local theTopLeft | |
----- | |
## Get geometry properties | |
put _WorkingGroupRect(the long ID of group "dgList" of me) into theListGroupRect | |
put item 1 to 2 of theListGroupRect into theTopLeft | |
put 0 into theControlIndex | |
## Make sure there are enough controls | |
_FillListWithJustEnoughControls | |
put the result into controlCountWasModified | |
## We use sequencing to draw, not indexes | |
set the wholeMatches to true | |
put item sTableObjectsA["base sequence for visible controls"] to -1 of sIndexSequencing into theIndexesInSequence | |
put _ControlsRequiredToFillSpace() into theMaxControlNumber | |
## filter the list of sequences | |
put item 1 to theMaxControlNumber of theIndexesInSequence into theIndexesInSequence | |
## Get lookup table for indexes that already have controls with correct data | |
local theMasterControlList | |
put _GenerateReorderedControlList(sTableObjectsA["all row controls"], theIndexesInSequence) into theMasterControlList | |
put the lockMessages into msgsAreLocked | |
put not controlCountWasModified and sTableObjectsA["current"]["indexes"] is theIndexesInSequence into noRedrawNeeded | |
-- put theIndexesInSequence && the milliseconds & cr into msg | |
if not noRedrawNeeded or pForceRefresh then | |
put empty into sTableObjectsA["visible row controls"] | |
local theRow1Color, theRow2Color, controlHeightIsFixed | |
put _GetEffectiveColor("row color") into theRow1Color | |
put _GetEffectiveColor("alternate row color") into theRow2Color | |
put sTableObjectsA["base sequence for visible controls"] - 1 into theSequence | |
put the dgProps["fixed row height"] of me into controlHeightIsFixed | |
repeat for each item theIndex in theIndexesInSequence | |
## Get control reference | |
add 1 to theControlIndex | |
add 1 to theSequence | |
## This will speed things up and keep us from experiencing visual lag | |
lock messages | |
put line theControlIndex of theMasterControlList into theControl | |
if there is not a theControl then _ThrowError kErrCantFindObject, "control not found when drawing form:" && theControl | |
unlock messages | |
set the visible of theControl to true | |
put theControl & cr after sTableObjectsA["visible row controls"] | |
put the dgDataControl of theControl into theEffectiveControl | |
lock messages | |
## Set geometry | |
if controlHeightIsFixed then | |
put theTopLeft & comma & item 3 of theListGroupRect & comma & item 2 of theTopLeft + sControlHeights into theRect | |
else | |
put theTopLeft & comma & item 3 of theListGroupRect & comma & item 2 of theTopLeft + the height of theControl into theRect | |
end if | |
## Set row color | |
if there is a graphic "Background" of theEffectiveControl then | |
if theSequence mod 2 is kAlternatingRowModValue then | |
set the backgroundColor of graphic "Background" of theEffectiveControl to theRow2Color | |
else | |
set the backgroundColor of graphic "Background" of theEffectiveControl to theRow1Color | |
end if | |
end if | |
## If control index is not the index we are working on then | |
## Load new data | |
local theCurrentIndex | |
put the dgIndex of theControl into theCurrentIndex | |
if theCurrentIndex is not theIndex then | |
## Allow developer to do something if unloading control | |
if theCurrentIndex is not empty then | |
unlock messages | |
dispatch "PreFillInData" to theControl ## standardized and documented in 1.0.2 build 6 | |
-- dispatch "ResetDataGridControl" to theControl | |
lock messages | |
end if | |
set the dgIndex of theControl to theIndex | |
local theDataA | |
if sDataArray[theIndex] is NULL then | |
unlock messages | |
GetDataForLine theSequence, theDataA | |
dispatch "FillInData" to theControl with theDataA | |
lock messages | |
else | |
unlock messages | |
dispatch "FillInData" to theControl with sDataArray[theIndex] | |
lock messages | |
end if | |
else | |
-- put "not drawing index:" && theIndex & cr after msg | |
end if | |
## Set the rect AFTER filling in data in case filling in data causes the group to resize | |
unlock messages | |
set the topLeft of theControl to theTopLeft ## So developer can always count on topleft of controls in template code | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
## Resize to fit height | |
if not controlHeightIsFixed then | |
lock messages | |
put item 2 of theRect + the formattedHeight of theControl into item 4 of theRect | |
set the rect of theControl to theRect | |
unlock messages | |
end if | |
put the bottom of theControl into item 2 of theTopLeft | |
## Hilited index? | |
if theIndex is among the items of sHilitedIndexes then | |
_HiliteControl theEffectiveControl, true | |
else | |
_HiliteControl theEffectiveControl, false | |
end if | |
end repeat | |
## Store current sequences in list | |
put theIndexesInSequence into sTableObjectsA["current"]["indexes"] | |
end if | |
## Filter control list and reset controls that aren't part of visible list | |
put line (the number of lines of sTableObjectsA["visible row controls"] + 1) to -1 of theMasterControlList into theMasterControlList | |
_ResetControls theMasterControlList | |
return empty | |
end _list.DrawControlsInRealTime | |
private command _list.DrawCachedControlList pForceRefresh | |
----- | |
local isVisible | |
local msgsAreLocked | |
local noRedrawNeeded | |
local theControl | |
local theFirstControlHeight | |
local theIndex | |
local theIndexesInSequence | |
local theListGroupRect, theListGroupWidth | |
local theOldListControls | |
local theRowColor | |
local theSequence | |
local theTopLeft | |
----- | |
put false into noRedrawNeeded | |
put empty into theFirstControlHeight | |
put the lockMessages into msgsAreLocked | |
## Get geometry properties | |
put _WorkingGroupRect(the long ID of group "dgList" of me) into theListGroupRect | |
put item 1 to 2 of theListGroupRect into theTopLeft | |
put item 3 of theListGroupRect - item 1 of theListGroupRect into theListGroupWidth | |
## We use sequencing to draw, not indexes | |
set the wholeMatches to true | |
put item sTableObjectsA["base sequence for visible controls"] to -1 of sIndexSequencing into theIndexesInSequence | |
## Do we need to redraw? | |
put line 1 of sTableObjectsA["visible row controls"] into theControl | |
if there is a theControl then | |
put the dgIndex of theControl is item 1 of theIndexesInSequence into noRedrawNeeded | |
end if | |
if not noRedrawNeeded or pForceRefresh then | |
-- put "calling " & param(0) & cr after msg ## important log | |
## Cache currently visible controls | |
put sTableObjectsA["visible row controls"] into theOldListControls | |
put empty into sTableObjectsA["visible row controls"] | |
put empty into sTableObjectsA["current"]["indexes"] | |
local theRow1Color, theRow2Color | |
put _GetEffectiveColor("row color") into theRow1Color | |
put _GetEffectiveColor("alternate row color") into theRow2Color | |
put sTableObjectsA["base sequence for visible controls"] - 1 into theSequence | |
## This will speed things up and keep us from experiencing visual lag | |
lock messages | |
-- put "theIndexesInSequence:" && theIndexesInSequence & cr after msg ## important log | |
repeat for each item theIndex in theIndexesInSequence | |
add 1 to theSequence | |
put theIndex & comma after sTableObjectsA["current"]["indexes"] | |
## Get control reference | |
-- put "control (" & theIndex & "):" && theControl & cr after msg ## important log | |
put sControlOfIndexA[theIndex] into theControl | |
put theControl & cr after sTableObjectsA["visible row controls"] | |
unlock messages | |
local theEffectiveControl | |
put the dgDataControl of theControl into theEffectiveControl | |
lock messages | |
## Set row color | |
if there is a graphic "Background" of theEffectiveControl then | |
if theSequence mod 2 is kAlternatingRowModValue then | |
set the backgroundColor of graphic "Background" of theEffectiveControl to theRow2Color | |
else | |
set the backgroundColor of graphic "Background" of theEffectiveControl to theRow1Color | |
end if | |
end if | |
unlock messages | |
put the visible of theControl into isVisible | |
set the visible of theControl to true | |
if not isVisible then | |
dispatch "ShowDataGridControl" to theControl | |
end if | |
## Set coordinates | |
set the topLeft of theControl to theTopLeft | |
put the bottom of theControl into item 2 of theTopLeft | |
## Hilited index? | |
if theIndex is among the items of sHilitedIndexes then | |
_HiliteControl theEffectiveControl, true | |
else | |
_HiliteControl theEffectiveControl, false | |
end if | |
lock messages | |
## We need to know the height of first control as it must be able to scroll off screen | |
if theFirstControlHeight is empty then | |
put the height of theControl into theFirstControlHeight | |
end if | |
## Check for exit | |
if item 2 of theTopLeft >= item 4 of theListGroupRect + theFirstControlHeight then | |
exit repeat | |
end if | |
end repeat | |
delete the last char of sTableObjectsA["visible row controls"] | |
delete the last char of sTableObjectsA["current"]["indexes"] | |
-- put "----" & cr after msg ## important log | |
## We end repeat loop with locked messages | |
unlock messages | |
## Hide previously visible list controls that are no longer visible. | |
## This is performed after we know which controls are visible this time around so | |
## that controls that are visible are not hid then shown again. | |
repeat for each line theControl in theOldListControls | |
if theControl is not among the lines of sTableObjectsA["visible row controls"] then | |
try | |
set the visible of theControl to false | |
catch e | |
-- put theOrigListControls | |
-- put cr & "---" & cr & the short name of the me & cr & "can't set visible of:" && theControl & cr & theIndexesInSequence after msg | |
end try | |
-- dispatch "HideDataGridControl" to theControl | |
dispatch "HideControl" to theControl ## standardized in 1.0.2 build 6 | |
end if | |
end repeat | |
end if | |
set the lockMessages to msgsAreLocked | |
return empty | |
end _list.DrawCachedControlList | |
private command _list.CalculateFormattedHeight | |
----- | |
local msgsAreLocked | |
local theControl | |
local theIndex | |
local theListGroupRect | |
local theRect | |
local theTemplateGroup | |
local theTopLeft | |
local theDataA | |
local theError | |
----- | |
## If height is not fixed then we calculate height by looping through data and drawing controls. | |
put the dgProps["row template"] of me into theTemplateGroup | |
if theTemplateGroup is empty then return empty | |
## This handler loops through all records and gets the height of each one. The total height is then stored | |
lock screen | |
put the lockMessages into msgsAreLocked | |
put _WorkingGroupRect(the long ID of group "dgList" of me) into theListGroupRect | |
lock messages | |
copy theTemplateGroup to group "dgList" of me | |
put it into theControl | |
unlock messages | |
try ## Watch for user errors | |
put item 1 to 2 of theListGroupRect into theTopLeft | |
put theTopLeft & comma & item 3 of theListGroupRect & comma & item 2 of theListGroupRect + the height of theControl into theRect | |
lock messages | |
set the rect of theControl to theRect ## Only do this once. | |
unlock messages | |
if the dgProps["fixed row height"] of me then | |
put the dgProps["row height"] of me into sControlHeights | |
if sControlHeights is not an integer or sControlHeights < 1 then | |
if sDataArray[1] is NULL then | |
GetDataForLine 1, theDataA | |
dispatch "FillInData" to theControl with theDataA | |
else | |
dispatch "FillInData" to theControl with sDataArray[1] | |
end if | |
dispatch "LayoutControl" to theControl with theRect | |
put the formattedHeight of theControl into sControlHeights | |
end if | |
put sControlHeights * the number of elements of sDataArray into sFormattedHeight | |
else | |
repeat for each key theIndex in sDataArray | |
local theSequence | |
add 1 to theSequence | |
## Get height | |
if sDataArray[theIndex] is NULL then | |
GetDataForLine theSequence, theDataA | |
dispatch "CalculateFormattedHeight" to theControl with theDataA | |
if it is "not handled" or it is "unhandled" then | |
dispatch "FillInData" to theControl with theDataA | |
set the topLeft of theControl to theTopLeft ## So developer can rely on topleft of controls in template code. | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
put the formattedHeight of theControl into sControlHeights[theIndex] | |
else | |
put the result into sControlHeights[theIndex] | |
end if | |
else | |
dispatch "CalculateFormattedHeight" to theControl with sDataArray[theIndex] | |
if it is "not handled" or it is "unhandled" then | |
dispatch "FillInData" to theControl with sDataArray[theIndex] | |
set the topLeft of theControl to theTopLeft ## So developer can rely on topleft of controls in template code. | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
put the formattedHeight of theControl into sControlHeights[theIndex] | |
else | |
put the result into sControlHeights[theIndex] | |
end if | |
end if | |
add sControlHeights[theIndex] to sFormattedHeight | |
end repeat | |
end if | |
catch e | |
put e into theError | |
end try | |
lock messages | |
delete theControl | |
unlock messages | |
unlock screen | |
set the lockMessages to msgsAreLocked | |
if theError is not empty then throw theError | |
end _list.CalculateFormattedHeight | |
private command _list.HiliteIndexesInVisibleControls | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
set the lockMessages to false | |
set the wholeMatches to true | |
repeat for each line theControl in sTableObjectsA["visible row controls"] | |
local theBoolean | |
put the dgIndex of theControl is among the items of sHilitedIndexes into theBoolean | |
_HiliteControl the dgDataControl of theControl, theBoolean | |
end repeat | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
end _list.HiliteIndexesInVisibleControls | |
--> Type: Table | |
private command _table.UpdateHeaderLabel pColumn, pLabel, pEncoding | |
if the paramCount is 1 then | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
put theColsA[pColumn]["label"] into pLabel | |
put theColsA[pColumn]["encoding"] into pEncoding | |
end if | |
local theGroup | |
put sTableObjectsA["columns"][pColumn]["header"]["group"] into theGroup | |
if theGroup is not empty then | |
if pLabel is empty then put pColumn into pLabel | |
set the dgLabel [pEncoding] of theGroup to pLabel | |
end if | |
end _table.UpdateHeaderLabel | |
private command _table.UpdateHeaderTooltip pColumn, pTooltip | |
local theColsA, theGroup | |
if the paramCount is 1 then | |
put the dgProps["column properties"] of me into theColsA | |
put theColsA[pColumn]["tooltip"] into pTooltip | |
end if | |
put sTableObjectsA["columns"][pColumn]["header"]["group"] into theGroup | |
if theGroup is not empty then | |
set the dgTooltip of theGroup to pTooltip | |
end if | |
end _table.UpdateHeaderTooltip | |
private command _table.ScrollColumnIntoView pColumn | |
local theColGroup, theColRect, theMaskRect, theDiff | |
put sTableObjectsA["columns"][pColumn]["group"] into theColGroup | |
if theColGroup is empty then return empty | |
put the rect of theColGroup into theColRect | |
put the rect of group "dgListMask" into theMaskRect | |
if the visible of scrollbar "dgScrollbar" of me then | |
put the left of scrollbar "dgScrollbar" of me into item 3 of theMaskRect | |
end if | |
put item 1 of theColRect - item 1 of theMaskRect into theDiff | |
if theDiff > 0 then | |
put item 3 of theColRect - item 3 of theMaskRect into theDiff | |
if theDiff < 0 then | |
put 0 into theDiff | |
end if | |
end if | |
if theDiff is not 0 then | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
unlock messages | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "hScroll", mobileControlGet(sScrollerId, "hScroll") + theDiff | |
end if | |
set the thumbPosition of scrollbar "dgHScrollbar" of me to the thumbPosition of scrollbar "dgHScrollbar" of me + theDiff | |
set the lockMessages to msgsAreLocked | |
end if | |
return empty | |
end _table.ScrollColumnIntoView | |
private command _table.LayoutDataArea | |
local theViewRect, theRect, theRootGroup | |
put _WorkingGroupRect(the long ID of me) into theViewRect | |
put theViewRect into theRect | |
put the long ID of group "dgHeaderComponents" of me into theRootGroup | |
## Shift all content | |
set the topLeft of theRootGroup to item 1 to 2 of theRect | |
## Reposition bottom element | |
local theSBWidth | |
if the visible of scrollbar "dgScrollbar" of me then put the width of scrollbar "dgScrollbar" of me into theSBWidth | |
else put the dgProps["scrollbar corner offset"] of me into theSBWidth | |
set the visible of graphic "dgCornerPiece" of group "dgHorizontalComponents" of me to the visible of scrollbar "dgScrollbar" of me | |
set the rect of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me to \ | |
item 1 of theRect, item 4 of theRect - the height of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me, \ | |
item 3 of theRect - theSBWidth, item 4 of theRect | |
## Header mask | |
put the bottom of group "dgHeaderMask" of theRootGroup into item 4 of theRect | |
set the rect of group "dgHeaderMask" of theRootGroup to theRect | |
## Define new top/bottom | |
if the visible of theRootGroup then | |
put item 4 of theRect into item 2 of theRect | |
end if | |
if the visible of group "dgHorizontalComponents" of me then | |
put the top of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me into item 4 of theRect | |
else | |
put item 4 of theViewRect - the dgProps["scrollbar corner offset"] of me into item 4 of theRect | |
end if | |
## V Scrollbar and corner piece | |
set the rect of scrollbar "dgScrollbar" of me to item 3 of theRect - the width of scrollbar "dgScrollbar" of me, \ | |
item 2 of theRect, item 3 of theRect, \ | |
item 4 of theRect | |
set the rect of graphic "dgCornerPiece" of group "dgHorizontalComponents" of me to \ | |
item 3 of theRect - the width of scrollbar "dgScrollbar" of me, \ | |
the top of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me, item 3 of theRect, \ | |
the bottom of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me | |
## Masks | |
## AL-2013-07-30 [[ Bug 10108 ]] Thumb and scrollbar appear over top of DataGrid | |
if the visible of scrollbar "dgScrollbar" of me then put item 3 of theRect - theSBWidth into item 3 of theRect | |
set the topLeft of group "dgListMask" of me to item 1 to 2 of theRect | |
set the rect of group "dgListMask" of me to theRect | |
set the rect of group "dgAlternatingRowsMask" of me to theRect | |
## Shift List Group | |
_table.ResizeHeaderBackground | |
_table.ResizeList | |
_table.RepositionColumns | |
_table.LayoutRowHilites | |
end _table.LayoutDataArea | |
private command _table.SetHeaderBkgrndHiliteColor pColor | |
repeat for each line theColumn in the dgProps["columns"] of me | |
local theGroup | |
put sTableObjectsA["columns"][theColumn]["header"]["group"] into theGroup | |
if there is a theGroup then | |
_table.HiliteHeaderBackground theGroup, pColor | |
end if | |
end repeat | |
end _table.SetHeaderBkgrndHiliteColor | |
private command _table.HiliteHeaderBackground pHeaderGroup, pColor | |
local setFillGradient, useAGradient | |
put pColor is an array into setFillGradient | |
put the number of lines of pColor is 2 into useAGradient | |
if there is a graphic "Background" of pHeaderGroup then | |
if setFillGradient then | |
repeat for each key theKey in pColor | |
set the fillGradient[theKey] of graphic "Background" of pHeaderGroup to pColor[theKey] | |
end repeat | |
else if useAGradient then | |
_SetGraphicGradient the long ID of graphic "Background" of pHeaderGroup, line 1 of pColor, line 2 of pColor | |
else | |
set the backgroundColor of graphic "Background" of pHeaderGroup to pColor | |
end if | |
end if | |
end _table.HiliteHeaderBackground | |
-- pTarget is optional and allows you to target a hilite background | |
private command _table.SetHeaderBkgrndGradient pStartColor, pEndColor | |
_SetGraphicGradient the long ID of graphic "dgBackground" of group "dgHeaderMask" of group "dgHeaderComponents" of me, \ | |
pStartColor, pEndColor | |
end _table.SetHeaderBkgrndGradient | |
private command _table.UpdateHeaderDividerColors | |
repeat for each line theColumn in the dgProps["columns"] of me | |
local theGroup | |
put sTableObjectsA["columns"][theColumn]["header"]["group"] into theGroup | |
if there is a theGroup then | |
set the foregroundColor of graphic "RightHilite" of theGroup to the dgProps["header divider color"] of me | |
set the foregroundColor of graphic "LeftHilite" of theGroup to the dgProps["header divider threeD color"] of me | |
end if | |
end repeat | |
end _table.UpdateHeaderDividerColors | |
private function _table.GetText pIncludeColumns | |
----- | |
local theColumn, theColumns | |
local theIndex | |
local theText | |
----- | |
put the dgProps["columns"] of me into theColumns | |
if pIncludeColumns then | |
put theColumns into theText | |
replace cr with tab in theText | |
put cr after theText | |
end if | |
repeat for each item theIndex in sIndexSequencing | |
repeat for each line theColumn in theColumns | |
put sDataArray[theIndex][theColumn] & tab after theText | |
end repeat | |
put cr into the last char of theText | |
end repeat | |
delete the last char of theText | |
return theText | |
end _table.GetText | |
private command _table.SetText pText, pColumns | |
----- | |
local i | |
local theColNum, theColumn, theColumns, theItemNoColumnA | |
local theDataA | |
local theItem | |
local theLine | |
local theSequencing | |
----- | |
lock screen | |
set the itemDelimiter to tab | |
if pColumns is empty then | |
put the dgProps["columns"] of me into pColumns | |
## Make sure we have some columns defined | |
local theColCount | |
put the number of lines of pColumns into theColCount | |
if the number of lines of pColumns < the number of items of line 1 of pText then | |
if pColumns is not empty then put cr after pColumns | |
repeat with i = 1 to the number of items of line 1 of pText | |
if i > theColCount then | |
put "Col" && i & cr after pColumns | |
end if | |
end repeat | |
delete the last char of pColumns | |
set the dgProps["columns"] of me to pColumns | |
end if | |
end if | |
put the number of lines of pColumns into theColCount | |
## Store column item no's and total column count | |
put 0 into i | |
repeat for each line theColumn in pColumns | |
add 1 to i | |
put theColumn into theItemNoColumnA[i] | |
end repeat | |
repeat for each line theLine in pText | |
local theRow | |
add 1 to theRow | |
put 0 into theColNum | |
put theRow & comma after theSequencing | |
if theLine is empty then | |
## Fill in empty records for empty lines | |
repeat for each line theColumn in pColumns | |
put empty into theDataA[theRow][theColumn] | |
end repeat | |
else | |
repeat for each item theItem in theLine | |
add 1 to theColNum | |
put theItem into theDataA[theRow][ theItemNoColumnA[theColNum] ] | |
if theColNum is theColCount then exit repeat | |
end repeat | |
end if | |
end repeat | |
delete the last char of theSequencing | |
set the dgData [theSequencing] of me to theDataA | |
unlock screen | |
end _table.SetText | |
private command _table.AddLine pText, pColumns, pLineNo | |
----- | |
local i | |
local theColNum, theColumn, theColumns, theItemNoColumnA | |
local theDataA | |
local theItem | |
local theLine,theResult | |
----- | |
lock screen | |
set the itemDelimiter to tab | |
if pColumns is empty then | |
put the dgProps["columns"] of me into pColumns | |
## Make sure we have some columns defined | |
local theColCount | |
put the number of lines of pColumns into theColCount | |
if the number of lines of pColumns < the number of items of line 1 of pText then | |
if pColumns is not empty then put cr after pColumns | |
repeat with i = 1 to the number of items of line 1 of pText | |
if i > theColCount then | |
put "Col" && i & cr after pColumns | |
end if | |
end repeat | |
delete the last char of pColumns | |
set the dgProps["columns"] of me to pColumns | |
end if | |
end if | |
put the number of lines of pColumns into theColCount | |
## Store column item no's and total column count | |
put 0 into i | |
repeat for each line theColumn in pColumns | |
add 1 to i | |
put theColumn into theItemNoColumnA[i] | |
end repeat | |
## add lines | |
repeat for each line theLine in pText | |
put 0 into theColNum | |
put empty into theDataA | |
repeat for each item theItem in theLine | |
add 1 to theColNum | |
put theItem into theDataA[ theItemNoColumnA[theColNum] ] | |
if theColNum is theColCount then exit repeat | |
end repeat | |
## Note: This redraws list each time. Officially we only support one line of text right | |
## now but we could bring the logic of AddData inline and not redraw until the end. | |
AddData theDataA, pLineNo | |
put the result into theResult | |
if pLineNo is not empty then add 1 to pLineNo | |
end repeat | |
unlock screen | |
return theResult | |
end _table.AddLine | |
private command _table.DrawWithProperties pSetVScrollTo, pForceRefresh | |
## We only scroll by entire records so if any scrolling is supposed to occur then | |
## start 1 higher | |
if pSetVScrollTo > 0 then add 1 to sTableObjectsA["base sequence for visible controls"] | |
_table.DrawControlsInRealTime pForceRefresh | |
if sTableObjectsA["base sequence for visible controls"] mod 2 is kAlternatingRowModValue then | |
set the top of graphic "dgAlternatingRows" of me to the top of group "dgAlternatingRowsMask" of me - sControlHeights | |
else | |
set the top of graphic "dgAlternatingRows" of me to the top of group "dgAlternatingRowsMask" of me | |
end if | |
end _table.DrawWithProperties | |
private command _table.DeleteColumn pColumn | |
----- | |
local msgsAreLocked | |
local theColPropsA | |
local theControl | |
----- | |
lock screen | |
put sTableObjectsA["columns"][pColumn]["header"]["group"] into theControl | |
if there is a theControl then delete theControl | |
put sTableObjectsA["columns"][pColumn]["group"] into theControl | |
if there is a theControl then delete theControl | |
put sTableObjectsA["columns"][pColumn]["divider control"] into theControl | |
if there is a theControl then delete theControl | |
delete local sTableObjectsA["columns"][pColumn] | |
## column properties | |
put the dgProps["column properties"] of me into theColPropsA | |
delete local theColPropsA[pColumn] | |
put the lockMessages into msgsAreLocked | |
lock messages | |
set the dgProps["column properties"] of me to theColPropsA | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
end _table.DeleteColumn | |
private command _table.DeleteColumnControls | |
----- | |
local i | |
local msgsAreLocked | |
local theColumn | |
----- | |
put the lockMessages into msgsAreLocked | |
lock messages ## for speed | |
repeat until there is not a group 1 of group "dgList" of group "dgListMask" of me | |
## Nested groups will mess up the count as we delete | |
delete group 1 of group "dgList" of group "dgListMask" of me | |
end repeat | |
repeat with i = 1 to the number of controls of group "dgList" of group "dgListMask" of me | |
delete control 1 of group "dgList" of group "dgListMask" of me | |
end repeat | |
repeat until there is not a group 1 of group "dgDividers" of group "dgListMask" of me | |
delete group 1 of group "dgDividers" of group "dgListMask" of me | |
end repeat | |
repeat with i = 1 to the number of controls of group "dgDividers" of group "dgListMask" of me | |
delete control 1 of group "dgDividers" of group "dgListMask" of me | |
end repeat | |
repeat until there is not a group 1 of group "dgHeader" of group "dgHeaderComponents" of me | |
delete group 1 of group "dgHeader" of group "dgHeaderComponents" of me | |
end repeat | |
repeat with i = 1 to the number of controls of group "dgHeader" of group "dgHeaderComponents" of me | |
delete control 1 of group "dgHeader" of group "dgHeaderComponents" of me | |
end repeat | |
repeat for each line theColumn in the dgProps["columns"] of me | |
delete local sTableObjectsA["columns"][theColumn] | |
end repeat | |
set the lockMessages to msgsAreLocked | |
end _table.DeleteColumnControls | |
private command _table.DeleteDataControls | |
----- | |
local i | |
local msgsAreLocked | |
local theColGroup, theColumn | |
----- | |
put the lockMessages into msgsAreLocked | |
lock messages ## for speed | |
## cleanup | |
## Hilite graphics | |
repeat for each line theControl in sTableObjectsA["row hilite controls"] | |
delete theControl | |
end repeat | |
put empty into sTableObjectsA["row hilite controls"] | |
repeat with i = 1 to the number of controls of group "dgHighlights" of group "dgListMask" of me | |
delete control 1 of group "dgHighlights" of group "dgListMask" of me | |
end repeat | |
## Columns | |
repeat for each line theColumn in the dgProps["columns"] of me | |
put sTableObjectsA["columns"][theColumn]["group"] into theColGroup | |
if theColGroup is empty then next repeat | |
## stored | |
repeat for each line theControl in sTableObjectsA["columns"][theColumn]["row controls"] | |
delete theControl | |
end repeat | |
## cleanup | |
repeat with i = 1 to the number of groups of theColGroup | |
delete group 1 of theColGroup | |
end repeat | |
repeat with i = 1 to the number of controls of theColGroup | |
delete control 1 of theColGroup | |
end repeat | |
put empty into sTableObjectsA["columns"][theColumn]["row controls"] | |
end repeat | |
set the lockMessages to msgsAreLocked | |
end _table.DeleteDataControls | |
private command _table.LayoutRowHilites | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
local theRect | |
put the rect of group "dgList" of group "dgListMask" of me into theRect | |
put item 2 of theRect + sControlHeights into item 4 of theRect | |
repeat for each line theControl in sTableObjectsA["row hilite controls"] | |
set the rect of theControl to theRect | |
local theTop | |
put item 4 of theRect into theTop | |
put theTop into item 2 of theRect | |
put theTop + sControlHeights into item 4 of theRect | |
set the backgroundColor of theControl to _GetHiliteColor() | |
end repeat | |
set the lockMessages to msgsAreLocked | |
end _table.LayoutRowHilites | |
private command _table.FillListWithJustEnoughControls | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
local controlCountChanged, theTemplateGroup, theColumns, theResourceStack, theRequiredControlCount, theCurrentControlCount | |
put false into controlCountChanged | |
put the dgProps["row template"] of me into theTemplateGroup | |
put _table.VisibleColumns() into theColumns | |
put _ResourceStack() into theResourceStack | |
## Determine required control count | |
put _ControlsRequiredToFillSpace() into theRequiredControlCount | |
## Make sure we have enough controls to fill visible space | |
put sTableObjectsA["row control count"] into theCurrentControlCount | |
-- put the number of lines of (sTableObjectsA["columns"][line 1 of theColumns]["row controls"]) into theCurrentControlCount | |
lock messages ## for speed | |
if theRequiredControlCount > theCurrentControlCount then | |
## | |
## Create highlight graphics | |
## | |
reset the templategraphic | |
set the opaque of the templategraphic to true | |
set the lineSize of the templategraphic to 0 | |
set the antialiased of the templategraphic to false | |
-- put _CardOf() into theDGCard | |
if sTableObjectsA["row hilite controls"] is not empty then put cr after sTableObjectsA["row hilite controls"] | |
repeat with i = theCurrentControlCount + 1 to theRequiredControlCount | |
create graphic ("hilite" && format("%04d", i)) in group "dgHighlights" of group "dgListMask" of me | |
put "control id" && word 3 of it && "of me" & cr after sTableObjectsA["row hilite controls"] | |
set the visible of it to false | |
end repeat | |
delete the last char of sTableObjectsA["row hilite controls"] | |
reset the templategraphic | |
_table.LayoutRowHilites | |
## | |
## Create column controls | |
## | |
_table.CreateControlsForColumns theColumns, theRequiredControlCount | |
## Store first column in list of all controls so other handler can use it for information purposes | |
put true into controlCountChanged | |
else if theRequiredControlCount < theCurrentControlCount then | |
## To many controls: Leave as is for later. //Delete them as this makes management much easier with controls spread across multiple columns. | |
else | |
## We have just enough controls | |
end if | |
set the lockMessages to msgsAreLocked | |
return controlCountChanged | |
end _table.FillListWithJustEnoughControls | |
private command _table.CreateControlsForColumns pColumns, pRequiredControlCount | |
----- | |
local i | |
local msgsAreLocked | |
local theColPropsA, theColumn, theColWidth | |
local theControl | |
local theCurrentControlCount | |
local theResourceStack | |
local theTemplateGroup | |
local theTopLeft | |
local theMargins | |
----- | |
put the lockMessages into msgsAreLocked | |
unlock messages | |
put the dgProps["column margins"] of me into theMargins | |
lock messages | |
if sTableObjectsA["row control count"] is not an integer then put 0 into sTableObjectsA["row control count"] | |
if pRequiredControlCount is not an integer or pRequiredControlCount < 0 then put sTableObjectsA["row control count"] into pRequiredControlCount | |
## Initialize | |
put the dgProps["row template"] of me into theTemplateGroup | |
put _ResourceStack() into theResourceStack | |
-- put _CardOf() into theDGCard | |
put the dgProps["column properties"] of me into theColPropsA | |
## Create | |
repeat for each line theColumn in pColumns | |
put the number of lines of sTableObjectsA["columns"][theColumn]["row controls"] into theCurrentControlCount | |
if theCurrentControlCount >= pRequiredControlCount then next repeat | |
## We store each column's controls in separate list | |
## Initialize geometry vars for this column | |
if sTableObjectsA["columns"][theColumn]["row controls"] is not empty then | |
put the bottomLeft of (the last line of sTableObjectsA["columns"][theColumn]["row controls"]) into theTopLeft | |
put cr after sTableObjectsA["columns"][theColumn]["row controls"] | |
else | |
put the topLeft of group theColumn of group "dgList" of me into theTopLeft | |
end if | |
put the width of group theColumn of group "dgList" of me into theColWidth | |
repeat with i = theCurrentControlCount + 1 to pRequiredControlCount | |
if sTableObjectsA["columns"][theColumn]["uses custom template"] then | |
copy control theColumn of theTemplateGroup to group theColumn of group "dgList" of me | |
## Use default behavior if control doesn't have one assigned. | |
if the behavior of it is empty then | |
if the dgProps["default column behavior"] of me is empty then | |
set the behavior of it to the long ID of button "Default Column" of theResourceStack | |
else | |
set the behavior of it to the dgProps["default column behavior"] of me | |
end if | |
end if | |
else | |
_table.ConfigureTemplateFieldForColumn line 1 of pColumns | |
create field in group theColumn of group "dgList" of me | |
set the textAlign of it to theColPropsA[theColumn]["alignment"] | |
set the margins of it to theMargins | |
if the dgProps["default column behavior"] of me is empty then | |
set the behavior of it to the long ID of button "Default Column" of theResourceStack | |
else | |
set the behavior of it to the dgProps["default column behavior"] of me | |
end if | |
end if | |
put it into theControl | |
set the name of theControl to theColumn && format("%04d", i) | |
## Take control of geometry | |
set the lockloc of theControl to true | |
local theRect | |
put theTopLeft into theRect | |
put item 1 of theRect + theColWidth into item 3 of theRect | |
put item 2 of theRect + sControlHeights into item 4 of theRect | |
set the topLeft of theControl to theTopLeft | |
set the rect of theControl to theRect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
lock messages | |
add sControlHeights to item 2 of theTopLeft | |
## We store each column's controls in separate list | |
put "control id" && word 3 of theControl && "of me" & cr after sTableObjectsA["columns"][theColumn]["row controls"] | |
end repeat | |
delete the last char of sTableObjectsA["columns"][theColumn]["row controls"] | |
end repeat | |
put the number of lines of sTableObjectsA["columns"][line 1 of pColumns]["row controls"] into sTableObjectsA["row control count"] | |
unlock messages | |
set the lockMessages to msgsAreLocked | |
return empty | |
end _table.CreateControlsForColumns | |
private command _table.DrawControlsInRealTime pForceRefresh | |
local theColumnsA | |
put _table.AreColumnsVisibleWithinMask() into theColumnsA | |
if the keys of sTableObjectsA["columns"] is empty or sTableObjectsA["columns"][line 1 of theColumnsA["visible"]]["group"] is empty then | |
_table.CreateColumns | |
end if | |
## Make sure there are enough controls | |
_table.FillListWithJustEnoughControls | |
local controlCountWasModified | |
put the result into controlCountWasModified | |
## We use sequencing to draw, not indexes | |
set the wholeMatches to true | |
local theIndexesInSequence | |
put item sTableObjectsA["base sequence for visible controls"] to -1 of sIndexSequencing into theIndexesInSequence | |
local theControlIndex | |
put 0 into theControlIndex | |
## Do we need to redraw? | |
local noRedrawNeeded | |
put not controlCountWasModified and \ | |
sTableObjectsA["base sequence for last rendering"] is sTableObjectsA["base sequence for visible controls"] into noRedrawNeeded | |
if not noRedrawNeeded or pForceRefresh then | |
if pForceRefresh then | |
## force refresh should redraw all columns at once | |
_table.DrawColumns _table.VisibleColumns() | |
-- put "drawing all columns:" && the milliseconds | |
else | |
_table.DrawColumns theColumnsA["visible"] --the dgProps["columns"] of me | |
## If user is interacting with scrollbar then no need to throttle. | |
## Just redraw on mouseUp | |
cancel sPendingMsgsA["draw hidden columns"] | |
if not sRunningActionsA["user is vscrolling"] then | |
local theHiddenColumns | |
put theColumnsA["hidden"] into theHiddenColumns | |
send "table.DrawColumns theHiddenColumns" to me in 200 milliseconds | |
put the result into sPendingMsgsA["draw hidden columns"] | |
end if | |
end if | |
## Cache what we started on last time | |
put sTableObjectsA["base sequence for visible controls"] into sTableObjectsA["base sequence for last rendering"] | |
end if | |
return empty | |
end _table.DrawControlsInRealTime | |
## Needed for send call in _table.DrawControlsInRealTime | |
command table.DrawColumns pColumns, pIndexesToDraw | |
_table.DrawColumns pColumns, pIndexesToDraw | |
end table.DrawColumns | |
private command _table.DrawColumns pColumns, pIndexesToDraw | |
local theStart | |
put the milliseconds into theStart #####!!!!! | |
if pColumns is empty then return empty | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
set the wholeMatches to true | |
## Default is not to pass in value for pIndexes to draw in which case | |
## we use the base sequence for visible controls to determine indexes to draw | |
if pIndexesToDraw is not empty then | |
local theSequence | |
put itemOffset(item 1 of pIndexesToDraw, sIndexSequencing) - 1 into theSequence | |
else | |
put item sTableObjectsA["base sequence for visible controls"] to \ | |
(sTableObjectsA["base sequence for visible controls"] + sTableObjectsA["row control count"] - 1) of sIndexSequencing into pIndexesToDraw | |
put sTableObjectsA["base sequence for visible controls"] - 1 into theSequence | |
end if | |
## Initialize now as this is used regardless of whether or not we have indexes to draw | |
local theControlIndex | |
put 0 into theControlIndex | |
if pIndexesToDraw is not empty then | |
## Get geometry properties | |
local theListGroupRect, theTopLeft | |
put _WorkingGroupRect(the long ID of group "dgList" of me) into theListGroupRect | |
put item 1 to 2 of theListGroupRect into theTopLeft | |
## How many vertical controls do we need to draw? | |
-- put _ControlsRequiredToFillSpace() into theMaxControlNumber | |
repeat for each line theColumn in pColumns | |
## Get lookup table for indexes that already have controls with correct data | |
local theTopLeftA, theMasterControlList | |
put the topLeft of sTableObjectsA["columns"][theColumn]["group"] into theTopLeftA[theColumn] | |
put _GenerateReorderedControlList(sTableObjectsA["columns"][theColumn]["row controls"], pIndexesToDraw) into theMasterControlList[theColumn] | |
## Store line indexes | |
put empty into sTableObjectsA["columns"][theColumn]["control line numbers"] | |
repeat for each line theControl in theMasterControlList[theColumn] | |
put lineOffset(theControl, sTableObjectsA["columns"][theColumn]["row controls"]) & comma after sTableObjectsA["columns"][theColumn]["control line numbers"] | |
end repeat | |
delete the last char of sTableObjectsA["columns"][theColumn]["control line numbers"] | |
end repeat | |
-- PrintKeys theMasterControlList | |
-- put cr & cr & the executioncontexts after msg | |
## Draw it | |
unlock messages | |
repeat for each item theIndex in pIndexesToDraw | |
add 1 to theControlIndex | |
add 1 to theSequence | |
## get row data | |
local theDataA | |
if sDataArray[theIndex] is NULL then | |
GetDataForLine theSequence, theDataA | |
end if | |
## Deal with rows controls | |
repeat for each line theColumn in pColumns | |
put line theControlIndex of theMasterControlList[theColumn] into theControl | |
-- if there is not a theControl then | |
-- put "Control for index " & theControlIndex & " does not exist for column " & theColumn & cr & cr & \ | |
-- the executioncontexts & cr & cr & \ | |
-- "theMasterControlList:" & cr & _PrintKeys(theMasterControlList) & cr & cr & \ | |
-- "sDataArray:" && _PrintKeys(sDataArray) after msg | |
-- end if | |
local theCurrentIndex | |
put the dgIndex of theControl into theCurrentIndex | |
if theIndex is not theCurrentIndex then | |
## Allow developer to do something if unloading control | |
if theCurrentIndex is not empty then | |
unlock messages | |
dispatch "PreFillInData" to theControl | |
lock messages | |
end if | |
set the visible of theControl to true | |
lock messages | |
set the dgIndex of theControl to theIndex | |
unlock messages | |
if sDataArray[theIndex] is NULL then | |
dispatch "FillInData" to theControl with theDataA[theColumn] | |
else | |
dispatch "FillInData" to theControl with sDataArray[theIndex][theColumn] | |
end if | |
else | |
-- put "not drawing index " & theIndex & " for column" && theColumn & cr after msg | |
-- no need to redraw | |
end if | |
set the topLeft of theControl to theTopLeftA[theColumn] | |
add sControlHeights to item 2 of theTopLeftA[theColumn] | |
end repeat | |
## Hilited index? | |
if theIndex is among the items of sHilitedIndexes then | |
_table.HiliteRow theControlIndex, true | |
else | |
_table.HiliteRow theControlIndex, false | |
end if | |
end repeat | |
put pIndexesToDraw into sTableObjectsA["current"]["indexes"] | |
end if | |
## Reset hilites and hide any extra controls for columns | |
lock messages | |
repeat for each line theColumn in pColumns | |
_ResetControls line (theControlIndex + 1) to -1 of theMasterControlList[theColumn] | |
end repeat | |
repeat with theControlIndex = theControlIndex + 1 to the number of lines of theMasterControlList[line 1 of pColumns] | |
_table.HiliteRow theControlIndex, false | |
end repeat | |
unlock messages | |
-- put "time spent in" && param(0) & ":" && the milliseconds - theStart & cr --after msg | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
-- put theLog & cr after msg | |
return empty | |
end _table.DrawColumns | |
private command _table.HiliteIndexesInVisibleControls | |
local theColumn, i, theIndexes, theBoolean | |
put line 1 of _table.VisibleColumns() into theColumn | |
if theColumn is empty then return empty | |
set the wholeMatches to true | |
lock screen | |
put 0 into i | |
put item sTableObjectsA["base sequence for visible controls"] to \ | |
(sTableObjectsA["base sequence for visible controls"] + sTableObjectsA["row control count"]) of sIndexSequencing into theIndexes | |
repeat for each item theIndex in theIndexes | |
add 1 to i | |
put theIndex is among the items of sHilitedIndexes into theBoolean | |
_table.HiliteRow i, theBoolean | |
end repeat | |
unlock screen | |
end _table.HiliteIndexesInVisibleControls | |
private command _table.HiliteRow pRow, pBoolean | |
local theControl, theColumns | |
put line pRow of sTableObjectsA["row hilite controls"] into theControl | |
if theControl is empty then return empty | |
put _table.VisibleColumns() into theColumns | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
unlock messages | |
lock screen | |
set the visible of theControl to pBoolean | |
repeat for each line theColumn in theColumns | |
local theLineNo | |
put item pRow of sTableObjectsA["columns"][theColumn]["control line numbers"] into theLineNo | |
put line theLineNo of sTableObjectsA["columns"][theColumn]["row controls"] into theControl | |
if theControl is empty then exit repeat | |
set the dgHilite of theControl to pBoolean | |
end repeat | |
unlock screen | |
set the lockMessages to msgsAreLocked | |
end _table.HiliteRow | |
private command _table.ConfigureTemplateFieldForColumn pColumn, pFieldHeight | |
reset the templatefield | |
if pFieldHeight is empty then put kDefaultRowHeight into pFieldHeight | |
set the traversalOn of the templatefield to false | |
set the autoTab of the templatefield to true | |
set the dontWrap of the templatefield to true | |
set the showBorder of the templatefield to false | |
set the borderWidth of the templatefield to 2 | |
set the margins of the templatefield to 8 | |
set the lockText of the templatefield to true | |
set the opaque of the templatefield to false | |
set the height of the templatefield to pFieldHeight | |
return empty | |
end _table.ConfigureTemplateFieldForColumn | |
private command _table.CacheCustomTemplateUsage | |
local theTemplateGroup, theColumns, templateExists | |
put the dgProps["row template"] of me into theTemplateGroup | |
put the dgProps["columns"] of me into theColumns | |
put there is a theTemplateGroup into templateExists | |
repeat for each line theColumn in theColumns | |
if templateExists then | |
put there is a control theColumn of theTemplateGroup \ | |
and the long ID of the owner of control theColumn of theTemplateGroup is the long ID of theTemplateGroup \ | |
into sTableObjectsA["columns"][theColumn]["uses custom template"] | |
put there is a control (theColumn && "[Header]") of theTemplateGroup \ | |
and the long ID of the owner of control (theColumn && "[Header]") of theTemplateGroup is the long ID of theTemplateGroup \ | |
into sTableObjectsA["columns"][theColumn]["header"]["uses custom template"] | |
else | |
put false into sTableObjectsA["columns"][theColumn]["uses custom template"] | |
put false into sTableObjectsA["columns"][theColumn]["header"]["uses custom template"] | |
end if | |
end repeat | |
end _table.CacheCustomTemplateUsage | |
private command _table.CalculateFormattedHeight | |
## Cache which are custom | |
_table.CacheCustomTemplateUsage | |
## Fill in row height | |
put the dgProps["row height"] of me into sControlHeights | |
if sControlHeights is not an integer or sControlHeights < 1 then put kDefaultRowHeight into sControlHeights | |
put sControlHeights * the number of elements of sDataArray into sFormattedHeight | |
end _table.CalculateFormattedHeight | |
private command _table.RegenerateColumns | |
lock screen | |
_table.CreateColumns | |
_table.LayoutRowHilites | |
_table.CreateControlsForColumns _table.VisibleColumns() | |
-- set the uEffectiveColumnWidths of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me \ | |
-- to _table.GetEffectiveColumnWidths() | |
unlock screen | |
end _table.RegenerateColumns | |
## Return list of visible controls in proper order | |
private function _ListOfVisibleControls | |
local theControls | |
if _ControlType() is "table" then | |
## list of controls for a column contains all visible controls. | |
## There might be less data than controls. | |
repeat for each line theControl in sTableObjectsA["columns"][ _table.FirstVisibleColumn() ]["row controls"] | |
if the visible of theControl then | |
put theControl & cr after theControls | |
end if | |
end repeat | |
delete the last char of theControls | |
return _GenerateReorderedControlList(theControls, sTableObjectsA["current"]["indexes"]) | |
else | |
return sTableObjectsA["visible row controls"] | |
end if | |
end _ListOfVisibleControls | |
private function _table.FirstVisibleColumn | |
----- | |
local theColPropsA, theColumn, theColumns | |
local theVisibleColumns | |
----- | |
put the dgProps["columns"] of me into theColumns | |
put the dgProps["column properties"] of me into theColPropsA | |
repeat for each line theColumn in theColumns | |
if theColPropsA[theColumn]["visible"] is not false then | |
return theColumn | |
end if | |
end repeat | |
return empty | |
end _table.FirstVisibleColumn | |
private function _table.VisibleColumns | |
----- | |
local theColPropsA, theColumn, theColumns | |
local theVisibleColumns | |
----- | |
put the dgProps["columns"] of me into theColumns | |
put the dgProps["column properties"] of me into theColPropsA | |
repeat for each line theColumn in theColumns | |
if theColPropsA[theColumn]["visible"] is not false then | |
put theColumn & cr after theVisibleColumns | |
end if | |
end repeat | |
delete the last char of theVisibleColumns | |
return theVisibleColumns | |
end _table.VisibleColumns | |
## Returns array [hidden|visible] containing list of column | |
## names that are hidden and visible within the list mask | |
private function _table.AreColumnsVisibleWithinMask | |
local theColumns, theWidths | |
put _table.VisibleColumns() into theColumns | |
put _table.GetEffectiveColumnWidths() into theWidths | |
local theMinX, theRect, theVisibleWidth, theMaxX, theItemNo, theTotalWidth | |
put the dgHScroll of me into theMinX | |
put _WorkingGroupRect(the long ID of group "dgListMask" of me) into theRect | |
put the left of scrollbar "dgScrollbar" of me - item 1 of theRect into theVisibleWidth | |
put theMinX + theVisibleWidth into theMaxX | |
put 0 into theItemNo | |
put 0 into theTotalWidth | |
repeat for each line theColumn in theColumns | |
local theColumnsA | |
if theTotalWidth > theMaxX then | |
put theColumn & cr after theColumnsA["hidden"] | |
else | |
add 1 to theItemNo | |
local theColWidth | |
put item theItemNo of theWidths into theColWidth | |
if theTotalWidth + theColWidth < theMinX then | |
put theColumn & cr after theColumnsA["hidden"] | |
add theColWidth to theTotalWidth | |
else | |
put theColumn & cr after theColumnsA["visible"] | |
add theColWidth to theTotalWidth | |
end if | |
end if | |
end repeat | |
return theColumnsA | |
end _table.AreColumnsVisibleWithinMask | |
private command _table.CreateColumns | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
local theResourceStack | |
put _ResourceStack() into theResourceStack | |
local theOrigHScroll | |
put _GetHScrollPercent() into theOrigHScroll | |
_ResetHScrollToZero | |
## Note: We generate a group for all columns whether visible or not | |
## This is just easier for the time being. Perhaps change later. | |
local theColumns, theTopLeft | |
put the dgProps["columns"] of me into theColumns | |
put item 1 to 2 of the rect of group "dgList" of me into theTopLeft | |
## Loop through existing columns and delete out those that are no | |
## longer included | |
set the wholeMatches to true | |
repeat for each key theColumn in sTableObjectsA["columns"] | |
if theColumn is not among the lines of theColumns then | |
_table.DeleteColumn theColumn | |
end if | |
end repeat | |
## delete column props (I don't think this is necessary since | |
## adding _table.deleteColumn but haven't tested yet) | |
local theColPropsA | |
put the dgProps["column properties"] of me into theColPropsA | |
repeat for each key theColumn in theColPropsA | |
if theColumn is not among the lines of theColumns then | |
delete local theColPropsA[theColumn] | |
end if | |
end repeat | |
_table.CacheCustomTemplateUsage | |
_table.CreateHeaders | |
## Prepare to create controls | |
reset the templategroup | |
set the lockloc of the templategroup to true | |
set the showBorder of the templategroup to false | |
set the margins of the templategroup to 0 | |
set the borderWidth of the templategroup to 0 | |
set the showName of the templategroup to false | |
reset the templategraphic | |
-- put _CardOf() into theDGCard | |
## Create controls | |
repeat for each line theColumn in theColumns | |
## Set default values | |
if theColumn is not among the keys of theColPropsA then | |
put true into theColPropsA[theColumn]["visible"] | |
put "left" into theColPropsA[theColumn]["alignment"] | |
put "ascending" into theColPropsA[theColumn]["sort direction"] | |
put "text" into theColPropsA[theColumn]["sort type"] | |
put false into theColPropsA[theColumn]["sort is case sensitive"] | |
put 100 into theColPropsA[theColumn]["width"] | |
put 40 into theColPropsA[theColumn]["min width"] | |
put 1000 into theColPropsA[theColumn]["max width"] | |
put true into theColPropsA[theColumn]["resizable"] | |
end if | |
## Column | |
if sTableObjectsA["columns"][theColumn]["group"] is empty then | |
create group theColumn in group "dgList" of me | |
put "control id" && word 3 of it && "of me" into sTableObjectsA["columns"][theColumn]["group"] | |
set the topLeft of it to theTopLeft | |
set the visible of it to theColPropsA[theColumn]["visible"] | |
set the behavior of it to the long ID of button "Column Group" of group "Behaviors" of theResourceStack | |
end if | |
## column divider | |
if sTableObjectsA["columns"][theColumn]["divider control"] is empty then | |
create graphic in group "dgDividers" of group "dgListMask" of me | |
put "control id" && word 3 of it && "of me" into sTableObjectsA["columns"][theColumn]["divider control"] | |
set the enabled of it to false ## don't want it to get messages | |
set the foregroundColor of it to the dgProps["column divider color"] of me | |
set the rect of it to theTopLeft, item 1 of theTopLeft + 1, item 2 of theTopLeft + 1 | |
end if | |
end repeat | |
reset the templategroup | |
reset the templategraphic | |
## Toggle visibility controls | |
repeat for each line theColumn in theColumns | |
set the visible of sTableObjectsA["columns"][theColumn]["group"] to theColPropsA[theColumn]["visible"] | |
set the visible of sTableObjectsA["columns"][theColumn]["header"]["group"] to theColPropsA[theColumn]["visible"] | |
end repeat | |
set the dgProps["column properties"] of me to theColPropsA | |
_table.ResizeColumns | |
unlock messages | |
## Outside of locked messages so content draws | |
_SetHScrollPercent theOrigHScroll | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
end _table.CreateColumns | |
private command _table.CreateHeaders | |
local theResourceStack | |
put _ResourceStack() into theResourceStack | |
-- put _CardOf() into theDGCard | |
local theColumns, theHeaderGroup, theHeaderRect | |
put the dgProps["columns"] of me into theColumns | |
put the long ID of group "dgHeaderMask" of group "dgHeaderComponents" of me into theHeaderGroup | |
put the rect of theHeaderGroup into theHeaderRect | |
put item 2 of theHeaderRect + the height of theHeaderGroup into item 4 of theHeaderRect | |
## Clear out existing headers that don't exist | |
set the wholeMatches to true | |
repeat for each key theColumn in sTableObjectsA["columns"] | |
if theColumn is not among the lines of theColumns and sTableObjectsA["columns"][theColumn]["header"]["group"] is not empty then | |
local theControl | |
put sTableObjectsA["columns"][theColumn]["header"]["group"] into theControl | |
delete theControl | |
delete local sTableObjectsA["columns"][theColumn]["header"] | |
end if | |
end repeat | |
local theTemplateGroup | |
put the dgProps["row template"] of me into theTemplateGroup | |
## Create header groups | |
local theColsA, sortByThisColumn | |
put the dgProps["column properties"] of me into theColsA | |
put the dgProps["sort by column"] of me into sortByThisColumn | |
repeat for each line theColumn in theColumns | |
## Column | |
if sTableObjectsA["columns"][theColumn]["header"]["group"] is empty then | |
if sTableObjectsA["columns"][theColumn]["header"]["uses custom template"] and \ | |
theTemplateGroup is not empty then | |
## Use custom template provided by user... | |
copy control (theColumn && "[Header]") of theTemplateGroup to group "dgHeader" of me | |
local theGroup | |
put it into theGroup | |
set the name of theGroup to theColumn | |
set the lockloc of theGroup to true | |
put "control id" && word 3 of theGroup && "of me" into sTableObjectsA["columns"][theColumn]["header"]["group"] | |
local theRect | |
put item 1 to 2 of theHeaderRect, item 1 of theHeaderRect + 10, item 4 of theHeaderRect into theRect | |
set the rect of theGroup to theRect | |
else | |
_table.CreateDefaultHeaderGroup theColumn, theHeaderRect | |
put the result into theGroup | |
if the dgProps["default header behavior"] of me is empty then | |
set the behavior of theGroup to the long ID of button "Default Header" of theResourceStack | |
else | |
set the behavior of theGroup to the dgProps["default header behavior"] of me | |
end if | |
end if | |
unlock messages | |
if theColsA[theColumn]["label"] is not empty then | |
set the dgLabel [theColsA[theColumn]["encoding"]] of theGroup to theColsA[theColumn]["label"] | |
else | |
set the dgLabel of theGroup to theColumn | |
end if | |
set the dgTooltip of theGroup to theColsA[theColumn]["tooltip"] | |
set the dgHilite of theGroup to theColumn is sortByThisColumn | |
set the dgAlignment of theGroup to theColsA[theColumn]["header"]["alignment"] | |
lock messages | |
end if | |
end repeat | |
end _table.CreateHeaders | |
private command _table.CreateDefaultHeaderGroup pColumn, pHeaderRect | |
local theHeaderGroup | |
put the long ID of group "dgHeaderMask" of group "dgHeaderComponents" of me into theHeaderGroup | |
reset the templategroup | |
set the lockloc of the templategroup to true | |
set the showBorder of the templategroup to false | |
set the margins of the templategroup to 0 | |
set the borderWidth of the templategroup to 0 | |
set the showName of the templategroup to false | |
reset the templatefield | |
set the borderWidth of the templatefield to 0 | |
set the showFocusBorder of the templatefield to false | |
set the autoHilite of the templatefield to false | |
set the threeD of the templatefield to false | |
set the lockText of the templatefield to true | |
set the dontWrap of the templatefield to true | |
set the opaque of the templatefield to false | |
set the textAlign of the templatefield to "left" | |
set the height of the templatefield to the textSize of the templatefield + 4 | |
set the traversalOn of the templateField to false | |
-- put _CardOf() into theDGCard | |
create group pColumn in group "dgHeader" of theHeaderGroup | |
local theGroup | |
put it into theGroup | |
put "control id" && word 3 of theGroup && "of me" into sTableObjectsA["columns"][pColumn]["header"]["group"] | |
local theRect | |
put item 1 to 2 of pHeaderRect, item 1 of pHeaderRect + 10, item 4 of pHeaderRect into theRect | |
set the rect of theGroup to theRect | |
## Background | |
reset the templategraphic | |
set the style of the templategraphic to "rectangle" | |
set the opaque of the templategraphic to true | |
set the backgroundColor of the templategraphic to empty | |
set the lineSize of the templategraphic to 0 | |
set the antialiased of the templategraphic to true | |
create graphic "Background" in theGroup | |
set the rect of it to theRect | |
_table.HiliteHeaderBackground theGroup, the dgProps["header background hilite color"] of me | |
## Field | |
set the width of the templatefield to item 3 of theRect - item 1 of theRect | |
create field "HeaderLabel" in theGroup | |
local theField | |
put it into theField | |
## EJB - fix for bug 9575 21/02/12 | |
## Old line | |
## set the height of it to the formattedHeight of it - the bottomMargin of it | |
## Now checking the height is > 0 as this line was failing where the dg was on a card that had not been opened | |
## as the formattedHeight was 0 | |
local tHeight | |
put the formattedHeight of it - the bottomMargin of it into tHeight | |
if tHeight < 1 then | |
## nothing | |
else | |
set the height of it to tHeight | |
end if | |
set the topLeft of theField to item 1 to 2 of theRect | |
## left hilite | |
set the lineSize of the templategraphic to 1 | |
set the antialiased of the templategraphic to false | |
create graphic "LeftHilite" in theGroup | |
set the foregroundColor of it to the dgProps["header divider threeD color"] of me | |
set the rect of it to item 1 of theRect + 1, item 2 of theRect, item 1 of theRect + 2, item 4 of theRect | |
## right hilite | |
create graphic "RightHilite" in theGroup | |
set the foregroundColor of it to the dgProps["header divider color"] of me | |
set the rect of it to item 3 of theRect - 1, item 2 of theRect, item 3 of theRect, item 4 of theRect | |
## Sort arrow | |
reset the templatebutton | |
set the style of the templatebutton to "transparent" | |
set the opaque of the templatebutton to false | |
set the showName of the templatebutton to false | |
set the threeD of the templatebutton to false | |
set the showBorder of the templatebutton to false | |
set the hiliteBorder of the templatebutton to false | |
set the borderWidth of the templatebutton to 0 | |
set the width of the templatebutton to 9 | |
set the height of the templatebutton to 8 | |
create button "SortArrow" in theGroup | |
reset the templategroup | |
reset the templatefield | |
reset the templategraphic | |
reset the templatebutton | |
return theGroup | |
end _table.CreateDefaultHeaderGroup | |
private command _table.RepositionHeadersAndColumns | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
local theOrigHScrollPercent | |
put _GetHScrollPercent() into theOrigHScrollPercent | |
_ResetHScrollToZero | |
lock messages | |
_table.ResizeColumns | |
_table.RepositionHeaders | |
_table.RepositionColumns | |
## Restore H Scroll | |
_ConfigureHScrollbar | |
set the lockMessages to msgsAreLocked | |
## Outside of lock messages so content redraws | |
_SetHScrollPercent theOrigHScrollPercent | |
unlock screen | |
end _table.RepositionHeadersAndColumns | |
private command _table.ResizeColumns | |
## in case it gets called before initialization | |
if the keys of sTableObjectsA["columns"] is empty then return empty | |
put 0 into sFormattedWidth | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
## Start at 0 point | |
local theOrigVScrollPercent, theOrigHScrollPercent | |
put _GetVScrollPercent() into theOrigVScrollPercent | |
put _GetHScrollPercent() into theOrigHScrollPercent | |
_ResetScrollsToZero | |
lock messages | |
## First resize headers | |
_table.ResizeHeaders | |
## Now resize columns | |
local theColumns | |
put _table.VisibleColumns() into theColumns | |
## Get in column widths | |
local theColWidths | |
put _table.GetEffectiveColumnWidths() into theColWidths | |
## Set rects | |
local theItemNo, theColWidth, theGroup, theRect | |
repeat for each line theColumn in theColumns | |
add 1 to theItemNo | |
put item theItemNo of theColWidths into theColWidth | |
add theColWidth to sFormattedWidth | |
put sTableObjectsA["columns"][theColumn]["group"] into theGroup | |
put the rect of theGroup into theRect | |
put item 1 of theRect + theColWidth into item 3 of theRect | |
set the rect of theGroup to theRect | |
put item 2 of theRect + sControlHeights into item 4 of theRect | |
repeat for each line theControl in sTableObjectsA["columns"][theColumn]["row controls"] | |
put the rect of theControl into theRect | |
put item 1 of theRect + theColWidth into item 3 of theRect | |
set the rect of theControl to theRect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
lock messages | |
end repeat | |
end repeat | |
_AutoHideHScrollbar | |
_table.LayoutDataArea | |
## Old methods below. Didn't deal with auto hiding hscrollbar though. | |
-- _table.RepositionColumns | |
-- _table.ResizeList | |
-- _table.LayoutRowHilites | |
## Restore H Scroll | |
_ConfigureHScrollbar | |
_ConfigureScrollbar | |
set the lockMessages to msgsAreLocked | |
## Outside of lockmessages so they redraw | |
_SetHScrollPercent theOrigHScrollPercent | |
_SetVScrollPercent theOrigVScrollPercent | |
unlock screen | |
end _table.ResizeColumns | |
private command _table.ResizeList | |
local theRect | |
put the rect of group "dgList" of me into theRect | |
put the top of group "dgListMask" of me into item 2 of theRect | |
put item 1 of theRect + the width of graphic "dgBackground" of group "dgHeaderMask" of group "dgHeaderComponents" of me into item 3 of theRect | |
put the bottom of group "dgListMask" of me into item 4 of theRect | |
set the rect of group "dgList" of me to theRect | |
end _table.ResizeList | |
private command _table.ResizeHeaders | |
local theOrigHScroll | |
put _GetHScrollPercent() into theOrigHScroll | |
_ResetHScrollToZero | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
local theColumns, includeAllColumns, theColWidths, theGroupHeight, theTopLeft | |
put the dgProps["columns"] of me into theColumns | |
put true into includeAllColumns | |
put _table.GetEffectiveColumnWidths(includeAllColumns) into theColWidths | |
put the height of group "dgHeaderMask" of me into theGroupHeight | |
put item 1 to 2 of the rect of group "dgHeaderComponents" of me into theTopLeft | |
local theHeaderWidth, theReduction | |
put 0 into theHeaderWidth | |
put 0 into theReduction | |
repeat for each line theColumn in theColumns | |
local theItemNo, theColWidth, theRect | |
add 1 to theItemNo | |
put item theItemNo of theColWidths into theColWidth | |
put theTopLeft into theRect | |
put item 1 of theRect + (theColWidth- theReduction) into item 3 of theRect | |
put item 2 of theRect + theGroupHeight into item 4 of theRect | |
local theGroup | |
put sTableObjectsA["columns"][theColumn]["header"]["group"] into theGroup | |
set the rect of theGroup to theRect | |
unlock messages | |
dispatch "LayoutControl" to theGroup with theRect | |
lock messages | |
put item 3 of theRect & "," & item 2 of theRect into theTopLeft | |
add theColWidth to theHeaderWidth | |
## all columns after 1 need to be reduced in width in order to make room for divider | |
-- put 1 into theReduction | |
end repeat | |
_table.ResizeHeaderBackground | |
_table.RepositionHeaders | |
unlock messages | |
_SetHScrollPercent theOrigHScroll | |
set the lockMessages to msgsAreLocked | |
end _table.ResizeHeaders | |
private command _table.ResizeHeaderBackground | |
local theRect, theHeaderWidth, theSBWidth | |
## Resize Background to fill visible space. Covers at least width of list group | |
if the width of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me > \ | |
the width of group "dgHeaderMask" of group "dgHeaderComponents" of me then | |
put the rect of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me into theRect | |
else | |
put the rect of group "dgHeaderMask" of group "dgHeaderComponents" of me into theRect | |
end if | |
put item 3 of theRect - item 1 of theRect into theHeaderWidth | |
if the visible of scrollbar "dgScrollbar" of me then put the width of scrollbar "dgScrollbar" of me into theSBWidth | |
else put 0 into theSBWidth | |
put item 1 of theRect + max(the width of me, theHeaderWidth + theSBWidth) into item 3 of theRect | |
put item 2 of theRect + the height of group "dgHeaderMask" of group "dgHeaderComponents" of me into item 4 of theRect | |
set the rect of graphic "dgBackground" of group "dgHeaderMask" of group "dgHeaderComponents" of me to theRect | |
set the rect of graphic "dgHeaderBottomBorder" of group "dgHeaderMask" of group "dgHeaderComponents" of me to item 1 of theRect, \ | |
item 4 of theRect - 1, item 3 of theRect, item 4 of theRect | |
end _table.ResizeHeaderBackground | |
private command _table.RepositionHeaders | |
local theColumns, theHeaderGroup, theTopLeft, theItemNo, theGroup | |
put _table.VisibleColumns() into theColumns | |
put the long ID of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me into theHeaderGroup | |
put item 1 of the rect of group "dgHeaderComponents" of me into theTopLeft | |
put the top of theHeaderGroup into item 2 of theTopLeft | |
repeat for each line theColumn in theColumns | |
add 1 to theItemNo | |
## Headers | |
put sTableObjectsA["columns"][theColumn]["header"]["group"] into theGroup | |
if the visible of theGroup then | |
set the topLeft of theGroup to theTopLeft | |
put the right of theGroup into item 1 of theTopLeft | |
end if | |
end repeat | |
end _table.RepositionHeaders | |
private command _table.RepositionColumns | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
local theColumns | |
put _table.VisibleColumns() into theColumns | |
local theOffset, theMasterRect, theTopLeft | |
put the hScroll of group "dgListMask" of me into theOffset | |
put the rect of group "dgListMask" of me into theMasterRect | |
subtract theOffset from item 1 of theMasterRect | |
subtract theOffset from item 3 of theMasterRect | |
put item 1 to 2 of theMasterRect into theTopLeft | |
local theGroup, theGraphic | |
repeat for each line theColumn in theColumns | |
## columns | |
put sTableObjectsA["columns"][theColumn]["group"] into theGroup | |
if theGroup is not empty and the visible of theGroup then | |
set the topLeft of theGroup to theTopLeft | |
set the rect of theGroup to theTopLeft, the right of theGroup, item 4 of theMasterRect | |
put the right of theGroup into item 1 of theTopLeft | |
## Column dividers | |
put sTableObjectsA["columns"][theColumn]["divider control"] into theGraphic | |
set the rect of theGraphic to item 1 of theTopLeft - 1, item 2 of theTopLeft, \ | |
item 1 of theTopLeft, item 4 of theMasterRect | |
end if | |
end repeat | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
end _table.RepositionColumns | |
private function _table.GetEffectiveColumnWidths pIncludeAllColumns | |
----- | |
local i | |
local theColPropsA, theColumn, theColumns | |
local theFillerWidth | |
local theWidths | |
----- | |
if pIncludeAllColumns then | |
put the dgProps["columns"] of me into theColumns | |
else | |
put _table.VisibleColumns() into theColumns | |
end if | |
put the dgProps["column properties"] of me into theColPropsA | |
repeat for each line theColumn in theColumns | |
if theColPropsA[theColumn]["width"] is empty then | |
put kDefaultTableColWidth & comma after theWidths | |
else | |
put theColPropsA[theColumn]["width"] & comma after theWidths | |
end if | |
end repeat | |
delete the last char of theWidths | |
-- put max(the last item of theWidths, kDefaultTableColWidth) into theFillerWidth | |
-- repeat with i = the number of items of theWidths to the number of lines of theColumns | |
-- put theFillerWidth into item i of theWidths | |
-- end repeat | |
return theWidths | |
end _table.GetEffectiveColumnWidths | |
--> Custom Properties (Data Manipulation) | |
setprop dgText [pTextIncludesColumnNames] pText | |
----- | |
local theError | |
local theResult | |
local theColumns, theStartLineNo | |
----- | |
lock screen | |
try | |
if pTextIncludesColumnNames is true then | |
put line 1 of pText into theColumns | |
replace tab with cr in theColumns | |
put 2 into theStartLineNo | |
else | |
put empty into theColumns | |
put 1 into theStartLineNo | |
end if | |
switch _ControlType() | |
case "table" | |
_table.SetText line theStartLineNo to -1 of pText, theColumns | |
break | |
case "form" | |
default | |
_list.SetText line theStartLineNo to -1 of pText, theColumns | |
end switch | |
put the result into theResult | |
_DataCanBeRepresentedAsText true | |
catch e | |
put e into theError | |
end try | |
unlock screen | |
if theError is not empty then throw theError | |
return theResult | |
end dgText | |
getprop dgText [pIncludeColumnNames] | |
if the keys of sDataArray is empty then _RestorePersistentData ## In case control hasn't been opened yet | |
switch _ControlType() | |
case "table" | |
return _table.GetText(pIncludeColumnNames) | |
break | |
case "form" | |
default | |
return _list.GetText(pIncludeColumnNames) | |
end switch | |
end dgText | |
getprop dgDataOfIndex [pIndex] | |
return sDataArray[pIndex] | |
end dgDataOfIndex | |
setprop dgDataOfIndex [pIndex] pDataArray | |
_DataCanBeRepresentedAsText false | |
put pDataArray into sDataArray[pIndex] | |
_StorePersistentData | |
_RefreshIndexes pIndex | |
end dgDataOfIndex | |
getprop dgDataOfLine [pLine] | |
local theIndex | |
put the dgIndexOfLine [pLine] of me into theIndex | |
return sDataArray[theIndex] | |
end dgDataOfLine | |
setprop dgDataOfLine [pLine] pDataArray | |
_DataCanBeRepresentedAsText false | |
local theIndex | |
put the dgIndexOfLine [pLine] of me into theIndex | |
put pDataArray into sDataArray[theIndex] | |
_StorePersistentData | |
_RefreshIndexes theIndex | |
end dgDataOfLine | |
getprop dgData | |
if the keys of sDataArray is empty then _RestorePersistentData ## In case control hasn't been opened yet | |
return sDataArray | |
end dgData | |
setprop dgData [pSequencing] pDataArray | |
lock screen | |
## We must initialize before going on | |
if not sInit then _Initialize | |
_ResetData | |
put pDataArray into sDataArray | |
if pSequencing is not empty then | |
put pSequencing into sIndexSequencing | |
else | |
## Populate sequencing. This determines order of indexes in array | |
put the keys of sDataArray into sIndexSequencing | |
sort lines of sIndexSequencing numeric | |
replace cr with comma in sIndexSequencing | |
end if | |
switch _ControlType() | |
case "table" | |
if the dgProps["sort by column"] of me is not empty then | |
SortByColumn the dgProps["sort by column"] of me | |
end if | |
break | |
end switch | |
_StorePersistentData | |
_DrawList | |
unlock screen | |
end dgData | |
## We can set the number of records. You can only get the number of lines as the number | |
## of lines is representative of the number of records. | |
getprop dgNumberOfLines | |
return max(0, the number of elements of sDataArray) | |
end dgNumberOfLines | |
getprop dgNumberOfRecords | |
return max(0, the number of elements of sDataArray) | |
end dgNumberOfRecords | |
## Calling this wipes out the array and creates empty values for | |
## records up to pNumber. These records will be filled in on an as needed basis | |
## when scrolling through the list. | |
setprop dgNumberOfRecords pNumber | |
_DataCanBeRepresentedAsText false | |
if pNumber is not an integer or pNumber < 0 then put 0 into pNumber | |
set the dgProps["persistent data"] of me to false ## can't be persistent | |
put empty into sDataArray | |
put empty into sIndexSequencing | |
repeat with i = 1 to pNumber | |
put NULL into sDataArray[i] | |
put i & comma after sIndexSequencing | |
end repeat | |
delete the last char of sIndexSequencing | |
_DrawList | |
end dgNumberOfRecords | |
--> Custom Properties (General) | |
private function _ControlType | |
return the dgProps["style"] of me | |
end _ControlType | |
getprop dgAnimating | |
return sIsAnimating | |
end dgAnimating | |
getprop uScriptLocal [pVarName] | |
local theDo | |
put "return" && pVarName into theDo | |
do theDo | |
end uScriptLocal | |
setprop dgFocus pValue | |
if pValue then | |
## Don't pull focus from child control | |
if the long ID of me is not in the long ID of the focusedObject then | |
focus on graphic "dgBackground" of me | |
_UpdateHiliteColor | |
end if | |
else | |
focus on nothing | |
end if | |
end dgFocus | |
-- Returns column number of the target. Number is relative to visible columns. | |
getprop dgColumnNumber | |
local theColumn | |
put the dgColumn of the target into theColumn | |
if theColumn is not empty then | |
set the wholeMatches to true | |
return lineOffset(theColumn, _table.VisibleColumns()) | |
else | |
return 0 | |
end if | |
end dgColumnNumber | |
## This property is dynamic so we don't have to | |
## constantly update property as we add/delete controls | |
## The target is a list control | |
getprop dgLine | |
local theControl | |
put the dgDataControl of the target into theControl | |
if the long ID of theControl is not the long ID of the target then pass dgLine | |
local theIndex | |
put the dgIndex of theControl into theIndex | |
return the dgLineOfIndex[theIndex] of me | |
end dgLine | |
## Returns indexes in order of sequencing | |
getprop dgIndexes | |
return sIndexSequencing | |
end dgIndexes | |
setprop dgIndexes pIndexes | |
put pIndexes into sIndexSequencing | |
_StorePersistentSequence | |
lock screen | |
_ResetIndexesOnControls | |
_RedrawList | |
unlock screen | |
end dgIndexes | |
## Lines and sequences are synonymous | |
getprop dgIndexOfLine [pLine] | |
set the wholeMatches to true | |
return item pLine of sIndexSequencing | |
end dgIndexOfLine | |
## Use to reorder an index | |
setprop dgLineOfIndex [pIndex] pLine | |
_SetSequenceOfIndex pIndex, pLine | |
RefreshList | |
end dgLineOfIndex | |
getprop dgLineOfIndex [pIndex] | |
set the wholeMatches to true | |
return itemOffset(pIndex, sIndexSequencing) | |
end dgLineOfIndex | |
## doesn't refresh | |
command SetLineOfIndex pIndex, pLine | |
_SetSequenceOfIndex pIndex, pLine | |
end SetLineOfIndex | |
getprop dgVisibleLines | |
return _VisibleSequences() | |
end dgVisibleLines | |
private function _VisibleSequences | |
local theControls | |
put _ListOfVisibleControls() into theControls | |
if theControls is empty or the keys of sDataArray is empty then return "0,0" | |
local theMaskRect, theFirstSequence, theLastSequence | |
put the rect of group "dgList" of me into theMaskRect | |
put empty into theFirstSequence | |
put empty into theLastSequence | |
repeat for each line theControl in theControls | |
local theRect, theLineNo | |
put the rect of theControl into theRect | |
## When checking if opposite coordinate is in view don't use =. = does not mean visible in this case. | |
## e.g. is top of control above the bottom of the mask rect? | |
if (item 2 of theRect >= item 2 of theMaskRect and item 2 of theRect < item 4 of theMaskRect) or \ | |
(item 4 of theRect > item 2 of theMaskRect and item 4 of theRect <= item 4 of theMaskRect) then | |
put the dgLine of theControl into theLineNo | |
if theLineNo > 0 then | |
if theFirstSequence is empty then put theLineNo into theFirstSequence | |
else put min(theLineNo, theFirstSequence) into theFirstSequence | |
if theLastSequence is empty then put theLineNo into theLastSequence | |
else put max(theLineNo, theLastSequence) into theLastSequence | |
end if | |
end if | |
end repeat | |
return theFirstSequence & comma & theLastSequence | |
end _VisibleSequences | |
## Returns a return delimited list of the value of pKey for all highlighted indexes. | |
getprop dgKeyValuesOfHilitedIndexes [pKey] | |
return GetKeyValuesOfIndexes(sHilitedIndexes, cr, pKey) | |
end dgKeyValuesOfHilitedIndexes | |
getprop dgFormattedHeight | |
return sFormattedHeight | |
end dgFormattedHeight | |
getprop dgFormattedWidth | |
return sFormattedWidth | |
end dgFormattedWidth | |
## temporary aliases | |
setprop uProps [pProp] pValue | |
if the target is not me then pass uProps | |
set the dgProps[pProp] of me to pValue | |
end uProps | |
getprop uProps[pProp] | |
if the target is not me then pass uProps | |
return the dgProps[pProp] of me | |
end uProps[pProp] | |
## end temporary aliases | |
setprop dgProp [pProp] pValue | |
set the dgProps[pProp] of me to pValue | |
end dgProp | |
getprop dgProp [pProp] | |
return the dgProps[pProp] of me | |
end dgProp | |
private function _IsListOPositivefIntegers pList, pMatchCount | |
if pMatchCount and the number of items of pList is not pMatchCount then return false | |
repeat for each item theItem in pList | |
if theItem is not an integer or theItem < 0 then return false | |
end repeat | |
return true | |
end _IsListOPositivefIntegers | |
## Setting this property resets control height properties | |
setprop dgProps [pProp] pValue | |
if the target is not me then pass dgProps | |
switch pProp | |
case "ascending sort icon" | |
case "descending sort icon" | |
if pValue is not empty and pValue is not an integer then _ThrowError kErrInvalidInteger, pValue && "is not an integer" | |
lock messages | |
set the dgProps[pProp] of me to pValue | |
unlock messages | |
lock screen | |
local theSortBy | |
put the dgProps["sort by column"] of me into theSortBy | |
repeat for each key theColumn in sTableObjectsA["columns"] | |
repeat for each line theControl in sTableObjectsA["columns"][theColumn]["header"]["group"] | |
set the dgHilite of theControl to theColumn is theSortBy | |
end repeat | |
end repeat | |
unlock screen | |
break | |
case "border color" | |
if pValue is not a color and pValue is not empty then | |
_ThrowError kErrInvalidColor, pValue && "is not a color" | |
end if | |
lock screen | |
set the borderColor of me to pValue | |
set the foregroundColor of graphic "dgHeaderBottomBorder" of me to pValue | |
unlock screen | |
break | |
case "header margins" | |
case "column margins" | |
if pValue is not empty and pValue is not an integer and not (_IsListOPositivefIntegers(pValue, 4)) then | |
_ThrowError kErrInvalidProperty, "'" & pValue & "' is not a valid margin" | |
end if | |
if pValue is an integer then | |
get pValue | |
repeat with i = 2 to 4 | |
put it into item i of pValue | |
end repeat | |
end if | |
lock messages | |
set the dgProps[pProp] of me to pValue | |
unlock messages | |
if pProp is "header margins" then | |
repeat for each key theColumn in sTableObjectsA["columns"] | |
repeat for each line theControl in sTableObjectsA["columns"][theColumn]["header"]["group"] | |
dispatch "LayoutControl" to theControl with the rect of theControl | |
end repeat | |
end repeat | |
else | |
ResetList | |
end if | |
break | |
case "scrollbar width" | |
if pValue is not "auto" and (pValue is not an integer or pValue < 0) then \ | |
then _ThrowError kErrInvalidInteger, pValue && "is not an integer >= 0 or 'auto'" | |
lock messages | |
set the dgProps[pProp] of me to pValue | |
unlock messages | |
lock screen | |
_SetScrollbarWidth pValue | |
if _ControlType() is "table" then | |
_table.LayoutDataArea | |
RefreshList | |
end if | |
unlock screen | |
break | |
case "header height" | |
if pValue is not an integer or pValue < 0 then _ThrowError kErrInvalidInteger, pValue && "is not an integer >= 0" | |
lock screen | |
local theOrigVScroll, theRect | |
put _GetVScrollPercent() into theOrigVScroll | |
put the rect of group "dgHeaderMask" of me into theRect | |
put item 2 of theRect + pValue into item 4 of theRect | |
set the rect of group "dgHeaderMask" of me to theRect | |
_table.LayoutDataArea | |
_table.ResizeHeaders | |
#<mikey> | |
# Attempt to fix bug 12427 - resizing a header vertically doesn't resize the column dividers (also called the leftHilite and rightHilight). | |
# We do it here instead of _table.resizeHeader or _table.repositionHeaders b/c those handlers are designed to handle the user resizing the column by dragging | |
# on it. To resize the column dividers there would mess with performance. _table.ResizeHeaderBackground does not touch the column headers. | |
local rectOfMe | |
repeat for each line theColumn in the childControlNames of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me | |
put the rect of graphic "leftHilite" of group theColumn of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me into rectOfMe | |
put item 4 of theRect into item 4 of rectOfMe | |
set the rect of graphic "leftHilite" of group theColumn of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me to rectOfMe | |
put the rect of graphic "rightHilite" of group theColumn of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me into rectOfMe | |
put item 4 of theRect into item 4 of rectOfMe | |
set the rect of graphic "rightHilite" of group theColumn of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me to rectOfMe | |
end repeat # for each line theColumn of the childControlNames of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me | |
#</mikey> | |
ResizeToFit # TDK-2014-07-25: The list needs to be redrawn to fit in the new mask area. | |
unlock screen | |
break | |
case "text font" | |
lock screen | |
if pValue is empty then | |
set the textFont of group "dgList" of me to pValue | |
else | |
local theStyle, theSize | |
put the textStyle of group "dgList" of me into theStyle | |
put the textSize of group "dgList" of me into theSize | |
set the textFont of group "dgList" of me to pValue | |
set the textStyle of group "dgList" of me to theStyle | |
set the textSize of group "dgList" of me to theSize | |
end if | |
unlock screen | |
break | |
case "text style" | |
set the textStyle of group "dgList" of me to pValue | |
break | |
case "text size" | |
set the textSize of group "dgList" of me to pValue | |
break | |
case "text color" | |
set the textColor of group "dgList" of me to _ColorToRGB(pValue) | |
break | |
case "header text font" | |
lock screen | |
if pValue is empty then | |
set the textFont of group "dgHeader" of me to pValue | |
else | |
put the textStyle of group "dgHeader" of me into theStyle | |
put the textSize of group "dgHeader" of me into theSize | |
set the textFont of group "dgHeader" of me to pValue | |
set the textStyle of group "dgHeader" of me to theStyle | |
set the textSize of group "dgHeader" of me to theSize | |
end if | |
unlock screen | |
break | |
case "header text style" | |
lock screen | |
set the textStyle of group "dgHeader" of me to pValue | |
unlock screen | |
break | |
case "header text size" | |
lock screen | |
set the textSize of group "dgHeader" of me to pValue | |
unlock screen | |
break | |
case "header text color" | |
lock screen | |
set the textColor of group "dgHeader" of me to _ColorToRGB(pValue) | |
unlock screen | |
break | |
case "show vscrollbar" | |
lock messages | |
if pValue is "auto" then | |
set the dgProps[pProp] of me to pValue | |
else | |
put pValue is true into pValue | |
set the dgProps[pProp] of me to pValue | |
end if | |
unlock messages | |
if pValue is "auto" then | |
_ToggleVScrollBarVisibility the thumbSize of scrollbar "dgScrollbar" of me < the endValue of scrollbar "dgScrollbar" of me | |
else | |
_ToggleVScrollBarVisibility the dgProps[pProp] of me | |
end if | |
break | |
case "show hscrollbar" | |
lock messages | |
if pValue is "auto" then | |
set the dgProps[pProp] of me to pValue | |
else | |
put pValue is true into pValue | |
set the dgProps[pProp] of me to pValue | |
end if | |
unlock messages | |
if _ControlType() is "table" then | |
if pValue is "auto" then | |
_ToggleHScrollBarVisibility the thumbSize of scrollbar "dgHScrollbar" of me < the endValue of scrollbar "dgHScrollbar" of me | |
else | |
_ToggleHScrollBarVisibility the dgProps[pProp] of me | |
end if | |
end if | |
break | |
case "corner color" | |
local theGraphic | |
put the long ID of graphic "dgCornerPiece" of group "dgHorizontalComponents" of me into theGraphic | |
set the backgroundColor of theGraphic to empty | |
if pValue is an array then | |
## Setting fillGradient | |
repeat for each key theKey in pValue | |
set the fillGradient[theKey] of theGraphic to pValue[theKey] | |
end repeat | |
else if the number of lines of pValue is 2 and line 1 of pValue is a color and line 2 of pValue is a color then | |
## Create gradient for developer | |
_SetGraphicGradient theGraphic, line 1 of pValue, line 2 of pValue | |
else if pValue is a color then | |
set the backgroundColor of theGraphic to pValue | |
else | |
set the backgroundColor of theGraphic to kDefaultCornerColor | |
put kDefaultCornerColor into pValue | |
end if | |
lock messages | |
set the dgProps[pProp] of me to pValue | |
unlock messages | |
unlock screen | |
break | |
case "header background color" | |
lock screen | |
put the long ID of graphic "dgBackground" of group "dgHeaderMask" of group "dgHeaderComponents" of me into theGraphic | |
set the backgroundColor of theGraphic to empty | |
if pValue is an array then | |
## Setting fillGradient | |
repeat for each key theKey in pValue | |
set the fillGradient[theKey] of theGraphic to pValue[theKey] | |
end repeat | |
else if the number of lines of pValue is 2 and line 1 of pValue is a color and line 2 of pValue is a color then | |
## Create gradient for developer | |
_table.SetHeaderBkgrndGradient line 1 of pValue, line 2 of pValue | |
else if pValue is a color then | |
set the backgroundColor of theGraphic to pValue | |
else | |
_table.SetHeaderBkgrndGradient kHeaderBkgrndStartColor, kHeaderBkgrndEndColor | |
put kHeaderBkgrndStartColor & cr & kHeaderBkgrndEndColor into pValue | |
end if | |
lock messages | |
set the dgProps[pProp] of me to pValue | |
unlock messages | |
unlock screen | |
break | |
case "header background hilite color" | |
lock screen | |
if pValue is not an array and \ | |
(line 1 of pValue is not a color and line 2 of pValue is not a color) and \ | |
pValue is not a color then | |
put kHeaderBkgrndHiliteStartColor & cr & kHeaderBkgrndHiliteEndColor into pValue | |
end if | |
_table.SetHeaderBkgrndHiliteColor pValue | |
lock messages | |
set the dgProps[pProp] of me to pValue | |
unlock messages | |
unlock screen | |
break | |
case "header divider color" | |
case "header divider threed color" | |
lock screen | |
if pValue is not a color then | |
if pProp is "header divider color" then | |
put kHeaderDividerColor into pValue | |
else | |
put kHeaderDividerThreeDColor into pValue | |
end if | |
else | |
put _ColorToRGB(pValue) into pValue | |
end if | |
lock messages | |
set the dgProps[pProp] of me to pValue | |
unlock messages | |
_table.UpdateHeaderDividerColors pValue | |
unlock screen | |
break | |
case "style" | |
lock screen | |
_DeleteDataControls | |
if pValue is "table" then | |
lock screen | |
show group "dgDividers" of group "dgListMask" of me | |
set the visible of group "dgHeaderComponents" of me to the dgProps["show header"] of me is true | |
show group "dgHorizontalComponents" of me | |
_table.RegenerateColumns | |
-- set the uEffectiveColumnWidths of group "dgHeader" of group "dgHeaderMask" of group "dgHeaderComponents" of me \ | |
-- to _table.GetEffectiveColumnWidths() | |
## Set any props that have special settings for table. | |
lock messages | |
set the dgProps["cache controls"] of me to false | |
unlock messages | |
unlock screen | |
else | |
put "form" into pValue | |
hide group "dgDividers" of group "dgListMask" of me | |
hide group "dgHeaderComponents" of me | |
hide group "dgHorizontalComponents" of me | |
_table.DeleteColumnControls | |
put empty into sTableObjectsA | |
end if | |
lock messages ## in case target is a child of me | |
set the dgProps["style"] of me to pValue | |
unlock messages | |
ResizeToFit | |
unlock screen | |
break | |
case "record template" ## early dev versions | |
case "row template" | |
put "row template" into pProp | |
if pValue is not empty and there is not a pValue then | |
answer quote & pValue & quote && "does not exist. Cannot set" && pProp & "." | |
else if pValue is not empty and word 1 of pValue is not "group" then | |
answer quote & pValue & quote && "is not a group. Cannot set" && pProp & "." | |
else | |
if pValue is not empty then | |
put _CustomControlReference(pValue) into pValue | |
end if | |
lock messages ## in case target is a child of me | |
set the dgProps["row template"] of me to pValue | |
unlock messages | |
put empty into sFormattedHeight | |
put empty into sControlHeights | |
end if | |
break | |
case "persistent data" | |
lock messages ## in case target is a child of me | |
set the dgProps["persistent data"] of me to pValue is true | |
unlock messages | |
if pValue then | |
_StorePersistentData | |
else | |
set the customProperties["dgCache"] of me to empty | |
end if | |
break | |
case "fixed control height" ## early dev builds | |
case "fixed row height" | |
put "fixed row height" into pProp | |
## Setting this property resets control height properties | |
lock messages | |
set the dgProps[pProp] of me to pValue is true | |
unlock messages | |
put empty into sFormattedHeight | |
put empty into sControlHeights | |
ResetList | |
break | |
case "multiple lines" | |
case "cache controls" | |
case "auto hilite" | |
case "animate selections" | |
case "data can be represented as text" | |
case "dim on focusOut" | |
case "allow editing" | |
case "allow column resizing" | |
case "scroll when vscrollbar is hidden" | |
case "scroll when hscrollbar is hidden" | |
case "scroll selections into view" | |
lock messages | |
set the dgProps[pProp] of me to pValue is true | |
unlock messages | |
break | |
case "alternate row colors" | |
lock messages | |
set the dgProps[pProp] of me to pValue is true | |
unlock messages | |
lock screen | |
_DrawAlternatingRows | |
_ShowAlternatingRows | |
if _ControlType() is "form" then | |
_list.UpdateAlternatingRowColors | |
end if | |
unlock screen | |
break | |
case "opaque" | |
set the opaque of graphic "dgBackground" of me to pValue is true | |
break | |
case "background color" | |
if pValue is a color or pValue is empty then | |
put _ColorToRGB(pValue) into pValue | |
set the backgroundColor of graphic "dgBackground" of me to pValue | |
lock messages | |
set the dgProps[pProp] of me to pValue | |
unlock messages | |
else | |
_ThrowError kErrInvalidColor, pValue && "is not a color" | |
end if | |
break | |
case "row color" | |
if pValue is a color or pValue is empty then | |
lock messages | |
set the dgProps[pProp] of me to _ColorToRGB(pValue) | |
unlock messages | |
## Update colors | |
lock screen | |
_DrawAlternatingRows | |
if _ControlType() is "form" then | |
_list.UpdateAlternatingRowColors | |
end if | |
unlock screen | |
else | |
_ThrowError kErrInvalidColor, pValue && "is not a color" | |
end if | |
break | |
case "alternate row color" | |
if pValue is a color or pValue is empty then | |
lock messages | |
set the dgProps[pProp] of me to _ColorToRGB(pValue) | |
unlock messages | |
## Update colors | |
lock screen | |
_DrawAlternatingRows | |
if _ControlType() is "form" then | |
_list.UpdateAlternatingRowColors | |
end if | |
unlock screen | |
else | |
_ThrowError kErrInvalidColor, pValue && "is not a color" | |
end if | |
break | |
case "column divider color" | |
if pValue is a color or pValue is empty then | |
lock messages | |
set the dgProps[pProp] of me to _ColorToRGB(pValue) | |
unlock messages | |
repeat for each line theColumn in the dgProps["columns"] of me | |
put sTableObjectsA["columns"][theColumn]["divider control"] into theControl | |
if there is a theControl then | |
set the foregroundColor of theControl to _GetEffectiveColor("column divider color") | |
end if | |
end repeat | |
else | |
_ThrowError kErrInvalidColor, pValue && "is not a color" | |
end if | |
break | |
case "hilite color" | |
case "dimmed hilite color" | |
if pValue is a color or pValue is empty then | |
lock messages | |
set the dgProps[pProp] of me to _ColorToRGB(pValue) | |
unlock messages | |
## update color | |
if _ControlType() is "table" then | |
_table.LayoutRowHilites | |
else | |
_HiliteIndexesInVisibleControls | |
end if | |
else | |
_ThrowError kErrInvalidColor, pValue && "is not a color" | |
end if | |
break | |
case "hilited text color" | |
if pValue is a color or pValue is empty then | |
lock messages | |
set the dgProps[pProp] of me to _ColorToRGB(pValue) | |
unlock messages | |
else | |
_ThrowError kErrInvalidColor, pValue && "is not a color" | |
end if | |
break | |
case "scrollbar offset" | |
## deprecated, no longer used | |
if pValue is not a point then | |
_ThrowError kErrInvalidPoint, pValue && "is not a point" | |
end if | |
lock messages | |
set the dgProps["scrollbar corner offset"] of me to item 2 of pValue | |
unlock messages | |
ResizeToFit | |
break | |
case "scrollbar corner offset" | |
if pValue is not an integer or pValue < 0 then | |
_ThrowError kErrInvalidInteger, pValue && "is not an integer >= 0" | |
end if | |
lock messages | |
set the dgProps[pProp] of me to pValue | |
unlock messages | |
ResizeToFit | |
break | |
case "row height" | |
if pValue is not empty and (pValue is not an integer or pValue < 0) then | |
_ThrowError kErrInvalidInteger, pValue && "is not an integer" | |
else | |
lock messages | |
set the dgProps[pProp] of me to pValue | |
unlock messages | |
lock screen | |
ResetList | |
unlock screen | |
end if | |
break | |
## Table | |
case "sort by column" | |
_SortByColumn pValue | |
break | |
case "show header" | |
-- lock messages | |
-- set the dgProps [pProp] of me to pValue | |
-- unlock messages | |
lock screen | |
set the visible of group "dgHeaderComponents" of me to pValue is true | |
ResizeToFit | |
unlock screen | |
break | |
case "columns" | |
put word 1 to -1 of pValue into pValue | |
repeat for each line theLine in pValue | |
if theLine is empty then _ThrowError kErrInvalidProperty, "column name cannot be empty'" | |
local theLinesA | |
if theLinesA[theLine] is not empty then _ThrowError kErrInvalidProperty, "duplicate column name '" & theLine & "'" | |
put 1 into theLinesA[theLine] | |
end repeat | |
lock messages | |
set the dgProps [pProp] of me to pValue | |
## Don't sort by a non-existent column | |
if the dgProps["sort by column"] of me is not empty \ | |
and the dgProps["sort by column"] of me is not among the lines of pValue then | |
set the dgProps["sort by column"] of me to empty | |
end if | |
unlock messages | |
lock screen | |
_table.RegenerateColumns | |
_table.DrawColumns _table.VisibleColumns() | |
unlock screen | |
break | |
case "column labels" | |
lock screen | |
repeat for each line theColumn in the dgProps["columns"] of me | |
add 1 to i | |
set the dgColumnLabel [theColumn] of me to line i of pValue | |
end repeat | |
unlock screen | |
break | |
case "column widths" | |
lock screen | |
local theLastWidth, theWidth | |
put item -1 of pValue into theLastWidth | |
if theLastWidth is not an integer then | |
_ThrowError kErrInvalidInteger, "invalid column width value '" & pValue & "'" | |
end if | |
repeat for each line theColumn in the dgProps["columns"] of me | |
add 1 to i | |
put item i of pValue into theWidth | |
if theWidth is not an integer then put theLastWidth into theWidth | |
_StoreColWidth theColumn, theWidth | |
end repeat | |
_table.ResizeColumns | |
unlock screen | |
break | |
case "show column dividers" | |
set the visible of group "dgDividers" of group "dgListMask" of me to pValue is true | |
break | |
case "column alignments" | |
lock screen | |
repeat for each line theColumn in the dgProps["columns"] of me | |
add 1 to i | |
set the dgColumnAlignment [theColumn] of me to item i of pValue | |
end repeat | |
unlock screen | |
break | |
case "column visibility" | |
lock screen | |
repeat for each line theColumn in the dgProps["columns"] of me | |
add 1 to i | |
set the dgColumnIsVisible [theColumn] of me to item i of pValue | |
end repeat | |
unlock screen | |
break | |
case "default column behavior" | |
if pValue is not empty then | |
put _CustomControlReference(pValue) into pValue | |
end if | |
lock messages ## in case target is a child of me | |
set the dgProps["default column behavior"] of me to pValue | |
unlock messages | |
break | |
case "default header behavior" | |
if pValue is not empty then | |
put _CustomControlReference(pValue) into pValue | |
end if | |
lock messages ## in case target is a child of me | |
set the dgProps["default header behavior"] of me to pValue | |
unlock messages | |
break | |
case "control type" | |
lock messages | |
set the dgProps[pProp] of me to "Data Grid" | |
unlock messages | |
break | |
default | |
throw "invalid property '" & pProp & "'" | |
end switch | |
-- pass dgProps | |
return empty | |
end dgProps | |
getprop dgProps [pProp] | |
if the target is not me then pass dgProps | |
local theValue | |
switch pProp | |
case "ascending sort icon" | |
lock messages | |
get the dgProps[pProp] of me | |
unlock messages | |
if it < 1 then get "103004" | |
return it | |
break | |
case "descending sort icon" | |
lock messages | |
get the dgProps[pProp] of me | |
unlock messages | |
if it < 1 then get "103005" | |
return it | |
break | |
case "header margins" | |
case "column margins" | |
lock messages | |
get the dgProps[pProp] of me | |
if it is empty then get 8 | |
unlock messages | |
return it | |
break | |
case "header height" | |
return the height of group "dgHeader" of me | |
break | |
case "text font" | |
return the textFont of group "dgList" of me | |
break | |
case "effective text font" | |
return the effective textFont of group "dgList" of me | |
break | |
case "text style" | |
return the textStyle of group "dgList" of me | |
break | |
case "effective text style" | |
return the effective textStyle of group "dgList" of me | |
break | |
case "text size" | |
return the textSize of group "dgList" of me | |
break | |
case "effective text size" | |
return the effective textSize of group "dgList" of me | |
break | |
case "header text font" | |
return the textFont of group "dgHeader" of me | |
break | |
case "effective header text font" | |
return the effective textFont of group "dgHeader" of me | |
break | |
case "header text style" | |
return the textStyle of group "dgHeader" of me | |
break | |
case "effective header text style" | |
return the effective textStyle of group "dgHeader" of me | |
break | |
case "header text size" | |
return the textSize of group "dgHeader" of me | |
break | |
case "effective header text size" | |
return the effective textSize of group "dgHeader" of me | |
break | |
case "header text color" | |
return the textColor of group "dgHeader" of me | |
break | |
case "effective header text color" | |
return the effective textColor of group "dgHeader" of me | |
break | |
case "effective alternate row color" | |
case "effective column divider color" | |
case "effective row color" | |
case "effective dimmed hilite color" | |
case "effective hilite color" | |
return _GetEffectiveColor(word 2 to -1 of pProp) | |
break | |
case "effective text color" | |
return the effective textColor of group "dgList" of me | |
break | |
case "text color" | |
return the textColor of group "dgList" of me | |
break | |
case "hilited text color" | |
return _GetEffectiveColor(pProp) | |
break | |
case "border color" | |
return the borderColor of me | |
break | |
case "effective border color" | |
return the effective borderColor of me | |
break | |
case "background color" | |
local tColor | |
if the dgProps[pProp] of me is not a color then | |
return the backgroundColor of graphic "dgBackground" of me | |
else | |
return the dgProps[pProp] of me | |
end if | |
break | |
case "opaque" | |
return the opaque of graphic "dgBackground" of me | |
break | |
case "visible columns" | |
if _ControlType() is "table" then | |
return _table.VisibleColumns() | |
else | |
return empty | |
end if | |
break | |
case "column widths" | |
local theColPropsA | |
put the dgProps["column properties"] of me into theColPropsA | |
repeat for each line theColumn in the dgProps["columns"] of me | |
put theColPropsA[theColumn]["width"] & comma after theValue | |
end repeat | |
delete the last char of theValue | |
return theValue | |
break | |
case "column labels" | |
put the dgProps["column properties"] of me into theColPropsA | |
repeat for each line theColumn in the dgProps["columns"] of me | |
put theColPropsA[theColumn]["label"] & cr after theValue | |
end repeat | |
delete the last char of theValue | |
return theValue | |
break | |
case "column visibility" | |
put the dgProps["column properties"] of me into theColPropsA | |
repeat for each line theColumn in the dgProps["columns"] of me | |
put theColPropsA[theColumn]["visible"] & comma after theValue | |
end repeat | |
delete the last char of theValue | |
return theValue | |
break | |
case "column alignments" | |
put the dgProps["column properties"] of me into theColPropsA | |
repeat for each line theColumn in the dgProps["columns"] of me | |
put theColPropsA[theColumn]["alignment"] & comma after theValue | |
end repeat | |
delete the last char of theValue | |
return theValue | |
break | |
case "show column dividers" | |
return the visible of group "dgDividers" of group "dgListMask" of me | |
break | |
case "show header" | |
return the visible of group "dgHeaderComponents" of me | |
break | |
case "effective hilite color" | |
return _GetHiliteColor() | |
break | |
case "effective scrollbar width" | |
return the width of scrollbar "dgScrollbar" of me | |
break | |
case "scrollbar corner offset" | |
## Forced integers >= 0 | |
lock messages | |
put the dgProps[pProp] of me into theValue | |
unlock messages | |
return max(0, theValue) | |
break | |
case "record template" ## early dev versions | |
return the dgProps["row template"] of me | |
break | |
case "fixed control height" ## early dev versions | |
return the dgProps["fixed row height"] of me | |
break | |
end switch | |
pass dgProps | |
end dgProps | |
## Renames a column | |
setprop dgColumnName [pColumn] pValue | |
if word 1 to -1 of pValue is empty or char 1 of pValue is space or the last char of pValue is space then | |
_ThrowError kErrInvalidProperty, quote & pValue & quote && "is not a valid column name" | |
end if | |
set the wholeMatches to true | |
## Update list of columns, replacing line with old name with new name | |
local theColumns, theLineNo, theBadLineNo | |
put the dgProps["columns"] of me into theColumns | |
put lineOffset(pColumn, theColumns) into theLineNo | |
if theLineNo is 0 then _ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
put lineOffset(pValue, theColumns) into theBadLineNo | |
if theBadLineNo > 0 then _ThrowError kErrRenameErrorInDestination, "column '" & pValue & "' already exists" | |
lock screen | |
local theSortByColumn | |
put the dgProps["sort by column"] of me into theSortByColumn | |
## Rename column | |
put pValue into line theLineNo of theColumns | |
lock messages | |
set the dgProps["columns"] of me to theColumns | |
## rename column properties | |
local theColPropsA | |
put the dgProps["column properties"] of me into theColPropsA | |
put theColPropsA[pColumn] into theColPropsA[pValue] | |
set the dgProps["column properties"] of me to theColPropsA | |
unlock messages | |
## Rename data keys | |
repeat for each key theKey in sDataArray | |
put sDataArray[theKey][pColumn] into sDataArray[theKey][pValue] | |
delete local sDataArray[theKey][pColumn] | |
end repeat | |
_table.DeleteColumn pColumn | |
## refresh | |
_table.RegenerateColumns | |
_table.DrawColumns pValue | |
if theSortByColumn is pColumn then | |
_SortByColumn pValue | |
end if | |
_StorePersistentData | |
unlock screen | |
end dgColumnName | |
setprop dgColumnSortType [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
if pValue is not among the items kSortTypes then | |
_ThrowError kErrInvalidProperty, "invalid column sort type value '" & pValue & "'" | |
end if | |
put toLower(pValue) into theColsA[pColumn]["sort type"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
## Update sort if need be | |
if the dgProps["sort by column"] of me is pColumn then | |
_SortByColumn pColumn | |
end if | |
end dgColumnSortType | |
getprop dgColumnSortType [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["sort type"] | |
end dgColumnSortType | |
setprop dgColumnSortDirection [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
if pValue is not among the items "ascending,descending" then | |
_ThrowError kErrInvalidProperty, "invalid column sort direction value '" & pValue & "'" | |
end if | |
put toLower(pValue) into theColsA[pColumn]["sort direction"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
## Update sort if need be | |
if the dgProps["sort by column"] of me is pColumn then | |
_SortByColumn pColumn | |
end if | |
end dgColumnSortDirection | |
getprop dgColumnSortDirection [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["sort direction"] | |
end dgColumnSortDirection | |
setprop dgColumnSortIsCaseSensitive [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
if pValue is not a boolean then | |
_ThrowError kErrInvalidBoolean, "invalid boolean value for column sort is case sensitive '" & pValue & "'" | |
end if | |
put toLower(pValue) into theColsA[pColumn]["sort is case sensitive"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
## Update sort if need be | |
if the dgProps["sort by column"] of me is pColumn then | |
_SortByColumn pColumn | |
end if | |
end dgColumnSortIsCaseSensitive | |
getprop dgColumnSortIsCaseSensitive [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["sort is case sensitive"] | |
end dgColumnSortIsCaseSensitive | |
setprop dgColumnIsVisible [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
if pValue is not a boolean then | |
_ThrowError kErrInvalidBoolean, "invalid boolean value for column visibility '" & pValue & "'" | |
end if | |
put toLower(pValue) into theColsA[pColumn]["visible"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
if sTableObjectsA["columns"][pColumn]["group"] is not empty then | |
lock screen | |
set the visible of sTableObjectsA["columns"][pColumn]["group"] to pValue | |
set the visible of sTableObjectsA["columns"][pColumn]["header"]["group"] to pValue | |
set the visible of sTableObjectsA["columns"][pColumn]["divider control"] to pValue | |
_table.RepositionHeadersAndColumns | |
if sTableObjectsA["row control count"] > 0 then | |
_table.CreateControlsForColumns pColumn | |
_table.DrawColumns pColumn | |
## check for scrollbar show/hide | |
if the dgProps["show hscrollbar"] of me is "auto" then | |
## todo: optimize so we don't toggle unless we need to | |
local theSetting | |
put the visible of group "dgHorizontalComponents" of me into theSetting | |
_AutoHideHScrollbar | |
if the visible of group "dgHorizontalComponents" of me is not theSetting then | |
_ToggleHScrollBarVisibility the visible of group "dgHorizontalComponents" of me | |
end if | |
end if | |
end if | |
unlock screen | |
end if | |
end dgColumnIsVisible | |
getprop dgColumnIsVisible [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["visible"] | |
end dgColumnIsVisible | |
setprop dgColumnLabel [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
put pValue into theColsA[pColumn]["label"] | |
if the platform is "macos" then put "mac" into theColsA[pColumn]["encoding"] | |
else put "iso" into theColsA[pColumn]["encoding"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
_table.UpdateHeaderLabel pColumn, pValue, theColsA[pColumn]["encoding"] | |
end dgColumnLabel | |
getprop dgColumnLabel [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["label"] | |
end dgColumnLabel | |
getprop dgColumnLabelEncoding [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["encoding"] | |
end dgColumnLabelEncoding | |
setprop dgColumnTooltip [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
put pValue into theColsA[pColumn]["tooltip"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
_table.UpdateHeaderTooltip pColumn, pValue | |
end dgColumnTooltip | |
getprop dgColumnTooltip [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["tooltip"] | |
end dgColumnTooltip | |
setprop dgColumnIsEditable [pColumn] pBoolean | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
put pBoolean is true into theColsA[pColumn]["editable"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
return empty | |
end dgColumnIsEditable | |
getprop dgColumnIsEditable [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["editable"] is not false ## default is true | |
end dgColumnIsEditable | |
setprop dgColumnIsResizable [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
if pValue is not a boolean then | |
_ThrowError kErrInvalidBoolean, "invalid boolean value for column is resizable '" & pValue & "'" | |
end if | |
put toLower(pValue) into theColsA[pColumn]["resizable"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
return empty | |
end dgColumnIsResizable | |
getprop dgColumnIsResizable [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["resizable"] | |
end dgColumnIsResizable | |
setprop dgColumnAlignment [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
if pValue is not among the items "left,right,center" then | |
_ThrowError kErrInvalidProperty, "invalid column alignment value '" & pValue & "'" | |
end if | |
put toLower(pValue) into theColsA[pColumn]["alignment"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
## Update alignment | |
lock screen | |
if not sTableObjectsA["columns"][pColumn]["uses custom template"] then | |
repeat for each line theControl in sTableObjectsA["columns"][pColumn]["row controls"] | |
set the textAlign of theControl to pValue | |
end repeat | |
end if | |
unlock screen | |
end dgColumnAlignment | |
getprop dgColumnAlignment [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["alignment"] | |
end dgColumnAlignment | |
setprop dgHeaderAlignment [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
if pValue is not among the items "left,right,center" then | |
_ThrowError kErrInvalidProperty, "invalid header alignment value '" & pValue & "'" | |
end if | |
put toLower(pValue) into theColsA[pColumn]["header"]["alignment"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
## Update alignment | |
lock screen | |
if sTableObjectsA["columns"][pColumn]["header"]["group"] is not empty then | |
set the dgAlignment of sTableObjectsA["columns"][pColumn]["header"]["group"] to pValue | |
end if | |
unlock screen | |
end dgHeaderAlignment | |
getprop dgHeaderAlignment [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
return theColsA[pColumn]["header"]["alignment"] | |
end dgHeaderAlignment | |
setprop dgColumnWidth [pColumn] pValue | |
_StoreColWidth pColumn, pValue | |
_table.ResizeColumns | |
end dgColumnWidth | |
private command _StoreColWidth pColumn, pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
if pValue is not an integer then | |
_ThrowError kErrInvalidInteger, "invalid column width value '" & pValue & "'" | |
end if | |
if theColsA[pColumn]["min width"] is empty then put 40 into theColsA[pColumn]["min width"] | |
if theColsA[pColumn]["max width"] is empty then put 1000 into theColsA[pColumn]["max width"] | |
put max(theColsA[pColumn]["min width"], min(pValue, theColsA[pColumn]["max width"])) into theColsA[pColumn]["width"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
end _StoreColWidth | |
getprop dgColumnWidth [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if theColsA[pColumn]["width"] is empty then return 100 | |
else return theColsA[pColumn]["width"] | |
end dgColumnWidth | |
function _ColumnHeaderGroup pColumn | |
return sTableObjectsA["columns"][pColumn]["header"]["group"] | |
end _ColumnHeaderGroup | |
setprop dgColumnMinWidth [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
if pValue is not an integer then | |
_ThrowError kErrInvalidInteger, "invalid column width value '" & pValue & "'" | |
end if | |
put pValue into theColsA[pColumn]["min width"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
## current size must be at least min size | |
if theColsA[pColumn]["width"] is empty or theColsA[pColumn]["min width"] > theColsA[pColumn]["width"] then | |
set the dgColumnWidth[pColumn] of me to theColsA[pColumn]["min width"] | |
end if | |
return empty | |
end dgColumnMinWidth | |
getprop dgColumnMinWidth [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if theColsA[pColumn]["min width"] is not empty then | |
return theColsA[pColumn]["min width"] | |
else | |
return 40 | |
end if | |
end dgColumnMinWidth | |
setprop dgColumnMaxWidth [pColumn] pValue | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if pColumn is not among the keys of theColsA then | |
_ThrowError kErrInvalidProperty, "column '" & pColumn & "' does not exist" | |
end if | |
if pValue is not an integer then | |
_ThrowError kErrInvalidInteger, "invalid column width value '" & pValue & "'" | |
end if | |
put pValue into theColsA[pColumn]["max width"] | |
lock messages | |
set the dgProps["column properties"] of me to theColsA | |
unlock messages | |
## current size must be at least min size | |
if theColsA[pColumn]["width"] is empty or theColsA[pColumn]["width"] > theColsA[pColumn]["max width"] then | |
set the dgColumnWidth[pColumn] of me to theColsA[pColumn]["max width"] | |
end if | |
return empty | |
end dgColumnMaxWidth | |
getprop dgColumnMaxWidth [pColumn] | |
local theColsA | |
put the dgProps["column properties"] of me into theColsA | |
if theColsA[pColumn]["max width"] is not empty then | |
return theColsA[pColumn]["max width"] | |
else | |
return 1000 | |
end if | |
end dgColumnMaxWidth | |
## deprecated | |
getprop dgColumnCustomControl | |
return the dgColumnTemplate of me | |
end dgColumnCustomControl | |
getprop dgColumnTemplate [pColumn] | |
local theTemplateGroup | |
put the dgProps["row template"] of me into theTemplateGroup | |
if there is a control pColumn of theTemplateGroup then | |
return the long ID of control pColumn of theTemplateGroup | |
else | |
return empty | |
end if | |
end dgColumnTemplate | |
getprop dgColumnHeaderTemplate [pColumn] | |
local theTemplateGroup | |
put the dgProps["row template"] of me into theTemplateGroup | |
if there is a control (pColumn && "[Header]") of theTemplateGroup then | |
return the long ID of control (pColumn && "[Header]") of theTemplateGroup | |
else | |
return empty | |
end if | |
end dgColumnHeaderTemplate | |
getprop dgWorkingRect | |
if the long ID of the target is the long ID of me then | |
return _WorkingGroupRect(the long ID of group "dgList" of me) | |
else | |
return _WorkingGroupRect(the long ID of the target) | |
end if | |
end dgWorkingRect | |
getprop dgFindIndex [pKeyValues] | |
----- | |
local foundAMatch, theFoundIndex | |
local theIndex | |
local theKey | |
----- | |
split pKeyValues by cr and ":" ## Provide multiple lines of key:value to perform AND search | |
repeat for each key theIndex in sDataArray | |
## Developer can pass in multiple search strings to perform an AND search | |
repeat for each key theKey in pKeyValues | |
if sDataArray[theIndex][theKey] is word 1 to -1 of pKeyValues[theKey] then | |
put true into foundAMatch | |
else | |
put false into foundAMatch | |
end if | |
## AND search didn't pan out. Move on to next index. | |
if not foundAMatch then exit repeat | |
end repeat | |
if foundAMatch then | |
put theIndex into theFoundIndex | |
exit repeat | |
end if | |
end repeat | |
return max(0, theFoundIndex) | |
end dgFindIndex | |
getprop dgFindLine [pKeyValues] | |
local theFoundIndex | |
put the dgFindIndex [pKeyValues] of me into theFoundIndex | |
if theFoundIndex > 0 then | |
return the dgLineOfIndex[theFoundIndex] of me | |
else | |
return 0 | |
end if | |
end dgFindLine | |
getprop dgDataControlOfIndex [pIndex] | |
if _ControlType() is "form" then | |
if the dgProps["cache controls"] of me then | |
return the long ID of sControlOfIndexA[pIndex] | |
else | |
## only visible controls are in play | |
repeat for each line theControl in sTableObjectsA["visible row controls"] | |
if the dgIndex of theControl is pIndex then | |
return the long ID of theControl | |
end if | |
end repeat | |
end if | |
end if | |
return empty | |
end dgDataControlOfIndex[pIndex] | |
function ColumnControlOfIndex pColumn, pIndex | |
if _ControlType() is "table" then | |
## only visible controls are in play | |
repeat for each line theControl in sTableObjectsA["columns"][pColumn]["row controls"] | |
if the dgIndex of theControl is pIndex then | |
return the long id of theControl ## resolve the shortened 'of me' reference | |
end if | |
end repeat | |
end if | |
return empty | |
end ColumnControlOfIndex | |
function ColumnControlOfLine pColumn, pLine | |
local theIndex | |
put the dgIndexOfLine [pLine] of me into theIndex | |
return ColumnControlOfIndex(pColumn, theIndex) | |
end ColumnControlOfLine | |
getprop dgDataControlOfLine [pLine] | |
local theIndex | |
put the dgIndexOfLine [pLine] of me into theIndex | |
return the dgDataControlOfIndex [theIndex] of me | |
end dgDataControlOfLine | |
## Returns the rect of the control connected with pIndex. Rect | |
## is relative to this group but takes into account current vscroll. | |
## This may not prove very useful if you don't have control caching on. | |
getprop dgRectOfIndex [pIndex] | |
local theControl, theRect, theSequence, theControlBottom | |
put the dgDataControlOfIndex[pIndex] of me into theControl | |
if theControl is not empty then | |
put the rect of theControl into theRect | |
if the dgProps["fixed row height"] of me then | |
put the dgLineOfIndex[pIndex] of me into theSequence | |
put sControlHeights * theSequence into item 4 of theRect | |
put item 4 of theRect - sControlHeights into item 2 of theRect | |
else | |
put 0 into theControlBottom | |
repeat for each item theIndex in sIndexSequencing | |
add sControlHeights[theIndex] to theControlBottom | |
if theIndex is pIndex then | |
exit repeat | |
end if | |
end repeat | |
put theControlBottom into item 4 of theRect | |
put theControlBottom - sControlHeights[theIndex] into item 2 of theRect | |
end if | |
## Adjust for vscroll | |
local theVScroll | |
put the dgVScroll of me into theVScroll | |
subtract theVScroll from item 2 of theRect | |
subtract theVScroll from item 4 of theRect | |
## Now adjust for position of control | |
add the top of group "dgListMask" of me to item 2 of theRect | |
add the top of group "dgListMask" of me to item 4 of theRect | |
end if | |
return theRect | |
end dgRectOfIndex | |
getprop dgControl | |
return the long ID of me | |
end dgControl | |
getprop dgVScroll | |
if __HasMobileScroller() then | |
return mobileControlGet(sScrollerId, "vScroll") | |
end if | |
return round(the thumbPosition of scrollbar "dgScrollbar" of me) | |
end dgVScroll | |
setprop dgVScroll pValue | |
_SetVScroll pValue | |
end dgVScroll | |
private command _SetVScroll pValue | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
set the thumbPosition of scrollbar "dgScrollbar" of me to pValue | |
put the thumbPosition of scrollbar "dgScrollbar" of me into pValue | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "vScroll", pValue | |
end if | |
set the lockMessages to msgsAreLocked | |
if not msgsAreLocked and the visible of scrollbar "dgScrollbar" of me then | |
send "scrollbarDrag pValue" to scrollbar "dgScrollbar" of me | |
## dispatch slows things down terribly here | |
-- dispatch "scrollbarDrag" to scrollbar "dgScrollbar" of me with pValue | |
else | |
_ScrollListV pValue | |
end if | |
unlock screen | |
end _SetVScroll | |
## Vscroll percentage is useful when refreshing the list with new data and keeping the scroll in relatively the same position | |
getprop dgVScrollPercent | |
return _GetVScrollPercent() | |
end dgVScrollPercent | |
private function _GetVScrollPercent | |
local theWorkingEndValue | |
put __WorkingScrollVEndValue() into theWorkingEndValue | |
if theWorkingEndValue > 0 then | |
if __HasMobileScroller() then | |
return mobileControlGet(sScrollerId, "vScroll") / theWorkingEndValue | |
end if | |
return round(the thumbPosition of scrollbar "dgScrollbar" of me) / theWorkingEndValue | |
else | |
return 0 | |
end if | |
end _GetVScrollPercent | |
setprop dgVScrollPercent pPercent | |
_SetVScrollPercent pPercent | |
end dgVScrollPercent | |
private function __WorkingScrollVEndValue | |
local theWorkingEndValue | |
if the environment is not "mobile" then | |
put the endValue of scrollbar "dgScrollbar" of me - the thumbSize of scrollbar "dgScrollbar" of me into theWorkingEndValue | |
else if __HasMobileScroller() then | |
local tContentRect | |
put mobileControlGet(sScrollerId, "contentrect") into tContentRect | |
put item 4 of tContentRect - the height of group "dgList" of me into theWorkingEndValue | |
else | |
put 0 into theWorkingEndValue | |
end if | |
return theWorkingEndValue | |
end __WorkingScrollVEndValue | |
private command _SetVScrollPercent pPercent | |
_SetVScroll round(__WorkingScrollVEndValue() * pPercent) | |
end _SetVScrollPercent | |
getprop dgHScroll | |
if __HasMobileScroller() then | |
return mobileControlGet(sScrollerId, "hScroll") | |
else | |
return round(the thumbPosition of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me) | |
end if | |
end dgHScroll | |
setprop dgHScroll pValue | |
_SetHScroll pValue | |
end dgHScroll | |
private command _SetHScroll pValue | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
set the thumbPosition of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me to pValue | |
put the thumbPosition of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me into pValue | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "hScroll", pValue | |
end if | |
set the lockMessages to msgsAreLocked | |
if not msgsAreLocked and the visible of group "dgHorizontalComponents" of me then | |
send "scrollbarDrag pValue" to scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me | |
## dispatch slows things down terribly here | |
-- dispatch "scrollbarDrag" to scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me with pValue | |
else | |
_ScrollListH pValue | |
end if | |
unlock screen | |
end _SetHScroll | |
private command _ScrollListH pScrollValue | |
lock screen | |
set the hScroll of group "dgHeaderMask" of group "dgHeaderComponents" of me to pScrollValue | |
set the hScroll of group "dgListMask" of me to pScrollValue | |
unlock screen | |
end _ScrollListH | |
getprop dgHScrollPercent | |
return _GetHScrollPercent() | |
end dgHScrollPercent | |
private function __WorkingScrollHEndValue | |
local theWorkingEndValue | |
if the environment is not "mobile" then | |
put the endValue of scrollbar "dgHScrollbar" of me - the thumbSize of scrollbar "dgHScrollbar" of me into theWorkingEndValue | |
else if __HasMobileScroller() then | |
local tContentRect | |
put mobileControlGet(sScrollerId, "contentrect") into tContentRect | |
put item 3 of tContentRect - the width of group "dgList" of me into theWorkingEndValue | |
else | |
put 0 into theWorkingEndValue | |
end if | |
return theWorkingEndValue | |
end __WorkingScrollHEndValue | |
private function _GetHScrollPercent | |
local theWorkingEndValue | |
put __WorkingScrollHEndValue() into theWorkingEndValue | |
if theWorkingEndValue > 0 then | |
if __HasMobileScroller() then | |
return mobileControlGet(sScrollerId, "hScroll") / theWorkingEndValue | |
end if | |
return round(the thumbPosition of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me) / theWorkingEndValue | |
else | |
return 0 | |
end if | |
end _GetHScrollPercent | |
setprop dgHScrollPercent pPercent | |
_SetHScrollPercent pPercent | |
end dgHScrollPercent | |
private command _ResetScrollsToZero | |
_ResetVScrollToZero | |
_ResetHScrollToZero | |
end _ResetScrollsToZero | |
private command _ResetVScrollToZero | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
set the lockMessages to true | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "vScroll", 0 | |
end if | |
set the thumbPosition of scrollbar "dgScrollbar" of me to 0 | |
set the vScroll of group "dgList" of group "dgListMask" of me to 0 | |
set the lockMessages to msgsAreLocked | |
end _ResetVScrollToZero | |
private command _ResetHScrollToZero | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
set the lockMessages to true | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "hScroll", 0 | |
end if | |
set the thumbPosition of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me to 0 | |
set the hScroll of group "dgHeaderMask" of group "dgHeaderComponents" of me to 0 | |
set the hScroll of group "dgListMask" of me to 0 | |
set the lockMessages to msgsAreLocked | |
end _ResetHScrollToZero | |
private command _SetHScrollPercent pPercent | |
_SetHScroll round(__WorkingScrollHEndValue() * pPercent) | |
end _SetHScrollPercent | |
getprop dgDataControls | |
local theControls, theList | |
if _ControlType() is "form" then | |
if the dgProps["cache controls"] of me then | |
## sTableObjectsA["visible row controls"] only contains visible controls but all controls are valid. | |
put sTableObjectsA["all row controls"] into theControls | |
else | |
put sTableObjectsA["visible row controls"] into theControls | |
end if | |
repeat for each line theControl in theControls | |
put the long ID of theControl & cr after theList | |
end repeat | |
delete the last char of theList | |
return theList | |
else | |
return empty | |
end if | |
end dgDataControls | |
getprop dgHilitedIndex | |
return sHilitedIndexes ## indexes will be in order of selection | |
end dgHilitedIndex | |
getprop dgHilitedIndexes | |
return sHilitedIndexes ## indexes will be in order of selection | |
end dgHilitedIndexes | |
setprop dgHilitedIndex pIndexes | |
_SetHilitedIndexes pIndexes | |
return the result | |
end dgHilitedIndex | |
## Hmmm, this handler needs to ensure that the one of the hilited indexes | |
## will be visible on screen. For that we may need to query the range of | |
## indexes currently visible and then scroll the index into view if need be. | |
setprop dgHilitedIndexes pIndexes | |
_SetHilitedIndexes pIndexes | |
return the result | |
end dgHilitedIndexes | |
private command _SetHilitedIndexes pIndexes | |
put _SortIndexesSequentially(pIndexes) into pIndexes | |
lock screen | |
local theReturnValue | |
if pIndexes is not sHilitedIndexes then | |
put pIndexes into sHilitedIndexes | |
if the dgProps["scroll selections into view"] of me is not false then | |
ScrollIndexIntoView item 1 of sHilitedIndexes | |
end if | |
_HiliteIndexesInVisibleControls | |
put true into theReturnValue | |
else | |
put false into theReturnValue | |
end if | |
unlock screen | |
return theReturnValue | |
end _SetHilitedIndexes | |
## Lines/Sequences are synonymous | |
getprop dgHilitedLines | |
return _GetHilitedSequences() | |
end dgHilitedLines | |
getprop dgHilitedLine | |
return _GetHilitedSequences() | |
end dgHilitedLine | |
private function _GetHilitedSequences | |
local theHilitedSequences,theIndex,theItemNo | |
set the wholeMatches to true | |
repeat for each item theIndex in sHilitedIndexes | |
put itemOffset(theIndex, sIndexSequencing ) into theItemNo | |
if theItemNo > 0 then | |
put theItemNo & comma after theHilitedSequences | |
end if | |
end repeat | |
delete the last char of theHilitedSequences | |
return theHilitedSequences | |
end _GetHilitedSequences | |
## Lines/Sequences are synonymous | |
setprop dgHilitedLine pSequences | |
_SetHilitedSequences pSequences | |
return the result | |
end dgHilitedLine | |
setprop dgHilitedLines pSequences | |
_SetHilitedSequences pSequences | |
return the result | |
end dgHilitedLines | |
private command _SetHilitedSequences pSequences | |
local theIndex,theSequence | |
local theHilitedIndexes | |
local theMaxSequence | |
## We want indexes to be in order of selection | |
sort items of pSequences numeric | |
put the number of items of sIndexSequencing into theMaxSequence | |
repeat for each item theSequence in pSequences | |
put item min(theSequence, theMaxSequence) of sIndexSequencing into theIndex | |
if theIndex is not empty then | |
put theIndex & comma after theHilitedIndexes | |
end if | |
end repeat | |
delete the last char of theHilitedIndexes | |
set the dgHilitedIndexes of me to theHilitedIndexes | |
return the result | |
end _SetHilitedSequences | |
--> Private | |
private function _CardOf | |
local theCharNo,theControl | |
put the long ID of me into theControl | |
put offset(" of card ", theControl) into theCharNo | |
delete char 1 to (theCharNo - 1) of theControl | |
put offset(" of stack ", theControl) into theCharNo | |
delete char theCharNo to -1 of theControl | |
return theControl | |
end _CardOf | |
private command _RefreshIndexes pIndexes | |
local redrawTheList, theVScroll, theVisibleIndexes | |
put false into redrawTheList | |
lock screen | |
put _GetVScrollPercent() into theVScroll | |
put _VisibleIndexes() into theVisibleIndexes | |
repeat for each item theIndex in pIndexes | |
_UpdateIndexWithNewData theIndex | |
set the wholeMatches to true | |
if theIndex is among the items of theVisibleIndexes then | |
_ResetControlsOfIndex theIndex | |
put true into redrawTheList | |
end if | |
end repeat | |
if redrawTheList then | |
_RedrawList theVScroll | |
end if | |
unlock screen | |
end _RefreshIndexes | |
private function _ColorToRGB pColor | |
if pColor is a color then | |
if the number of items of pColor is not 3 then | |
lock screen | |
local theGraphic, theOrigColor | |
put the long ID of graphic "dgBackground" of me into theGraphic | |
put the backColor of theGraphic into theOrigColor | |
set the backColor of theGraphic to pColor | |
set the backpixel of theGraphic to the effective backpixel of theGraphic | |
put the backColor of theGraphic into pColor | |
set the backColor of theGraphic to theOrigColor | |
unlock screen | |
end if | |
end if | |
return pColor | |
end _ColorToRGB | |
private command _SetGraphicGradient pGraphic, pStartColor, pEndColor | |
local theRect, theX, theY | |
put the rect of pGraphic into theRect | |
set the backgroundColor of pGraphic to empty | |
set the fillgradient["quality"] of pGraphic to "normal" | |
set the fillgradient["repeat"] of pGraphic to 1 | |
set the fillgradient["mirror"] of pGraphic to false | |
set the fillgradient["wrap"] of pGraphic to true | |
set the fillgradient["type"] of pGraphic to "linear" | |
set the fillGradient["ramp"] of pGraphic to "0.00000," & pStartColor & cr & "1.00000," & pEndColor | |
put item 1 of theRect into theX | |
put item 2 of theRect into theY | |
## take gradient from the top to the bottom. | |
## Spread it out via the X | |
set the fillgradient["from"] of pGraphic to theX, theY | |
set the fillgradient["to"] of pGraphic to theX, theY + (item 4 of theRect - item 2 of theRect) | |
set the fillGradient["via"] of pGraphic to theX + 40, theY | |
return empty | |
end _SetGraphicGradient | |
on scrollerBeginDrag | |
_UserStartedVScrolling | |
pass scrollerBeginDrag | |
end scrollerBeginDrag | |
on scrollerEndDrag | |
_UserStoppedVScrolling | |
pass scrollerEndDrag | |
end scrollerEndDrag | |
on scrollerDidScroll pHScroll, pVScroll | |
dgScrollbarDragV max(0,pVScroll) | |
dgScrollbarDragH max(0,pHScroll) | |
pass scrollerDidScroll | |
end scrollerDidScroll | |
## This handler is called from the vscroll bar when user releases mouse. | |
## It draws any columns not currently visible within the mask area. | |
## Note that we do not currently take into account whether or not the vscroll changed | |
## so if the user clicked an released without scrolling we would still redraw. If this becomes | |
## a problem then add a flag in mouseDown. | |
command _UserStoppedVScrolling | |
put false into sRunningActionsA["user is vscrolling"] | |
local theColumnsA | |
if _ControlType() is "table" then | |
put _table.AreColumnsVisibleWithinMask() into theColumnsA | |
_table.DrawColumns theColumnsA["hidden"] | |
end if | |
end _UserStoppedVScrolling | |
command _UserStartedVScrolling | |
put true into sRunningActionsA["user is vscrolling"] | |
end _UserStartedVScrolling | |
command PrintKeys pArray | |
if pArray is not an array then put sDataArray into pArray | |
put _PrintKeys(pArray) | |
end PrintKeys | |
private function _ResourceStack | |
local theStack, theCharNo | |
put the behavior of me into theStack | |
if theStack is not empty then | |
put offset(" of stack", theStack) into theCharNo | |
delete char 1 to (theCharNo + 3) of theStack | |
end if | |
return theStack | |
end _ResourceStack | |
private function _PrintKeys @pArray, pDimension | |
if pDimension is empty then put 0 into pDimension | |
local theKeys, theText, theTempArray | |
put the keys of pArray into theKeys | |
sort theKeys numeric | |
repeat for each line theKey in theKeys | |
if pArray[theKey] is an array then | |
put _printCharXTimes(space, pDimension * 5) & theKey & cr after theText | |
put pArray[theKey] into theTempArray | |
put _PrintKeys(theTempArray, pDimension + 1) after theText | |
else | |
put _printCharXTimes(space, pDimension * 5) & theKey & ":" && "`" & pArray[theKey] & "`" & cr after theText | |
end if | |
end repeat | |
return theText | |
end _PrintKeys | |
private function _printCharXTimes pChar, pTimes | |
local theStr | |
repeat with i = 1 to pTimes | |
put pChar after theStr | |
end repeat | |
return theStr | |
end _printCharXTimes | |
function _CustomControlReference pControl | |
----- | |
local theFirstCharToDelete | |
local theLastCharToDelete | |
local theOffset | |
local theStack | |
----- | |
put the long ID of pControl into pControl | |
## Get id without hierarchy | |
if word 1 of pControl is not among the items of "card,stack" then | |
## Strip any nested refs | |
if pControl contains "of group id" then | |
put length(word 1 to 4 of pControl) + 1 into theFirstCharToDelete | |
put offset(" card id", pControl) - 1 into theLastCharToDelete | |
delete char theFirstCharToDelete to theLastCharToDelete of pControl | |
end if | |
local theStackOffset, theSecondStackOffset | |
put offset(" of stack ", pControl) into theStackOffset | |
put offset(" of stack ", pControl, theStackOffset) into theSecondStackOffset | |
if theSecondStackOffset > 0 then | |
## Strip mainstack ref if substack. | |
## We want user to move stacks around. | |
add theSecondStackOffset to theStackOffset | |
delete char theStackOffset to -1 of pControl | |
else | |
## Shorten stack name | |
put char (theStackOffset + 4) to -1 of pControl into theStack | |
put the short name of theStack into theStack # get stack short name | |
put quote & theStack & quote into char (theStackOffset + 10) to -1 of pControl | |
end if | |
## Strip card ref | |
## Taken out so that one can locate the card of a stack that a reference is on | |
## without looping through every card on the stack. | |
-- put offset(" of card ", pControl) into theFirstCharToDelete | |
-- put offset(" of stack ", pControl) into theLastCharToDelete | |
-- delete char theFirstCharToDelete + 1 to theLastCharToDelete of pControl | |
end if | |
return pControl | |
end _CustomControlReference | |
private function _TopIsVisible pRect, pMaskRect | |
return item 2 of pRect < item 4 of pMaskRect and item 2 of pRect > item 2 of pMaskRect | |
end _TopIsVisible | |
private function _BottomIsVisible pRect, pMaskRect | |
return item 4 of pRect > item 2 of pMaskRect and item 4 of pRect < item 4 of pMaskRect | |
end _BottomIsVisible | |
private function _RectIsAtLeastAsTallAsMask pRect, pMaskRect | |
return item 4 of pRect - item 2 of pRect >= item 4 of pMaskRect - item 2 of pMaskRect | |
end _RectIsAtLeastAsTallAsMask | |
private function _RectCoversMask pRect, pMaskRect | |
return _RectIsAtLeastAsTallAsMask(pRect, pMaskRect) and \ | |
item 2 of pRect <= item 2 of pMaskRect and \ | |
item 4 of pRect >= item 4 of pMaskRect | |
end _RectCoversMask | |
private function _TopIsClipped pRect, pMaskRect | |
return item 4 of pRect > item 2 of pMaskRect and item 2 of pRect < item 2 of pMaskRect | |
end _TopIsClipped | |
private function _BottomIsClipped pRect, pMaskRect | |
return item 2 of pRect < item 4 of pMaskRect and item 4 of pRect > item 4 of pMaskRect | |
end _BottomIsClipped | |
private function _TopAndBottomAreClipped pRect, pMaskRect | |
return item 2 of pRect >= item 4 of pMaskRect or item 4 of pRect <= item 2 of pMaskRect | |
end _TopAndBottomAreClipped | |
private command _SelectTargetControl pControl | |
----- | |
local theControl | |
local theIndex | |
local thePreviouslyHilitedIndexes | |
----- | |
if pControl is empty then put the dgDataControl of the target into pControl | |
if pControl is not empty then | |
put the dgIndex of pControl into theIndex | |
put sHilitedIndexes into thePreviouslyHilitedIndexes | |
## single click always inserts index into first index var | |
put theIndex into sFirstIndexClickedWithShiftKeyDown | |
set the dgHilitedIndexes of me to the dgIndex of pControl | |
## Clicked on a control | |
if the long ID of me is not in the long ID of the focusedObject then | |
focus on graphic "dgBackground" of me | |
end if | |
_SelectionChanged thePreviouslyHilitedIndexes | |
end if | |
end _SelectTargetControl | |
private function _KeyedParametersToArray pString | |
split pString by comma and ":" | |
return pString | |
end _KeyedParametersToArray | |
private function _SortIndexesSequentially pIndexes | |
local theIndex | |
local theItemNo | |
local theIndexes | |
----- | |
set the wholeMatches to true | |
## Algorithm: Inserts indexes into same line as sequence then strips | |
## empty lines. Seems like it would be faster then creating table of sequences/indexes, | |
## performing a sort and then extracting the index column. Haven't timed though. | |
repeat for each item theIndex in pIndexes | |
put itemOffset(theIndex, sIndexSequencing) into theItemNo | |
if theItemNo > 0 then | |
put theIndex into line theItemNo of theIndexes | |
end if | |
end repeat | |
filter theIndexes without empty ## Get rid of empty lines | |
replace cr with comma in theIndexes | |
return theIndexes | |
end _SortIndexesSequentially | |
private command _ProcessNewIndexData pIndex | |
----- | |
local theControl | |
local theTemplateGroup | |
local msgsAreLocked | |
----- | |
put the lockMessages into msgsAreLocked | |
## Create cached control as needed | |
if the dgProps["cache controls"] of me and _ControlType() is "form" then | |
put the dgProps["row template"] of me into theTemplateGroup | |
lock messages | |
copy theTemplateGroup to group "dgList" of me | |
put it into theControl | |
put "control id" && word 3 of theControl && "of me" into sControlOfIndexA[pIndex] | |
set the visible of theControl to false | |
set the dgIndex of theControl to pIndex | |
unlock messages | |
put sControlOfIndexA[pIndex] into line (the number of lines of sTableObjectsA["all row controls"] + 1) of sTableObjectsA["all row controls"] | |
end if | |
## Since number of records has changed we need to redraw alternating rows | |
_DrawAlternatingRows | |
_UpdateIndexWithNewData pIndex | |
## We only need to update formattedheight when working with fixed control heights. | |
## Otherwise the formatted height is adjusted when new data is updated. | |
if sFormattedHeight is not empty then | |
if the dgProps["fixed row height"] of me then | |
add sControlHeights to sFormattedHeight | |
_ConfigureScrollbar | |
else | |
_ConfigureScrollbar | |
_AutoHideVScrollbar ## this only works for fixed height | |
end if | |
end if | |
set the lockMessages to msgsAreLocked | |
return theControl | |
end _ProcessNewIndexData | |
private command _UpdateIndexWithNewData pIndex | |
----- | |
local deleteTheControl, theControl, theLine, theRect | |
local theListGroupRect | |
local theTemplateGroup | |
local theFocusedControl | |
local msgsAreLocked | |
local heightIsFixed | |
----- | |
put the long ID of the focusedObject into theFocusedControl | |
put the dgDataControlOfIndex[pIndex] of me into theControl | |
put the lockMessages into msgsAreLocked | |
put the dgProps["fixed row height"] of me into heightIsFixed | |
## If not fixed height and no control exists then create one | |
## and mark it for deletion | |
if theControl is empty and not heightIsFixed then | |
put true into deleteTheControl | |
put the dgProps["row template"] of me into theTemplateGroup | |
lock messages | |
copy theTemplateGroup to group "dgList" of me | |
put it into theControl | |
end if | |
## Insert data into control | |
if theControl is not empty then | |
unlock messages | |
put _WorkingGroupRect(the long ID of group "dgList" of me) into theListGroupRect | |
put the dgLine of theControl into theLine ## passing this param is deprecated | |
put the rect of theControl into theRect | |
put item 1 of theRect + (item 3 of theListGroupRect - item 1 of theListGroupRect) into item 3 of theRect | |
dispatch "FillInData" to theControl with sDataArray[pIndex] | |
lock messages | |
set the rect of theControl to theRect ## FillInData could possibly extend elements outside of the original rect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
lock messages | |
if not heightIsFixed then | |
put item 2 of theRect + the formattedHeight of theControl into item 4 of theRect | |
set the rect of theControl to theRect | |
end if | |
end if | |
## Now update formatted height if we are not dealing with fixed control heights | |
if not the dgProps["fixed row height"] of me and sFormattedHeight is not empty then | |
## First we need to subtract existing height from cached | |
if sControlHeights[pIndex] is an integer and sFormattedHeight is not empty then | |
subtract sControlHeights[pIndex] from sFormattedHeight | |
end if | |
## Now cache control height | |
put the height of theControl into sControlHeights[pIndex] | |
add sControlHeights[pIndex] to sFormattedHeight | |
_ConfigureScrollbar | |
end if | |
## Note: fixed control height does NOT need formattedheight updated. | |
## Cleanup if not caching and not fixed control height | |
if deleteTheControl then | |
lock messages | |
delete theControl | |
end if | |
## Make sure focus stays with us | |
if the long ID of me is in theFocusedControl then | |
lock messages | |
if there is not a theFocusedControl then | |
focus on graphic "dgBackground" of me | |
else | |
focus on theFocusedControl | |
end if | |
unlock messages | |
end if | |
set the lockMessages to msgsAreLocked | |
return empty | |
end _UpdateIndexWithNewData | |
private function _VisibleIndexes | |
----- | |
local theControl | |
local theIndexes | |
----- | |
switch _ControlType() | |
case "table" | |
put item sTableObjectsA["base sequence for visible controls"] to \ | |
(sTableObjectsA["base sequence for visible controls"] + sTableObjectsA["row control count"]) of sIndexSequencing into theIndexes | |
break | |
default | |
repeat for each line theControl in sTableObjectsA["visible row controls"] | |
put the dgIndex of theControl & comma after theIndexes | |
end repeat | |
delete the last char of theIndexes | |
end switch | |
return theIndexes | |
end _VisibleIndexes | |
private command _ConfigureScrollbar | |
local theEndValue | |
if sFormattedHeight > 0 then | |
put sFormattedHeight into theEndValue | |
else | |
put the height of group "dgList" of me into theEndValue | |
end if | |
lock screen | |
## If messages are not locked then scrollbarDrag is sent when group height is increased. | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
local thePercent | |
put _GetVScrollPercent() into thePercent | |
## Setting thumbsize 2nd is important because engine limits thumbsize based on endValue | |
set the endValue of scrollbar "dgScrollbar" of me to max(the height of group "dgList" of me, theEndValue) | |
set the thumbSize of scrollbar "dgScrollbar" of me to the height of group "dgList" of me | |
local thePageIncrement | |
put the thumbSize of scrollbar "dgScrollbar" of me into thePageIncrement | |
if sControlHeights is an integer then subtract sControlHeights from thePageIncrement | |
else subtract 16 from thePageIncrement ## Why 16? Why not. | |
set the pageIncrement of scrollbar "dgScrollbar" of me to thePageIncrement | |
if the dgProps["fixed row height"] of me and sControlHeights is an integer then | |
set the lineIncrement of scrollbar "dgScrollbar" of me to sControlHeights | |
else | |
set the lineIncrement of scrollbar "dgScrollbar" of me to round(the pageIncrement of scrollbar "dgScrollbar" of me / 16) | |
end if | |
if __HasMobileScroller() then | |
local tContentRect | |
put mobileControlGet(sScrollerId, "contentrect") into tContentRect | |
put max(the height of group "dgList" of me, theEndValue) into item 4 of tContentRect | |
mobileControlSet sScrollerId, "contentrect", tContentRect | |
end if | |
## in lock messages so no redrawing occurs | |
_SetVScrollPercent thePercent | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
end _ConfigureScrollbar | |
private command _ConfigureHScrollbar | |
if _ControlType() is "table" then | |
local theScrollBar | |
put the long ID of scrollbar "dgHScrollbar" of group "dgHorizontalComponents" of me into theScrollBar | |
local theRect | |
put _WorkingGroupRect(the long ID of group "dgListMask" of me) into theRect | |
local theViewWidth | |
if the visible of scrollbar "dgScrollbar" of me then | |
put the left of scrollbar "dgScrollbar" of me - item 1 of theRect into theViewWidth | |
else | |
put the right of group "dgListMask" of me - item 1 of theRect into theViewWidth | |
end if | |
set the endValue of theScrollBar to max(sFormattedWidth, theViewWidth) | |
set the thumbSize of theScrollBar to theViewWidth | |
set the pageIncrement of theScrollBar to the thumbSize of theScrollBar | |
if __HasMobileScroller() then | |
local tContentRect | |
put mobileControlGet(sScrollerId, "contentrect") into tContentRect | |
put max(sFormattedWidth, theViewWidth) into item 3 of tContentRect | |
mobileControlSet sScrollerId, "contentrect", tContentRect | |
end if | |
## Show/hide last column divider shadow | |
_SetVisibilityOfColumnDividers | |
end if | |
end _ConfigureHScrollbar | |
private command _SetVisibilityOfColumnDividers | |
local theRect, theViewWidth, theColumns, theColCount, i, theControl | |
put _WorkingGroupRect(the long ID of group "dgListMask" of me) into theRect | |
put the left of scrollbar "dgScrollbar" of me - item 1 of theRect into theViewWidth | |
## Show/hide last column divider shadow | |
put _table.VisibleColumns() into theColumns | |
put the number of lines of theColumns into theColCount | |
repeat for each line theColumn in theColumns | |
add 1 to i | |
put sTableObjectsA["columns"][theColumn]["divider control"] into theControl | |
if there is not a theControl then next repeat | |
if i < theColCount then | |
set the visible of theControl to true | |
else | |
## Special rules for last one | |
if the width of group "dgHeader" of group "dgHeaderMask" of me < theViewWidth - 1 then | |
## Show | |
set the visible of theControl to true | |
else | |
## Hide | |
set the visible of theControl to false | |
end if | |
end if | |
end repeat | |
end _SetVisibilityOfColumnDividers | |
private command _AutoHideVScrollbar | |
if the environment is "mobile" then | |
return empty | |
end if | |
## Determine scrollbars | |
## Doesn't work without fixed control height. We trap for this when setting prop but developer | |
## could lock messages and set dgProps["autohide scrollbar"]. | |
if the dgProps["show vscrollbar"] of me is "auto" and the dgProps["fixed row height"] of me then | |
set the visible of scrollbar "dgScrollbar" of me to sFormattedHeight > the height of group "dgList" of me | |
local theRect | |
put the rect of group "dgList" of me into theRect | |
if the visible of scrollbar "dgScrollbar" of me then | |
put the left of scrollbar "dgScrollbar" of me into item 3 of theRect | |
else | |
put the right of scrollbar "dgScrollbar" of me into item 3 of theRect | |
end if | |
set the rect of group "dgListMask" of me to theRect | |
if _ControlType() is "table" then | |
_table.ResizeList | |
else | |
set the rect of group "dgList" of me to theRect | |
end if | |
end if | |
end _AutoHideVScrollbar | |
## Call whenever width of all columns changes | |
private command _AutoHideHScrollbar | |
if the environment is "mobile" then | |
return empty | |
end if | |
if _ControlType() is "table" then | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
if the dgProps["show hscrollbar"] of me is "auto" then | |
local theWidth | |
put sFormattedWidth into theWidth | |
## list mask extends behind scrollbar so negate that space in calculation | |
if the visible of scrollbar "dgScrollbar" of me then | |
add the width of scrollbar "dgScrollbar" of me to theWidth | |
end if | |
set the visible of group "dgHorizontalComponents" of me to theWidth > the width of group "dgListMask" of me | |
end if | |
set the lockMessages to msgsAreLocked | |
end if | |
end _AutoHideHScrollbar | |
-- returns the rect where you can draw content in a group without making scrollbars appear | |
private function _WorkingGroupRect pGroup | |
local theRect, theWidth | |
put the rect of pGroup into theRect | |
add the leftMargin of pGroup to item 1 of theRect | |
add the topMargin of pGroup to item 2 of theRect | |
subtract the rightMargin of pGroup from item 3 of theRect | |
subtract the bottomMargin of pGroup from item 4 of theRect | |
if the hScrollbar of pGroup then subtract the scrollbarWidth of pGroup from item 4 of theRect | |
if the vScrollbar of pGroup then subtract the scrollbarWidth of pGroup from item 3 of theRect | |
if the showBorder of pGroup then | |
put the borderWidth of pGroup into theWidth | |
add theWidth to item 1 of theRect | |
add theWidth to item 2 of theRect | |
subtract theWidth from item 3 of theRect | |
subtract theWidth from item 4 of theRect | |
end if | |
return theRect | |
end _WorkingGroupRect | |
private function _MaxStartingSequence | |
local theListGroupRect, theListGroupHeight, theMaxStartingSequence, theRequiredControlCount, theIndex | |
if the dgProps["fixed row height"] of me then | |
put _WorkingGroupRect(the long ID of group "dgListMask" of me) into theListGroupRect | |
put item 4 of theListGroupRect - item 2 of theListGroupRect into theListGroupHeight | |
put max(1, ((theListGroupHeight / sControlHeights) div 1)) into theMaxStartingSequence ## floor | |
## This was returning one number too low in tests with tables. (0.9.8.5) | |
put _ControlsRequiredToFillSpace() into theRequiredControlCount | |
else | |
## Start from the back and work our way down | |
put _WorkingGroupRect(the long ID of group "dgListMask" of me) into theListGroupRect | |
put item 4 of theListGroupRect - item 2 of theListGroupRect into theListGroupHeight | |
## Add controls until we exceed the list group height | |
repeat with theItemNo = the number of items of sIndexSequencing down to 1 | |
put item theItemNo of sIndexSequencing into theIndex | |
add 1 to theRequiredControlCount | |
local theNecessaryHeight | |
add sControlHeights[theIndex] to theNecessaryHeight | |
if theNecessaryHeight >= theListGroupHeight then | |
exit repeat | |
end if | |
end repeat | |
local theFirstControlHeight, theHeight | |
## Get height of first control in list | |
put sControlHeights[theIndex] into theFirstControlHeight | |
## The list group should be able to scroll the first control in the list out of view | |
## Add additional controls until we get to that number | |
repeat with theItemNo = theItemNo - 1 down to 1 | |
-- answer theItemNo | |
-- repeat for each item theIndex in (item theRequiredControlCount + 1 to -1 of theIndexList) | |
add 1 to theRequiredControlCount | |
add sControlHeights[theIndex] to theHeight | |
if theHeight >= theFirstControlHeight then | |
exit repeat | |
end if | |
end repeat | |
end if | |
local theNumberOfRecords | |
put max(0, the number of elements of sDataArray) into theNumberOfRecords | |
put theNumberOfRecords - theRequiredControlCount + 1 into theMaxStartingSequence | |
return max(1, theMaxStartingSequence) | |
end _MaxStartingSequence | |
-- sTableObjectsA["base sequence for visible controls"] is only necessary if controls are not a fixed height | |
private function _ControlsRequiredToFillSpace | |
local theFirstControlHeight,theIndex,theIndexList,theListGroupRect | |
local theNecessaryHeight,theRequiredControlCount | |
## Cached for performance | |
## Reset when resized | |
put empty into sControlsRequiredToFillSpace | |
if sControlsRequiredToFillSpace is empty then | |
put _WorkingGroupRect(the long ID of group "dgListMask" of me) into theListGroupRect | |
if the dgProps["fixed row height"] of me then | |
if _ControlType() is "table" then | |
## Since tables scroll by row we only need number of controls to fill visible area. | |
## Note: this could be updated if we add property for scrolling by row vs. pixel. | |
put item 4 of theListGroupRect - item 2 of theListGroupRect + sControlHeights into theNecessaryHeight | |
put ((theNecessaryHeight / sControlHeights) div 1) into sControlsRequiredToFillSpace ## floor + 1 | |
else | |
## We want to fill with number of controls to fill visible space + 1 control | |
put item 4 of theListGroupRect - item 2 of theListGroupRect + sControlHeights into theNecessaryHeight | |
put ((theNecessaryHeight / sControlHeights) div 1) + 1 into sControlsRequiredToFillSpace ## floor + 1 | |
end if | |
else | |
## Beginning from the starting index add up control heights until we reach height of list group | |
put item sTableObjectsA["base sequence for visible controls"] to -1 of sIndexSequencing into theIndexList | |
put sControlHeights[item 1 of theIndexList] into theFirstControlHeight | |
local theListGroupHeight | |
put item 4 of theListGroupRect - item 2 of theListGroupRect into theListGroupHeight | |
repeat for each item theIndex in theIndexList | |
add 1 to sControlsRequiredToFillSpace | |
add sControlHeights[theIndex] to theNecessaryHeight | |
if theNecessaryHeight >= theListGroupHeight then | |
exit repeat | |
end if | |
end repeat | |
## The list group should be able to scroll the first control in the list out of view | |
## Add additional controls until we get to that number | |
repeat for each item theIndex in (item sControlsRequiredToFillSpace + 1 to -1 of theIndexList) | |
add 1 to sControlsRequiredToFillSpace | |
local theHeight | |
add sControlHeights[theIndex] to theHeight | |
if theHeight >= theFirstControlHeight then | |
exit repeat | |
end if | |
end repeat | |
end if | |
end if | |
return sControlsRequiredToFillSpace | |
end _ControlsRequiredToFillSpace | |
private command _ResizeCachedControls | |
local msgsAreLocked, theListGroupRect, theTopLeft | |
put the lockMessages into msgsAreLocked | |
_AutoHideVScrollbar ## only works for fixed height | |
put _WorkingGroupRect(the long ID of group "dgList" of me) into theListGroupRect | |
put item 1 to 2 of theListGroupRect into theTopLeft | |
lock screen | |
if the dgProps["fixed row height"] of me then | |
## Control heights and total height remain unchanged | |
repeat for each line theControl in sTableObjectsA["all row controls"] | |
local theIndex, theRect | |
put the dgIndex of theControl into theIndex | |
set the topLeft of theControl to theTopLeft ## This allows developer to always rely on top and left of controls in template code | |
put theTopLeft & comma & item 3 of theListGroupRect & comma & item 2 of theTopLeft + the height of theControl into theRect | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
end repeat | |
else | |
put empty into sControlHeights | |
put 0 into sFormattedHeight | |
repeat for each line theControl in sTableObjectsA["all row controls"] | |
put the dgIndex of theControl into theIndex | |
set the topLeft of theControl to theTopLeft ## This allows developer to always rely on top and left of controls in template code | |
put theTopLeft & comma & item 3 of theListGroupRect & comma & item 2 of theTopLeft + the height of theControl into theRect | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
lock messages | |
put item 2 of theRect + the formattedHeight of theControl into item 4 of theRect | |
set the rect of theControl to theRect | |
put item 4 of theRect - item 2 of theRect into sControlHeights[theIndex] | |
add sControlHeights[theIndex] to sFormattedHeight | |
end repeat | |
end if | |
unlock screen | |
set the lockMessages to msgsAreLocked | |
end _ResizeCachedControls | |
private command _SetSequenceOfIndex pIndex, pSequence | |
local theCurrentSequence,theError | |
put min(max(1, pSequence), the number of items of sIndexSequencing) into pSequence | |
set the wholeMatches to true | |
put itemOffset(pIndex, sIndexSequencing) into theCurrentSequence | |
if theCurrentSequence > 0 then | |
delete item theCurrentSequence of sIndexSequencing | |
end if | |
put pIndex & comma before item pSequence of sIndexSequencing | |
if the last char of sIndexSequencing is comma then | |
delete the last char of sIndexSequencing | |
end if | |
return theError | |
end _SetSequenceOfIndex | |
private command _SelectionChanged pPreviouslyHilitedIndexes | |
dispatch "selectionChanged" with sHilitedIndexes, pPreviouslyHilitedIndexes | |
end _SelectionChanged | |
command _ScrollListV pVScroll, pForceRefresh | |
local theFraction,theGroupVScroll,theSequence, theError | |
-- put the milliseconds & cr & the executioncontexts | |
-- put false into sRunningActionsA["v scroll"] | |
if sRunningActionsA["v scroll"] then | |
put true into sRunningActionsA["resend _scrolllistv"] | |
return empty | |
end if | |
put true into sRunningActionsA["v scroll"] | |
try | |
if sFormattedHeight > 0 then | |
if pVScroll is not an integer then | |
put the dgVScroll of me into pVScroll | |
end if | |
## Sequence can't be too high | |
local theMaxStartingSequence | |
put _MaxStartingSequence() into theMaxStartingSequence | |
if the dgProps["fixed row height"] of me then | |
put (pVScroll / sControlHeights) + 1 into theSequence | |
put theSequence mod 1 into theFraction | |
put theSequence div 1 into theSequence | |
put round(sControlHeights * theFraction) into theGroupVScroll | |
## If sequence is too high then adjust group vscroll accordingly | |
repeat with i = 1 to theSequence - theMaxStartingSequence | |
add sControlHeights to theGroupVScroll | |
end repeat | |
-- put "max sequence:" && theMaxStartingSequence && \ | |
-- "sequence:" && theSequence && "total records:" && the dgNumberOfRecords of me && \ | |
-- "fraction:" && theFraction && "group vscroll:" && theGroupVScroll & cr after msg | |
put min(theMaxStartingSequence, theSequence) into theSequence | |
else | |
## Random sized records | |
local theFormattedHeight | |
put 0 into theFormattedHeight | |
put 0 into theSequence | |
repeat for each item theIndex in sIndexSequencing | |
add 1 to theSequence | |
add sControlHeights[theIndex] to theFormattedHeight | |
if theFormattedHeight >= pVScroll then | |
put pVScroll - (theFormattedHeight - sControlHeights[theIndex]) into theGroupVScroll | |
exit repeat | |
end if | |
end repeat | |
end if | |
_DrawListWithProperties theSequence, theGroupVScroll, pForceRefresh | |
end if | |
catch e | |
put e into theError | |
end try | |
put false into sRunningActionsA["v scroll"] | |
if theError is not empty then | |
put false into sRunningActionsA["resend _scrolllistv"] | |
_ReportBehaviorError theError | |
end if | |
if sRunningActionsA["resend _scrolllistv"] then | |
-- put empty into pVScroll ## ?? Do we want the next round to determine where to go based on position of sb at time? | |
## I don't think so as little tests show that scrolling when dragging scrollbar is smoother if we keep value | |
if the keys of the dragData is empty then ## messages don't go during drag opeations. Don't send. | |
send "_ScrollListV pVScroll, pForceRefresh" to me in 0 milliseconds | |
end if | |
put false into sRunningActionsA["resend _scrolllistv"] | |
end if | |
end _ScrollListV | |
private command _ReportBehaviorError pError | |
local theBehaviorType, theLastLineNo | |
-- put the executioncontexts & cr & cr & pError | |
if the environment is "development" and revAppVersion() is not 0 then | |
if _ControlType() is "table" then put "column" into theBehaviorType | |
else put "row" into theBehaviorType | |
answer "An error has occurred in behavior for the " & theBehaviorType & " template:" & cr & \ | |
revIDELookupError("execution", pError) with "OK" or "Edit Script" | |
if it is "Edit Script" then | |
repeat for each line theLine in pError | |
if there is a (item 4 of theLine) then | |
if theLastLineNo is not an integer then put item 2 of theLine into theLastLineNo | |
showLineOfScript item 4 of theLine, theLastLineNo | |
exit repeat | |
else | |
put item 2 of theLine into theLastLineNo | |
end if | |
end repeat | |
end if | |
throw empty | |
else | |
throw pError | |
end if | |
end _ReportBehaviorError | |
# Parameters | |
# pObject : reference to the object owning the executing code | |
# pLine : the number of line about to be executed | |
# Description | |
# Called when the script editor may need to be updated because a new line | |
# of code is about to be executed in the debugger. If there is no script | |
# editor open for pObject then does nothing. | |
# Otherwise tells the script editor to display the appropriate line etc. | |
private command showLineOfScript pObject, pLine | |
local tScriptEditor | |
put revScriptEditor(the long ID of pObject) into tScriptEditor | |
if tScriptEditor is empty then | |
edit the script of pObject | |
end if | |
put revScriptEditor(the long ID of pObject) into tScriptEditor | |
if tScriptEditor is not empty then | |
local tState | |
put "edit" into tState | |
local tMode | |
send "revSEGetMode" to stack tScriptEditor | |
put the result into tMode | |
if tState is not tMode then | |
# This must be sent in time to allow the script editor a chance to set its current object, | |
# otherwise an infinite loop could occur. | |
send "revSESetMode tState" to stack tScriptEditor in 0 milliseconds | |
end if | |
local tFalseString | |
put "false" into tFalseString | |
# These must be sent in time to allow the script editor a chance to set its current object, | |
# otherwise an infinite loop could occur. | |
send "revSEGoExecutionPoint pObject, pLine, true" to stack tScriptEditor in 0 milliseconds | |
send "revSEUpdate tFalseString" to stack tScriptEditor in 0 milliseconds | |
# OK-2008-08-18 : Bug 6935 - Toplevel / uniconify the script editor here. | |
revGoScriptEditor the name of stack tScriptEditor | |
end if | |
end showLineOfScript | |
private command _CancelMessage pMsg | |
local theMessage,theMessages | |
repeat until (comma & pMsg & comma) is not in the pendingMessages | |
put the pendingMessages into theMessages | |
filter theMessages with "*," & pMsg & ",*" | |
repeat for each line theMessage in theMessages | |
cancel item 1 of theMessage | |
end repeat | |
end repeat | |
end _CancelMessage | |
private command _CacheControls | |
----- | |
local i | |
local msgsAreLocked | |
local theControl | |
local theIndex | |
local theExtraControls | |
local theListGroupRect | |
local theRecordCount | |
local theRect | |
local theSequence | |
local theTemplateGroup | |
local theTopLeft | |
local theControlHeight | |
----- | |
lock screen | |
put the lockMessages into msgsAreLocked | |
## If auto hiding scrollbar then calculate height and hide scrollbar before | |
## caching controls. | |
## Double check in case developer locked messages and set | |
## the dgProps["autohide scrollbar"] | |
if the dgProps["show vscrollbar"] of me is "auto" and the dgProps["fixed row height"] of me then | |
_CalculateFormattedHeight | |
_AutoHideVScrollbar | |
end if | |
put empty into sControlHeights | |
put 0 into sFormattedHeight | |
local controlHeightIsFixed | |
put the dgProps["fixed row height"] of me into controlHeightIsFixed | |
## Setup | |
put the dgProps["row template"] of me into theTemplateGroup | |
put max(0, the number of elements of sDataArray) into theRecordCount | |
put _WorkingGroupRect(the long ID of group "dgList" of me) into theListGroupRect | |
put item 1 to 2 of theListGroupRect into theTopLeft | |
## Delete any extra controls | |
put line (theRecordCount + 1) to -1 of sTableObjectsA["all row controls"] into theExtraControls | |
lock messages | |
repeat for each line theControl in theExtraControls | |
delete theControl | |
end repeat | |
delete line (theRecordCount + 1) to -1 of sTableObjectsA["all row controls"] | |
## Create controls as needed | |
repeat with i = the number of lines of sTableObjectsA["all row controls"] + 1 to theRecordCount | |
copy theTemplateGroup to group "dgList" of me | |
put "control id" && word 3 of it && "of me" & cr after sTableObjectsA["all row controls"] | |
set the name of it to the short name of it && format("%04d", i) | |
## Take over geometry | |
set the lockloc of it to true | |
end repeat | |
if the last char of sTableObjectsA["all row controls"] is cr then delete the last char of sTableObjectsA["all row controls"] | |
unlock messages | |
## Link each index with a control | |
put 0 into theSequence | |
repeat for each line theControl in sTableObjectsA["all row controls"] | |
add 1 to theSequence | |
put theControl into sControlOfIndexA[item theSequence of sIndexSequencing] | |
end repeat | |
## Fill in control data and resize | |
put 0 into theSequence | |
repeat for each item theIndex in sIndexSequencing | |
add 1 to theSequence | |
put sControlOfIndexA[theIndex] into theControl | |
## Set geometry | |
if controlHeightIsFixed then | |
if theControlHeight is empty then | |
## Cache to speed things up | |
put the height of theControl into theControlHeight | |
end if | |
put theTopLeft & comma & item 3 of theListGroupRect & comma & item 2 of theTopLeft + theControlHeight into theRect | |
else | |
put theTopLeft & comma & item 3 of theListGroupRect & comma & item 2 of theTopLeft + the height of theControl into theRect | |
end if | |
lock messages | |
set the dgIndex of theControl to theIndex | |
unlock messages | |
## Get height | |
if sDataArray[theIndex] is NULL then | |
local theDataA | |
GetDataForLine theSequence, theDataA | |
dispatch "CalculateFormattedHeight" to theControl with theDataA | |
if it is "unhandled" then | |
dispatch "FillInData" to theControl with theDataA | |
set the topLeft of theControl to theTopLeft ## After in case FillInData moves stuff around | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
if not controlHeightIsFixed then | |
## LockLoc = true so resize to fit height | |
put item 2 of theRect + the formattedHeight of theControl into item 4 of theRect | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
end if | |
put the height of theControl into sControlHeights[theIndex] | |
else | |
put the result into sControlHeights[theIndex] | |
end if | |
else | |
dispatch "CalculateFormattedHeight" to theControl with sDataArray[theIndex] | |
if it is "unhandled" then | |
dispatch "FillInData" to theControl with sDataArray[theIndex] | |
set the topLeft of theControl to theTopLeft ## After in case FillInData moves stuff around | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
if not controlHeightIsFixed then | |
## LockLoc = true so resize to fit height | |
put item 2 of theRect + the formattedHeight of theControl into item 4 of theRect | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
end if | |
put the height of theControl into sControlHeights[theIndex] | |
else | |
put the result into sControlHeights[theIndex] | |
end if | |
end if | |
## Add to formattedheight | |
add sControlHeights[theIndex] to sFormattedHeight | |
## All controls start off hidden | |
set the visible of theControl to false | |
end repeat | |
if the dgProps["fixed row height"] of me then | |
put sControlHeights[theIndex] into sControlHeights | |
end if | |
_ConfigureScrollbar | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
end _CacheControls | |
## Used when refreshing the list: call LayoutControl but nothing else. | |
## Code is modified version of _CacheControls | |
private command _LayoutCachedControls | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
put empty into sControlHeights | |
put 0 into sFormattedHeight | |
local controlHeightIsFixed, theListGroupRect, theTopLeft | |
put the dgProps["fixed row height"] of me into controlHeightIsFixed | |
put _WorkingGroupRect(the long ID of group "dgList" of me) into theListGroupRect | |
put item 1 to 2 of theListGroupRect into theTopLeft | |
repeat for each item theIndex in sIndexSequencing | |
local theControl, theControlHeight, theRect | |
put sControlOfIndexA[theIndex] into theControl | |
## Set geometry | |
if controlHeightIsFixed then | |
if theControlHeight is empty then | |
## Cache to speed things up | |
put the height of theControl into theControlHeight | |
end if | |
put theTopLeft & comma & item 3 of theListGroupRect & comma & item 2 of theTopLeft + theControlHeight into theRect | |
else | |
put theTopLeft & comma & item 3 of theListGroupRect & comma & item 2 of theTopLeft + the height of theControl into theRect | |
end if | |
set the topLeft of theControl to theTopLeft ## After in case FillInData moves stuff around | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
dispatch "LayoutControl" to theControl with theRect | |
if not controlHeightIsFixed then | |
## LockLoc = true so resize to fit height | |
put item 2 of theRect + the formattedHeight of theControl into item 4 of theRect | |
lock messages | |
set the rect of theControl to theRect | |
unlock messages | |
end if | |
put item 4 of theRect - item 2 of theRect into sControlHeights[theIndex] | |
## Add to formattedheight | |
add sControlHeights[theIndex] to sFormattedHeight | |
end repeat | |
if controlHeightIsFixed then | |
put sControlHeights[theIndex] into sControlHeights | |
end if | |
_ConfigureScrollbar | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
end _LayoutCachedControls | |
private command _HiliteControl pControl, pBoolean | |
if there is a graphic "Background" of pControl then | |
if pBoolean then | |
set the backgroundColor of graphic "Background" of pControl to _GetHiliteColor() | |
else | |
set the wholeMatches to true | |
local theLine | |
put itemOffset(the dgIndex of pControl, sIndexSequencing) into theLine | |
if theLine mod 2 is kAlternatingRowModValue then | |
set the backgroundColor of graphic "Background" of pControl to _GetEffectiveColor("alternate row color") | |
else | |
set the backgroundColor of graphic "Background" of pControl to _GetEffectiveColor("row color") | |
end if | |
end if | |
end if | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
unlock messages | |
set the dgHilite of pControl to pBoolean | |
set the lockMessages to msgsAreLocked | |
end _HiliteControl | |
private function _GetEffectiveColor pProperty | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
local theColor | |
put the dgProps[pProperty] of me into theColor | |
set the lockMessages to msgsAreLocked | |
if theColor is not a color then | |
switch pProperty | |
case "column divider color" | |
return the effective borderColor of me | |
break | |
case "row color" | |
return kRowColor | |
break | |
case "alternate row color" | |
if the dgProps["alternate row colors"] of me then | |
return kAlternateRowColor | |
else | |
return _GetEffectiveColor("row color") | |
end if | |
break | |
case "dimmed hilite color" | |
return kDefaultDimmedHiliteColor | |
break | |
case "hilite color" | |
return the hiliteColor | |
break | |
case "hilited text color" | |
if sSystemA["hilited text color"] is a color and not (the long ID of me is not in the long ID of the focusedObject and the dgProps["dim on focusOut"] of me is not false) then | |
return sSystemA["hilited text color"] | |
else | |
-- Check the RGB values of the hiliteColor; add them | |
-- up and average them - if the result >128 then show black text, and if it's | |
-- <=128 then show white text. (Thanks Ken) | |
local theHiliteColor | |
put _GetHiliteColor("hilite color") into theHiliteColor | |
if the number of items of theHiliteColor is not 3 then put _ColorToRGB(theHiliteColor) into theHiliteColor | |
local theAvg | |
put item 1 of theHiliteColor + item 2 of theHiliteColor + item 3 of theHiliteColor into theAvg | |
put round(theAvg / 3) into theAvg | |
if theAvg > 128 then return "0,0,0" | |
else return "255,255,255" | |
end if | |
break | |
end switch | |
else | |
return theColor | |
end if | |
end _GetEffectiveColor | |
private function _GetHiliteColor | |
## changed in 1.0.0 b4 | |
if the long ID of me is not in the long ID of the focusedObject and the dgProps["dim on focusOut"] of me is not false then | |
local theColor | |
put _GetEffectiveColor("dimmed hilite color") into theColor | |
put the dgProps["dimmed hilite color"] of me into theColor | |
if theColor is empty then | |
put kDefaultDimmedHiliteColor into theColor | |
end if | |
else | |
put the dgProps["hilite color"] of me into theColor | |
if theColor is empty then | |
put the hiliteColor into theColor | |
end if | |
end if | |
return theColor | |
end _GetHiliteColor | |
private function _IsThisModifierSetActive pModifiers | |
replace "alt" with "option" in pModifiers | |
set the wholeMatches to true | |
local theState | |
put the optionKey into theState | |
if "option" is among the items of pModifiers then | |
if theState is "up" then | |
return false | |
end if | |
else if theState is "down" then | |
return false | |
end if | |
put the shiftKey into theState | |
if "shift" is among the items of pModifiers then | |
if theState is "up" then | |
return false | |
end if | |
else if theState is "down" then | |
return false | |
end if | |
if the platform is "MacOS" then | |
put the commandKey into theState | |
if "command" is among the items of pModifiers then | |
if theState is "up" then | |
return false | |
end if | |
else if theState is "down" then | |
return false | |
end if | |
put the controlKey into theState | |
if "control" is among the items of pModifiers then | |
if theState is "up" then | |
return false | |
end if | |
else if theState is "down" then | |
return false | |
end if | |
else | |
put the commandKey into theState | |
if "command" is among the items of pModifiers or "control" is among the items of pModifiers then | |
if theState is "up" then | |
return false | |
end if | |
else if theState is "down" then | |
return false | |
end if | |
end if | |
return true | |
end _IsThisModifierSetActive | |
--> Animation | |
constant kFrameLength = 10 | |
constant kAnimationLength = 300 | |
command StartScrollAnimation pScrollTo | |
if sPendingMsgsA["UpdateScrollAnimation"] is not empty then | |
cancel sPendingMsgsA["UpdateScrollAnimation"] | |
put empty into sPendingMsgsA["UpdateScrollAnimation"] | |
end if | |
put true into sIsAnimating | |
local rightNow, scrollFrom | |
put the milliseconds into rightNow | |
put the dgVScroll of me into scrollFrom | |
ScheduleScrollAnimation scrollFrom, pScrollTo, rightNow, "ease in out", rightNow, rightNow + kAnimationLength | |
end StartScrollAnimation | |
command CancelAnimation | |
put false into sIsAnimating | |
cancel sPendingMsgsA["UpdateScrollAnimation"] | |
put empty into sPendingMsgsA["UpdateScrollAnimation"] | |
end CancelAnimation | |
command ScheduleScrollAnimation pScrollFrom, pScrollTo, pLastTime, pPhase, pPhaseStart, pPhaseEnd | |
if not sIsAnimating then return empty | |
local rightNow, theNextTime | |
put the milliseconds into rightNow | |
## Synchronize to the system clock. | |
put (rightNow - (rightNow mod kFrameLength)) + kFrameLength into theNextTime | |
send "UpdateScrollAnimation pScrollFrom, pScrollTo, pLastTime, pPhase, pPhaseStart, pPhaseEnd" to me in theNextTime - rightNow milliseconds | |
put the result into sPendingMsgsA["UpdateScrollAnimation"] | |
end ScheduleScrollAnimation | |
command UpdateScrollAnimation pScrollFrom, pScrollTo, pLastTime, pPhase, pPhaseStart, pPhaseEnd | |
put empty into sPendingMsgsA["UpdateScrollAnimation"] | |
if not sIsAnimating then return empty | |
local rightNow, theExponent | |
put the milliseconds into rightNow | |
put 2 into theExponent | |
local theValue | |
switch pPhase | |
case "ease in" | |
put round(aeEaseIn(pScrollFrom, pScrollTo, kAnimationLength, pLastTime - pPhaseStart, theExponent)) into theValue | |
break | |
case "ease in out" | |
case "ease in and out" | |
put round(aeEaseInOUt(pScrollFrom, pScrollTo, kAnimationLength, pLastTime - pPhaseStart, theExponent)) into theValue | |
break | |
case "ease out" | |
default | |
put round(aeEaseOut(pScrollFrom, pScrollTo, kAnimationLength, pLastTime - pPhaseStart, theExponent)) into theValue | |
end switch | |
lock screen | |
local theControl | |
put the long ID of the focusedObject into theControl | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
if not msgsAreLocked then lock messages | |
if __HasMobileScroller() then | |
mobileControlSet sScrollerId, "vScroll", theValue | |
end if | |
set the thumbPosition of scrollbar "dgScrollbar" of me to theValue | |
if not msgsAreLocked then unlock messages | |
_ScrollListV theValue | |
## Make sure focus stays with us | |
if the long ID of me is in theControl and (there is not a theControl or the long ID of me is not in the long ID of the focusedobject) then | |
focus on graphic "dgBackground" of me | |
end if | |
unlock screen | |
## Schedule next animation | |
if pScrollFrom > pScrollTo then | |
if theValue > pScrollTo then | |
ScheduleScrollAnimation pScrollFrom, pScrollTo, rightNow, pPhase, pPhaseStart, pPhaseEnd | |
else | |
put false into sIsAnimating | |
end if | |
else | |
if theValue < pScrollTo then | |
ScheduleScrollAnimation pScrollFrom, pScrollTo, rightNow, pPhase, pPhaseStart, pPhaseEnd | |
else | |
put false into sIsAnimating | |
end if | |
end if | |
end UpdateScrollAnimation | |
--> Errors (Private) | |
private command _ThrowError pErrNum, pMsg | |
## error number, line number, col number, message | |
if pErrNum is empty then put 567 into pErrNum ## external error | |
## Build error to throw using executionContexts | |
local theContexts | |
put the executionContexts into theContexts | |
## don't want reference to self | |
delete the last line of theContexts | |
local theContextsA, theCount, theError | |
put _ParseExecutionContexts(theContexts) into theContextsA | |
put item 2 of line 1 of the extents of theContextsA into theCount | |
put format("%u,%u,%u,%s\n", pErrNum, theContextsA[theCount]["line number"], 0, pMsg) after theError | |
repeat with i = theCount down to 1 | |
put format("%u,%u,%u,%s\n", pErrNum, theContextsA[i]["line number"], 0, theContextsA[i]["handler"]) after theError | |
put format("%u,%u,%u,%s\n", pErrNum, theContextsA[i]["line number"], 0, theContextsA[i]["object"]) after theError | |
end repeat | |
delete the last char of theError | |
throw theError | |
end _ThrowError | |
private function _ParseExecutionContexts pContexts | |
----- | |
local theContext | |
local theContextsA | |
local theItem | |
----- | |
## Each line has object-long-id,handler-name,line-number,[parent script object][,] | |
## Beware of quotes and commas in stack path name. They will get you. | |
## How to properly parse? | |
## How many entries? | |
repeat for each line theContext in pContexts | |
local theIndex, theItemNo | |
add 1 to theIndex | |
put 0 into theItemNo | |
repeat for each item theItem in theContext | |
add 1 to theItemNo | |
if theItem is an integer then | |
## found the line number | |
put theItem into theContextsA[theIndex]["line number"] | |
put item (theItemNo - 1) of theContext into theContextsA[theIndex]["handler"] | |
put item 1 to (theItemNo - 2) of theContext into theContextsA[theIndex]["object"] | |
put item (theItemNo + 1) to -1 of theContext into theContextsA[theIndex]["parents"] | |
end if | |
end repeat | |
end repeat | |
return theContextsA | |
end _ParseExecutionContexts | |
--> Line Breaks | |
command TruncateTail pFieldId, pTrailer | |
if pTrailer is empty then | |
put "..." into pTrailer | |
end if | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
local tMaxWidth | |
put the width of field ID pFieldId - the leftMargin of field ID pFieldId into tMaxWidth | |
if the formattedWidth of char 1 to -1 of field ID pFieldId <= tMaxWidth then | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
return false | |
end if | |
local tCharCount | |
put the number of chars of the text of field ID pFieldId into tCharCount | |
local tFieldText | |
put the HTMLText of field ID pFieldId into tFieldText | |
set the text of field ID pFieldId to pTrailer | |
local tTrailerWidth | |
put the formattedWidth of char 1 to -1 of field ID pFieldId into tTrailerWidth | |
set the HTMLText of field ID pFieldId to tFieldText | |
-- The 'linear' version requires n steps, where n is the average number of characters | |
-- that will fit into the field up to tMaxWidth. That version will behave well for small | |
-- widths, but will degrade significantly as the width increases. | |
-- This version requires log_2(n) steps, where n is the number of characters in the field | |
-- this will be much faster for larger widths, but slower for very small widths when the | |
-- line is very long. | |
-- The maximum length of the string we want is tMaxWidth minus the trailer width | |
subtract tTrailerWidth from tMaxWidth | |
-- These two vars store the number of characters before the string that is too small (tLow) | |
-- and the number of chars that makes the string too long (tHigh) | |
local tLow, tHigh | |
put 0 into tLow | |
put tCharCount into tHigh | |
-- tMid contains the current number of characters we are 'guessing' will fit in tWidth. | |
-- We make a initial estimate based on the width of the trailer. This initial estimate should | |
-- virtually eliminate any advantage the linear algorithm has over the binary search in the | |
-- case of a long line fitting into a small width. | |
local tMid | |
put tMaxWidth div (tTrailerWidth div 2) into tMid | |
-- We loop until they are one apart - in which case the char between positions tLow and tHigh | |
-- contains the break width. | |
repeat while tHigh - tLow > 1 | |
-- Compute the width of tMid characters. | |
local tWidth | |
put (the formattedWidth of char 1 to tMid + 1 of field ID pFieldId) into tWidth | |
if tWidth > tMaxWidth then | |
-- If the new width is too much, bring down the high point | |
put tMid into tHigh | |
else if tWidth < tMaxWidth then | |
-- If the new width is too little, bring up the low point | |
put tMid into tLow | |
else | |
-- If we have reached the target width then exit, adjusting by one (we want | |
-- the number of chars that goes from too small, to too much) | |
put tMid into tLow | |
put tMid + 1 into tHigh | |
exit repeat | |
end if | |
if tHigh - tLow <= 1 then | |
exit repeat | |
end if | |
put tLow + (tHigh - tLow) div 2 into tMid | |
end repeat | |
-- At this point tMid will contain the number of chars that causes a transition from too | |
-- short to too long. | |
put pTrailer into char tMid to -1 of field ID pFieldId | |
set the textFont of char tMid to -1 of field ID pFieldId to the effective textFont of field ID pFieldId | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
return true | |
end TruncateTail | |
--> Field Editing | |
local sTemplateFieldEditorA | |
private command _ResetTemplateFieldEditor | |
put "text" into sTemplateFieldEditorA["text type"] | |
put NULL into sTemplateFieldEditorA["text"] | |
put false into sTemplateFieldEditorA["select text"] | |
end _ResetTemplateFieldEditor | |
command DeleteFieldEditorAndOpenNext | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
unlock messages | |
local shiftKeyIsDown, theField, theColumn, theCurrentLineNo, theIndex | |
put _IsThisModifierSetActive("shift") into shiftKeyIsDown | |
put the dgTargetField of sFieldEditor into theField | |
put the dgColumn of the dgDataControl of theField into theColumn | |
put the dgTargetIndex of sFieldEditor into theIndex | |
put the dgLine of the dgDataControl of theField into theCurrentLineNo | |
DeleteFieldEditor true | |
## theField might not be a correct reference at this point as DeleteFieldEditor saves and redraws | |
if the dgIndex of the dgDataControl of theField is not theIndex then | |
put ColumnControlOfIndex(theColumn, theIndex) into theField ## This might not actually be a field. But what to do... | |
end if | |
local theDirection, theColumns, theLineNo | |
if shiftKeyIsDown then put "back" into theDirection | |
else put "forward" into theDirection | |
dispatch "OpenNextFieldEditor" to theField with theDirection | |
local theColNumList | |
if it is not "handled" then | |
## For tables we cycle through columns in current row | |
if _ControlType() is "table" then | |
set the wholeMatches to true | |
put _table.VisibleColumns() into theColumns | |
put lineOffset(theColumn, theColumns) into theLineNo | |
if the number of lines of theColumns > 1 and theLineNo > 0 then | |
## Generate list of column numbers to try and open | |
if theDirection is "forward" then | |
if theLineNo is the number of lines of theColumns then | |
repeat with i = 1 to the number of lines of theColumns | |
put i & comma after theColNumList | |
end repeat | |
delete the last char of theColNumList | |
else | |
repeat with i = theLineNo + 1 to the number of lines of theColumns | |
put i & comma after theColNumList | |
end repeat | |
repeat with i = 1 to theLineNo | |
put i & comma after theColNumList | |
end repeat | |
delete the last char of theColNumList | |
end if | |
else | |
if theLineNo is 1 then | |
put the number of lines of theColumns & comma into theColNumList | |
repeat with i = the number of lines of theColumns - 1 down to 1 | |
put i & comma after theColNumList | |
end repeat | |
delete the last char of theColNumList | |
else | |
put theLineNo - 1 & comma into theColNumList | |
repeat with i = theLineNo - 2 down to 1 | |
put i & comma after theColNumList | |
end repeat | |
repeat with i = the number of lines of theColumns down to theLineNo | |
put i & comma after theColNumList | |
end repeat | |
delete the last char of theColNumList | |
end if | |
end if | |
## Look for next column that supports editing | |
local jumpOutOfRepeat | |
put false into jumpOutOfRepeat | |
repeat for each item theNextColNum in theColNumList | |
local theNextColumn | |
put line theNextColNum of theColumns into theNextColumn | |
if the dgColumnIsEditable[theNextColumn] of me then | |
repeat for each line theControl in sTableObjectsA["columns"][theNextColumn]["row controls"] | |
if the dgLine of the dgDataControl of theControl is theCurrentLineNo then | |
dispatch "EditValue" to theControl | |
if it is "unhandled" then | |
## Try next column | |
next repeat | |
else | |
put true into jumpOutOfRepeat | |
_table.ScrollColumnIntoView theNextColumn | |
exit repeat | |
end if | |
end if | |
end repeat | |
end if | |
if jumpOutOfRepeat then exit repeat | |
end repeat | |
end if | |
end if | |
end if | |
unlock screen | |
set the lockMessages to msgsAreLocked | |
end DeleteFieldEditorAndOpenNext | |
command DeleteFieldEditor pSaveContents | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
if there is not a sFieldEditor then | |
## If error occurred when messages were broadcast below then you could end up with | |
## empty sFieldEditor. We get rid of it here. | |
## We clear out sFieldEditor so that if an error does occur we don't come back later | |
## and save changes. | |
if there is a field kFieldEditorName of group "dgListMask" of me then | |
lock messages | |
delete field kFieldEditorName of group "dgListMask" of me | |
set the lockMessages to msgsAreLocked | |
end if | |
return empty | |
end if | |
put pSaveContents is not false into pSaveContents | |
unlock messages | |
local theEditor, theResult, theField, theError | |
put sFieldEditor into theEditor | |
put empty into sFieldEditor | |
put empty into theResult | |
put the dgTargetField of theEditor into theField | |
## Keeps focus from getting messed up when dgDataOfIndex redraws | |
if theEditor is the long ID of the focusedObject then | |
lock messages | |
focus on graphic "dgBackground" of me ## Engine doesn't like deleting focused control in groups | |
unlock messages | |
end if | |
if pSaveContents and there is a theField then | |
local theIndex, theKey, contentHasChanged | |
put the dgTargetIndex of theEditor into theIndex | |
put the dgTargetKey of theEditor into theKey ## might be an array index | |
set the caseSensitive to true | |
put the HTMLText of theEditor is not the dgOriginalHTMLText of theEditor into contentHasChanged | |
set the caseSensitive to false | |
## Did user want us to automatically save? | |
if theIndex is an integer and (theKey is not empty and theKey is not an array) then | |
if contentHasChanged then | |
## Give the developer a chance to stop value from being set | |
try | |
dispatch "CloseFieldEditor" to theField with theEditor | |
if it is "handled" then | |
put the result into theResult | |
end if | |
if theResult is not "cancel" then | |
local theDataA | |
put sDataArray[theIndex] into theDataA | |
switch the dgTextType of theEditor | |
case "html" | |
put the HTMLText of theEditor into theDataA[theKey] | |
break | |
case "rtf" | |
put the RTFText of theEditor into theDataA[theKey] | |
break | |
case "unicode" | |
put the unicodeText of theEditor into theDataA[theKey] | |
break | |
case "utf8" | |
put the unicodeText of theEditor into theDataA[theKey] | |
put uniDecode(theDataA[theKey], "utf8") into theDataA[theKey] | |
break | |
default | |
put the text of theEditor into theDataA[theKey] | |
end switch | |
set the dgDataOfIndex [theIndex] of me to theDataA | |
end if | |
catch e | |
put e into theError | |
end try | |
else | |
try | |
dispatch "ExitFieldEditor" to theField with theEditor | |
if it is "handled" then | |
put the result into theResult | |
end if | |
catch e | |
put e into theError | |
end try | |
end if | |
else | |
## Announce our closing to the world | |
try | |
if contentHasChanged then | |
dispatch "CloseFieldEditor" to theField with theEditor | |
else | |
dispatch "ExitFieldEditor" to theField with theEditor | |
end if | |
if it is "handled" then | |
put the result into theResult | |
end if | |
catch e | |
put e into theError | |
end try | |
end if | |
end if ## pSaveContents check | |
lock screen | |
if there is a theEditor then delete theEditor | |
set the lockMessages to msgsAreLocked | |
## If user displayed an answer dialog in any callbacks we end | |
## up with unbalanced focusOut/In messages. This causes hilites | |
## to stay grey. Force update | |
put true into sFocusLeftMe | |
_UpdateHiliteColor | |
unlock screen | |
if theError is not empty then throw theError | |
return theResult | |
end DeleteFieldEditor | |
setprop dgTemplateFieldEditor [pProp] pValue | |
switch pProp | |
case "htmltext" | |
put "html" into sTemplateFieldEditorA["text type"] | |
put pValue into sTemplateFieldEditorA["text"] | |
break | |
case "rtftext" | |
put "rtf" into sTemplateFieldEditorA["text type"] | |
put pValue into sTemplateFieldEditorA["text"] | |
break | |
case "text" | |
put "text" into sTemplateFieldEditorA["text type"] | |
put pValue into sTemplateFieldEditorA["text"] | |
break | |
case "unicodetext" | |
put "unicode" into sTemplateFieldEditorA["text type"] | |
put pValue into sTemplateFieldEditorA["text"] | |
break | |
case "utf8text" | |
put "utf8" into sTemplateFieldEditorA["text type"] | |
put pValue into sTemplateFieldEditorA["text"] | |
break | |
case "select text" | |
if pValue is not a boolean then _ThrowError kErrInvalidBoolean, pValue && "is not a boolean" | |
put pValue into sTemplateFieldEditorA["select text"] | |
break | |
default | |
_ThrowError kErrInvalidProperty, pProp && "is an invalid property" | |
end switch | |
return empty | |
end dgTemplateFieldEditor | |
## Pass in pIndex and pKey to automatically update a record | |
## in the data grid. Otherwise update by hand in closeFieldEditor message. | |
command EditFieldText pField, pIndex, pKey | |
----- | |
local i | |
local theBorderWidth | |
local theClickChunk, theClickField, theClickLine | |
local theError | |
local theMargins | |
local theText | |
----- | |
if there is not a pField then _ThrowError kErrCantFindObject, "could not find field to create field editor for" | |
lock screen | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
## Cache where the person clicked as this data will change if | |
## OpenInlineEditor changes position of the target | |
if the clickField is not empty then put the long ID of the clickField into theClickField | |
else put empty into theClickField | |
put the clickCharChunk into theClickChunk | |
put the clickLine into theClickLine | |
focus on nothing | |
## Delete existing | |
DeleteFieldEditor | |
reset the templatefield | |
put the margins of pField into theMargins | |
if the number of items of theMargins is 1 then | |
repeat with i = 1 to 3 | |
put "," & item 1 of theMargins after theMargins | |
end repeat | |
end if | |
put the borderWidth of pField into theBorderWidth | |
if sTemplateFieldEditorA["text"] is NULL then | |
put the text of pField into sTemplateFieldEditorA["text"] ## default | |
end if | |
set the autoTab of the templatefield to the autoTab of pField | |
set the dontWrap of the templatefield to the dontWrap of pField | |
set the tabStops of the templatefield to the tabStops of pField | |
set the margins of the templatefield to theMargins | |
set the borderWidth of the templatefield to theBorderWidth | |
set the textAlign of the templatefield to the effective textAlign of pField | |
set the textFont of the templatefield to the effective textFont of pField | |
set the textSize of the templatefield to the effective textSize of pField | |
set the textStyle of the templatefield to the effective textStyle of pField | |
set the fixedLineHeight of the templatefield to the fixedLineHeight of pField | |
set the opaque of the templatefield to true | |
set the threeD of the templatefield to true | |
create field kFieldEditorName in group "dgListMask" of me | |
put the long ID of it into sFieldEditor | |
set the behavior of sFieldEditor to the long ID of button "Field Editor" of _ResourceStack() | |
set the rect of sFieldEditor to the rect of pField ## so formattedHeight will be correct in _resizeFieldToFitContent | |
switch sTemplateFieldEditorA["text type"] | |
case "rtf" | |
set the RTFText of sFieldEditor to sTemplateFieldEditorA["text"] | |
put the HTMLText of sFieldEditor into theText | |
break | |
case "unicode" | |
set the unicodeText of sFieldEditor to sTemplateFieldEditorA["text"] | |
put the HTMLText of sFieldEditor into theText | |
break | |
case "utf8" | |
set the unicodeText of sFieldEditor to uniEncode(sTemplateFieldEditorA["text"], "utf8") | |
put the HTMLText of sFieldEditor into theText | |
break | |
case "html" | |
put sTemplateFieldEditorA["text"] into theText | |
break | |
case "text" | |
set the text of sFieldEditor to sTemplateFieldEditorA["text"] | |
put the HTMLText of sFieldEditor into theText | |
break | |
end switch | |
set the HTMLText of sFieldEditor to theText | |
set the dgIsFieldEditor of sFieldEditor to true | |
set the dgOriginalHTMLText of sFieldEditor to theText | |
set the dgTextType of sFieldEditor to sTemplateFieldEditorA["text type"] | |
set the dgTargetField of sFieldEditor to pField | |
set the dgTargetIndex of sFieldEditor to pIndex # hilited line can change on selectionchanged occasions | |
set the dgTargetKey of sFieldEditor to pKey | |
unlock messages | |
## Bug when editing right|center-aligned text that is wider than field. | |
if the textAlign of sFieldEditor is not "left" and the formattedWidth of sFieldEditor >= the width of sFieldEditor then | |
set the textAlign of sFieldEditor to "left" | |
end if | |
## Shout our existence out to the world | |
try | |
dispatch "preOpenFieldEditor" to pField with sFieldEditor | |
catch e | |
put e into theError | |
lock messages | |
delete sFieldEditor | |
unlock messages | |
end try | |
if theError is not empty then throw theError | |
lock messages | |
focus on sFieldEditor | |
focus on sFieldEditor ## When tabbing through table cells engine would not focus on sFieldEditor without 2nd call | |
-- put sFieldEditor & cr && the long id of the focusedobject | |
unlock messages | |
if sTemplateFieldEditorA["select text"] is not false then | |
select char 1 to -1 of sFieldEditor | |
else | |
if theClickField is not empty and theClickField is the long ID of pField then | |
if theClickChunk is not empty then | |
select after char (word 2 of theClickChunk) of sFieldEditor | |
else if theClickLine is not empty then | |
select after line (word 2 of theClickLine) of sFieldEditor | |
else | |
select after char -1 of sFieldEditor | |
end if | |
else | |
select after char -1 of sFieldEditor | |
end if | |
end if | |
reset the templatefield | |
_ResetTemplateFieldEditor | |
set the lockMessages to msgsAreLocked | |
unlock screen | |
return empty | |
end EditFieldText | |
--> Private (Persistent Data) | |
## updates data and sequence | |
private command _StorePersistentData | |
if the dgProps["persistent data"] of me then | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
set the dgCache["data"] of me to sDataArray | |
set the dgCache["sequencing"] of me to sIndexSequencing | |
set the lockMessages to msgsAreLocked | |
end if | |
end _StorePersistentData | |
## Just updates the sequence | |
private command _StorePersistentSequence | |
if the dgProps["persistent data"] of me then | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
set the dgCache["sequencing"] of me to sIndexSequencing | |
set the lockMessages to msgsAreLocked | |
end if | |
end _StorePersistentSequence | |
private command _RestorePersistentData | |
if the dgProps["persistent data"] of me then | |
local msgsAreLocked | |
put the lockMessages into msgsAreLocked | |
lock messages | |
put the dgCache["data"] of me into sDataArray | |
put the dgCache["sequencing"] of me into sIndexSequencing | |
set the lockMessages to msgsAreLocked | |
end if | |
end _RestorePersistentData | |
--> Private (Alternating Row Colors) | |
private command _ShowAlternatingRows | |
lock screen | |
local theRect, theStartSequence | |
switch _ControlType() | |
case "table" | |
put the rect of group "dgListMask" of me into theRect | |
add sControlHeights to item 4 of theRect | |
set the rect of graphic "dgAlternatingRows" of me to theRect | |
put sTableObjectsA["base sequence for visible controls"] into theStartSequence | |
if theStartSequence is not an integer then put 1 into theStartSequence | |
if theStartSequence mod 2 is kAlternatingRowModValue then | |
set the top of graphic "dgAlternatingRows" of me to the top of group "dgAlternatingRowsMask" of me - sControlHeights | |
else | |
set the top of graphic "dgAlternatingRows" of me to the top of group "dgAlternatingRowsMask" of me | |
end if | |
set the visible of graphic "dgAlternatingRows" of me to the dgProps["alternate row colors"] of me | |
break | |
case "form" | |
default | |
if the dgProps["alternate row colors"] of me and sFormattedHeight < the height of group "dgList" of me then | |
put the rect of group "dgListMask" of me into theRect | |
put item 2 of theRect + sFormattedHeight into item 2 of theRect | |
set the rect of graphic "dgAlternatingRows" of me to theRect | |
show graphic "dgAlternatingRows" of me | |
else | |
hide graphic "dgAlternatingRows" of me | |
end if | |
end switch | |
unlock screen | |
return empty | |
end _ShowAlternatingRows | |
private command _FillInEmptyRowHeight | |
## Fill in default row height if empty | |
if the dgProps["alternate row colors"] of me and the dgProps["row height"] of me is empty then | |
if sControlHeights is an integer then | |
set the dgProps["row height"] of me to sControlHeights | |
else if the keys of sControlHeights is not empty then | |
set the dgProps["row height"] of me to sControlHeights[1] | |
end if | |
end if | |
return empty | |
end _FillInEmptyRowHeight | |
private command _DrawAlternatingRows | |
----- | |
local theOffset | |
local theOtherColor | |
local theRowColor, theRowHeight | |
local theTemplateGroup | |
local theStartSequence | |
----- | |
put sTableObjectsA["base sequence for visible controls"] into theStartSequence | |
if _ControlType() is "form" then | |
## For forms the alternating lines draw from the bottom of displayed rows down. | |
## That means we need to determine alternating row color from last record down. | |
## Since the rows only show up if there aren't enough controls to fill the space | |
## we can just use the extents of the data array. If it is too high then no big deal. | |
put max(1, the number of elements of sDataArray + 1) into theStartSequence | |
else | |
if theStartSequence is not an integer then | |
put 1 into theStartSequence | |
end if | |
end if | |
_FillInEmptyRowHeight | |
local alternateTheColors | |
put the dgProps["alternate row colors"] of me into alternateTheColors | |
set the visible of graphic "dgAlternatingRows" of me to alternateTheColors | |
if alternateTheColors then | |
local theAlternateRowColor, theWidth, theFirstColor, theSecondColor | |
put _GetEffectiveColor("alternate row color") into theAlternateRowColor | |
put _GetEffectiveColor("row color") into theRowColor | |
put the dgProps["row height"] of me into theRowHeight | |
if theRowHeight is not an integer then put kDefaultRowHeight into theRowHeight | |
put 0 into theOffset | |
put 1 into theWidth | |
if theStartSequence mod 2 is kAlternatingRowModValue then | |
put theAlternateRowColor into theFirstColor | |
put theRowColor into theSecondColor | |
else | |
put theRowColor into theFirstColor | |
put theAlternateRowColor into theSecondColor | |
end if | |
_CreateAlternatingColorImage the long ID of image "dgAlternatingRows" of me, theFirstColor, theSecondColor, \ | |
theRowHeight, theWidth, theOffset | |
set the backgroundPattern of graphic "dgAlternatingRows" of me to empty | |
set the backgroundPattern of graphic "dgAlternatingRows" of me to the ID of image "dgAlternatingRows" of me | |
else | |
set the backgroundPattern of graphic "dgAlternatingRows" of me to empty | |
set the text of image "dgAlternatingRows" of me to empty | |
end if | |
return empty | |
end _DrawAlternatingRows | |
private command _CreateAlternatingColorImage pDestImg, pColor1, pColor2, pRowHeight, pWidth, pOffset | |
----- | |
local theColor1Pixel, theColor2Pixel | |
local theColor1Row, theColor1RowImgData, theColor2Row, theColor2RowImgData, theRow | |
local theOffsetRow | |
local thePixel | |
local theRect | |
----- | |
if pRowHeight is empty then put 18 into pRowHeight | |
if pWidth is empty then put 500 into pWidth | |
put max(0, pOffset) into pOffset | |
## turn strings into arrays | |
split pColor1 by comma | |
split pColor2 by comma | |
## Create a pixel for color 1 and 2 | |
put numToChar(0) into theColor1Pixel | |
put numToChar(pColor1[1]) after theColor1Pixel | |
put numToChar(pColor1[2]) after theColor1Pixel | |
put numToChar(pColor1[3]) after theColor1Pixel | |
put numToChar(0) into theColor2Pixel | |
put numToChar(pColor2[1]) after theColor2Pixel | |
put numToChar(pColor2[2]) after theColor2Pixel | |
put numToChar(pColor2[3]) after theColor2Pixel | |
## build 1 complete row of using pixel | |
repeat with thePixel = 1 to pWidth | |
put theColor1Pixel after theColor1RowImgData | |
put theColor2Pixel after theColor2RowImgData | |
end REPEAT | |
## if pOffset is set then build sliver of row 2 color | |
repeat with theRow = 1 to pOffset | |
put theColor2RowImgData after theOffsetRow | |
end repeat | |
## build color 1 row | |
repeat with theRow = 1 to pRowHeight | |
put theColor1RowImgData after theColor1Row | |
end REPEAT | |
## finish building row 2 color | |
repeat with theRow = 1 to pRowHeight - pOffset | |
put theColor2RowImgData after theColor2Row | |
end repeat | |
put the rect of pDestImg into theRect | |
put item 1 of theRect + pWidth into item 3 of theRect | |
put item 2 of theRect + pRowHeight * 2 into item 4 of theRect | |
## Worka round crash bug when "jpeg" | |
local theOrigCompr | |
put the paintCompression into theOrigCompr | |
set the paintCompression to "rle" | |
lock screen | |
set the rect of pDestImg to theRect | |
set the imageData of pDestImg to theOffsetRow & theColor1Row & theColor2Row | |
unlock screen | |
set the paintCompression to theOrigCompr | |
return empty | |
end _CreateAlternatingColorImage | |
private function __HasMobileScroller | |
return the environment is "mobile" and \ | |
sScrollerId is not empty and \ | |
sScrollerId is among the lines of mobileControls() | |
end __HasMobileScroller |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment