|
1 // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <bookmark.h> |
|
17 #include "bkmrk.h" |
|
18 #include "bkmrkfolder.h" |
|
19 |
|
20 /** |
|
21 RBkNode constructor |
|
22 @publishedPartner |
|
23 @released |
|
24 */ |
|
25 EXPORT_C RBkNode::RBkNode() : iItem(NULL) |
|
26 { |
|
27 } |
|
28 |
|
29 /** |
|
30 Closes the RBkNode handle and decrements the reference count |
|
31 @publishedPartner |
|
32 @released |
|
33 */ |
|
34 EXPORT_C void RBkNode::Close() |
|
35 { |
|
36 if (iItem) |
|
37 { |
|
38 iItem->DecRefCount(); |
|
39 } |
|
40 iItem = NULL; |
|
41 } |
|
42 |
|
43 /** |
|
44 Returns the type of the object that the RBkNode handle is connected to |
|
45 |
|
46 @return The type of the object connected to |
|
47 @publishedPartner |
|
48 @released |
|
49 */ |
|
50 EXPORT_C Bookmark::TType RBkNode::Type() const |
|
51 { |
|
52 // The handle must be open and attached to a concrete bookmark object |
|
53 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
54 |
|
55 return iItem->Type(); |
|
56 } |
|
57 |
|
58 /** |
|
59 Method for querying the bookmark item's database id. |
|
60 |
|
61 @return The item's ID. |
|
62 @publishedPartner |
|
63 @released |
|
64 */ |
|
65 EXPORT_C Bookmark::TItemId RBkNode::Id() const |
|
66 { |
|
67 // The handle must be open and attached to a concrete bookmark object |
|
68 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
69 |
|
70 return iItem->Id(); |
|
71 } |
|
72 |
|
73 /** |
|
74 Method for querying a bookmark item's parent folder. Only the root will not have |
|
75 a parent. The method will leave if an attempt is made to call this on the root folder |
|
76 |
|
77 @return The opened RBkFolder object. |
|
78 @leave KErrNotFound This will leave if called on the root folder (the root folder has no parent). |
|
79 @publishedPartner |
|
80 @released |
|
81 */ |
|
82 EXPORT_C RBkFolder RBkNode::OpenParentL() const |
|
83 { |
|
84 // The handle must be open and attached to a concrete bookmark object |
|
85 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
86 |
|
87 CBookmarkFolder* parent = iItem->Parent(); |
|
88 if (!parent) |
|
89 { |
|
90 User::Leave(KErrNotFound); |
|
91 } |
|
92 return parent->OpenFolder(); |
|
93 } |
|
94 |
|
95 /** |
|
96 Opens RBkBookmark handle from this RBkNode handle. This is usually done when iterating through a folder. |
|
97 You must ensure that this RBkNode handle is of the type: Bookmark::ETypeBookmark before calling this method. |
|
98 |
|
99 This method does NOT close the RBkNode handle |
|
100 |
|
101 @return The opened RBkBookmark object |
|
102 @leave Bookmark::KErrWrongType The method will leave if an attempt is made to call this with a RBkNode handle which is not of |
|
103 the Bookmark::ETypeBookmark type. |
|
104 @publishedPartner |
|
105 @released |
|
106 */ |
|
107 EXPORT_C RBkBookmark RBkNode::OpenBookmarkL() const |
|
108 { |
|
109 // The handle must be open and attached to a concrete bookmark object |
|
110 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
111 |
|
112 if (Type() != Bookmark::ETypeBookmark) |
|
113 { |
|
114 // can't create a bookmark from a folder item |
|
115 User::Leave(Bookmark::KErrWrongType); |
|
116 } |
|
117 |
|
118 RBkBookmark handle; |
|
119 handle.SetItem(*iItem); |
|
120 return handle; |
|
121 } |
|
122 |
|
123 /** |
|
124 Opens RBkFolder handle from this RBkNode handle. This is usually done when iterating through a folder. |
|
125 You must ensure that this RBkNode handle is of the type: Bookmark::ETypeFolder before calling this method. |
|
126 |
|
127 This method does NOT close the RBkNode handle |
|
128 |
|
129 @return The opened RBkFolder object. |
|
130 @leave Bookmark::KErrWrongType The method will leave if an attempt is made to call this with a RBkNode handle which is not of |
|
131 the Bookmark::ETypeFolder type. |
|
132 @publishedPartner |
|
133 @released |
|
134 */ |
|
135 EXPORT_C RBkFolder RBkNode::OpenFolderL() const |
|
136 { |
|
137 // The handle must be open and attached to a concrete bookmark object |
|
138 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
139 |
|
140 if (Type() != Bookmark::ETypeFolder) |
|
141 { |
|
142 // can't create a bookmark from a folder item |
|
143 User::Leave(Bookmark::KErrWrongType); |
|
144 } |
|
145 |
|
146 RBkFolder handle; |
|
147 handle.SetItem(*iItem); |
|
148 return handle; |
|
149 } |
|
150 |
|
151 /** |
|
152 Moves the item to another folder |
|
153 |
|
154 This function will leave if called on the root folder or an out of memory condition |
|
155 occurs. |
|
156 |
|
157 @param aNewParent The new parent folder |
|
158 @leave Bookmark::KErrReadOnly This function will leave if the item is read only. |
|
159 @publishedPartner |
|
160 @released |
|
161 */ |
|
162 EXPORT_C void RBkNode::SetParentL(RBkFolder& aNewParent) |
|
163 { |
|
164 // The handle must be open and attached to a concrete bookmark object |
|
165 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
166 |
|
167 LeaveIfRootL(); |
|
168 // Push the bookmark internal item into the cleanupstack using TCleanupItem. |
|
169 // else the iItem will not get deleted in case of a leave from AppendL function. |
|
170 CleanupStack::PushL ( TCleanupItem ( RBkNode::DeleteBkmrkItem, iItem ) ); |
|
171 aNewParent.Folder()->AppendL(*iItem); |
|
172 CleanupStack::Pop (); |
|
173 } |
|
174 |
|
175 /** |
|
176 Gets the items title text. |
|
177 |
|
178 @return A reference to the bookmark's title text |
|
179 @publishedPartner |
|
180 @released |
|
181 */ |
|
182 EXPORT_C const TDesC& RBkNode::Title() const |
|
183 { |
|
184 // The handle must be open and attached to a concrete bookmark object |
|
185 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
186 |
|
187 return iItem->Title(); |
|
188 } |
|
189 |
|
190 /** |
|
191 Sets the item's title text. Folder titles must be unique. |
|
192 |
|
193 @param aTitle The item's new title. |
|
194 @publishedPartner |
|
195 @released |
|
196 */ |
|
197 EXPORT_C void RBkNode::SetTitleL(const TDesC& aTitle) |
|
198 { |
|
199 // The handle must be open and attached to a concrete bookmark object |
|
200 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
201 |
|
202 LeaveIfRootL(); |
|
203 iItem->SetTitleL(aTitle); |
|
204 } |
|
205 |
|
206 /** |
|
207 Gets the item description text. |
|
208 |
|
209 @return Reference to the bookmark's description text |
|
210 @publishedPartner |
|
211 @released |
|
212 */ |
|
213 EXPORT_C const TDesC& RBkNode::DescriptionL() const |
|
214 { |
|
215 CBkmrkProperties* properties = ExtendedPropertiesL(); |
|
216 return properties->Description(); |
|
217 } |
|
218 |
|
219 /** |
|
220 Sets the item's decription text. |
|
221 |
|
222 @param aDescription The item's new description |
|
223 @publishedPartner |
|
224 @released |
|
225 */ |
|
226 EXPORT_C void RBkNode::SetDescriptionL(const TDesC& aDescription) |
|
227 { |
|
228 LeaveIfRootL(); |
|
229 CBkmrkProperties* properties = ExtendedPropertiesL(); |
|
230 properties->SetDescriptionL(aDescription); |
|
231 } |
|
232 |
|
233 /** |
|
234 Returns whether the item is public or private |
|
235 |
|
236 @return ETrue if the bookmark or folder is public, otherwise EFalse |
|
237 @publishedPartner |
|
238 @released |
|
239 */ |
|
240 EXPORT_C TBool RBkNode::IsPublic() const |
|
241 { |
|
242 // The handle must be open and attached to a concrete bookmark object |
|
243 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
244 return iItem->IsPublic(); |
|
245 } |
|
246 |
|
247 /** |
|
248 Sets the public or private status. |
|
249 |
|
250 @param aPublic Whether the item is to be public or private |
|
251 @publishedPartner |
|
252 @released |
|
253 */ |
|
254 EXPORT_C void RBkNode::SetPublicL(TBool aPublic) |
|
255 { |
|
256 // The handle must be open and attached to a concrete bookmark object |
|
257 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
258 LeaveIfRootL(); |
|
259 iItem->SetPublicL(aPublic); |
|
260 } |
|
261 |
|
262 /** |
|
263 Returns whether the item is read only or not |
|
264 |
|
265 @return ETrue if the bookmark or folder can be written to, otherwise EFalse |
|
266 @publishedPartner |
|
267 @released |
|
268 */ |
|
269 EXPORT_C TBool RBkNode::IsWritable() const |
|
270 { |
|
271 // The handle must be open and attached to a concrete bookmark object |
|
272 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
273 return iItem->IsWritable();; |
|
274 } |
|
275 |
|
276 /** |
|
277 Sets item to Read Only. An item can always be made Read Only, but the bookmark database must be opened as |
|
278 Bookmark::EVisibilityManager in order to set an item back to Read/Write. |
|
279 This function will leave if called on the root folder. |
|
280 |
|
281 @see Bookmark::TVisibility |
|
282 @param aWritable Whether the item is to be read only or not |
|
283 @leave KErrPermissionDenied This function will leave, when the bookmark database is not opened in |
|
284 Bookmark::EVisibilityManager. |
|
285 @leave Bookmark::KErrOperationDenied This function will leave, if called on the root folder. |
|
286 @publishedPartner |
|
287 @released |
|
288 */ |
|
289 EXPORT_C void RBkNode::SetWritableL(TBool aWritable) |
|
290 { |
|
291 // The handle must be open and attached to a concrete bookmark object |
|
292 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
293 |
|
294 LeaveIfRootL(); |
|
295 iItem->SetWritableL(aWritable); |
|
296 } |
|
297 |
|
298 /** |
|
299 Returns the ID if an icon is assigned to this bookmark item. |
|
300 |
|
301 @return The ID of the icon or KNullBookmarkID if no item has been assigned. |
|
302 @publishedPartner |
|
303 @released |
|
304 */ |
|
305 EXPORT_C Bookmark::TAttachmentId RBkNode::IconIdL() const |
|
306 { |
|
307 CBkmrkProperties* properties = ExtendedPropertiesL(); |
|
308 return properties->IconId(); |
|
309 } |
|
310 |
|
311 /** |
|
312 Sets the icon. Call this with KNullIconID to remove the icon. Icons are stored centrally in |
|
313 the database and are shared by bookmark items. If the icon oes not exist in the database this |
|
314 method will leave |
|
315 |
|
316 @param aIconId The ID of the icon. |
|
317 @publishedPartner |
|
318 @released |
|
319 */ |
|
320 EXPORT_C void RBkNode::SetIconIdL(Bookmark::TAttachmentId aIconId) |
|
321 { |
|
322 LeaveIfRootL(); |
|
323 CBkmrkProperties* properties = ExtendedPropertiesL(); |
|
324 properties->SetIconIdL(aIconId); |
|
325 } |
|
326 |
|
327 /** |
|
328 Gets a custom property for the given property ID. This method will leave |
|
329 if no such property has been registered on the database |
|
330 |
|
331 @param aPropertyId The ID of the property you wish to get |
|
332 @param aValue An integer the data value |
|
333 @publishedPartner |
|
334 @released |
|
335 */ |
|
336 EXPORT_C void RBkNode::GetCustomPropertyL(TUid aPropertyId, TInt& aValue) const |
|
337 { |
|
338 User::LeaveIfError(ExtendedPropertiesL()->CustomProperties().GetCustomProperty(aPropertyId, aValue)); |
|
339 } |
|
340 |
|
341 /** |
|
342 Gets a custom property for the given property ID. This method will leave |
|
343 if no such property has been registered on the database |
|
344 |
|
345 @param aPropertyId The ID of the property you wish to get |
|
346 @param aValue A floating point data value |
|
347 @publishedPartner |
|
348 @released |
|
349 */ |
|
350 EXPORT_C void RBkNode::GetCustomPropertyL(TUid aPropertyId, TReal& aValue) const |
|
351 { |
|
352 User::LeaveIfError(ExtendedPropertiesL()->CustomProperties().GetCustomProperty(aPropertyId, aValue)); |
|
353 } |
|
354 |
|
355 /** |
|
356 Gets a custom property for the given property ID. This method will leave |
|
357 if no such property has been registered on the database |
|
358 |
|
359 @param aPropertyId The ID of the property you wish to get |
|
360 @param aValue A 16 bit descriptor |
|
361 @publishedPartner |
|
362 @released |
|
363 */ |
|
364 EXPORT_C void RBkNode::GetCustomPropertyL(TUid aPropertyId, TDes& aValue) const |
|
365 { |
|
366 User::LeaveIfError(ExtendedPropertiesL()->CustomProperties().GetCustomProperty(aPropertyId, aValue)); |
|
367 } |
|
368 |
|
369 /** |
|
370 Gets a custom property for the given property ID. This method will leave |
|
371 if no such property has been registered on the database |
|
372 |
|
373 @param aPropertyId The ID of the property you wish to get |
|
374 @param aValue An 8 bit descriptor |
|
375 @publishedPartner |
|
376 @released |
|
377 */ |
|
378 EXPORT_C void RBkNode::GetCustomPropertyL(TUid aPropertyId, TDes8& aValue) const |
|
379 { |
|
380 User::LeaveIfError(ExtendedPropertiesL()->CustomProperties().GetCustomProperty(aPropertyId, aValue)); |
|
381 } |
|
382 |
|
383 /** |
|
384 Assigns a new custom property value to the bookmark item. This method will leave |
|
385 if no such property has been registered on the database |
|
386 |
|
387 @param aPropertyId The ID of the property you wish to set |
|
388 @param aValue An integer data value |
|
389 @publishedPartner |
|
390 @released |
|
391 */ |
|
392 EXPORT_C void RBkNode::SetCustomPropertyL(TUid aPropertyId, TInt aValue) |
|
393 { |
|
394 LeaveIfRootL(); |
|
395 ExtendedPropertiesL()->CustomProperties().SetCustomPropertyL(aPropertyId, aValue); |
|
396 iItem->SetDirty(); |
|
397 } |
|
398 |
|
399 /** |
|
400 Assigns a new custom property value to the bookmark item. This method will leave |
|
401 if no such property has been registered on the database |
|
402 |
|
403 @param aPropertyId The ID of the property you wish to set |
|
404 @param aValue A floating point data value |
|
405 @publishedPartner |
|
406 @released |
|
407 */ |
|
408 EXPORT_C void RBkNode::SetCustomPropertyL(TUid aPropertyId, TReal aValue) __SOFTFP |
|
409 { |
|
410 LeaveIfRootL(); |
|
411 ExtendedPropertiesL()->CustomProperties().SetCustomPropertyL(aPropertyId, aValue); |
|
412 iItem->SetDirty(); |
|
413 } |
|
414 |
|
415 /** |
|
416 Assigns a new custom property value to the bookmark item. This method will leave |
|
417 if no such property has been registered on the database |
|
418 |
|
419 @param aPropertyId The ID of the property you wish to set |
|
420 @param aValue A 16 bit descriptor |
|
421 @publishedPartner |
|
422 @released |
|
423 */ |
|
424 EXPORT_C void RBkNode::SetCustomPropertyL(TUid aPropertyId, const TDesC& aValue) |
|
425 { |
|
426 LeaveIfRootL(); |
|
427 ExtendedPropertiesL()->CustomProperties().SetCustomPropertyL(aPropertyId, aValue); |
|
428 iItem->SetDirty(); |
|
429 } |
|
430 |
|
431 /** |
|
432 Assigns a new custom property value to the bookmark item. This method will leave |
|
433 if no such property has been registered on the database |
|
434 |
|
435 @param aPropertyId The ID of the property you wish to set |
|
436 @param aValue An 8 bit descriptor |
|
437 @publishedPartner |
|
438 @released |
|
439 */ |
|
440 EXPORT_C void RBkNode::SetCustomPropertyL(TUid aPropertyId, const TDesC8& aValue) |
|
441 { |
|
442 LeaveIfRootL(); |
|
443 ExtendedPropertiesL()->CustomProperties().SetCustomPropertyL(aPropertyId, aValue); |
|
444 iItem->SetDirty(); |
|
445 } |
|
446 |
|
447 /** |
|
448 Returns the item's owner if it is a private bookmark. |
|
449 |
|
450 @see Bookmark::TVisibility |
|
451 @return The item's owner security ID |
|
452 @leave KErrPermissionDenied This function will leave when the bookmark database |
|
453 is not opened as Bookmark::EVisibilityManager. |
|
454 @publishedPartner |
|
455 @released |
|
456 */ |
|
457 EXPORT_C TSecureId RBkNode::OwnerL() const |
|
458 { |
|
459 // The handle must be open and attached to a concrete bookmark object |
|
460 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
461 iItem->LeaveIfNotInManagerModeL (); |
|
462 TSecureId id; |
|
463 iItem->GetOwnerL(id); |
|
464 return id; |
|
465 } |
|
466 |
|
467 /** |
|
468 Sets the owner security ID of this bookmark item. All applications which have opened the bookmark database as |
|
469 Bookmark::EVisibilityManager can use this function. In other visibility modes the aOwner must be the current SID, |
|
470 otherwise function will leave with KErrPermissionDenied |
|
471 |
|
472 @param aOwner The new owner ID. |
|
473 @leave KErrPermissionDenied When the aOwner is not the current SID and the bookmark database is opened in |
|
474 non-manager mode. |
|
475 @leave Bookmark::KErrOperationDenied This function will leave, if called on the root folder. |
|
476 @publishedPartner |
|
477 @released |
|
478 */ |
|
479 EXPORT_C void RBkNode::SetOwnerL(TSecureId aOwner) |
|
480 { |
|
481 // The handle must be open and attached to a concrete bookmark object |
|
482 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
483 LeaveIfRootL(); |
|
484 iItem->SetOwnerL(aOwner); |
|
485 } |
|
486 |
|
487 /** |
|
488 Not intended for external use. |
|
489 |
|
490 @internalComponent |
|
491 */ |
|
492 CBookmarkBase* RBkNode::Item() const |
|
493 { |
|
494 return iItem; |
|
495 } |
|
496 |
|
497 /** |
|
498 Not intended for external use. |
|
499 |
|
500 @internalComponent |
|
501 */ |
|
502 void RBkNode::SetItem(CBookmarkBase& aItem) |
|
503 { |
|
504 // Must not try to assign an already open item |
|
505 __ASSERT_ALWAYS(!iItem, User::Panic(Bookmark::KBookmarkErrCorrupt, Bookmark::KErrCorrupt)); |
|
506 |
|
507 iItem = &aItem; |
|
508 iItem->IncRefCount(); |
|
509 } |
|
510 |
|
511 /** |
|
512 Not intended for external use. |
|
513 |
|
514 @internalComponent |
|
515 */ |
|
516 CBkmrkProperties* RBkNode::ExtendedPropertiesL() const |
|
517 { |
|
518 // The handle must be open and attached to a concrete bookmark object |
|
519 __ASSERT_ALWAYS(iItem, User::Panic(Bookmark::KBookmarkErrHandleNotOpen, Bookmark::KErrNotOpen)); |
|
520 |
|
521 CBkmrkProperties* properties = &(iItem->ExtendedPropertiesL()); |
|
522 return properties; |
|
523 } |
|
524 |
|
525 void RBkNode::LeaveIfRootL() const |
|
526 { |
|
527 if (Title().Compare(Bookmark::KTxtRootTitle) == 0) |
|
528 { |
|
529 User::Leave(Bookmark::KErrOperationDenied); |
|
530 } |
|
531 } |
|
532 |
|
533 /** |
|
534 Sets the last modified time to an arbitrary value. Only applications which have opened the bookmark |
|
535 database as Bookmark::EVisibilityManager can use this function, otherwise it will leave with |
|
536 KErrPermissionDenied error code. |
|
537 |
|
538 Note: The last modified time is automatically set to the current time during a commit. This |
|
539 function should only be used if the caller needs to set to a different time (for example when |
|
540 synchronising bookmarks). |
|
541 |
|
542 @param aTime The time to set. |
|
543 @leave KErrPermissionDenied This function will leave, when the bookmark database is not opened in |
|
544 Bookmark::EVisibilityManager. |
|
545 @publishedPartner |
|
546 @released |
|
547 */ |
|
548 EXPORT_C void RBkNode::SetLastModifiedL ( const TTime& aTime ) |
|
549 { |
|
550 iItem->LeaveIfNotInManagerModeL (); |
|
551 |
|
552 CBkmrkProperties* properties = ExtendedPropertiesL(); |
|
553 properties->SetLastModified ( aTime ); |
|
554 iItem->SetDirty(); |
|
555 } |
|
556 |
|
557 /** |
|
558 Returns the last modified time. If the last modified time was never set, this will return |
|
559 the value of midnight January 1st 1970 UTC. This can occur when using a version 1.0.0 database. |
|
560 |
|
561 @return Last modified date value. |
|
562 @publishedPartner |
|
563 @released |
|
564 */ |
|
565 EXPORT_C TTime RBkNode::LastModifiedL () const |
|
566 { |
|
567 CBkmrkProperties* properties = ExtendedPropertiesL(); |
|
568 return properties->LastModified(); |
|
569 } |
|
570 |
|
571 void RBkNode::DeleteBkmrkItem ( TAny* aPtr ) |
|
572 { |
|
573 CBookmarkBase* bkItem = ( CBookmarkBase* )aPtr; |
|
574 if ( bkItem ) |
|
575 { |
|
576 bkItem->DecRefCount (); |
|
577 delete bkItem; |
|
578 bkItem = NULL; |
|
579 } |
|
580 } |