Mercurial > projects > dwt-mac
comparison dwt/graphics/Region.d @ 34:5123b17c98ef
Ported dwt.events.*, dwt.graphics.GC, Region, dwt.internal.image.*
author | Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com> |
---|---|
date | Sun, 14 Sep 2008 01:45:57 +0200 |
parents | a9ab4c738ed8 |
children | db5a898b2119 |
comparison
equal
deleted
inserted
replaced
33:965ac0a77267 | 34:5123b17c98ef |
---|---|
5 * which accompanies this distribution, and is available at | 5 * which accompanies this distribution, and is available at |
6 * http://www.eclipse.org/legal/epl-v10.html | 6 * http://www.eclipse.org/legal/epl-v10.html |
7 * | 7 * |
8 * Contributors: | 8 * Contributors: |
9 * IBM Corporation - initial API and implementation | 9 * IBM Corporation - initial API and implementation |
10 * | |
11 * Port to the D programming language: | |
12 * Jacob Carlborg <jacob.carlborg@gmail.com> | |
10 *******************************************************************************/ | 13 *******************************************************************************/ |
11 module dwt.graphics.Region; | 14 module dwt.graphics.Region; |
12 | |
13 import dwt.dwthelper.utils; | |
14 | 15 |
15 | 16 |
16 import dwt.DWT; | 17 import dwt.DWT; |
17 import dwt.DWTError; | 18 import dwt.DWTError; |
18 import dwt.DWTException; | 19 import dwt.DWTException; |
19 import dwt.internal.Callback; | |
20 import dwt.internal.cocoa.NSAffineTransform; | 20 import dwt.internal.cocoa.NSAffineTransform; |
21 import dwt.internal.cocoa.NSBezierPath; | 21 import dwt.internal.cocoa.NSBezierPath; |
22 import dwt.internal.cocoa.NSPoint; | 22 import dwt.internal.cocoa.NSPoint; |
23 import dwt.internal.cocoa.NSRect; | 23 import dwt.internal.cocoa.NSRect; |
24 import dwt.internal.cocoa.OS; | 24 import dwt.internal.cocoa.OS; |
25 | |
26 import tango.text.convert.Format; | |
27 | |
28 import dwt.dwthelper.utils; | |
29 import dwt.graphics.Device; | |
30 import dwt.graphics.Point; | |
31 import dwt.graphics.Rectangle; | |
32 import dwt.graphics.Resource; | |
33 import dwt.internal.c.qd.Quickdraw; | |
34 import dwt.internal.c.qd.QuickdrawTypes; | |
25 | 35 |
26 /** | 36 /** |
27 * Instances of this class represent areas of an x-y coordinate | 37 * Instances of this class represent areas of an x-y coordinate |
28 * system that are aggregates of the areas covered by a number | 38 * system that are aggregates of the areas covered by a number |
29 * of polygons. | 39 * of polygons. |
42 * public API. It is marked public only so that it can be shared | 52 * public API. It is marked public only so that it can be shared |
43 * within the packages provided by DWT. It is not available on all | 53 * within the packages provided by DWT. It is not available on all |
44 * platforms and should never be accessed from application code. | 54 * platforms and should never be accessed from application code. |
45 * </p> | 55 * </p> |
46 */ | 56 */ |
47 public int handle; | 57 public RgnHandle handle; |
48 | 58 |
49 /** | 59 /** |
50 * Constructs a new empty region. | 60 * Constructs a new empty region. |
51 * | 61 * |
52 * @exception DWTError <ul> | 62 * @exception DWTError <ul> |
77 * @since 3.0 | 87 * @since 3.0 |
78 */ | 88 */ |
79 public this(Device device) { | 89 public this(Device device) { |
80 super(device); | 90 super(device); |
81 handle = OS.NewRgn(); | 91 handle = OS.NewRgn(); |
82 if (handle is 0) DWT.error(DWT.ERROR_NO_HANDLES); | 92 if (handle is null) DWT.error(DWT.ERROR_NO_HANDLES); |
83 init(); | 93 init(); |
84 } | 94 } |
85 | 95 |
86 this(Device device, int handle) { | 96 this(Device device, RgnHandle handle) { |
87 super(device); | 97 super(device); |
88 this.handle = handle; | 98 this.handle = handle; |
89 } | 99 } |
90 | 100 |
91 /** | 101 /** |
110 add(pointArray, pointArray.length); | 120 add(pointArray, pointArray.length); |
111 } | 121 } |
112 | 122 |
113 void add(int[] pointArray, int count) { | 123 void add(int[] pointArray, int count) { |
114 if (count <= 2) return; | 124 if (count <= 2) return; |
115 int polyRgn = OS.NewRgn(); | 125 RgnHandle polyRgn = OS.NewRgn(); |
116 OS.OpenRgn(); | 126 OS.OpenRgn(); |
117 OS.MoveTo(cast(short)pointArray[0], cast(short)pointArray[1]); | 127 OS.MoveTo(cast(short)pointArray[0], cast(short)pointArray[1]); |
118 for (int i = 1; i < count / 2; i++) { | 128 for (int i = 1; i < count / 2; i++) { |
119 OS.LineTo(cast(short)pointArray[2 * i], cast(short)pointArray[2 * i + 1]); | 129 OS.LineTo(cast(short)pointArray[2 * i], cast(short)pointArray[2 * i + 1]); |
120 } | 130 } |
164 * @since 3.1 | 174 * @since 3.1 |
165 */ | 175 */ |
166 public void add(int x, int y, int width, int height) { | 176 public void add(int x, int y, int width, int height) { |
167 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); | 177 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); |
168 if (width < 0 || height < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | 178 if (width < 0 || height < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); |
169 int rectRgn = OS.NewRgn(); | 179 RgnHandle rectRgn = OS.NewRgn(); |
170 short[] r = new short[4]; | 180 Rect r; |
171 OS.SetRect(r, cast(short)x, cast(short)y, cast(short)(x + width),cast(short)(y + height)); | 181 OS.SetRect(&r, cast(short)x, cast(short)y, cast(short)(x + width),cast(short)(y + height)); |
172 OS.RectRgn(rectRgn, r); | 182 OS.RectRgn(rectRgn, &r); |
173 OS.UnionRgn(handle, rectRgn, handle); | 183 OS.UnionRgn(handle, rectRgn, handle); |
174 OS.DisposeRgn(rectRgn); | 184 OS.DisposeRgn(rectRgn); |
175 } | 185 } |
176 | 186 |
177 /** | 187 /** |
209 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | 219 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> |
210 * </ul> | 220 * </ul> |
211 */ | 221 */ |
212 public bool contains(int x, int y) { | 222 public bool contains(int x, int y) { |
213 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); | 223 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); |
214 short[] point = new short[]{cast(short)x, cast(short)y}; | 224 Point point = Point(cast(short)x, cast(short)y); |
215 return OS.PtInRgn(point, handle); | 225 return OS.PtInRgn(point, handle); |
216 } | 226 } |
217 | 227 |
218 /** | 228 /** |
219 * Returns <code>true</code> if the given point is inside the | 229 * Returns <code>true</code> if the given point is inside the |
235 return contains(pt.x, pt.y); | 245 return contains(pt.x, pt.y); |
236 } | 246 } |
237 | 247 |
238 NSAffineTransform transform; | 248 NSAffineTransform transform; |
239 void convertRgn(NSAffineTransform transform) { | 249 void convertRgn(NSAffineTransform transform) { |
240 int newRgn = OS.NewRgn(); | 250 RgnHandle newRgn = OS.NewRgn(); |
241 Callback callback = new Callback(this, "convertRgn", 4); | 251 RegionToRectsUPP proc = &convertRgn; |
242 int proc = callback.getAddress(); | |
243 if (proc is 0) DWT.error(DWT.ERROR_NO_MORE_CALLBACKS); | |
244 this.transform = transform; | 252 this.transform = transform; |
245 OS.QDRegionToRects(handle, OS.kQDParseRegionFromTopLeft, proc, newRgn); | 253 OS.QDRegionToRects(handle, OS.kQDParseRegionFromTopLeft, proc, newRgn); |
246 this.transform = null; | 254 this.transform = null; |
247 callback.dispose(); | |
248 OS.CopyRgn(newRgn, handle); | 255 OS.CopyRgn(newRgn, handle); |
249 OS.DisposeRgn(newRgn); | 256 OS.DisposeRgn(newRgn); |
250 } | 257 } |
251 | 258 |
252 int convertRgn(int message, int rgn, int r, int newRgn) { | 259 extern(C) private static OSStatus* convertRgn(ushort message, RgnHandle rgn, Rect* r, void* newRgn) { |
253 if (message is OS.kQDRegionToRectsMsgParse) { | 260 if (message is OS.kQDRegionToRectsMsgParse) { |
254 short[] rect = new short[4]; | 261 Rect rect; |
255 OS.memmove(rect, r, rect.length * 2); | 262 OS.memmove(&rect, r, rect.sizeof); |
256 NSPoint point = new NSPoint(); | 263 NSPoint point = NSPoint(); |
257 int polyRgn = OS.NewRgn(); | 264 RgnHandle polyRgn = OS.NewRgn(); |
258 OS.OpenRgn(); | 265 OS.OpenRgn(); |
259 point.x = rect[1]; | 266 point.x = rect.left; |
260 point.y = rect[0]; | 267 point.y = rect.top; |
261 point = transform.transformPoint(point); | 268 point = transform.transformPoint(point); |
262 short startX, startY; | 269 short startX, startY; |
263 OS.MoveTo(startX = cast(short)point.x, startY = cast(short)point.y); | 270 OS.MoveTo(startX = cast(short)point.x, startY = cast(short)point.y); |
264 point.x = rect[3]; | 271 point.x = rect.right; |
265 point.y = rect[0]; | 272 point.y = rect.top; |
266 point = transform.transformPoint(point); | 273 point = transform.transformPoint(point); |
267 OS.LineTo(cast(short)Math.round(point.x), cast(short)point.y); | 274 OS.LineTo(cast(short)Math.round(point.x), cast(short)point.y); |
268 point.x = rect[3]; | 275 point.x = rect.right; |
269 point.y = rect[2]; | 276 point.y = rect.bottom; |
270 point = transform.transformPoint(point); | 277 point = transform.transformPoint(point); |
271 OS.LineTo(cast(short)Math.round(point.x), cast(short)Math.round(point.y)); | 278 OS.LineTo(cast(short)Math.round(point.x), cast(short)Math.round(point.y)); |
272 point.x = rect[1]; | 279 point.x = rect.left; |
273 point.y = rect[2]; | 280 point.y = rect.bottom; |
274 point = transform.transformPoint(point); | 281 point = transform.transformPoint(point); |
275 OS.LineTo(cast(short)point.x, cast(short)Math.round(point.y)); | 282 OS.LineTo(cast(short)point.x, cast(short)Math.round(point.y)); |
276 OS.LineTo(startX, startY); | 283 OS.LineTo(startX, startY); |
277 OS.CloseRgn(polyRgn); | 284 OS.CloseRgn(polyRgn); |
278 OS.UnionRgn(newRgn, polyRgn, newRgn); | 285 OS.UnionRgn(newRgn, polyRgn, newRgn); |
279 OS.DisposeRgn(polyRgn); | 286 OS.DisposeRgn(polyRgn); |
280 } | 287 } |
281 return 0; | 288 return null; |
282 } | 289 } |
283 | 290 |
284 void destroy() { | 291 void destroy() { |
285 OS.DisposeRgn(handle); | 292 OS.DisposeRgn(handle); |
286 handle = 0; | 293 handle = 0; |
294 * @param object the object to compare with this object | 301 * @param object the object to compare with this object |
295 * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise | 302 * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise |
296 * | 303 * |
297 * @see #hashCode | 304 * @see #hashCode |
298 */ | 305 */ |
299 public bool equals(Object object) { | 306 public int opEquals(Object object) { |
300 if (this is object) return true; | 307 if (this is object) return true; |
301 if (!( null !is cast(Region)object )) return false; | 308 if (!( null !is cast(Region)object )) return false; |
302 Region region = cast(Region)object; | 309 Region region = cast(Region)object; |
303 return handle is region.handle; | 310 return handle is region.handle; |
304 } | 311 } |
305 | 312 |
313 alias opEquals equals; | |
314 | |
306 /** | 315 /** |
307 * Returns a rectangle which represents the rectangular | 316 * Returns a rectangle which represents the rectangular |
308 * union of the collection of polygons the receiver | 317 * union of the collection of polygons the receiver |
309 * maintains to describe its area. | 318 * maintains to describe its area. |
310 * | 319 * |
316 * | 325 * |
317 * @see Rectangle#union | 326 * @see Rectangle#union |
318 */ | 327 */ |
319 public Rectangle getBounds() { | 328 public Rectangle getBounds() { |
320 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); | 329 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); |
321 short[] bounds = new short[4]; | 330 Rect bounds; |
322 OS.GetRegionBounds(handle, bounds); | 331 OS.GetRegionBounds(handle, &bounds); |
323 int width = bounds[3] - bounds[1]; | 332 int width = bounds.right - bounds.left; |
324 int height = bounds[2] - bounds[0]; | 333 int height = bounds.bottom - bounds.left; |
325 return new Rectangle(bounds[1], bounds[0], width, height); | 334 return new Rectangle(bounds.left, bounds.top, width, height); |
326 } | 335 } |
327 | 336 |
328 NSBezierPath getPath() { | 337 NSBezierPath getPath() { |
329 Callback callback = new Callback(this, "regionToRects", 4); | |
330 if (callback.getAddress() is 0) DWT.error(DWT.ERROR_NO_MORE_CALLBACKS); | |
331 NSBezierPath path = NSBezierPath.bezierPath(); | 338 NSBezierPath path = NSBezierPath.bezierPath(); |
332 path.retain(); | 339 path.retain(); |
333 OS.QDRegionToRects(handle, OS.kQDParseRegionFromTopLeft, callback.getAddress(), path.id); | 340 OS.QDRegionToRects(handle, OS.kQDParseRegionFromTopLeft, ®ionToRects, path.id_); |
334 callback.dispose(); | 341 if (path.isEmpty()) path.appendBezierPathWithRect(NSRect()); |
335 if (path.isEmpty()) path.appendBezierPathWithRect(new NSRect()); | |
336 return path; | 342 return path; |
337 } | 343 } |
338 | 344 |
339 NSPoint pt = new NSPoint(); | 345 NSPoint pt = NSPoint(); |
340 short[] rect = new short[4]; | 346 Rect rect; |
341 int regionToRects(int message, int rgn, int r, int path) { | 347 extern(C) private static OSStatus* regionToRects(ushort message, RgnHandle rgn, Rect* r, void* path) { |
342 if (message is OS.kQDRegionToRectsMsgParse) { | 348 if (message is OS.kQDRegionToRectsMsgParse) { |
343 OS.memmove(rect, r, rect.length * 2); | 349 OS.memmove(rect, r, rect.sizeof); |
344 pt.x = rect[1]; | 350 pt.x = rect.left; |
345 pt.y = rect[0]; | 351 pt.y = rect.top; |
346 OS.objc_msgSend(path, OS.sel_moveToPoint_1, pt); | 352 OS.objc_msgSend(path, OS.sel_moveToPoint_1, pt); |
347 pt.x = rect[3]; | 353 pt.x = rect.right; |
348 OS.objc_msgSend(path, OS.sel_lineToPoint_1, pt); | 354 OS.objc_msgSend(path, OS.sel_lineToPoint_1, pt); |
349 pt.x = rect[3]; | 355 pt.x = rect.right; |
350 pt.y = rect[2]; | 356 pt.y = rect.bottom; |
351 OS.objc_msgSend(path, OS.sel_lineToPoint_1, pt); | 357 OS.objc_msgSend(path, OS.sel_lineToPoint_1, pt); |
352 pt.x = rect[1]; | 358 pt.x = rect.left; |
353 OS.objc_msgSend(path, OS.sel_lineToPoint_1, pt); | 359 OS.objc_msgSend(path, OS.sel_lineToPoint_1, pt); |
354 OS.objc_msgSend(path, OS.sel_closePath); | 360 OS.objc_msgSend(path, OS.sel_closePath); |
355 } | 361 } |
356 return 0; | 362 return null; |
357 } | 363 } |
358 | 364 |
359 public static Region carbon_new(Device device, int handle) { | 365 public static Region carbon_new(Device device, RgnHandle handle) { |
360 return new Region(device, handle); | 366 return new Region(device, handle); |
361 } | 367 } |
362 | 368 |
363 /** | 369 /** |
364 * Returns an integer hash code for the receiver. Any two | 370 * Returns an integer hash code for the receiver. Any two |
368 * | 374 * |
369 * @return the receiver's hash | 375 * @return the receiver's hash |
370 * | 376 * |
371 * @see #equals | 377 * @see #equals |
372 */ | 378 */ |
373 public int hashCode() { | 379 public hash_t toHash() { |
374 return handle; | 380 return handle; |
375 } | 381 } |
382 | |
383 alias toHash hashCode; | |
376 | 384 |
377 /** | 385 /** |
378 * Intersects the given rectangle to the collection of polygons | 386 * Intersects the given rectangle to the collection of polygons |
379 * the receiver maintains to describe its area. | 387 * the receiver maintains to describe its area. |
380 * | 388 * |
415 * @since 3.1 | 423 * @since 3.1 |
416 */ | 424 */ |
417 public void intersect(int x, int y, int width, int height) { | 425 public void intersect(int x, int y, int width, int height) { |
418 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); | 426 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); |
419 if (width < 0 || height < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | 427 if (width < 0 || height < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); |
420 int rectRgn = OS.NewRgn(); | 428 RgnHandle rectRgn = OS.NewRgn(); |
421 short[] r = new short[4]; | 429 Rect r; |
422 OS.SetRect(r, cast(short)x, cast(short)y, cast(short)(x + width),cast(short)(y + height)); | 430 OS.SetRect(&r, cast(short)x, cast(short)y, cast(short)(x + width),cast(short)(y + height)); |
423 OS.RectRgn(rectRgn, r); | 431 OS.RectRgn(rectRgn, &r); |
424 OS.SectRgn(handle, rectRgn, handle); | 432 OS.SectRgn(handle, rectRgn, handle); |
425 OS.DisposeRgn(rectRgn); | 433 OS.DisposeRgn(rectRgn); |
426 } | 434 } |
427 | 435 |
428 /** | 436 /** |
466 * | 474 * |
467 * @see Rectangle#intersects(Rectangle) | 475 * @see Rectangle#intersects(Rectangle) |
468 */ | 476 */ |
469 public bool intersects (int x, int y, int width, int height) { | 477 public bool intersects (int x, int y, int width, int height) { |
470 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); | 478 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); |
471 short[] r = new short[4]; | 479 Rect r; |
472 OS.SetRect(r, cast(short)x, cast(short)y, cast(short)(x + width),cast(short)(y + height)); | 480 OS.SetRect(&r, cast(short)x, cast(short)y, cast(short)(x + width),cast(short)(y + height)); |
473 return OS.RectInRgn(rect, handle); | 481 return OS.RectInRgn(&rect, handle); |
474 } | 482 } |
475 | 483 |
476 /** | 484 /** |
477 * Returns <code>true</code> if the given rectangle intersects | 485 * Returns <code>true</code> if the given rectangle intersects |
478 * with any of the polygons the receiver maintains to describe | 486 * with any of the polygons the receiver maintains to describe |
504 * invoke any other method using the region. | 512 * invoke any other method using the region. |
505 * | 513 * |
506 * @return <code>true</code> when the region is disposed, and <code>false</code> otherwise | 514 * @return <code>true</code> when the region is disposed, and <code>false</code> otherwise |
507 */ | 515 */ |
508 public bool isDisposed() { | 516 public bool isDisposed() { |
509 return handle is 0; | 517 return handle is null; |
510 } | 518 } |
511 | 519 |
512 /** | 520 /** |
513 * Returns <code>true</code> if the receiver does not cover any | 521 * Returns <code>true</code> if the receiver does not cover any |
514 * area in the (x, y) coordinate plane, and <code>false</code> if | 522 * area in the (x, y) coordinate plane, and <code>false</code> if |
542 */ | 550 */ |
543 public void subtract (int[] pointArray) { | 551 public void subtract (int[] pointArray) { |
544 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); | 552 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); |
545 if (pointArray is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); | 553 if (pointArray is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); |
546 if (pointArray.length < 2) return; | 554 if (pointArray.length < 2) return; |
547 int polyRgn = OS.NewRgn(); | 555 RgnHandle polyRgn = OS.NewRgn(); |
548 OS.OpenRgn(); | 556 OS.OpenRgn(); |
549 OS.MoveTo(cast(short)pointArray[0], cast(short)pointArray[1]); | 557 OS.MoveTo(cast(short)pointArray[0], cast(short)pointArray[1]); |
550 for (int i = 1; i < pointArray.length / 2; i++) { | 558 for (int i = 1; i < pointArray.length / 2; i++) { |
551 OS.LineTo(cast(short)pointArray[2 * i], cast(short)pointArray[2 * i + 1]); | 559 OS.LineTo(cast(short)pointArray[2 * i], cast(short)pointArray[2 * i + 1]); |
552 } | 560 } |
597 * @since 3.1 | 605 * @since 3.1 |
598 */ | 606 */ |
599 public void subtract(int x, int y, int width, int height) { | 607 public void subtract(int x, int y, int width, int height) { |
600 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); | 608 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); |
601 if (width < 0 || height < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | 609 if (width < 0 || height < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); |
602 int rectRgn = OS.NewRgn(); | 610 RgnHandle rectRgn = OS.NewRgn(); |
603 short[] r = new short[4]; | 611 Rect r; |
604 OS.SetRect(r, cast(short)x, cast(short)y, cast(short)(x + width),cast(short)(y + height)); | 612 OS.SetRect(r, cast(short)x, cast(short)y, cast(short)(x + width),cast(short)(y + height)); |
605 OS.RectRgn(rectRgn, r); | 613 OS.RectRgn(rectRgn, &r); |
606 OS.DiffRgn(handle, rectRgn, handle); | 614 OS.DiffRgn(handle, rectRgn, handle); |
607 OS.DisposeRgn(rectRgn); | 615 OS.DisposeRgn(rectRgn); |
608 } | 616 } |
609 | 617 |
610 /** | 618 /** |
676 * | 684 * |
677 * @return a string representation of the receiver | 685 * @return a string representation of the receiver |
678 */ | 686 */ |
679 public String toString () { | 687 public String toString () { |
680 if (isDisposed()) return "Region {*DISPOSED*}"; | 688 if (isDisposed()) return "Region {*DISPOSED*}"; |
681 return "Region {" + handle + "}"; | 689 return Format("Region {{}{}" , handle , "}"); |
682 } | 690 } |
683 } | 691 } |