|
1 /* |
|
2 * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description : ESMR entry processor implementation |
|
15 * Version : %version: e002sa33#11 % |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 // INCLUDE FILES |
|
21 |
|
22 #include "cmrcaleventprocessor.h" |
|
23 #include "esmrinternaluid.h" |
|
24 #include "mesmrcalentry.h" |
|
25 #include "esmrhelper.h" |
|
26 #include "esmrentryhelper.h" |
|
27 #include "cmrcalentry.h" |
|
28 #include "cesmrrecurrenceinfohandler.h" |
|
29 #include "emailtrace.h" |
|
30 |
|
31 #include <e32std.h> |
|
32 #include <calentry.h> |
|
33 #include <calinstance.h> |
|
34 #include <calrrule.h> |
|
35 #include <caluser.h> |
|
36 #include <caltime.h> |
|
37 #include <caleninterimutils2.h> |
|
38 #include <magnentryui.h> |
|
39 #include <ct/rcpointerarray.h> |
|
40 |
|
41 /// Unnamed namespace for local definitions |
|
42 namespace { |
|
43 |
|
44 _LIT( KReplaceLineFeedChars, "\r\n" ); |
|
45 _LIT( KLineFeed, " \x2029"); |
|
46 |
|
47 #ifdef _DEBUG |
|
48 |
|
49 // Definition for module panic text |
|
50 _LIT( KMRCalEntryProcessor, "MRCalEntryProcessor" ); |
|
51 |
|
52 /** |
|
53 * ESMREntryProcessor panic codes |
|
54 */ |
|
55 enum TMRCalEntryProcessorPanic |
|
56 { |
|
57 // Empty input array |
|
58 EMRCalEventProcessorEmptyEntryArray, |
|
59 // Invalid processor mode |
|
60 EMRCalEventProcessorInvalidType, |
|
61 // Invalid processor mode |
|
62 EMRCalEventProcessorInvalidMode, |
|
63 // Non-supported method called |
|
64 EMRCalEventProcessorNotSupported |
|
65 }; |
|
66 |
|
67 /** |
|
68 * Raises panic. |
|
69 * @param aPanic Panic code |
|
70 */ |
|
71 void Panic(TMRCalEntryProcessorPanic aPanic) |
|
72 { |
|
73 User::Panic( KMRCalEntryProcessor(), aPanic); |
|
74 } |
|
75 |
|
76 #endif // _DEBUG |
|
77 |
|
78 /** |
|
79 * Maps CCalEntry defined entry type to ESMR entry type. |
|
80 * |
|
81 * @param aType Calendar entry type |
|
82 * @return TESMRCalendarEventType Entry type |
|
83 */ |
|
84 TESMRCalendarEventType MapCalEntryTypeToMRType( |
|
85 CCalEntry::TType aType ) |
|
86 { |
|
87 TESMRCalendarEventType returnType(EESMREventTypeAppt); |
|
88 |
|
89 switch(aType) |
|
90 { |
|
91 case CCalEntry::EAppt: |
|
92 { |
|
93 returnType = EESMREventTypeAppt; |
|
94 break; |
|
95 } |
|
96 case CCalEntry::ETodo: |
|
97 { |
|
98 returnType = EESMREventTypeETodo; |
|
99 break; |
|
100 } |
|
101 case CCalEntry::EEvent: |
|
102 { |
|
103 returnType = EESMREventTypeEEvent; |
|
104 break; |
|
105 } |
|
106 case CCalEntry::EReminder: |
|
107 { |
|
108 returnType = EESMREventTypeEReminder; |
|
109 break; |
|
110 } |
|
111 case CCalEntry::EAnniv: |
|
112 { |
|
113 returnType = EESMREventTypeEAnniv; |
|
114 break; |
|
115 } |
|
116 default: |
|
117 // This should not happen |
|
118 __ASSERT_DEBUG( |
|
119 EFalse, |
|
120 Panic( EMRCalEventProcessorInvalidType ) ); |
|
121 User::Leave( KErrArgument ); |
|
122 break; |
|
123 } |
|
124 |
|
125 return returnType; |
|
126 } |
|
127 |
|
128 HBufC* ReplaceCharactersFromBufferLC( const TDesC& aTarget, |
|
129 const TDesC& aFindString, |
|
130 const TDesC& aReplacement ) |
|
131 { |
|
132 HBufC* newBuffer = aTarget.AllocLC(); |
|
133 TPtr16 ptr = newBuffer->Des(); |
|
134 |
|
135 // find next occurance: |
|
136 TInt offset = ptr.Find(aFindString); |
|
137 while ( offset != KErrNotFound ) |
|
138 { |
|
139 // replace the data: |
|
140 ptr.Replace( offset, aFindString.Length(), aReplacement); |
|
141 |
|
142 // find next occurance: |
|
143 offset = ptr.Find(aFindString); |
|
144 } |
|
145 |
|
146 return newBuffer; |
|
147 } |
|
148 |
|
149 } // namespace |
|
150 |
|
151 // ======== MEMBER FUNCTIONS ======== |
|
152 |
|
153 // --------------------------------------------------------------------------- |
|
154 // CMRCalEntryProcessor::CMRCalEntryProcessor |
|
155 // --------------------------------------------------------------------------- |
|
156 // |
|
157 CMRCalEntryProcessor::CMRCalEntryProcessor( |
|
158 MESMRCalDbMgr& aDbMgr ) |
|
159 : iDbMgr( aDbMgr ) |
|
160 { |
|
161 FUNC_LOG; |
|
162 } |
|
163 |
|
164 // --------------------------------------------------------------------------- |
|
165 // CMRCalEntryProcessor::~CMRCalEntryProcessor |
|
166 // --------------------------------------------------------------------------- |
|
167 // |
|
168 CMRCalEntryProcessor::~CMRCalEntryProcessor() |
|
169 { |
|
170 FUNC_LOG; |
|
171 |
|
172 delete iCalEntry; |
|
173 } |
|
174 |
|
175 // --------------------------------------------------------------------------- |
|
176 // CMRCalEntryProcessor::ConstructL |
|
177 // --------------------------------------------------------------------------- |
|
178 // |
|
179 void CMRCalEntryProcessor::ConstructL() |
|
180 { |
|
181 FUNC_LOG; |
|
182 } |
|
183 |
|
184 // --------------------------------------------------------------------------- |
|
185 // CMRCalEntryProcessor::NewL |
|
186 // --------------------------------------------------------------------------- |
|
187 // |
|
188 CMRCalEntryProcessor* CMRCalEntryProcessor::NewL( |
|
189 TAny* aDbMgr ) |
|
190 { |
|
191 FUNC_LOG; |
|
192 |
|
193 MESMRCalDbMgr* dbMgr = static_cast<MESMRCalDbMgr*>(aDbMgr); |
|
194 return CreateL( *dbMgr ); |
|
195 } |
|
196 |
|
197 // --------------------------------------------------------------------------- |
|
198 // CMRCalEntryProcessor::CreateL |
|
199 // --------------------------------------------------------------------------- |
|
200 // |
|
201 CMRCalEntryProcessor* CMRCalEntryProcessor::CreateL( |
|
202 MESMRCalDbMgr& aDbMgr ) |
|
203 { |
|
204 FUNC_LOG; |
|
205 |
|
206 CMRCalEntryProcessor* self = new (ELeave) CMRCalEntryProcessor( aDbMgr ); |
|
207 CleanupStack::PushL( self ); |
|
208 self->ConstructL(); |
|
209 CleanupStack::Pop( self ); |
|
210 |
|
211 return self; |
|
212 } |
|
213 |
|
214 // --------------------------------------------------------------------------- |
|
215 // CMRCalEntryProcessor::ScenarioData |
|
216 // --------------------------------------------------------------------------- |
|
217 // |
|
218 const TESMRScenarioData& CMRCalEntryProcessor::ScenarioData() const |
|
219 { |
|
220 FUNC_LOG; |
|
221 return iScenData; |
|
222 } |
|
223 |
|
224 // --------------------------------------------------------------------------- |
|
225 // CMRCalEntryProcessor::ContainsProcessedEntry |
|
226 // --------------------------------------------------------------------------- |
|
227 // |
|
228 TBool CMRCalEntryProcessor::ContainsProcessedEntry() const |
|
229 { |
|
230 FUNC_LOG; |
|
231 return (iCalEntry != NULL); |
|
232 } |
|
233 |
|
234 // --------------------------------------------------------------------------- |
|
235 // CMRCalEntryProcessor::ProcessL |
|
236 // --------------------------------------------------------------------------- |
|
237 // |
|
238 void CMRCalEntryProcessor::ProcessL( |
|
239 const MAgnEntryUi::TAgnEntryUiInParams* aParams, |
|
240 RPointerArray<CCalEntry>* aEntries ) |
|
241 { |
|
242 FUNC_LOG; |
|
243 |
|
244 iParams = aParams; |
|
245 ProcessL( aEntries ); |
|
246 |
|
247 if ( MAgnEntryUi::EViewEntry != iParams->iEditorMode ) |
|
248 { |
|
249 iScenData.iViewMode = EESMREditMR; |
|
250 } |
|
251 } |
|
252 |
|
253 // --------------------------------------------------------------------------- |
|
254 // CMRCalEntryProcessor::ProcessL |
|
255 // --------------------------------------------------------------------------- |
|
256 // |
|
257 void CMRCalEntryProcessor::ProcessL( |
|
258 RPointerArray<CCalEntry>* aEntries ) |
|
259 { |
|
260 FUNC_LOG; |
|
261 |
|
262 __ASSERT_DEBUG( aEntries->Count() > 0, |
|
263 Panic( EMRCalEventProcessorEmptyEntryArray ) ); |
|
264 |
|
265 iCalEntries = aEntries; |
|
266 |
|
267 CCalEntry& entry = *( (*iCalEntries)[ 0 ] ); |
|
268 ProcessL( iParams, entry, ETrue ); |
|
269 } |
|
270 |
|
271 // --------------------------------------------------------------------------- |
|
272 // CMRCalEntryProcessor::ProcessL |
|
273 // --------------------------------------------------------------------------- |
|
274 // |
|
275 void CMRCalEntryProcessor::ProcessL( |
|
276 const MAgnEntryUi::TAgnEntryUiInParams* aParams, |
|
277 CCalEntry& aEntry, |
|
278 TBool aSetDefaultValuesToEntry, |
|
279 const TBool aTypeChanging ) |
|
280 { |
|
281 FUNC_LOG; |
|
282 iParams = aParams; |
|
283 |
|
284 // Plain calendar entries are always used from calendar application |
|
285 iScenData.iCallingApp = EESMRAppCalendar; |
|
286 iScenData.iEntryType = MapCalEntryTypeToMRType( aEntry.EntryTypeL() ); |
|
287 |
|
288 if ( aParams->iEditorMode == MAgnEntryUi::EViewEntry ) |
|
289 { |
|
290 iScenData.iViewMode = EESMRViewMR; |
|
291 } |
|
292 else |
|
293 { |
|
294 iScenData.iViewMode = EESMREditMR; |
|
295 } |
|
296 |
|
297 ConvertTextFieldLineFeedsL( aEntry ); |
|
298 |
|
299 CreateEntryL( aEntry, aSetDefaultValuesToEntry ); |
|
300 |
|
301 // When creating a new repeating meeting and adding participants, |
|
302 // meeting request editor is opened and all occurences should be edited |
|
303 if ( ( iCalEntry->IsRecurrentEventL() && |
|
304 MAgnEntryUi::ECreateNewEntry == iParams->iEditorMode ) || |
|
305 ( iCalEntry->IsRepeatingMeetingL( aEntry ) && aTypeChanging ) ) |
|
306 { |
|
307 iCalEntry->SetModifyingRuleL( |
|
308 MESMRCalEntry::EESMRAllInSeries, aTypeChanging ); |
|
309 } |
|
310 else //Default case |
|
311 { |
|
312 iCalEntry->SetModifyingRuleL( |
|
313 MESMRCalEntry::EESMRThisOnly, aTypeChanging ); |
|
314 } |
|
315 |
|
316 // If entry is new entry, we want to set default values to it. |
|
317 // On the other hand, when changing entry type from one type to another, |
|
318 // we do not want to set default values |
|
319 if ( iParams && MAgnEntryUi::ECreateNewEntry == |
|
320 iParams->iEditorMode && |
|
321 aSetDefaultValuesToEntry ) |
|
322 { |
|
323 iCalEntry->SetDefaultValuesToEntryL(); |
|
324 } |
|
325 } |
|
326 |
|
327 // --------------------------------------------------------------------------- |
|
328 // CMRCalEntryProcessor::ProcessL |
|
329 // --------------------------------------------------------------------------- |
|
330 // |
|
331 void CMRCalEntryProcessor::ProcessL( |
|
332 CCalInstance& /*aInstance*/ ) |
|
333 { |
|
334 FUNC_LOG; |
|
335 |
|
336 __ASSERT_DEBUG( EFalse, Panic(EMRCalEventProcessorNotSupported) ); |
|
337 |
|
338 User::Leave( KErrNotSupported ); |
|
339 } |
|
340 |
|
341 // --------------------------------------------------------------------------- |
|
342 // CMRCalEntryProcessor::SwitchProcessorToModeL |
|
343 // --------------------------------------------------------------------------- |
|
344 // |
|
345 void CMRCalEntryProcessor::SwitchProcessorToModeL( |
|
346 TMRProcessorMode aMode ) |
|
347 { |
|
348 FUNC_LOG; |
|
349 |
|
350 if ( EMRProcessorModeView == aMode ) |
|
351 { |
|
352 iScenData.iViewMode = EESMRViewMR; |
|
353 } |
|
354 else if ( EMRProcessorModeEdit == aMode ) |
|
355 { |
|
356 iScenData.iViewMode = EESMREditMR; |
|
357 } |
|
358 } |
|
359 |
|
360 // --------------------------------------------------------------------------- |
|
361 // CMRCalEntryProcessor::ESMREntryL |
|
362 // --------------------------------------------------------------------------- |
|
363 // |
|
364 MESMRCalEntry& CMRCalEntryProcessor::ESMREntryL() |
|
365 { |
|
366 FUNC_LOG; |
|
367 |
|
368 if ( !iCalEntry ) |
|
369 { |
|
370 CreateEntryL( *(*iCalEntries)[ 0 ], ETrue ); |
|
371 } |
|
372 |
|
373 return *iCalEntry; |
|
374 } |
|
375 |
|
376 // --------------------------------------------------------------------------- |
|
377 // CMRCalEntryProcessor::ResetL |
|
378 // --------------------------------------------------------------------------- |
|
379 // |
|
380 void CMRCalEntryProcessor::ResetL() |
|
381 { |
|
382 FUNC_LOG; |
|
383 |
|
384 iScenData.iViewMode = EESMRViewUndef; |
|
385 iScenData.iCallingApp = EESMRAppCalendar; |
|
386 |
|
387 delete iCalEntry; |
|
388 iCalEntry = NULL; |
|
389 } |
|
390 |
|
391 // --------------------------------------------------------------------------- |
|
392 // CMRCalEntryProcessor::ProcessOutputParametersL |
|
393 // --------------------------------------------------------------------------- |
|
394 // |
|
395 void CMRCalEntryProcessor::ProcessOutputParametersL( |
|
396 MAgnEntryUi::TAgnEntryUiOutParams& aOutParams, |
|
397 TESMRCommand aCommand ) |
|
398 { |
|
399 FUNC_LOG; |
|
400 |
|
401 ASSERT( iCalEntry ); |
|
402 |
|
403 TBool handlingInstanceOnly( |
|
404 MESMRCalEntry::EESMRCalEntryMeeting == iCalEntry->Type() && |
|
405 iCalEntry->IsRecurrentEventL() && |
|
406 MESMRCalEntry::EESMRThisOnly == iCalEntry->RecurrenceModRule() ); |
|
407 |
|
408 switch ( aCommand ) |
|
409 { |
|
410 case EESMRCmdSaveMR: |
|
411 case EESMRCmdTodoMarkAsDone: |
|
412 case EESMRCmdTodoMarkAsNotDone: |
|
413 { |
|
414 aOutParams.iAction = MAgnEntryUi::EMeetingSaved; |
|
415 if ( handlingInstanceOnly ) |
|
416 { |
|
417 aOutParams.iAction = MAgnEntryUi::EInstanceRescheduled; |
|
418 } |
|
419 } |
|
420 break; |
|
421 |
|
422 case EESMRCmdCalEntryUIDelete: |
|
423 case EESMRCmdDeleteMR: |
|
424 { |
|
425 aOutParams.iAction = MAgnEntryUi::EMeetingDeleted; |
|
426 if ( handlingInstanceOnly ) |
|
427 { |
|
428 aOutParams.iAction = MAgnEntryUi::EInstanceDeleted; |
|
429 } |
|
430 } |
|
431 break; |
|
432 |
|
433 default: |
|
434 { |
|
435 aOutParams.iAction = MAgnEntryUi::ENoAction; |
|
436 } |
|
437 break; |
|
438 } |
|
439 |
|
440 if ( handlingInstanceOnly ) |
|
441 { |
|
442 aOutParams.iNewInstanceDate = |
|
443 iCalEntry->Entry().StartTimeL(); |
|
444 } |
|
445 else if ( MAgnEntryUi::ENoAction != aOutParams.iAction ) |
|
446 { |
|
447 TCalTime instanceTime = iCalEntry->Entry().StartTimeL(); |
|
448 |
|
449 if ( MESMRCalEntry::EESMRAllInSeries == iCalEntry->RecurrenceModRule() ) |
|
450 { |
|
451 // For recurrent event there might be exceptions |
|
452 // and therefore the first possible start time |
|
453 // is set to putput params |
|
454 TCalTime end; |
|
455 CESMRRecurrenceInfoHandler* recurrenceHandler = |
|
456 CESMRRecurrenceInfoHandler::NewLC( iCalEntry->Entry() ); |
|
457 |
|
458 recurrenceHandler->GetFirstInstanceTimeL( |
|
459 instanceTime, |
|
460 end ); |
|
461 |
|
462 CleanupStack::PopAndDestroy( recurrenceHandler ); |
|
463 recurrenceHandler = NULL; |
|
464 aOutParams.iNewInstanceDate = instanceTime; |
|
465 } |
|
466 else |
|
467 { |
|
468 aOutParams.iNewInstanceDate.SetTimeLocalL( |
|
469 iCalEntry->Entry().StartTimeL().TimeLocalL() ); |
|
470 } |
|
471 } |
|
472 else |
|
473 { |
|
474 aOutParams.iNewInstanceDate.SetTimeLocalL( Time::NullTTime() ); |
|
475 } |
|
476 } |
|
477 |
|
478 // --------------------------------------------------------------------------- |
|
479 // CMRCalEntryProcessor::SetPhoneOwnerL |
|
480 // --------------------------------------------------------------------------- |
|
481 // |
|
482 TBool CMRCalEntryProcessor::IsDataOk() const |
|
483 { |
|
484 FUNC_LOG; |
|
485 |
|
486 TBool retVal( ETrue ); |
|
487 |
|
488 // Check that all values has been set and there is no |
|
489 // undefined values. Policies cannot be resolved, if |
|
490 // scenario data is undefined. |
|
491 if ( EESMRRoleUndef == iScenData.iRole || |
|
492 EESMRViewUndef == iScenData.iViewMode || |
|
493 EESMRAppUndef == iScenData.iCallingApp ) |
|
494 { |
|
495 retVal = EFalse; |
|
496 } |
|
497 |
|
498 return retVal; |
|
499 } |
|
500 |
|
501 // --------------------------------------------------------------------------- |
|
502 // CMRCalEntryProcessor::CreateEntryL |
|
503 // --------------------------------------------------------------------------- |
|
504 // |
|
505 void CMRCalEntryProcessor::CreateEntryL( CCalEntry& aEntry, |
|
506 TBool aSetDefaultValuesToEntry ) |
|
507 { |
|
508 FUNC_LOG; |
|
509 if ( !iCalEntry ) |
|
510 { |
|
511 if ( iParams && aSetDefaultValuesToEntry ) |
|
512 { |
|
513 CCalEntry::TType entryType( aEntry.EntryTypeL() ); |
|
514 if ( CCalEntry::EAppt == entryType || |
|
515 CCalEntry::EAnniv == entryType ) |
|
516 { |
|
517 // Setting start and end time for child entry |
|
518 ESMREntryHelper::CheckRepeatUntilValidityL( |
|
519 aEntry, |
|
520 iParams->iInstanceDate ); |
|
521 |
|
522 ESMREntryHelper::SetInstanceStartAndEndL( |
|
523 aEntry, |
|
524 aEntry, |
|
525 iParams->iInstanceDate ); |
|
526 } |
|
527 else if ( CCalEntry::ETodo == entryType ) |
|
528 { |
|
529 TTime start = aEntry.StartTimeL().TimeLocalL(); |
|
530 TTime end = aEntry.EndTimeL().TimeLocalL(); |
|
531 |
|
532 if ( Time::NullTTime() == start && |
|
533 Time::NullTTime() == end ) |
|
534 { |
|
535 start.HomeTime(); |
|
536 end.HomeTime(); |
|
537 |
|
538 TCalTime startCalTime; |
|
539 TCalTime endCalTime; |
|
540 |
|
541 startCalTime.SetTimeLocalL( start ); |
|
542 endCalTime.SetTimeLocalL( end ); |
|
543 |
|
544 aEntry.SetStartAndEndTimeL( startCalTime, endCalTime ); |
|
545 } |
|
546 } |
|
547 } |
|
548 |
|
549 iCalEntry = CMRCalEntry::NewL( aEntry, iDbMgr ); |
|
550 } |
|
551 } |
|
552 |
|
553 // --------------------------------------------------------------------------- |
|
554 // CMRCalEntryProcessor::ConvertTextFieldLineFeedsL |
|
555 // --------------------------------------------------------------------------- |
|
556 // |
|
557 void CMRCalEntryProcessor::ConvertTextFieldLineFeedsL() |
|
558 { |
|
559 FUNC_LOG; |
|
560 |
|
561 TInt entryCount( iCalEntries->Count() ); |
|
562 for (TInt i(0); i < entryCount; ++i ) |
|
563 { |
|
564 ConvertTextFieldLineFeedsL( *(*iCalEntries)[ i ] ); |
|
565 } |
|
566 } |
|
567 |
|
568 |
|
569 // --------------------------------------------------------------------------- |
|
570 // CMRCalEntryProcessor::ConvertTextFieldLineFeedsL |
|
571 // --------------------------------------------------------------------------- |
|
572 // |
|
573 void CMRCalEntryProcessor::ConvertTextFieldLineFeedsL( CCalEntry& aEntry ) |
|
574 { |
|
575 TInt ret = aEntry.DescriptionL().Find(KReplaceLineFeedChars); |
|
576 if ( ret != KErrNotFound ) |
|
577 { |
|
578 HBufC* newDescription = |
|
579 ReplaceCharactersFromBufferLC( |
|
580 aEntry.DescriptionL(), |
|
581 KReplaceLineFeedChars(), |
|
582 KLineFeed() ); |
|
583 aEntry.SetDescriptionL( *newDescription ); |
|
584 CleanupStack::PopAndDestroy( newDescription ); |
|
585 } |
|
586 |
|
587 // Location field: |
|
588 ret = aEntry.LocationL().Find(KReplaceLineFeedChars); |
|
589 if ( ret != KErrNotFound ) |
|
590 { |
|
591 HBufC* newLocation = |
|
592 ReplaceCharactersFromBufferLC( |
|
593 aEntry.DescriptionL(), |
|
594 KReplaceLineFeedChars(), |
|
595 KLineFeed() ); |
|
596 aEntry.SetDescriptionL( *newLocation ); |
|
597 CleanupStack::PopAndDestroy( newLocation ); |
|
598 } |
|
599 |
|
600 // Subject field: |
|
601 ret = aEntry.SummaryL().Find(KReplaceLineFeedChars); |
|
602 if ( ret != KErrNotFound ) |
|
603 { |
|
604 HBufC* newSubject = |
|
605 ReplaceCharactersFromBufferLC( |
|
606 aEntry.DescriptionL(), |
|
607 KReplaceLineFeedChars(), |
|
608 KLineFeed() ); |
|
609 aEntry.SetDescriptionL( *newSubject ); |
|
610 CleanupStack::PopAndDestroy( newSubject ); |
|
611 } |
|
612 } |
|
613 |
|
614 // EOF |
|
615 |