|
boredzo@2402
|
1 |
//
|
|
boredzo@2402
|
2 |
// GrowlBezelWindowView.m
|
|
boredzo@2402
|
3 |
// Display Plugins
|
|
boredzo@2402
|
4 |
//
|
|
boredzo@2402
|
5 |
// Created by Jorge Salvador Caffarena on 09/09/04.
|
|
boredzo@2402
|
6 |
// Copyright 2004 Jorge Salvador Caffarena. All rights reserved.
|
|
boredzo@2402
|
7 |
//
|
|
boredzo@2402
|
8 |
|
|
boredzo@2402
|
9 |
#import "GrowlBezelWindowView.h"
|
|
boredzo@2402
|
10 |
#import "GrowlBezelPrefs.h"
|
|
boredzo@2402
|
11 |
#import "GrowlImageAdditions.h"
|
|
boredzo@2402
|
12 |
#import "GrowlBezierPathAdditions.h"
|
|
boredzo@2402
|
13 |
|
|
Rudy@4246
|
14 |
#define BORDER_RADIUS 20.0
|
|
boredzo@2402
|
15 |
|
|
boredzo@2402
|
16 |
@implementation GrowlBezelWindowView
|
|
boredzo@2402
|
17 |
|
|
boredzo@2402
|
18 |
- (id) initWithFrame:(NSRect) frame {
|
|
boredzo@2402
|
19 |
if ((self = [super initWithFrame:frame])) {
|
|
boredzo@2402
|
20 |
layoutManager = [[NSLayoutManager alloc] init];
|
|
boredzo@2402
|
21 |
}
|
|
boredzo@2402
|
22 |
return self;
|
|
boredzo@2402
|
23 |
}
|
|
boredzo@2402
|
24 |
|
|
boredzo@2402
|
25 |
- (void) dealloc {
|
|
boredzo@2402
|
26 |
[icon release];
|
|
boredzo@2402
|
27 |
[title release];
|
|
boredzo@2402
|
28 |
[text release];
|
|
boredzo@2402
|
29 |
[textColor release];
|
|
boredzo@2402
|
30 |
[backgroundColor release];
|
|
boredzo@2402
|
31 |
[layoutManager release];
|
|
boredzo@2402
|
32 |
|
|
boredzo@2402
|
33 |
[super dealloc];
|
|
boredzo@2402
|
34 |
}
|
|
boredzo@2402
|
35 |
|
|
Rudy@4246
|
36 |
static void CharcoalShadeInterpolate( void *info, const CGFloat *inData, CGFloat *outData ) {
|
|
Rudy@4246
|
37 |
// const CGFloat colors[2] = {0.15, 0.35};
|
|
Rudy@4246
|
38 |
const CGFloat colors[2] = {27.0 / 255.0 * 1.5, 58.0 / 255.0};
|
|
Rudy@4246
|
39 |
|
|
Rudy@4246
|
40 |
CGFloat a = inData[0] * 2.0;
|
|
Rudy@4246
|
41 |
CGFloat a_coeff;
|
|
Rudy@4246
|
42 |
CGFloat c;
|
|
Rudy@4246
|
43 |
|
|
Rudy@4246
|
44 |
if (a > 1.0)
|
|
Rudy@4246
|
45 |
a = 2.0 - a;
|
|
Rudy@4246
|
46 |
a_coeff = 1.0 - a;
|
|
boredzo@2402
|
47 |
c = a * colors[1] + a_coeff * colors[0];
|
|
boredzo@2402
|
48 |
outData[0] = c;
|
|
boredzo@2402
|
49 |
outData[1] = c;
|
|
boredzo@2402
|
50 |
outData[2] = c;
|
|
Rudy@4246
|
51 |
outData[3] = *(CGFloat *)info;
|
|
boredzo@2402
|
52 |
}
|
|
boredzo@2402
|
53 |
|
|
boredzo@2402
|
54 |
- (void) drawRect:(NSRect)rect {
|
|
ingmarstein@2941
|
55 |
#pragma unused(rect)
|
|
ofri@2708
|
56 |
//Make sure that we don't draw in the main thread
|
|
rudy@2807
|
57 |
//if ([super dispatchDrawingToThread:rect]) {
|
|
ofri@2708
|
58 |
NSRect b = [self bounds];
|
|
ofri@2708
|
59 |
CGRect bounds = CGRectMake(b.origin.x, b.origin.y, b.size.width, b.size.height);
|
|
ingmarstein@2721
|
60 |
|
|
ofri@2708
|
61 |
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
|
ingmarstein@2721
|
62 |
|
|
ofri@2708
|
63 |
addRoundedRectToPath(context, bounds, BORDER_RADIUS);
|
|
ingmarstein@2721
|
64 |
|
|
Rudy@4246
|
65 |
CGFloat opacityPref = BEZEL_OPACITY_DEFAULT;
|
|
rudy@2807
|
66 |
READ_GROWL_PREF_FLOAT(BEZEL_OPACITY_PREF, GrowlBezelPrefDomain, &opacityPref);
|
|
Rudy@4246
|
67 |
CGFloat alpha = opacityPref * 0.01;
|
|
ingmarstein@2721
|
68 |
|
|
ofri@2708
|
69 |
int style = 0;
|
|
rudy@2807
|
70 |
READ_GROWL_PREF_INT(BEZEL_STYLE_PREF, GrowlBezelPrefDomain, &style);
|
|
ofri@2708
|
71 |
switch (style) {
|
|
ofri@2708
|
72 |
default:
|
|
ofri@2708
|
73 |
case 0:
|
|
ofri@2708
|
74 |
// default style
|
|
ofri@2708
|
75 |
[[backgroundColor colorWithAlphaComponent:alpha] set];
|
|
ofri@2708
|
76 |
CGContextFillPath(context);
|
|
ofri@2708
|
77 |
break;
|
|
ofri@2708
|
78 |
case 1:
|
|
ofri@2708
|
79 |
// charcoal
|
|
ofri@2708
|
80 |
CGContextSaveGState(context);
|
|
ofri@2708
|
81 |
CGContextClip(context);
|
|
ingmarstein@2721
|
82 |
|
|
ofri@2708
|
83 |
struct CGFunctionCallbacks callbacks = { 0U, CharcoalShadeInterpolate, NULL };
|
|
ofri@2708
|
84 |
CGFunctionRef function = CGFunctionCreate( &alpha,
|
|
ofri@2708
|
85 |
1U,
|
|
ofri@2708
|
86 |
/*domain*/ NULL,
|
|
ofri@2708
|
87 |
4U,
|
|
ofri@2708
|
88 |
/*range*/ NULL,
|
|
ofri@2708
|
89 |
&callbacks );
|
|
ofri@2708
|
90 |
CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();
|
|
ofri@2708
|
91 |
CGPoint src, dst;
|
|
ofri@2708
|
92 |
src.x = CGRectGetMinX(bounds);
|
|
ofri@2708
|
93 |
src.y = CGRectGetMinY(bounds);
|
|
ofri@2708
|
94 |
dst.x = CGRectGetMaxX(bounds);
|
|
ofri@2708
|
95 |
dst.y = src.y;
|
|
ofri@2708
|
96 |
CGShadingRef shading = CGShadingCreateAxial(cspace, src, dst,
|
|
ofri@2708
|
97 |
function, false, false);
|
|
ingmarstein@2721
|
98 |
|
|
ofri@2708
|
99 |
CGContextDrawShading(context, shading);
|
|
ingmarstein@2721
|
100 |
|
|
ofri@2708
|
101 |
CGShadingRelease(shading);
|
|
ofri@2708
|
102 |
CGColorSpaceRelease(cspace);
|
|
ofri@2708
|
103 |
CGFunctionRelease(function);
|
|
ingmarstein@2721
|
104 |
|
|
ofri@2708
|
105 |
CGContextRestoreGState(context);
|
|
ofri@2708
|
106 |
break;
|
|
ofri@2708
|
107 |
}
|
|
ingmarstein@2721
|
108 |
|
|
ofri@2708
|
109 |
int sizePref = BEZEL_SIZE_NORMAL;
|
|
rudy@2807
|
110 |
READ_GROWL_PREF_INT(BEZEL_SIZE_PREF, GrowlBezelPrefDomain, &sizePref);
|
|
ingmarstein@2721
|
111 |
|
|
ofri@2708
|
112 |
// rects
|
|
ofri@2708
|
113 |
NSRect titleRect, textRect;
|
|
ofri@2708
|
114 |
NSPoint iconPoint;
|
|
ofri@2708
|
115 |
int maxRows;
|
|
ofri@2708
|
116 |
NSSize maxIconSize;
|
|
ofri@2708
|
117 |
if (sizePref == BEZEL_SIZE_NORMAL) {
|
|
Rudy@4246
|
118 |
titleRect.origin.x = 12.0;
|
|
Rudy@4246
|
119 |
titleRect.origin.y = 90.0;
|
|
Rudy@4246
|
120 |
titleRect.size.width = 187.0;
|
|
Rudy@4246
|
121 |
titleRect.size.height = 30.0;
|
|
Rudy@4246
|
122 |
textRect.origin.x = 12.0;
|
|
Rudy@4246
|
123 |
textRect.origin.y = 4.0;
|
|
Rudy@4246
|
124 |
textRect.size.width = 187.0;
|
|
Rudy@4246
|
125 |
textRect.size.height = 80.0;
|
|
ofri@2708
|
126 |
maxRows = 4;
|
|
Rudy@4246
|
127 |
maxIconSize.width = 72.0;
|
|
Rudy@4246
|
128 |
maxIconSize.height = 72.0;
|
|
Rudy@4246
|
129 |
iconPoint.x = 70.0;
|
|
Rudy@4246
|
130 |
iconPoint.y = 120.0;
|
|
ofri@2708
|
131 |
} else {
|
|
Rudy@4246
|
132 |
titleRect.origin.x = 8.0;
|
|
Rudy@4246
|
133 |
titleRect.origin.y = 52.0;
|
|
Rudy@4246
|
134 |
titleRect.size.width = 143.0;
|
|
Rudy@4246
|
135 |
titleRect.size.height = 24.0;
|
|
Rudy@4246
|
136 |
textRect.origin.x = 8.0;
|
|
Rudy@4246
|
137 |
textRect.origin.y = 4.0;
|
|
Rudy@4246
|
138 |
textRect.size.width = 143.0;
|
|
Rudy@4246
|
139 |
textRect.size.height = 49.0;
|
|
ofri@2708
|
140 |
maxRows = 2;
|
|
Rudy@4246
|
141 |
maxIconSize.width = 48.0;
|
|
Rudy@4246
|
142 |
maxIconSize.height = 48.0;
|
|
Rudy@4246
|
143 |
iconPoint.x = 57.0;
|
|
Rudy@4246
|
144 |
iconPoint.y = 83.0;
|
|
ofri@2708
|
145 |
}
|
|
ingmarstein@2721
|
146 |
|
|
ofri@2708
|
147 |
NSShadow *textShadow = [[NSShadow alloc] init];
|
|
Rudy@4246
|
148 |
NSSize shadowSize = {0.0, -2.0};
|
|
ofri@2708
|
149 |
[textShadow setShadowOffset:shadowSize];
|
|
Rudy@4246
|
150 |
[textShadow setShadowBlurRadius:3.0];
|
|
ofri@2708
|
151 |
[textShadow setShadowColor:[NSColor blackColor]];
|
|
ingmarstein@2721
|
152 |
|
|
ofri@2708
|
153 |
// Draw the title, resize if text too big
|
|
Rudy@4246
|
154 |
CGFloat titleFontSize = 20.0;
|
|
ofri@2708
|
155 |
NSMutableParagraphStyle *parrafo = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
|
ofri@2708
|
156 |
[parrafo setAlignment:NSCenterTextAlignment];
|
|
ofri@2708
|
157 |
NSMutableDictionary *titleAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
|
|
ofri@2708
|
158 |
textColor, NSForegroundColorAttributeName,
|
|
ofri@2708
|
159 |
parrafo, NSParagraphStyleAttributeName,
|
|
ofri@2708
|
160 |
[NSFont boldSystemFontOfSize:titleFontSize], NSFontAttributeName,
|
|
ofri@2708
|
161 |
textShadow, NSShadowAttributeName,
|
|
ofri@2708
|
162 |
nil];
|
|
Rudy@4246
|
163 |
CGFloat accumulator = 0.0;
|
|
ofri@2708
|
164 |
BOOL minFontSize = NO;
|
|
ofri@2708
|
165 |
NSSize titleSize = [title sizeWithAttributes:titleAttributes];
|
|
ingmarstein@2721
|
166 |
|
|
Rudy@4246
|
167 |
while (titleSize.width > (NSWidth(titleRect) - (titleSize.height * 0.5))) {
|
|
Rudy@4246
|
168 |
minFontSize = ( titleFontSize < 12.9 );
|
|
ofri@2708
|
169 |
if (minFontSize)
|
|
ofri@2708
|
170 |
break;
|
|
Rudy@4246
|
171 |
titleFontSize -= 1.9;
|
|
Rudy@4246
|
172 |
accumulator += 0.5;
|
|
ofri@2708
|
173 |
[titleAttributes setObject:[NSFont boldSystemFontOfSize:titleFontSize] forKey:NSFontAttributeName];
|
|
ofri@2708
|
174 |
titleSize = [title sizeWithAttributes:titleAttributes];
|
|
ofri@2708
|
175 |
}
|
|
ingmarstein@2721
|
176 |
|
|
Rudy@4246
|
177 |
titleRect.origin.y += GrowlCGFloatCeiling(accumulator);
|
|
ofri@2708
|
178 |
titleRect.size.height = titleSize.height;
|
|
ingmarstein@2721
|
179 |
|
|
ofri@2708
|
180 |
if (minFontSize)
|
|
ofri@2708
|
181 |
[parrafo setLineBreakMode:NSLineBreakByTruncatingTail];
|
|
ofri@2708
|
182 |
[title drawInRect:titleRect withAttributes:titleAttributes];
|
|
ofri@2708
|
183 |
[titleAttributes release];
|
|
ingmarstein@2721
|
184 |
|
|
Rudy@4246
|
185 |
NSFont *textFont = [NSFont systemFontOfSize:14.0];
|
|
ofri@2708
|
186 |
NSMutableDictionary *textAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
|
|
ofri@2708
|
187 |
textColor, NSForegroundColorAttributeName,
|
|
ofri@2708
|
188 |
parrafo, NSParagraphStyleAttributeName,
|
|
ofri@2708
|
189 |
textFont, NSFontAttributeName,
|
|
ofri@2708
|
190 |
textShadow, NSShadowAttributeName,
|
|
ofri@2708
|
191 |
nil];
|
|
ofri@2708
|
192 |
[textShadow release];
|
|
ofri@2708
|
193 |
[parrafo release];
|
|
ingmarstein@2721
|
194 |
|
|
Rudy@4246
|
195 |
CGFloat height = [self descriptionHeight:text attributes:textAttributes width:textRect.size.width];
|
|
Rudy@4246
|
196 |
CGFloat lineHeight = [layoutManager defaultLineHeightForFont:textFont];
|
|
Rudy@4246
|
197 |
NSInteger rowCount = height / lineHeight;
|
|
ingmarstein@2721
|
198 |
|
|
ofri@2708
|
199 |
if (rowCount > maxRows)
|
|
Rudy@4246
|
200 |
[textAttributes setObject:[NSFont systemFontOfSize:12.0] forKey:NSFontAttributeName];
|
|
ofri@2708
|
201 |
[text drawInRect:textRect withAttributes:textAttributes];
|
|
ofri@2708
|
202 |
[textAttributes release];
|
|
ingmarstein@2721
|
203 |
|
|
ofri@2708
|
204 |
NSRect iconRect;
|
|
ofri@2708
|
205 |
iconRect.origin = iconPoint;
|
|
ofri@2708
|
206 |
iconRect.size = maxIconSize;
|
|
ofri@2708
|
207 |
[icon setFlipped:NO];
|
|
Rudy@4246
|
208 |
[icon drawScaledInRect:iconRect operation:NSCompositeSourceOver fraction:1.0];
|
|
bgannin@3983
|
209 |
[super drawRect:rect];
|
|
rudy@2807
|
210 |
//}
|
|
boredzo@2402
|
211 |
}
|
|
boredzo@2402
|
212 |
|
|
boredzo@2402
|
213 |
- (void) setIcon:(NSImage *)anIcon {
|
|
boredzo@2402
|
214 |
[icon release];
|
|
boredzo@2402
|
215 |
icon = [anIcon retain];
|
|
boredzo@2402
|
216 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
217 |
}
|
|
boredzo@2402
|
218 |
|
|
boredzo@2402
|
219 |
- (void) setTitle:(NSString *)aTitle {
|
|
boredzo@2402
|
220 |
[title release];
|
|
boredzo@2402
|
221 |
title = [aTitle copy];
|
|
boredzo@2402
|
222 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
223 |
}
|
|
boredzo@2402
|
224 |
|
|
boredzo@2402
|
225 |
- (void) setText:(NSString *)aText {
|
|
boredzo@2402
|
226 |
[text release];
|
|
boredzo@2402
|
227 |
text = [aText copy];
|
|
boredzo@2402
|
228 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
229 |
}
|
|
boredzo@2402
|
230 |
|
|
boredzo@2402
|
231 |
- (void) setPriority:(int)priority {
|
|
boredzo@2402
|
232 |
NSString *key;
|
|
boredzo@2402
|
233 |
NSString *textKey;
|
|
boredzo@2402
|
234 |
switch (priority) {
|
|
boredzo@2402
|
235 |
case -2:
|
|
boredzo@2402
|
236 |
key = GrowlBezelVeryLowBackgroundColor;
|
|
boredzo@2402
|
237 |
textKey = GrowlBezelVeryLowTextColor;
|
|
boredzo@2402
|
238 |
break;
|
|
boredzo@2402
|
239 |
case -1:
|
|
boredzo@2402
|
240 |
key = GrowlBezelModerateBackgroundColor;
|
|
boredzo@2402
|
241 |
textKey = GrowlBezelModerateTextColor;
|
|
boredzo@2402
|
242 |
break;
|
|
boredzo@2402
|
243 |
case 1:
|
|
boredzo@2402
|
244 |
key = GrowlBezelHighBackgroundColor;
|
|
boredzo@2402
|
245 |
textKey = GrowlBezelHighTextColor;
|
|
boredzo@2402
|
246 |
break;
|
|
boredzo@2402
|
247 |
case 2:
|
|
boredzo@2402
|
248 |
key = GrowlBezelEmergencyBackgroundColor;
|
|
boredzo@2402
|
249 |
textKey = GrowlBezelEmergencyTextColor;
|
|
boredzo@2402
|
250 |
break;
|
|
boredzo@2402
|
251 |
case 0:
|
|
boredzo@2402
|
252 |
default:
|
|
boredzo@2402
|
253 |
key = GrowlBezelNormalBackgroundColor;
|
|
boredzo@2402
|
254 |
textKey = GrowlBezelNormalTextColor;
|
|
boredzo@2402
|
255 |
break;
|
|
boredzo@2402
|
256 |
}
|
|
boredzo@2402
|
257 |
|
|
boredzo@2402
|
258 |
[backgroundColor release];
|
|
boredzo@2402
|
259 |
|
|
boredzo@2402
|
260 |
Class NSDataClass = [NSData class];
|
|
boredzo@2402
|
261 |
NSData *data = nil;
|
|
boredzo@2402
|
262 |
|
|
rudy@2807
|
263 |
READ_GROWL_PREF_VALUE(key, GrowlBezelPrefDomain, NSData *, &data);
|
|
Rudy@4262
|
264 |
if(data)
|
|
Rudy@4262
|
265 |
CFMakeCollectable(data);
|
|
Rudy@4259
|
266 |
if (data && [data isKindOfClass:NSDataClass]) {
|
|
Rudy@4259
|
267 |
backgroundColor = [NSUnarchiver unarchiveObjectWithData:data];
|
|
Rudy@4259
|
268 |
} else {
|
|
boredzo@2402
|
269 |
backgroundColor = [NSColor blackColor];
|
|
Rudy@4259
|
270 |
}
|
|
boredzo@2402
|
271 |
[backgroundColor retain];
|
|
boredzo@2402
|
272 |
[data release];
|
|
boredzo@2402
|
273 |
data = nil;
|
|
boredzo@2402
|
274 |
|
|
boredzo@2402
|
275 |
[textColor release];
|
|
rudy@2807
|
276 |
READ_GROWL_PREF_VALUE(textKey, GrowlBezelPrefDomain, NSData *, &data);
|
|
Rudy@4259
|
277 |
if(data)
|
|
Rudy@4259
|
278 |
CFMakeCollectable(data);
|
|
Rudy@4259
|
279 |
if (data && [data isKindOfClass:NSDataClass]) {
|
|
Rudy@4259
|
280 |
textColor = [NSUnarchiver unarchiveObjectWithData:data];
|
|
Rudy@4259
|
281 |
} else {
|
|
boredzo@2402
|
282 |
textColor = [NSColor whiteColor];
|
|
Rudy@4259
|
283 |
}
|
|
boredzo@2402
|
284 |
[textColor retain];
|
|
boredzo@2402
|
285 |
[data release];
|
|
Rudy@4259
|
286 |
data = nil;
|
|
boredzo@2402
|
287 |
}
|
|
boredzo@2402
|
288 |
|
|
Rudy@4246
|
289 |
- (CGFloat) descriptionHeight:(NSString *)theText attributes:(NSDictionary *)attributes width:(CGFloat)width {
|
|
boredzo@2402
|
290 |
NSSize containerSize;
|
|
boredzo@2402
|
291 |
containerSize.width = width;
|
|
boredzo@2402
|
292 |
containerSize.height = FLT_MAX;
|
|
boredzo@2402
|
293 |
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:theText attributes:attributes];
|
|
boredzo@2402
|
294 |
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
|
|
Rudy@4246
|
295 |
[textContainer setLineFragmentPadding:0.0];
|
|
boredzo@2402
|
296 |
|
|
boredzo@2402
|
297 |
[layoutManager addTextContainer:textContainer];
|
|
boredzo@2402
|
298 |
[textStorage addLayoutManager:layoutManager];
|
|
boredzo@2402
|
299 |
[layoutManager glyphRangeForTextContainer:textContainer]; // force layout
|
|
boredzo@2402
|
300 |
|
|
Rudy@4246
|
301 |
CGFloat textHeight = [layoutManager usedRectForTextContainer:textContainer].size.height;
|
|
boredzo@2402
|
302 |
[textContainer release];
|
|
boredzo@2402
|
303 |
[textStorage release];
|
|
boredzo@2402
|
304 |
|
|
Rudy@4246
|
305 |
return MAX (textHeight, 30.0);
|
|
boredzo@2402
|
306 |
}
|
|
boredzo@2402
|
307 |
|
|
boredzo@2402
|
308 |
#pragma mark -
|
|
boredzo@2402
|
309 |
|
|
boredzo@2402
|
310 |
- (id) target {
|
|
boredzo@2402
|
311 |
return target;
|
|
boredzo@2402
|
312 |
}
|
|
boredzo@2402
|
313 |
|
|
boredzo@2402
|
314 |
- (void) setTarget:(id) object {
|
|
boredzo@2402
|
315 |
target = object;
|
|
boredzo@2402
|
316 |
}
|
|
boredzo@2402
|
317 |
|
|
boredzo@2402
|
318 |
#pragma mark -
|
|
boredzo@2402
|
319 |
|
|
boredzo@2402
|
320 |
- (SEL) action {
|
|
boredzo@2402
|
321 |
return action;
|
|
boredzo@2402
|
322 |
}
|
|
boredzo@2402
|
323 |
|
|
boredzo@2402
|
324 |
- (void) setAction:(SEL) selector {
|
|
boredzo@2402
|
325 |
action = selector;
|
|
boredzo@2402
|
326 |
}
|
|
boredzo@2402
|
327 |
|
|
boredzo@2402
|
328 |
#pragma mark -
|
|
boredzo@2402
|
329 |
|
|
vinay@3127
|
330 |
- (BOOL) showsCloseBox {
|
|
vinay@3127
|
331 |
return NO;
|
|
vinay@3127
|
332 |
}
|
|
vinay@3127
|
333 |
|
|
boredzo@2402
|
334 |
@end
|