source: trunk/PrefsPane/src/HostTextFieldCell.m @ 1046

Revision 823, 7.6 KB checked in by speck, 3 years ago (diff)

Rule list in filter tab: copy/cut/paste of rules.

Line 
1/* Copyright (C) 2008-2009 Peter Speck
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#import "HostTextFieldCell.h"
18#import "Xml.h"
19#import "Filter.h"
20#import "Rule.h"
21
22@implementation HostTextFieldCell
23
24// Just returning an NSAttributedString in objectValueForTableColumn
25// makes Cocoa draw the text using weird antialiasing - very ugly and the text is not very readable.
26// So the colorized text drawing is done manually in 3 steps rather than using NSAttributedString.
27
28- (void)dealloc
29{
30    [_ruleE release];
31    [_filter release];
32    [super dealloc];
33}
34
35// NSTableView clones the cells when clicking in the text even when the cell isn't editable
36- (id)copyWithZone:(NSZone *)zone
37{
38    HostTextFieldCell* x = [super copyWithZone:zone];
39    [x->_ruleE retain];
40    [x->_filter retain];
41    return x;
42}
43
44- (void)setRuleE:(NSXMLElement*)ruleE filter:(Filter*)filter
45{
46    [_ruleE release];
47    _ruleE = [ruleE retain];
48    [filter release];
49    _filter = [filter retain];
50}
51
52- (NSString*)truncate:(NSString*)s forWidth:(CGFloat)maxWidth withAttributes:(NSDictionary*)dict
53{
54    if ([s sizeWithAttributes:dict].width <= maxWidth)
55        return s;
56    NSInteger len = [s length];
57    for (int idx = 1; idx < len; idx++) {
58        NSString* s2 = [@"…" stringByAppendingString:[s substringFromIndex:idx]];
59        if ([s2 sizeWithAttributes:dict].width <= maxWidth)
60            return s2;
61    }
62    return @"?"; // column narrower than a single char?
63}
64
65- (BOOL)useWhiteInView:(NSView*)controlView
66{
67    NSWindow* window = [controlView window];
68    return [self isHighlighted] && [window isKeyWindow] && [window firstResponder] == controlView;
69}
70
71- (BOOL)isDottedIP:(NSString*)host
72{
73    NSInteger len = [host length];
74    int minDotIndex = 1;
75    for (int i = 0; i < len; i++) {
76        unichar ch = [host characterAtIndex:i];
77        if (ch >= '0' && ch <= '9')
78            continue;
79        if (ch != '.')
80            return NO;
81        if (i < minDotIndex)
82            return NO;
83        minDotIndex = i + 2;
84    }
85    return YES;
86}
87
88static NSColor* darkOrange;
89static NSColor* darkGreen;
90
91static NSMutableDictionary* twoLevelDomains;
92
93static void addDomains(NSString* encoded)
94{
95    for (NSString* s in [encoded componentsSeparatedByString:@";"])
96        [twoLevelDomains setObject:@"1" forKey:s];
97}
98
99- (BOOL)drawColorized:(NSString*)host
100              atPoint:(NSPoint)pt
101       withAttributes:(NSMutableDictionary*)dict
102      withDomainColor:(NSColor*)domainColor
103{
104    if (!twoLevelDomains) {
105        twoLevelDomains = [[NSMutableDictionary dictionaryWithCapacity:10] retain];
106        // tons of magic TLDs:  http://wiki.greasespot.net/Magic_TLD
107        addDomains(@"co.uk;org.uk;edu.uk");
108        addDomains(@"com.au;org.au;edu.au");
109    }
110    NSInteger len = [host length];
111    NSInteger lastDot = len - 1;
112    while (true) {
113        if (lastDot <= 0)
114            return NO;
115        if ([host characterAtIndex:lastDot] == '.')
116            break;
117        lastDot--;
118    }
119    NSInteger firstDot = lastDot - 1;
120    while (true) {
121        if (firstDot < 0)
122            break;
123        if ([host characterAtIndex:firstDot] == '.')
124            break;
125        firstDot--;
126    }
127    if ([twoLevelDomains objectForKey:[host substringFromIndex:firstDot + 1]]) {
128        lastDot = firstDot--;
129        while (true) {
130            if (firstDot < 0)
131                break;
132            if ([host characterAtIndex:firstDot] == '.')
133                break;
134            firstDot--;
135        }
136    }
137    NSString* s1 = (firstDot < 0) ? @"" : [host substringToIndex:firstDot + 1];
138    NSString* s2 = [host substringWithRange:NSMakeRange(firstDot + 1, lastDot - firstDot - 1)];
139    NSString* s3 = [host substringFromIndex:lastDot];
140    //
141    [s1 drawAtPoint:pt withAttributes:dict];
142    pt.x += [s1 sizeWithAttributes:dict].width;
143    //
144    CGFloat w2 = [s2 sizeWithAttributes:dict].width;
145    [s3 drawAtPoint:NSMakePoint(pt.x + w2, pt.y) withAttributes:dict];
146    //
147    [dict setObject:domainColor forKey:NSForegroundColorAttributeName];
148    [s2 drawAtPoint:pt withAttributes:dict];
149    return YES;
150}
151
152static NSString* phoneText = @"\u260E  "; // old-style phone symbol.
153
154- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
155{
156    if (!darkOrange) {
157        darkOrange = [[NSColor colorWithDeviceRed:0.51f green:0.32f blue:0 alpha:1] retain];
158        darkGreen = [[NSColor colorWithDeviceRed:0 green:0.63f blue:0 alpha:1] retain];
159    }
160    NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:3];
161    [dict setObject:[self font] forKey:NSFontAttributeName];
162    NSPoint pt = NSMakePoint(cellFrame.origin.x, cellFrame.origin.y);
163    CGFloat horzSpace = cellFrame.size.width;
164    if ([Xml getBoolAttribute:_ruleE withName:@"mobile"]) {
165        [phoneText drawAtPoint:pt withAttributes:dict];
166        CGFloat w = [phoneText sizeWithAttributes:dict].width;
167        pt.x += w;
168        horzSpace -= w;
169    }
170    RuleType type = [Rule getRuleType:_ruleE];
171    if (type == KeywordRuleType) {
172        NSString* s = soft([Xml getAttribute:_ruleE withName:@"keyword"]);
173        NSString* keywordType = [Xml getAttribute:_ruleE withName:@"keyword-type"];
174        if ([keywordType isEqual:@"starts-with"])
175            s = [s stringByAppendingString:@"*"];
176        s = [self truncate:s forWidth:horzSpace withAttributes:dict];
177        pt.x += horzSpace - [s sizeWithAttributes:dict].width;
178        if ([keywordType isEqual:@"regexp"])
179            [dict setObject:darkOrange forKey:NSForegroundColorAttributeName];
180        else
181            [dict setObject:darkGreen forKey:NSForegroundColorAttributeName];
182        [s drawAtPoint:pt withAttributes:dict];
183        return;
184    }
185    NSString* s = soft([Xml getAttribute:_ruleE withName:@"host"]);
186    NSString* hostType = [Xml getAttribute:_ruleE withName:@"host-type"];
187    if (!hostType && ![s length])
188        s = @"*";
189    else if ([hostType isEqual:@"domain"])
190        s = [NSString stringWithFormat:@"*.%@", s];
191    else if (!s)
192        s = @"?";
193    s = [self truncate:s forWidth:horzSpace withAttributes:dict];
194    pt.x += horzSpace - [s sizeWithAttributes:dict].width;
195    if ([_filter safetySimpleRulesOnly] && ![Rule ruleIsSafe:_ruleE]) {
196        [dict setObject:[NSColor grayColor] forKey:NSForegroundColorAttributeName];
197        [dict setObject:[NSNumber numberWithUnsignedInt:1] forKey:NSStrikethroughStyleAttributeName];
198        [s drawAtPoint:pt withAttributes:dict];
199        return;
200    }
201    if ([hostType isEqual:@"regexp"]) {
202        [dict setObject:darkOrange forKey:NSForegroundColorAttributeName];
203        [s drawAtPoint:pt withAttributes:dict];
204        return;
205    }
206    NSColor* sel = [NSColor alternateSelectedControlColor];
207    sel = [sel colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
208    CGFloat r = [sel redComponent];
209    CGFloat g = [sel greenComponent];
210    CGFloat b = [sel blueComponent];
211    NSColor* domainColor;
212    if (fabs(r - g) < 0.2 && fabs(r - b) < 0.2) {
213        // gray selection color.
214        domainColor = [NSColor blueColor];
215    } else {
216        CGFloat hue, saturation, brightness, alpha;
217        [sel getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha];
218        //NSLog(@"hue: %f,  sat: %f,  bright: %f", hue, saturation, brightness);
219        hue += 0.5f;
220        if (r > 0.9f && g < 0.4f && b < 0.4f) {
221            // red hightlight -> cyan text which is too bright
222            //brightness *= [self useWhiteInView:controlView] ? 0.8f : 0.5f;
223            hue += 0.2f; // make it full blue.
224        }
225        if (hue >= 1)
226            hue -= 1;
227        domainColor = [NSColor colorWithCalibratedHue:hue saturation:saturation brightness:brightness alpha:alpha];
228    }
229    if ([self useWhiteInView:controlView])
230        [dict setObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName];
231    if ([self isDottedIP:s] || ![self drawColorized:s atPoint:pt withAttributes:dict withDomainColor:domainColor])
232        [s drawAtPoint:pt withAttributes:dict];
233}
234
235@end
Note: See TracBrowser for help on using the repository browser.