LPARAMETERS tlCopyHorizontally AS Boolean, tnSpacing AS Number #IF .F. PARAMETERS: tlCopyHorizontally (O) - Indicates whether the new controls should be copied and aligned to the right of the selected controls. If omitted, defaults to .F., the controls will be copied below the selected controls. tnSpacing (O) - Indicates how many pixels to leave between the selected group of controls, and the new controls. IF omitted, defaults to 5. USAGE: ON KEY LABEL CTRL+K DO oxCopyCtrl WITH .F., 10 NOTES: We experience a refresh problem with this routine. Although the controls were being copied and repositioned correctly, they often appeared to be cut off (so that only the top or left edge of the group of controls could be seen). Clicking in the form seemed to correct this, but it would be better if the display could be corrected programmatically. #ENDIF *-- When controls are copied and pasted without using the mouse, *-- the default position for the new controls is 8 pixels below *-- and 8 pixels to the right of the position of the selected *-- controls. #DEFINE DEFAULT_LEFT_OFFSET 8 #DEFINE DEFAULT_TOP_OFFSET 8 LOCAL laSelected[1], ; loControl AS Container, ; lnLeft, ; lnRight, ; lnTop, ; lnBottom, ; lnHOffset, ; lnVOffset *-- Check if the user wants to copy the controls horizontally, rather *-- than vertically. tlCopyHorizontally = ( VARTYPE( m.tlCopyHorizontally ) = "L" ; AND m.tlCopyHorizontally ) *-- If a numeric value for spacing was passed, use it, otherwise we'll *-- default to 5 pixels. tnSpacing = IIF( VARTYPE( m.tnSpacing ) = "N", INT( m.tnSpacing ), 5 ) *-- Initialize the position variables so that the first control that's *-- processed will update these variables with it's values. lnLeft = 1000000 lnRight = -1000000 lnTop = 1000000 lnBottom = -1000000 *-- Populate an array with object references for all selected controls. IF ASELOBJ( laSelected ) = 0 *-- Nothing was selected, so there's nothing to do. RETURN ENDIF *-- Determine the outer boundaries for the group of controls. FOR EACH loControl IN laSelected lnLeft = MIN( m.lnLeft, m.loControl.Left ) lnRight = MAX( m.lnRight, m.loControl.Left + m.loControl.Width ) lnTop = MIN( m.lnTop, m.loControl.Top ) lnBottom = MAX( m.lnBottom, m.loControl.Top + m.loControl.Height ) ENDFOR *-- Clear this variable, just to make sure we don't leave behind any *-- dangling object references. loControl = .NULL. IF m.tlCopyHorizontally *-- The controls are to copied horizontally (to the right), so the *-- new controls need to be moved to the right by the total width of *-- the selected *group* of controls, minus the distance to the right *-- that the new controls will have already moved, plus the spacing *-- that was specified. lnHOffset = m.lnRight - m.lnLeft - DEFAULT_LEFT_OFFSET + m.tnSpacing *-- Move the controls back up by the amount that they will have been *-- moved down, so that they align vertically with the original *-- controls. lnVOffset = -1 * DEFAULT_TOP_OFFSET ELSE *-- Move the controls back to the left by the amount that they will *-- have been moved to the right, so that they align horizontally with *-- the original controls. lnHOffset = -1 * DEFAULT_LEFT_OFFSET *-- The controls are to copied vertically (down), so the new controls *-- need to be moved down by the total height of the selected *group* *-- of controls, minus the distance down that the new controls will *-- have already moved, plus the spacing that was specified. lnVOffset = m.lnBottom - m.lnTop - DEFAULT_TOP_OFFSET + m.tnSpacing ENDIF *-- Flush the keyboard buffer, and copy the selected controls. KEYBOARD '{MOUSE}' PLAIN CLEAR SYS( 1500, "_MED_COPY", "_MEDIT" ) *-- We seem to need a pause here while the clipboard is being populated. *-- A half-second worked well for me, but your results may vary. WAIT WINDOW "" NOCLEAR TIMEOUT .5 *-- Flush the keyboard buffer again, and paste the copied controls. KEYBOARD '{MOUSE}' PLAIN CLEAR SYS( 1500, "_MED_PASTE", "_MEDIT" ) *-- A pause after the paste also seems to be needed. WAIT WINDOW "" NOCLEAR TIMEOUT .5 *-- The controls that were just pasted will now be the selected controls. *-- Get the object references for them into an array so that we can scan *-- through and adjust the positions of the controls. ASELOBJ( laSelected ) FOR EACH loControl IN laSelected loControl.Left = m.loControl.Left + m.lnHOffset loControl.Top = m.loControl.Top + m.lnVOffset ENDFOR *-- Mike Yearwood - Feb 21, 2006 *-- Setting focus to anything on the form/class designer *-- seems to remove the refreshing problems. *!* m.loControl.Parent.SetFocus() *-- Mike Yearwood - March 12, 2006 *-- Better yet, how about setting focus to the first *-- control in the new set. FOR EACH m.loControl IN laSelected IF PEMSTATUS(m.loControl,"SETFOCUS",5) m.loControl.SetFocus() EXIT ENDIF ENDFOR EACH m.loControl *-- Clear this variable, just to make sure we don't leave behind any *-- dangling object references. loControl = .NULL.