続グリッド

これの続き。一応違和感のないくらいには賢くなったはず。

Option キーをこんなに使うのも久しぶりというくらい使いまくり。
ちょっと長過ぎて小汚いコードになってしまった…。

追記: さっき Leopard の Terminal.app で使ってみたら、そもそも Terminal.app が高機能になっていて不要なのと仕様が違うのとで全然使い物になりませんでした。。。

on createOverlappingPair(_first, _second, _xRange)
	script OverlappingPair
		property FirstWindowIndex : _first
		property SecondWindowIndex : _second
		property xRange : _xRange
		to theOtherOneOf(theItem)
			if theItem = SecondWindowIndex then
				return FirstWindowIndex
			else
				return SecondWindowIndex
			end if
		end theOtherOneOf
	end script
	return OverlappingPair
end createOverlappingPair

on calculateOverlappingXRange(theBounds, theBoundsOfAnother)
	if the item 1 of theBounds > the item 3 of theBoundsOfAnother then
		return (the item 3 of theBoundsOfAnother) - (the item 1 of theBounds)
	else if the item 3 of theBounds < the item 1 of theBoundsOfAnother then
		return (the item 3 of theBounds) - (the item 1 of theBoundsOfAnother)
	else if the item 1 of theBounds < the item 3 of theBoundsOfAnother and ¬
		the item 3 of theBounds &#8805; the item 3 of theBoundsOfAnother then
		return (the item 3 of theBoundsOfAnother) - (the item 1 of theBounds)
	else if the item 3 of theBounds > the item 1 of theBoundsOfAnother and ¬
		the item 3 of theBoundsOfAnother &#8805; the item 3 of theBounds then
		return (the item 3 of theBounds) - (the item 1 of theBoundsOfAnother)
	end if
	return 0
end calculateOverlappingXRange

on calculateOverlappingYRange(theBounds, theBoundsOfAnother)
	if the item 2 of theBounds > the item 4 of theBoundsOfAnother then
		return (the item 4 of theBoundsOfAnother) - (the item 2 of theBounds)
	else if the item 4 of theBounds < the item 2 of theBoundsOfAnother then
		return (the item 4 of theBounds) - (the item 2 of theBoundsOfAnother)
	else if the item 2 of theBounds < the item 4 of theBoundsOfAnother and ¬
		the item 4 of theBounds &#8805; the item 4 of theBoundsOfAnother then
		return (the item 4 of theBoundsOfAnother) - (the item 2 of theBounds)
	else if the item 4 of theBounds > the item 2 of theBoundsOfAnother and ¬
		the item 4 of theBoundsOfAnother &#8805; the item 4 of theBounds then
		return (the item 4 of theBounds) - (the item 2 of theBoundsOfAnother)
	end if
	return 0
end calculateOverlappingYRange

on abs(theValue)
	if theValue < 0 then
		return -theValue
	else
		return theValue
	end if
end abs

tell application "Finder" to set theWorkspaceBounds to the bounds of the window of the desktop
tell application "Terminal"
	set theWindows to {}
	set theBoundsList to {}
	set theWindowIndicesSortedByPos to {}
	set theNumberOfWindows to (the count of the window) - 1
	if theNumberOfWindows &#8804; 1 then return
	repeat with theWindow in the windows
		if the index of theWindow > theNumberOfWindows ¬
			then exit repeat
		set theWindows to theWindows & {theWindow}
		set theBounds to the bounds of theWindow
		set theBoundsList to theBoundsList & {theBounds}
		set theWindowIndicesSortedByPos to theWindowIndicesSortedByPos & {0}
	end repeat
	
	set theOverlappingPairs to {}
	set theNearestNeighborXRangeList to {}
	set theWindowGroups to {}
	set theWindowGroupIndices to {}
	set theOverlappingXRange to 0
	repeat with theIndex from 1 to (the count of theBoundsList)
		set theMinimumIndex to 0
		set theMaximumOverlappingXRange to 0
		set theBounds to the item (theIndex) of theBoundsList
		set theMinimumXInThisIteration to 999999
		repeat with theAnotherIndex from 1 to the count of theBoundsList
			set theBoundsOfAnother to the item (theAnotherIndex) of theBoundsList
			if theIndex is not theAnotherIndex then
				set theOverlappingXRange to ¬
					me's calculateOverlappingXRange(theBounds, theBoundsOfAnother)
				if theMaximumOverlappingXRange = 0 or ¬
					theOverlappingXRange > theMaximumOverlappingXRange then
					set theMaximumOverlappingXRange to theOverlappingXRange
				end if
				set theOverlappingPairs to theOverlappingPairs & {¬
					me's createOverlappingPair(theIndex, theAnotherIndex, theOverlappingXRange)}
			end if
			if theAnotherIndex is not in theWindowIndicesSortedByPos and ¬
				theMinimumXInThisIteration &#8805; the item 1 of theBoundsOfAnother then
				set theMinimumIndex to theAnotherIndex
				set theMinimumXInThisIteration to the item 1 of theBoundsOfAnother
			end if
		end repeat
		
		set theWindowGroups to theWindowGroups & {{}}
		set theWindowGroupIndices to theWindowGroupIndices & {0}
		
		repeat until (the count of theNearestNeighborXRangeList) &#8805; theIndex
			set theNearestNeighborXRangeList to theNearestNeighborXRangeList & {null}
		end repeat
		set the item (theIndex) of theNearestNeighborXRangeList to theMaximumOverlappingXRange
		set the item (theIndex) of theWindowIndicesSortedByPos to theMinimumIndex
	end repeat
	
	set theRealNumberOfWindowGroups to 0
	set theAverageWidthOfTheWindow to 0
	
	repeat with theIndex from 1 to the count of theBoundsList
		set unpaired to true
		set theBounds to the item (theIndex) of theBoundsList
		set theNearestNeighborXRange to the item (theIndex) of theNearestNeighborXRangeList
		set theWidthOfTheWindow to the (item 3 of theBounds) - the (item 1 of theBounds)
		set theAverageWidthOfTheWindow to theAverageWidthOfTheWindow + theWidthOfTheWindow
		if theNearestNeighborXRange > theWidthOfTheWindow / 1.5 then
			set theWindowGroupIndex to the item (theIndex) of theWindowGroupIndices
			repeat with thePair in theOverlappingPairs
				if thePair's FirstWindowIndex = theIndex then
					if thePair's xRange &#8805; theNearestNeighborXRange then
						set theIndexOfTheOther to thePair's theOtherOneOf(theIndex)
						set theHeightOfTheWindow to the (item 4 of theBounds) - the (item 2 of theBounds)
						set theWindowGroupIndexOfTheOther to the item (theIndexOfTheOther) of theWindowGroupIndices
						set theOverlappingYRange to ¬
							me's calculateOverlappingYRange(theBounds, ¬
							the item (theIndexOfTheOther) of theBoundsList)
						if theOverlappingYRange < theHeightOfTheWindow / 3 then
							if theWindowGroupIndex = 0 then
								if theWindowGroupIndexOfTheOther ≠ 0 then
									set the item (theWindowGroupIndexOfTheOther) of theWindowGroups to the item (theWindowGroupIndexOfTheOther) of theWindowGroups & {theIndex}
									set the item (theIndex) of the theWindowGroupIndices to theWindowGroupIndexOfTheOther
								else
									set the item (theIndex) of theWindowGroups to the item (theIndex) of theWindowGroups & {theIndex, theIndexOfTheOther}
									set the item (theIndex) of theWindowGroupIndices to theIndex
									set the item (theIndexOfTheOther) of theWindowGroupIndices to theIndex
									set theRealNumberOfWindowGroups to theRealNumberOfWindowGroups + 1
								end if
							else
								if theWindowGroupIndexOfTheOther = 0 then
									set the item (theWindowGroupIndex) of theWindowGroups to the item (theWindowGroupIndex) of theWindowGroups & {theIndexOfTheOther}
									set the item (theIndexOfTheOther) of the theWindowGroupIndices to theWindowGroupIndex
								end if
							end if
							set unpaired to false
						end if
					end if
				end if
			end repeat
		end if
		if unpaired then
			set the item (theIndex) of theWindowGroups to the item (theIndex) of theWindowGroups & {theIndex}
			set the item (theIndex) of the theWindowGroupIndices to theIndex
			set theRealNumberOfWindowGroups to theRealNumberOfWindowGroups + 1
		end if
	end repeat
	set theAverageWidthOfTheWindow to theAverageWidthOfTheWindow / (the count of theBoundsList)
	if theRealNumberOfWindowGroups > 0 then
		set theGridWidth to ((the item 3 of theWorkspaceBounds) - (the item 1 of theWorkspaceBounds) - theAverageWidthOfTheWindow) / (theRealNumberOfWindowGroups - 1)
		if theGridWidth > theAverageWidthOfTheWindow then set theGridWidth to theAverageWidthOfTheWindow
		set theGridIndex to 0
		repeat with theIndex in theWindowIndicesSortedByPos
			set theIndex to the item (theIndex) of theWindowGroupIndices
			if the item (theIndex) of theWindows is not null then
				set theGroup to the item (theIndex) of theWindowGroups
				if the (count of theGroup) > 0 then
					set theBounds to the item (the first item of theGroup) of theBoundsList
					set theWidthOfTheWindow to the (item 3 of theBounds) - (the item 1 of theBounds)
					repeat with theWindowIndex in theGroup
						set theWindow to the item (theWindowIndex) of theWindows
						set thePosition to the position of theWindow
						set the item 1 of thePosition to theGridWidth * theGridIndex + the (item 1 of theWorkspaceBounds)
						set the position of theWindow to thePosition
						set the index of theWindow to theIndex
					end repeat
					set the item (theIndex) of theWindows to null
					set theGridIndex to theGridIndex + 1
				end if
			end if
		end repeat
	end if
end tell