Symbian port 0.3.0 as of blead@25911
[p5sagit/p5-mst-13.2.git] / symbian / PerlApp.cpp
1 /* Copyright (c) 2004-2005 Nokia. All rights reserved. */
2
3 /* The PerlApp application is licensed under the same terms as Perl itself.
4  * Note that this PerlApp is for Symbian/Series 60/80 smartphones and it has
5  * nothing whatsoever to do with the ActiveState PerlApp. */
6
7 #include "PerlApp.h"
8
9 #ifdef __SERIES60__
10 # include <avkon.hrh>
11 # include <aknnotewrappers.h> 
12 # include <AknCommonDialogs.h>
13 # ifndef __SERIES60_1X__
14 #  include <CAknFileSelectionDialog.h>
15 # endif
16 #endif /* #ifdef __SERIES60__ */
17
18 #ifdef __SERIES80__
19 # include <eikon.hrh>
20 # include <cknflash.h>
21 # include <ckndgopn.h>
22 # include <ckndgfob.h>
23 # include <eiklabel.h>
24 # include <cknconf.h>
25 #endif /* #ifdef __SERIES80__ */
26
27 #ifdef __UIQ__
28 # include <qikon.hrh>
29 # include <eikedwin.h>
30 # include <eiklabel.h>
31 #endif /* #ifdef __UIQ__ */
32
33 #include <apparc.h>
34 #include <e32base.h>
35 #include <e32cons.h>
36 #include <eikenv.h>
37 #include <bautils.h>
38 #include <eikappui.h>
39 #include <utf.h>
40 #include <f32file.h>
41
42 #include <coemain.h>
43
44 #ifndef PerlAppMinimal
45
46 #include "PerlApp.hrh"
47
48 #include "PerlApp.rsg"
49
50 #ifdef __SERIES80__
51 #include "Eikon.rsg"
52 #endif /* #ifdef __SERIES80__ */
53
54 #endif //#ifndef PerlAppMinimal
55
56 #include "EXTERN.h"
57 #include "perl.h"
58 #include "PerlBase.h"
59
60 const TUid KPerlAppUid = {
61 #ifdef PerlAppMinimalUid
62   PerlAppMinimalUid
63 #else
64   0x102015F6
65 #endif
66 };
67
68 _LIT(KDefaultScript, "default.pl");
69
70 // This is like the Symbian _LIT() but without the embedded L prefix,
71 // which enables using #defined constants (which need to carry their
72 // own L prefix).
73 #ifndef _LIT_NO_L
74 # define _LIT_NO_L(n, s) static const TLitC<sizeof(s)/2> n={sizeof(s)/2-1,s}
75 #endif // #ifndef _LIT_NO_L
76
77 #ifdef PerlAppMinimalName
78 _LIT_NO_L(KAppName, PerlAppMinimalName);
79 #else
80 _LIT(KAppName, "PerlApp");
81 #endif
82
83 #ifndef PerlAppMinimal
84
85 _LIT_NO_L(KFlavor, PERL_SYMBIANSDK_FLAVOR);
86 _LIT(KAboutFormat,
87      "Perl %d.%d.%d, Symbian port %d.%d.%d, built for %S SDK %d.%d");
88 _LIT(KCopyrightFormat,
89      "Copyright 1987-2005 Larry Wall and others, Symbian port Copyright Nokia 2004-2005");
90 _LIT(KInboxPrefix, "\\System\\Mail\\");
91 _LIT(KScriptPrefix, "\\Perl\\");
92
93 _LIT8(KModulePrefix, SITELIB); // SITELIB from Perl config.h
94
95 typedef TBuf<256>  TMessageBuffer;
96 typedef TBuf8<256> TPeekBuffer;
97 typedef TBuf8<256> TFileName8;
98
99 #endif // #ifndef PerlAppMinimal
100
101 // Usage: DEBUG_PRINTF((_L("%S"), &aStr))
102 #if 1
103 #define DEBUG_PRINTF(s) {TMessageBuffer message; message.Format s; YesNoDialogL(message);}
104 #endif
105
106 static void DoRunScriptL(TFileName aScriptName);
107
108 TUid CPerlAppApplication::AppDllUid() const
109 {
110     return KPerlAppUid;
111 }
112
113 enum TPerlAppPanic 
114 {
115     EPerlAppCommandUnknown = 1
116 };
117
118 void Panic(TPerlAppPanic aReason)
119 {
120     User::Panic(KAppName, aReason);
121 }
122
123 void CPerlAppUi::ConstructL()
124 {
125     BaseConstructL();
126     iAppView = CPerlAppView::NewL(ClientRect());
127     AddToStackL(iAppView);
128     iFs = NULL;
129     CEikonEnv::Static()->DisableExitChecks(ETrue); // Symbian FAQ-0577.
130 }
131
132 CPerlAppUi::~CPerlAppUi()
133 {
134     if (iAppView) {
135         iEikonEnv->RemoveFromStack(iAppView);
136         delete iAppView;
137         iAppView = NULL;
138     }
139     if (iFs) {
140         delete iFs;
141         iFs = NULL;
142     }
143     if (iDoorObserver) // Otherwise the embedding application waits forever.
144         iDoorObserver->NotifyExit(MApaEmbeddedDocObserver::EEmpty);
145 }
146
147 #ifndef PerlAppMinimal
148
149 static TBool OkCancelDialogL(TDesC& aMessage)
150 {
151 #ifdef __SERIES60__
152     CAknNoteDialog* dlg =
153         new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
154     dlg->PrepareLC(R_OK_CANCEL_DIALOG);
155     dlg->SetTextL(aMessage);
156     return dlg->RunDlgLD() == EAknSoftkeyOk;
157 #endif /* #ifdef __SERIES60__ */
158 #ifdef __SERIES80__
159     return CCknConfirmationDialog::RunDlgWithDefaultIconLD(aMessage, R_EIK_BUTTONS_CANCEL_OK);
160 #endif /* #ifdef __SERIES80__ */
161 #ifdef __UIQ__
162     CEikDialog* dlg = new (ELeave) CEikDialog();
163     return dlg->ExecuteLD(R_OK_CANCEL_DIALOG) == EEikBidOk;
164 #endif /* #ifdef __UIQ__ */
165 }
166
167 static TBool YesNoDialogL(TDesC& aMessage)
168 {
169 #ifdef __SERIES60__
170     CAknNoteDialog* dlg =
171         new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
172     dlg->PrepareLC(R_YES_NO_DIALOG);
173     dlg->SetTextL(aMessage);
174     return dlg->RunDlgLD() == EAknSoftkeyOk;
175 #endif /* #ifdef __SERIES60__ */
176 #ifdef __SERIES80__
177     return CCknConfirmationDialog::RunDlgWithDefaultIconLD(aMessage, R_EIK_BUTTONS_NO_YES);
178 #endif /* #ifdef __SERIES80__ */
179 #ifdef __UIQ__
180     CEikDialog* dlg = new (ELeave) CEikDialog();
181     return dlg->ExecuteLD(R_YES_NO_DIALOG) == EEikBidOk;
182 #endif /* #ifdef __UIQ__ */
183 }
184
185 static void InformationNoteL(TDesC& aMessage)
186 {
187 #ifdef __SERIES60__
188     CAknInformationNote* note = new (ELeave) CAknInformationNote;
189     note->ExecuteLD(aMessage);
190 #endif /* #ifdef __SERIES60__ */
191 #if defined(__SERIES80__) || defined(__UIQ__)
192     CEikonEnv::Static()->InfoMsg(aMessage);
193 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
194 }
195
196 static TInt WarningNoteL(TDesC& aMessage)
197 {
198 #ifdef __SERIES60__
199     CAknWarningNote* note = new (ELeave) CAknWarningNote;
200     return note->ExecuteLD(aMessage);
201 #endif /* #ifdef __SERIES60__ */
202 #if defined(__SERIES80__) || defined(__UIQ__)
203     CEikonEnv::Static()->AlertWin(aMessage);
204     return ETrue;
205 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
206 }
207
208 #if defined(__SERIES80__) || defined(__UIQ__)
209
210 CPerlAppTextQueryDialog::CPerlAppTextQueryDialog(HBufC*& aBuffer) :
211   iData(aBuffer)
212 {
213 }
214
215 TBool CPerlAppTextQueryDialog::OkToExitL(TInt /* aKeycode */)
216 {
217   iData = static_cast<CEikEdwin*>(Control(EPerlAppTextQueryInputField))->GetTextInHBufL();
218   return ETrue;
219 }
220
221 void CPerlAppTextQueryDialog::PreLayoutDynInitL()
222 {
223   SetTitleL(iTitle);
224   CEikLabel* promptLabel = ControlCaption(EPerlAppTextQueryInputField);
225   promptLabel->SetTextL(iPrompt);
226 }
227
228 /* TODO: OfferKeyEventL() so that newline can be seen as 'OK'.
229  * Or a hotkey for the button? */
230
231 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
232
233 static TInt TextQueryDialogL(const TDesC& aTitle, const TDesC& aPrompt, TDes& aData, const TInt aMaxLength)
234 {
235 #ifdef __SERIES60__
236     CAknTextQueryDialog* dlg =
237         new (ELeave) CAknTextQueryDialog(aData);
238     dlg->SetPromptL(aPrompt);
239     dlg->SetMaxLength(aMaxLength);
240     return dlg->ExecuteLD(R_TEXT_QUERY_DIALOG);
241 #endif /* #ifdef __SERIES60__ */
242 #if defined(__SERIES80__) || defined(__UIQ__)
243     HBufC* data = NULL;
244     CPerlAppTextQueryDialog* dlg =
245       new (ELeave) CPerlAppTextQueryDialog(data);
246     dlg->iTitle.Set(aTitle);
247     dlg->iPrompt.Set(aPrompt);
248     dlg->iMaxLength = aMaxLength;
249     if (dlg->ExecuteLD(R_PERL_ONELINER_DIALOG)) {
250       aData.Copy(*data);
251       return ETrue;
252     }
253     return EFalse;
254 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
255 }
256
257 static TBool FileQueryDialogL(TDes& aFilename)
258 {
259 #ifdef __SERIES60__
260   return AknCommonDialogs::RunSelectDlgLD(aFilename,
261                                           R_MEMORY_SELECTION_DIALOG);
262 #endif /* #ifdef __SERIES60__ */
263 #ifdef __SERIES80__
264   if (CCknOpenFileDialog::RunDlgLD(aFilename,
265                                     CCknFileListDialogBase::EShowAllDrives
266                                    |CCknFileListDialogBase::EShowSystemFilesAndFolders
267                                    |CCknFileListDialogBase::EShowBothFilesAndFolders
268                                    )) {
269     TEntry aEntry; // Be paranoid and check that the file is there.
270     RFs aFs;
271     aFs.Connect();
272     if (aFs.Entry(aFilename, aEntry) == KErrNone)
273       return ETrue;
274     else
275       CEikonEnv::Static()->InfoMsg(_L("File not found"));
276   }
277   return EFalse;
278 #endif /* #ifdef __SERIES80__ */
279 #ifdef __UIQ__
280   return EFalse; // No filesystem access in UIQ 2.x!
281 #endif /* #ifdef __UIQ__ */
282 }
283
284 // The isXXX() come from the Perl headers.
285 #define FILENAME_IS_ABSOLUTE(n) \
286         (isALPHA(((n)[0])) && ((n)[1]) == ':' && ((n)[2]) == '\\')
287
288 static TBool IsInPerl(TFileName aFileName)
289 {
290     TInt offset = aFileName.FindF(KScriptPrefix);
291     return ((offset == 0 && // \foo
292              aFileName[0] == '\\')
293             ||
294             (offset == 2 && // x:\foo
295              FILENAME_IS_ABSOLUTE(aFileName)));
296 }
297
298 static TBool IsInInbox(TFileName aFileName)
299 {
300     TInt offset = aFileName.FindF(KInboxPrefix);
301     return ((offset == 0 && // \foo
302              aFileName[0] == '\\')
303             ||
304             (offset == 2 && // x:\foo
305              FILENAME_IS_ABSOLUTE(aFileName)));
306 }
307
308 static TBool IsPerlModule(TParsePtrC aParsed)
309 {
310     return aParsed.Ext().CompareF(_L(".pm")) == 0; 
311 }
312
313 static TBool IsPerlScript(TParsePtrC aParsed)
314 {
315     return aParsed.Ext().CompareF(_L(".pl")) == 0; 
316 }
317
318 static void CopyFromInboxL(RFs aFs, const TFileName& aSrc, const TFileName& aDst)
319 {
320     TBool proceed = ETrue;
321     TMessageBuffer message;
322
323     message.Format(_L("%S is untrusted. Install only if you trust provider."), &aDst);
324     if (OkCancelDialogL(message)) {
325         message.Format(_L("Install as %S?"), &aDst);
326         if (OkCancelDialogL(message)) {
327             if (BaflUtils::FileExists(aFs, aDst)) {
328                 message.Format(_L("Replace old %S?"), &aDst);
329                 if (!OkCancelDialogL(message))
330                     proceed = EFalse;
331             }
332             if (proceed) {
333                 // Create directory?
334                 TInt err = BaflUtils::CopyFile(aFs, aSrc, aDst);
335                 if (err == KErrNone) {
336                     message.Format(_L("Installed %S"), &aDst);
337                     InformationNoteL(message);
338                 }
339                 else {
340                     message.Format(_L("Failure %d installing %S"), err, &aDst);
341                     WarningNoteL(message);
342                 }
343             }
344         }
345     }
346 }
347
348 static TBool FindPerlPackageName(TPeekBuffer aPeekBuffer, TInt aOff, TFileName& aFn)
349 {
350     aFn.SetMax();
351     TInt m = aFn.MaxLength();
352     TInt n = aPeekBuffer.Length();
353     TInt i = 0;
354     TInt j = aOff;
355
356     aFn.SetMax();
357     // The following is a little regular expression
358     // engine that matches Perl package names.
359     if (j < n && isSPACE(aPeekBuffer[j])) {
360         while (j < n && isSPACE(aPeekBuffer[j])) j++;
361         if (j < n && isALPHA(aPeekBuffer[j])) {
362             while (j < n && isALNUM(aPeekBuffer[j])) {
363                 while (j < n &&
364                        isALNUM(aPeekBuffer[j]) &&
365                        i < m)
366                     aFn[i++] = aPeekBuffer[j++];
367                 if (j + 1 < n &&
368                     aPeekBuffer[j    ] == ':' &&
369                     aPeekBuffer[j + 1] == ':' &&
370                     i < m) {
371                     aFn[i++] = '\\';
372                     j += 2;
373                     if (j < n &&
374                         isALPHA(aPeekBuffer[j])) {
375                         while (j < n &&
376                                isALNUM(aPeekBuffer[j]) &&
377                                i < m) 
378                             aFn[i++] = aPeekBuffer[j++];
379                     }
380                 }
381             }
382             while (j < n && isSPACE(aPeekBuffer[j])) j++;
383             if (j < n && aPeekBuffer[j] == ';' && i + 3 < m) {
384                 aFn.SetLength(i);
385                 aFn.Append(_L(".pm"));
386                 return ETrue;
387             }
388         }
389     }
390     return EFalse;
391 }
392
393 static void GuessPerlModule(TFileName& aGuess, TPeekBuffer aPeekBuffer, TParse aDrive)
394 {
395    TInt offset = aPeekBuffer.Find(_L8("package"));
396    if (offset != KErrNotFound) {
397        const TInt KPackageLen = 7;
398        TFileName q;
399
400        if (!FindPerlPackageName(aPeekBuffer, offset + KPackageLen, q))
401            return;
402
403        TFileName8 p;
404        p.Copy(aDrive.Drive());
405        p.Append(KModulePrefix);
406
407        aGuess.SetMax();
408        if (p.Length() + 1 + q.Length() < aGuess.MaxLength()) {
409            TInt i = 0, j;
410
411            for (j = 0; j < p.Length(); j++)
412                aGuess[i++] = p[j];
413            aGuess[i++] = '\\';
414            for (j = 0; j < q.Length(); j++)
415                aGuess[i++] = q[j];
416            aGuess.SetLength(i);
417        }
418        else
419            aGuess.SetLength(0);
420    }
421 }
422
423 static TBool LooksLikePerlL(TPeekBuffer aPeekBuffer)
424 {
425     return aPeekBuffer.Left(2).Compare(_L8("#!")) == 0 &&
426            aPeekBuffer.Find(_L8("perl")) != KErrNotFound;
427 }
428
429 static TBool InstallStuffL(const TFileName &aSrc, TParse aDrive, TParse aFile, TPeekBuffer aPeekBuffer, RFs aFs)
430 {
431     TFileName aDst;
432     TPtrC drive  = aDrive.Drive();
433     TPtrC namext = aFile.NameAndExt(); 
434
435     aDst.Format(_L("%S%S%S"), &drive, &KScriptPrefix, &namext);
436     if (!IsPerlScript(aDst) && !LooksLikePerlL(aPeekBuffer)) {
437         aDst.SetLength(0);
438         if (IsPerlModule(aDst))
439             GuessPerlModule(aDst, aPeekBuffer, aDrive);
440     }
441     if (aDst.Length() > 0) {
442         CopyFromInboxL(aFs, aSrc, aDst);
443         return ETrue;
444     }
445
446     return EFalse;
447 }
448
449 static TBool RunStuffL(const TFileName& aScriptName, TPeekBuffer aPeekBuffer)
450 {
451     TBool isModule = EFalse;
452
453     if (IsInPerl(aScriptName) &&
454         (IsPerlScript(aScriptName) ||
455          (isModule = IsPerlModule(aScriptName)) ||
456          LooksLikePerlL(aPeekBuffer))) {
457         TMessageBuffer message;
458
459         if (isModule)
460             message.Format(_L("Really run module %S?"), &aScriptName);
461         else 
462             message.Format(_L("Run %S?"), &aScriptName);
463         if (YesNoDialogL(message))
464             DoRunScriptL(aScriptName);
465         return ETrue;
466     }
467
468     return EFalse;
469 }
470
471 void CPerlAppUi::InstallOrRunL(const TFileName& aFileName)
472 {
473     TParse aFile;
474     TParse aDrive;
475     TMessageBuffer message;
476
477     aFile.Set(aFileName, NULL, NULL);
478     if (FILENAME_IS_ABSOLUTE(aFileName)) {
479         aDrive.Set(aFileName, NULL, NULL);
480     } else {
481         TFileName appName =
482           CEikonEnv::Static()->EikAppUi()->Application()->AppFullName();
483         aDrive.Set(appName, NULL, NULL);
484     }
485     if (!iFs)
486         iFs = &CEikonEnv::Static()->FsSession();
487     RFile f;
488     TInt err = f.Open(*iFs, aFileName, EFileRead);
489     if (err == KErrNone) {
490         TPeekBuffer aPeekBuffer;
491         err = f.Read(aPeekBuffer);
492         f.Close();  // Release quickly.
493         if (err == KErrNone) {
494             if (!(IsInInbox(aFileName) ?
495                   InstallStuffL(aFileName, aDrive, aFile, aPeekBuffer, *iFs) :
496                   RunStuffL(aFileName, aPeekBuffer))) {
497                 message.Format(_L("Failed for file %S"), &aFileName);
498                 WarningNoteL(message);
499             }
500         } else {
501             message.Format(_L("Error %d reading %S"), err, &aFileName);
502             WarningNoteL(message);
503         }
504     } else {
505         message.Format(_L("Error %d opening %S"), err, &aFileName);
506         WarningNoteL(message);
507     }
508     if (iDoorObserver)
509         delete CEikonEnv::Static()->EikAppUi();
510     else
511         Exit();
512 }
513
514 #endif // #ifndef PerlAppMinimal
515
516 static void DoRunScriptL(TFileName aScriptName)
517 {
518     CPerlBase* perl = CPerlBase::NewInterpreterLC();
519     TRAPD(error, perl->RunScriptL(aScriptName));
520 #ifndef PerlAppMinimal
521     if (error != KErrNone) {
522         TMessageBuffer message;
523         message.Format(_L("Error %d"), error);
524         YesNoDialogL(message);
525     }
526 #endif // #ifndef PerlAppMinimal
527     CleanupStack::PopAndDestroy(perl);
528 }
529
530 #ifndef PerlAppMinimal
531
532 void CPerlAppUi::OpenFileL(const TDesC& aFileName)
533 {
534     InstallOrRunL(aFileName);
535     return;
536 }
537
538 #endif // #ifndef PerlAppMinimal
539
540 TBool CPerlAppUi::ProcessCommandParametersL(TApaCommand aCommand, TFileName& /* aDocumentName */, const TDesC8& /* aTail */)
541 {
542     if (aCommand == EApaCommandRun) {
543         TFileName appName = Application()->AppFullName();
544         TParse p;
545         p.Set(KDefaultScript, &appName, NULL);
546         TEntry aEntry;
547         RFs aFs;
548         aFs.Connect();
549         if (aFs.Entry(p.FullName(), aEntry) == KErrNone) {
550             DoRunScriptL(p.FullName());
551             Exit();
552         }
553     }
554     return aCommand == EApaCommandOpen ? ETrue : EFalse;
555 }
556
557 #ifndef PerlAppMinimal
558
559 void CPerlAppUi::SetFs(const RFs& aFs)
560 {
561     iFs = (RFs*) &aFs;
562 }
563
564 #endif // #ifndef PerlAppMinimal
565
566 static void DoHandleCommandL(TInt aCommand) {
567 #ifndef PerlAppMinimal
568     TMessageBuffer message;
569 #endif // #ifndef PerlAppMinimal
570
571     switch(aCommand)
572     {
573 #ifndef PerlAppMinimal
574     case EPerlAppCommandAbout:
575         {
576             message.Format(KAboutFormat,
577                            PERL_REVISION,
578                            PERL_VERSION,
579                            PERL_SUBVERSION,
580                            PERL_SYMBIANPORT_MAJOR,
581                            PERL_SYMBIANPORT_MINOR,
582                            PERL_SYMBIANPORT_PATCH,
583                            &KFlavor,
584                            PERL_SYMBIANSDK_MAJOR,
585                            PERL_SYMBIANSDK_MINOR
586                            );
587             InformationNoteL(message);
588         }
589         break;
590     case EPerlAppCommandTime:
591         {
592             CPerlBase* perl = CPerlBase::NewInterpreterLC();
593             const char *const argv[] =
594               { "perl", "-le",
595                 "print 'Running in ', $^O, \"\\n\", scalar localtime" };
596             perl->ParseAndRun(sizeof(argv)/sizeof(char*), (char **)argv, 0);
597             CleanupStack::PopAndDestroy(perl);
598         }
599         break;
600      case EPerlAppCommandRunFile:
601         {
602             TFileName aScriptUtf16;
603             aScriptUtf16.Copy(_L("C:\\"));
604             if (FileQueryDialogL(aScriptUtf16))
605               DoRunScriptL(aScriptUtf16);
606         }
607         break;
608      case EPerlAppCommandOneLiner:
609         {
610 #ifdef __SERIES60__
611             _LIT(prompt, "Oneliner:");
612 #endif /* #ifdef __SERIES60__ */
613 #if defined(__SERIES80__) || defined(__UIQ__)
614             _LIT(prompt, "Code:"); // The title has "Oneliner" already.
615 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
616             CPerlAppUi* cAppUi =
617               STATIC_CAST(CPerlAppUi*, CEikonEnv::Static()->EikAppUi());
618             if (TextQueryDialogL(_L("Oneliner"),
619                                  prompt,
620                                  cAppUi->iOneLiner,
621                                  KPerlAppOneLinerSize)) {
622                const TUint KPerlAppUtf8Multi = 3;
623                 TBuf8<KPerlAppUtf8Multi * KPerlAppOneLinerSize> utf8;
624
625                 CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8, cAppUi->iOneLiner);
626                 CPerlBase* perl = CPerlBase::NewInterpreterLC();
627                 int argc = 3;
628                 char **argv = (char**) malloc(argc * sizeof(char *));
629                 User::LeaveIfNull(argv);
630
631                 TCleanupItem argvCleanupItem = TCleanupItem(free, argv);
632                 CleanupStack::PushL(argvCleanupItem);
633                 argv[0] = (char *) "perl";
634                 argv[1] = (char *) "-le";
635                 argv[2] = (char *) utf8.PtrZ();
636                 perl->ParseAndRun(argc, argv);
637                 CleanupStack::PopAndDestroy(2, perl);
638             }
639         }
640         break;
641      case EPerlAppCommandCopyright:
642         {
643             message.Format(KCopyrightFormat);
644             InformationNoteL(message);
645         }
646         break;
647      case EPerlAppCommandAboutCopyright:
648         {
649             TMessageBuffer m1;
650             TMessageBuffer m2;
651             m1.Format(KAboutFormat,
652                       PERL_REVISION,
653                       PERL_VERSION,
654                       PERL_SUBVERSION,
655                       PERL_SYMBIANPORT_MAJOR,
656                       PERL_SYMBIANPORT_MINOR,
657                       PERL_SYMBIANPORT_PATCH,
658                       &KFlavor,
659                       PERL_SYMBIANSDK_MAJOR,
660                       PERL_SYMBIANSDK_MINOR
661                       );
662             InformationNoteL(m1);
663             User::After((TTimeIntervalMicroSeconds32) (1000*1000));
664             m2.Format(KCopyrightFormat);
665             InformationNoteL(m2);
666         }
667         break;
668 #endif // #ifndef PerlAppMinimal
669     default:
670         Panic(EPerlAppCommandUnknown);
671     }
672 }
673
674 #ifdef __SERIES60__
675
676 void CPerlAppUi::HandleCommandL(TInt aCommand)
677 {
678     switch(aCommand)
679     {
680     case EEikCmdExit:
681     case EAknSoftkeyExit:
682         Exit();
683         break;
684     default:
685         DoHandleCommandL(aCommand);
686         break;
687     }
688 }
689
690 #endif /* #ifdef __SERIES60__ */
691
692 #if defined(__SERIES80__) || defined(__UIQ__)
693
694 void CPerlAppView::HandleCommandL(TInt aCommand) {
695     DoHandleCommandL(aCommand);
696 }
697
698 void CPerlAppUi::HandleCommandL(TInt aCommand) {
699     switch(aCommand)
700     {
701     case EEikCmdExit:
702         Exit();
703         break;
704     default:
705         iAppView->HandleCommandL(aCommand);
706         break;
707     }
708 }
709
710 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
711
712 CPerlAppView* CPerlAppView::NewL(const TRect& aRect)
713 {
714     CPerlAppView* self = CPerlAppView::NewLC(aRect);
715     CleanupStack::Pop(self);
716     return self;
717 }
718
719 CPerlAppView* CPerlAppView::NewLC(const TRect& aRect)
720 {
721     CPerlAppView* self = new (ELeave) CPerlAppView;
722     CleanupStack::PushL(self);
723     self->ConstructL(aRect);
724     return self;
725 }
726
727 void CPerlAppView::ConstructL(const TRect& aRect)
728 {
729     CreateWindowL();
730     SetRect(aRect);
731     ActivateL();
732 }
733
734 CPerlAppView::~CPerlAppView()
735 {
736 }
737
738 void CPerlAppView::Draw(const TRect& /*aRect*/) const
739 {
740     CWindowGc& gc = SystemGc();
741     TRect rect = Rect();
742     gc.Clear(rect);
743 }
744
745 CApaDocument* CPerlAppApplication::CreateDocumentL() 
746 {
747     CPerlAppDocument* document = new (ELeave) CPerlAppDocument(*this);
748     return document;
749 }
750
751 CEikAppUi* CPerlAppDocument::CreateAppUiL()
752 {
753     CPerlAppUi* appui = new (ELeave) CPerlAppUi();
754     return appui;
755 }
756
757
758 #ifndef PerlAppMinimal
759
760 CFileStore* CPerlAppDocument::OpenFileL(TBool aDoOpen, const TDesC& aFileName, RFs& aFs)
761 {
762     CPerlAppUi* appui =
763       STATIC_CAST(CPerlAppUi*, CEikonEnv::Static()->EikAppUi());
764     appui->SetFs(aFs);
765     if (aDoOpen)
766         appui->OpenFileL(aFileName);
767     return NULL;
768 }
769
770 #endif // #ifndef PerlAppMinimal
771
772 EXPORT_C CApaApplication* NewApplication() 
773 {
774     return new CPerlAppApplication;
775 }
776
777 GLDEF_C TInt E32Dll(TDllReason /*aReason*/)
778 {
779     return KErrNone;
780 }
781