1//
2//  PXFillTool.m
3//  Pixen-XCode
4//
5//  Created by Joe Osborn on Tue Nov 18 2003.
6//  Copyright (c) 2003 Open Sword Group. All rights reserved.
7//
8
9// This code is based on some from Will Leshner. Thanks, man!
10
11#import "PXFillTool.h"
12#import "PXCanvas.h"
13#import "PXCanvasController.h"
14#import "PXPoint.h"
15
16@implementation PXFillTool
17
18- (NSString *)name
19{
20	return NSLocalizedString(@"FILL_NAME", @"Fill Tool");
21}
22
23- actionName
24{
25    return NSLocalizedString(@"FILL_ACTION", @"Fill");
26}
27
28- color
29{
30    return color;
31}
32
33- (void)setColor:aColor
34{
35    color = aColor;
36}
37
38- (BOOL)shouldAbandonFillingAtPoint:(NSPoint)aPoint fromCanvasController:controller
39{
40	if([[self color] isEqual:[[controller canvas] colorAtPoint:aPoint]]) { return YES; }
41	//this is all to dodge the clear-on-clear bug.
42	if([[self color] alphaComponent] < .00125)
43	{
44		if([[controller canvas] colorAtPoint:aPoint] == nil) { return YES; }
45		if([[[controller canvas] colorAtPoint:aPoint] alphaComponent] < .00125) { return YES; }
46	}
47	return NO;
48}
49
50- (void)mouseDownAt:(NSPoint)aPoint fromCanvasController:controller
51{
52	if([self shouldAbandonFillingAtPoint:aPoint fromCanvasController:controller]) { return; }
53    [[self undoManager] setActionName:[self actionName]];
54    [[self undoManager] beginUndoGrouping];
55    [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
56    [self fillAtPoint:aPoint inCanvas:[controller canvas] replacingColor:[[controller canvas] colorAtPoint:aPoint] withColor:[self color]];
57    if([[self undoManager] groupingLevel] != 0)	{ [[self undoManager] endUndoGrouping];	}
58}
59
60- (void)registerUndoForReplacingColor:oldColor withColor:newColor atPoints:points inLayer:aLayer ofCanvas:aCanvas
61{
62    [[[self undoManager] prepareWithInvocationTarget:self] replaceColor:newColor withColor:oldColor atPoints:points inLayer:aLayer ofCanvas:aCanvas];
63    [[[self undoManager] prepareWithInvocationTarget:aCanvas] changedInRect:NSMakeRect(0, 0, [aCanvas size].width, [aCanvas size].height)];
64}
65
66- (void)replaceColor:oldColor withColor:newColor atPoints:points inLayer:aLayer ofCanvas:aCanvas
67{
68    if([[self undoManager] isUndoing] || [[self undoManager] isRedoing]) { [self registerUndoForReplacingColor:oldColor withColor:newColor atPoints:points inLayer:aLayer ofCanvas:aCanvas]; }
69    [aLayer setColor:newColor atPoints:points];
70}
71
72- (BOOL)point:(NSPoint)aPoint isUsefulForReplacing:oldColor inCanvas:aCanvas shouldCheckToTheLeft:(BOOL)check
73{
74    return
75	([[NSUserDefaults standardUserDefaults] boolForKey:@"PXShouldTile"] ? YES : (aPoint.y < [aCanvas size].height)) &&
76	([[NSUserDefaults standardUserDefaults] boolForKey:@"PXShouldTile"] ? YES : (aPoint.y >= 0)) &&
77	([[aCanvas colorAtPoint:aPoint] isEqual:oldColor] &&
78	(check ? ![[aCanvas colorAtPoint:NSMakePoint(aPoint.x-1, aPoint.y)] isEqual:oldColor] : YES) &&
79	([aCanvas canDrawAtPoint:aPoint]));
80}
81
82- (void)lookAround:(NSPoint)currentPoint inCanvas:aCanvas replacingColor:oldColor shouldCheckToTheLeft:(BOOL)check newPointsInto:points
83{
84    NSPoint upPoint = NSMakePoint(currentPoint.x, currentPoint.y+1);
85    if([self point:upPoint isUsefulForReplacing:oldColor inCanvas:aCanvas shouldCheckToTheLeft:check])
86    {
87        [points addObject:[PXPoint withNSPoint:upPoint]];
88    }
89    NSPoint downPoint = NSMakePoint(currentPoint.x, currentPoint.y-1);
90    if([self point:downPoint isUsefulForReplacing:oldColor inCanvas:aCanvas shouldCheckToTheLeft:check])
91    {
92        [points addObject:[PXPoint withNSPoint:downPoint]];
93    }
94}
95
96- (NSPoint)leftmostValidPointInRowOf:(NSPoint)point replacingColor:oldColor inCanvas:aCanvas
97{
98    NSPoint leftmost = point;
99	int columnsChecked = 0;
100    while(([[NSUserDefaults standardUserDefaults] boolForKey:@"PXShouldTile"] ? (columnsChecked < [aCanvas size].width) : leftmost.x >= 0) &&
101		  [[aCanvas colorAtPoint:NSMakePoint(leftmost.x-1, leftmost.y)] isEqual:oldColor])
102	{
103		leftmost.x--;
104		columnsChecked++;
105	}
106    return leftmost;
107}
108
109- drawLineStartingAtPoint:(NSPoint)currentPoint inCanvas:aCanvas replacingColor:oldColor withColor:newColor newPointsInto:points
110{
111    id thisTimeFilled = [[[NSMutableArray alloc] initWithCapacity:2048] autorelease];
112    [self lookAround:currentPoint inCanvas:aCanvas replacingColor:oldColor shouldCheckToTheLeft:NO newPointsInto:points];
113	int columnsChecked = 0;
114    while(([[NSUserDefaults standardUserDefaults] boolForKey:@"PXShouldTile"] ? (columnsChecked < [aCanvas size].width) : currentPoint.x < [aCanvas size].width) && [[aCanvas colorAtPoint:currentPoint] isEqual:oldColor])
115    {
116        [self lookAround:currentPoint inCanvas:aCanvas replacingColor:oldColor shouldCheckToTheLeft:YES newPointsInto:points];
117		if([aCanvas canDrawAtPoint:currentPoint]) { [thisTimeFilled addObject:[PXPoint withNSPoint:currentPoint]]; }
118        currentPoint.x++;
119		columnsChecked++;
120    }
121    [self activatePointWithOldColor:oldColor newColor:newColor atPoints:thisTimeFilled ofCanvas:aCanvas];
122    return thisTimeFilled;
123}
124
125- (void)activatePointWithOldColor:oldColor newColor:newColor atPoints:thisTimeFilled ofCanvas:aCanvas
126{
127    [self replaceColor:oldColor withColor:newColor atPoints:thisTimeFilled inLayer:[aCanvas activeLayer] ofCanvas:aCanvas];
128}
129
130- (void)fillAtPoint:(NSPoint)aPoint inCanvas:aCanvas replacingColor:oldColor withColor:newColor
131{
132    id points = [[[NSMutableArray alloc] initWithCapacity:50000] autorelease];
133    id filledPoints = [[[NSMutableArray alloc] initWithCapacity:50000] autorelease];
134    [points addObject:[PXPoint withNSPoint:aPoint]];
135    while([points count] > 0)
136    {
137        NSPoint current = [[points lastObject] pointValue];
138        [points removeLastObject];
139        [filledPoints addObjectsFromArray:
140            [self drawLineStartingAtPoint:[self leftmostValidPointInRowOf:current replacingColor:oldColor inCanvas:aCanvas]
141                                 inCanvas:aCanvas
142                           replacingColor:oldColor
143                                withColor:newColor
144                            newPointsInto:points]];
145    }
146    [aCanvas changedInRect:NSMakeRect(0, 0, [aCanvas size].width, [aCanvas size].height)];
147    [self registerUndoForReplacingColor:oldColor withColor:newColor atPoints:filledPoints inLayer:[aCanvas activeLayer] ofCanvas:aCanvas];
148}
149
150@end
151