home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: OtherApp / OtherApp.zip / osm2_102.zip / balloon.c < prev    next >
C/C++ Source or Header  |  1999-02-01  |  15KB  |  609 lines

  1. /*
  2.  * balloon.c - Message Balloon
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <ctype.h>
  9.  
  10. #define INCL_PM
  11. #include <os2.h>
  12.  
  13. #include "shapewin.h"
  14.  
  15. #include "osmulti2.h"
  16. #include "osmulres.h"
  17.  
  18. /*
  19.  * control message outputs
  20.  */
  21.  
  22. BOOL    useMsg = TRUE  ;
  23. BOOL    useSnd = FALSE ;
  24.  
  25. /*
  26.  * Dimensions for Message Balloon
  27.  */
  28.  
  29. #define TALK_CX         160     /* width            */
  30. #define TALK_CY         104     /* height           */
  31. #define TALK_OFF        16      /* Y above Bitmap   */
  32.  
  33. #define TALK_RND        15      /* Rounding Value   */
  34. #define TALK_RIM        3       /* Width of Rim     */
  35.  
  36. #define TALK_XADJUST    6
  37. #define TALK_YADJUST    10
  38.  
  39. #define TALK_CLR_FIL    CLR_BLUE        /* Filler       */
  40. #define TALK_CLR_RIM    CLR_BLACK       /* RIM          */
  41. #define TALK_CLR_BAK    CLR_WHITE       /* Message Back */
  42. #define TALK_CLR_CHR    CLR_BLACK       /* Message Char */
  43.  
  44. static  SIZEL       talkSiz ;           /* Size of Message Area     */
  45. static  RECTL       talkRct ;           /* Rect of Message Area     */
  46. static  SIZEL       talkBox ;           /* Size of Char Box         */
  47. static  POINTL      talkPos ;           /* Message Drawing Position */
  48.  
  49. /*
  50.  * Memory DC/PS & Bitmap for Balloon Message
  51.  */
  52.  
  53. HDC     hdcBalloon = NULLHANDLE ;
  54. HPS     hpsBalloon = NULLHANDLE ;
  55. HBITMAP hbmBalloon = NULLHANDLE ;
  56.  
  57. /*
  58.  * Mssages from Resource
  59.  */
  60.  
  61. static  PUCHAR  strNade  = NULL ;
  62. static  PUCHAR  strFuni  = NULL ;
  63. static  PUCHAR  strPsyu  = NULL ;
  64. static  PUCHAR  strNade1 = NULL ;
  65. static  PUCHAR  strNade2 = NULL ;
  66. static  PUCHAR  strNade3 = NULL ;
  67. static  PUCHAR  strNade4 = NULL ;
  68. static  PUCHAR  strFuni1 = NULL ;
  69. static  PUCHAR  strFuni2 = NULL ;
  70. static  PUCHAR  strFuni3 = NULL ;
  71. static  PUCHAR  strFuni4 = NULL ;
  72.  
  73. static  UCHAR   buff[256] ;
  74.  
  75. static  void    freeMessages(void)
  76. {
  77.     if (strNade) free(strNade) ;
  78.     if (strFuni) free(strFuni) ;
  79.     if (strPsyu) free(strPsyu) ;
  80.     
  81.     if (strNade1) free(strNade1) ;
  82.     if (strNade2) free(strNade2) ;
  83.     if (strNade3) free(strNade3) ;
  84.     if (strNade4) free(strNade4) ;
  85.  
  86.     if (strFuni1) free(strFuni1) ;
  87.     if (strFuni2) free(strFuni2) ;
  88.     if (strFuni3) free(strFuni3) ;
  89.     if (strFuni4) free(strFuni4) ;
  90.     
  91.     strNade = strFuni = strPsyu = NULL ;
  92.     strNade1 = strNade2 = strNade3 = strNade4 = NULL ;
  93.     strFuni1 = strFuni2 = strFuni3 = strFuni4 = NULL ;
  94. }
  95.  
  96. static  BOOL    loadMessages(HAB hab)
  97. {
  98.     ULONG   idNade, idFuni, idPsyu ;
  99.     ULONG   idNade1, idNade2, idNade3, idNade4 ;
  100.     ULONG   idFuni1, idFuni2, idFuni3, idFuni4 ;
  101.     
  102.     switch (ProgramLang) {
  103.     case NLS_JA :
  104.         idNade  = IDS_NADE  ;
  105.     idFuni  = IDS_FUNI  ;
  106.     idPsyu  = IDS_PSYU  ;
  107.     idNade1 = IDS_NADE1 ;
  108.     idNade2 = IDS_NADE2 ;
  109.     idNade3 = IDS_NADE3 ;
  110.     idNade4 = IDS_NADE4 ;
  111.     idFuni1 = IDS_FUNI1 ;
  112.     idFuni2 = IDS_FUNI2 ;
  113.     idFuni3 = IDS_FUNI3 ;
  114.     idFuni4 = IDS_FUNI4 ;
  115.         break ;
  116.     case NLS_EN :
  117.         idNade  = IDSE_NADE  ;
  118.     idFuni  = IDSE_FUNI  ;
  119.     idPsyu  = IDSE_PSYU  ;
  120.     idNade1 = IDSE_NADE1 ;
  121.     idNade2 = IDSE_NADE2 ;
  122.     idNade3 = IDSE_NADE3 ;
  123.     idNade4 = IDSE_NADE4 ;
  124.     idFuni1 = IDSE_FUNI1 ;
  125.     idFuni2 = IDSE_FUNI2 ;
  126.     idFuni3 = IDSE_FUNI3 ;
  127.     idFuni4 = IDSE_FUNI4 ;
  128.         break ;
  129.     default     :
  130.         idNade  = IDSE_NADE  ;
  131.     idFuni  = IDSE_FUNI  ;
  132.     idPsyu  = IDSE_PSYU  ;
  133.     idNade1 = IDSE_NADE1 ;
  134.     idNade2 = IDSE_NADE2 ;
  135.     idNade3 = IDSE_NADE3 ;
  136.     idNade4 = IDSE_NADE4 ;
  137.     idFuni1 = IDSE_FUNI1 ;
  138.     idFuni2 = IDSE_FUNI2 ;
  139.     idFuni3 = IDSE_FUNI3 ;
  140.     idFuni4 = IDSE_FUNI4 ;
  141.         break ;
  142.     }
  143.     
  144.     if (strNade == NULL) {
  145.         WinLoadString(hab, NULLHANDLE, idNade, 256, buff) ;
  146.     strNade = strdup(buff) ;
  147.     }
  148.     if (strFuni == NULL) {
  149.         WinLoadString(hab, NULLHANDLE, idFuni, 256, buff) ;
  150.     strFuni = strdup(buff) ;
  151.     }
  152.     if (strPsyu == NULL) {
  153.         WinLoadString(hab, NULLHANDLE, idPsyu, 256, buff) ;
  154.     strPsyu = strdup(buff) ;
  155.     }
  156.     if (strNade1 == NULL) {
  157.         WinLoadString(hab, NULLHANDLE, idNade1, 256, buff) ;
  158.     strNade1 = strdup(buff) ;
  159.     }
  160.     if (strNade2 == NULL) {
  161.         WinLoadString(hab, NULLHANDLE, idNade2, 256, buff) ;
  162.     strNade2 = strdup(buff) ;
  163.     }
  164.     if (strNade3 == NULL) {
  165.         WinLoadString(hab, NULLHANDLE, idNade3, 256, buff) ;
  166.     strNade3 = strdup(buff) ;
  167.     }
  168.     if (strNade4 == NULL) {
  169.         WinLoadString(hab, NULLHANDLE, idNade4, 256, buff) ;
  170.     strNade4 = strdup(buff) ;
  171.     }
  172.  
  173.     if (strFuni1 == NULL) {
  174.         WinLoadString(hab, NULLHANDLE, idFuni1, 256, buff) ;
  175.     strFuni1 = strdup(buff) ;
  176.     }
  177.     if (strFuni2 == NULL) {
  178.         WinLoadString(hab, NULLHANDLE, idFuni2, 256, buff) ;
  179.     strFuni2 = strdup(buff) ;
  180.     }
  181.     if (strFuni3 == NULL) {
  182.         WinLoadString(hab, NULLHANDLE, idFuni3, 256, buff) ;
  183.     strFuni3 = strdup(buff) ;
  184.     }
  185.     if (strFuni4 == NULL) {
  186.         WinLoadString(hab, NULLHANDLE, idFuni4, 256, buff) ;
  187.     strFuni4 = strdup(buff) ;
  188.     }
  189.     
  190.     if (strNade == NULL || strFuni == NULL || strPsyu == NULL) {
  191.         return FALSE ;
  192.     }
  193.     if (strNade1 == NULL || strNade2 == NULL || strNade3 == NULL || strNade4 == NULL) {
  194.         return FALSE ;
  195.     }
  196.     if (strFuni1 == NULL || strFuni2 == NULL || strFuni3 == NULL || strFuni4 == NULL) {
  197.         return FALSE ;
  198.     }
  199.     return TRUE ;
  200. }
  201.  
  202. /*
  203.  * balloonDispose - dispose resources for Balloon Message
  204.  */
  205.  
  206. void    balloonDispose(void)
  207. {
  208.     freeMessages() ;
  209.     
  210.     if (hbmBalloon != NULLHANDLE) {
  211.         GpiDeleteBitmap(hbmBalloon) ;
  212.     hbmBalloon = NULLHANDLE ;
  213.     }
  214.     if (hpsBalloon != NULLHANDLE) {
  215.         GpiSetBitmap(hpsBalloon, NULLHANDLE) ;
  216.         GpiDestroyPS(hpsBalloon) ;
  217.     hpsBalloon == NULLHANDLE ;
  218.     }
  219.     if (hdcBalloon != NULLHANDLE) {
  220.         DevCloseDC(hdcBalloon) ;
  221.     hdcBalloon = NULLHANDLE ;
  222.     }
  223. }
  224.  
  225. /*
  226.  * balloonCreate - create Memory DC/PS & Bitmap for Balloon Message
  227.  */
  228.  
  229. BOOL    balloonCreate(HAB hab)
  230. {
  231.     SIZEL   siz ;
  232.     POINTL  pt1, pt2 ;
  233.     BITMAPINFOHEADER2   bmi ;
  234.     POINTL  apt[TXTBOX_COUNT] ;
  235.  
  236.     if (loadMessages(hab) != TRUE) {
  237.         freeMessages() ;
  238.     return FALSE ;
  239.     }
  240.     
  241.     /*
  242.      * Create Memory DC/PS for Message Ballooon
  243.      */
  244.  
  245.     hdcBalloon =DevOpenDC(hab, OD_MEMORY, "*", 0, NULL, NULLHANDLE) ;
  246.     siz.cx = siz.cy = 0 ;
  247.     hpsBalloon = GpiCreatePS(hab, hdcBalloon, &siz,
  248.             PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC) ;
  249.     if (hdcBalloon == NULLHANDLE || hpsBalloon == NULLHANDLE) {
  250.         return FALSE ;
  251.     }
  252.     
  253.     /*
  254.      * create and associate bitmap for Message Balloon
  255.      */
  256.  
  257.     memset(&bmi, 0, sizeof(bmi)) ;
  258.     bmi.cbFix = sizeof(bmi) ;
  259.     bmi.cx = TALK_CX ;
  260.     bmi.cy = TALK_CY ;
  261.     bmi.cPlanes       = 1  ;
  262.     bmi.cBitCount     = 24 ;
  263.     bmi.ulCompression = 0  ;
  264.     bmi.cclrUsed      = 0  ;
  265.     bmi.cclrImportant = 0  ;
  266.  
  267.     hbmBalloon = GpiCreateBitmap(hpsBalloon, &bmi, 0, NULL, NULL) ;
  268.     
  269.     if (hbmBalloon == NULLHANDLE) {
  270.         return FALSE ;
  271.     }
  272.     GpiSetBitmap(hpsBalloon, hbmBalloon) ;
  273.     
  274.     /*
  275.      * draw message balloon
  276.      */
  277.  
  278.     pt1.x = 0 ;
  279.     pt1.y = 0 ;
  280.     pt2.x = TALK_CX ;
  281.     pt2.y = TALK_CY ;
  282.     
  283.     GpiMove(hpsBalloon, &pt1) ;
  284.     GpiSetColor(hpsBalloon, TALK_CLR_FIL) ;
  285.     GpiBox(hpsBalloon, DRO_FILL, &pt2, 0, 0) ;
  286.  
  287.     GpiMove(hpsBalloon, &pt1) ;
  288.     GpiSetColor(hpsBalloon, TALK_CLR_RIM) ;
  289.     GpiBox(hpsBalloon, DRO_FILL, &pt2, TALK_RND, TALK_RND) ;
  290.  
  291.     pt1.x += TALK_RIM ;
  292.     pt1.y += TALK_RIM ;
  293.     pt2.x -= TALK_RIM ;
  294.     pt2.y -= TALK_RIM ;
  295.     GpiMove(hpsBalloon, &pt1) ;
  296.     GpiSetColor(hpsBalloon, TALK_CLR_BAK) ;
  297.     GpiBox(hpsBalloon, DRO_FILL, &pt2, TALK_RND, TALK_RND) ;
  298.  
  299.     /*
  300.      * Calc. Message Positions
  301.      */
  302.  
  303.     talkRct.xLeft   = TALK_XADJUST ;
  304.     talkRct.yBottom = TALK_YADJUST ;
  305.     talkRct.xRight  = TALK_CX - TALK_XADJUST ;
  306.     talkRct.yTop    = TALK_CY - TALK_YADJUST ;
  307.  
  308.     TRACE("Message Area %d, %d - %d, %d\n",
  309.             talkRct.xLeft, talkRct.yBottom, talkRct.xRight, talkRct.yTop) ;
  310.  
  311.     talkSiz.cx = talkRct.xRight - talkRct.xLeft ;
  312.     talkSiz.cy = talkRct.yTop - talkRct.yBottom ;
  313.  
  314.     /*
  315.      * Query Text Size
  316.      */
  317.      
  318.     GpiQueryTextBox(hpsBalloon, 1, "M", TXTBOX_COUNT, apt) ;
  319.     talkBox.cx = apt[TXTBOX_TOPRIGHT].x - apt[TXTBOX_TOPLEFT].x   ;
  320.     talkBox.cy = apt[TXTBOX_TOPLEFT].y - apt[TXTBOX_BOTTOMLEFT].y ;
  321.     
  322.     TRACE("TextBox %d x %d\n", talkBox.cx, talkBox.cy) ;
  323.     
  324.     talkPos.x = talkRct.xLeft ;
  325.     talkPos.y = talkRct.yTop  ;
  326.     
  327.     return TRUE ;
  328. }
  329.  
  330. /*
  331.  * balloonShow/Hide - show / hide Balloon Message
  332.  */
  333.  
  334. static  BOOL    visible = FALSE ;
  335.  
  336. void    balloonShow(void)
  337. {
  338.     SWP     scr ;
  339.     SWP     swp ;
  340.     
  341.     TRACE("balloonShow\n") ;
  342.     
  343.     if (useMsg == FALSE) {
  344.         WinSetWindowPos(hwndTalk, NULLHANDLE, 0, 0, 0, 0, SWP_HIDE) ;
  345.         visible = FALSE ;
  346.         return ;
  347.     }
  348.     
  349.     WinQueryWindowPos(HWND_DESKTOP, &scr) ;
  350.     WinQueryWindowPos(hwndShape, &swp)    ;
  351.     
  352.     if (swp.x < (scr.cx / 2)) {
  353.         swp.x = swp.x + swp.cx ;        /* balloon at left  */
  354.     } else {
  355.         swp.x = swp.x - TALK_CX ;       /* balloon at right */
  356.     }
  357.     swp.y = swp.y + TALK_OFF ;
  358.  
  359.     WinSetWindowPos(hwndTalk, HWND_TOP, 
  360.             swp.x, swp.y, 0, 0, (SWP_MOVE | SWP_ZORDER | SWP_SHOW)) ;
  361.     visible = TRUE ;
  362. }
  363.  
  364. void    balloonHide(void)
  365. {
  366.     TRACE("balloonHide\n") ;
  367.  
  368.     WinSetWindowPos(hwndTalk, NULLHANDLE, 0, 0, 0, 0, SWP_HIDE) ;
  369.     visible = FALSE ;
  370. }
  371.  
  372. /*
  373.  * balloonMove - Move Balloon with Bitmap
  374.  */
  375.  
  376. void    balloonMove(void)
  377. {
  378.     TRACE("balloonMove\n") ;
  379.  
  380.     if (visible) {
  381.         balloonShow() ;
  382.     } else {
  383.         balloonHide() ;
  384.     }
  385. }
  386.  
  387. /*
  388.  * message drawing functions
  389.  */
  390.  
  391. static  void    cleanup(void)
  392. {
  393.     POINTL  pt1, pt2 ;
  394.     
  395.     /*
  396.      * clear message area
  397.      */
  398.      
  399.     pt1.x = talkRct.xLeft   ;
  400.     pt1.y = talkRct.yBottom ;
  401.     pt2.x = talkRct.xRight  ;
  402.     pt2.y = talkRct.yTop    ;
  403.     GpiMove(hpsBalloon, &pt1) ;
  404.     GpiSetColor(hpsBalloon, TALK_CLR_BAK) ;
  405.     GpiBox(hpsBalloon, DRO_FILL, &pt2, 0, 0) ;
  406.  
  407.     /*
  408.      * reset drawing position
  409.      */
  410.      
  411.     talkPos.x = talkRct.xLeft ;
  412.     talkPos.y = talkRct.yTop  ;
  413. }
  414.  
  415. static  void    scrollup(void)
  416. {
  417.     POINTL  apt[3] ;
  418.     POINTL  pt1, pt2 ;
  419.     
  420.     /*
  421.      * scrollup messages
  422.      */
  423.      
  424.     apt[0].x = talkRct.xLeft          ;
  425.     apt[0].y = talkRct.yBottom + talkBox.cy - 2 ;
  426.     apt[1].x = talkRct.xRight         ;
  427.     apt[1].y = talkRct.yTop           ;
  428.     apt[2].x = talkRct.xLeft          ;
  429.     apt[2].y = talkRct.yBottom - 2    ;
  430.     
  431.     GpiBitBlt(hpsBalloon, hpsBalloon, 3, apt, ROP_SRCCOPY, 0) ;
  432.  
  433.     /*
  434.      * clear bottom area
  435.      */
  436.  
  437.     pt1.x = talkRct.xLeft              ;
  438.     pt1.y = talkRct.yBottom - 2        ;
  439.     pt2.x = talkRct.xRight             ;
  440.     pt2.y = talkPos.y + talkBox.cy - 2 ;
  441.     GpiMove(hpsBalloon, &pt1) ;
  442.     GpiSetColor(hpsBalloon, TALK_CLR_BAK) ;
  443.     GpiBox(hpsBalloon, DRO_FILL, &pt2, 0, 0) ;
  444. }
  445.  
  446. static  void    drawLine(PUCHAR str, int len)
  447. {
  448.     talkPos.x = talkRct.xLeft ;
  449.     if ((talkPos.y - talkBox.cy) >= talkRct.yBottom) {
  450.         talkPos.y -= talkBox.cy ;
  451.     } else {
  452.         scrollup() ;
  453.     }
  454.     GpiSetColor(hpsBalloon, TALK_CLR_CHR) ;
  455.     GpiCharStringAt(hpsBalloon, &talkPos, len, str) ;
  456. }
  457.  
  458. #define iskanji(c)  (((c)>0x80 && (c)<0xa0) || ((c)>=0xe0 && (c)<0xfd))
  459.  
  460. static  void    drawWrapJa(PUCHAR str, int len)
  461. {
  462.     int     n, bump, wid ;
  463.     POINTL  apt[TXTBOX_COUNT] ;
  464.  
  465.     for (n = 0 ; len > 0 && n < len ; ) {
  466.         if (iskanji(str[n])) {
  467.         bump = 2 ;
  468.     } else {
  469.         bump = 1 ;
  470.     }
  471.         GpiQueryTextBox(hpsBalloon, (n + bump), str, TXTBOX_COUNT, apt) ;
  472.         wid = apt[TXTBOX_TOPRIGHT].x - apt[TXTBOX_TOPLEFT].x ;
  473.     if (wid < talkSiz.cx) {
  474.         n += bump ;
  475.         continue  ;
  476.     }
  477.         drawLine(str, n) ;
  478.         str += n ;
  479.         len -= n ;
  480.         n = 0 ;
  481.     }
  482.     if (n > 0) {
  483.         drawLine(str, n) ;
  484.     }
  485. }
  486.  
  487. static  void    drawWrapEn(PUCHAR str, int len)
  488. {
  489.     int     n, bump, wid ;
  490.     POINTL  apt[TXTBOX_COUNT] ;
  491.  
  492.     for (n = 0, bump = 0 ; len > 0 && (n + bump) < len ; ) {
  493.         if (! isspace(str[n + bump])) {
  494.             bump += 1 ;
  495.         continue ;
  496.     }
  497.         GpiQueryTextBox(hpsBalloon, (n + bump), str, TXTBOX_COUNT, apt) ;
  498.         wid = apt[TXTBOX_TOPRIGHT].x - apt[TXTBOX_TOPLEFT].x ;
  499.     if (wid < talkSiz.cx) {
  500.         n += bump ;
  501.         bump = 1  ;
  502.         continue  ;
  503.     }
  504.         drawLine(str, n) ;
  505.         str += n ;
  506.     len -= n ;
  507.         n = bump ;
  508.     bump = 1 ;
  509.     }
  510.     if (n > 0) {
  511.         drawLine(str, n) ;
  512.     }
  513. }
  514.  
  515. static  void    drawMessage(PUCHAR str)
  516. {
  517.     int     len, wid ;
  518.     POINTL  apt[TXTBOX_COUNT] ;
  519.  
  520.     len = strlen(str) ;
  521.     
  522.     GpiQueryTextBox(hpsBalloon, len, str, TXTBOX_COUNT, apt) ;
  523.     wid = apt[TXTBOX_TOPRIGHT].x - apt[TXTBOX_TOPLEFT].x ;
  524.     
  525.     if (wid < talkSiz.cx) {
  526.         drawLine(str, len) ;
  527.     } else if (ProgramLang == NLS_JA) {
  528.         drawWrapJa(str, len) ;
  529.     } else {
  530.         drawWrapEn(str, len) ;
  531.     }
  532.     WinSendMsg(hwndTalk, SHAPEWIN_MSG_UPDATE, MPFROMP(&talkRct), NULL) ;
  533. }
  534.  
  535. /*
  536.  * put Messages on Balloon
  537.  */
  538.  
  539. static  int     countNade = 0 ;
  540. static  int     countFuni = 0 ;
  541. static  BOOL    isMail = FALSE ;
  542.  
  543. void    balloonNade(void)
  544. {
  545.     drawMessage(strNade) ;
  546.     
  547.     countNade += 1 ;
  548.     
  549.     if (countNade == 4) {
  550.         drawMessage(strNade1) ;
  551.     } else if (countNade == 8) {
  552.         drawMessage(strNade2) ;
  553.     } else if (countNade == 12) {
  554.         drawMessage(strNade3) ;
  555.     } else if ((countNade % 4) == 0) {
  556.         drawMessage(strNade4) ;
  557.     }
  558.  
  559.     if (! visible) {
  560.         balloonShow() ;
  561.     }
  562. }
  563.  
  564. void    balloonFuni(void)
  565. {
  566.     drawMessage(strFuni) ;
  567.     
  568.     countFuni += 1 ;
  569.     
  570.     if (countFuni == 4) {
  571.         drawMessage(strFuni1) ;
  572.     } else if (countFuni == 8) {
  573.         drawMessage(strFuni2) ;
  574.     } else if (countFuni == 12) {
  575.         drawMessage(strFuni3) ;
  576.     } else if (countFuni == 16) {   /* 20 > causes 'psyu'   */
  577.         drawMessage(strFuni4) ;
  578.     }
  579.  
  580.     if (! visible) {
  581.         balloonShow() ;
  582.     }
  583. }
  584.  
  585. void    balloonPsyu(void)
  586. {
  587.     drawMessage(strPsyu) ;
  588.  
  589.     if (! visible) {
  590.         balloonShow() ;
  591.     }
  592.     countNade = 0 ;
  593.     countFuni = 0 ;
  594. }
  595.  
  596. void    balloonClear(void)
  597. {
  598.     TRACE("balloonClear\n") ;
  599.  
  600.     if (visible) {
  601.         balloonHide() ;
  602.     }
  603.  
  604.     countNade = 0  ;
  605.     countFuni = 0  ;
  606.  
  607.     cleanup() ;
  608. }
  609.