home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / os2cl016.zip / pmwin.cpp < prev    next >
C/C++ Source or Header  |  1996-05-05  |  16KB  |  621 lines

  1. /* 
  2.  
  3.  
  4.     pmwin.cpp (emx+gcc) 
  5.  
  6.     1995 Giovanni Iachello
  7.     This is freeware software. You can use or modify it as you wish,
  8.     provided that the part of code that I wrote remains freeware.
  9.     Freeware means that the source code must be available on request 
  10.     to anyone.
  11.     You must also include this notice in all files derived from this
  12.     file.
  13.  
  14.  
  15. */
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <stdarg.h>
  20.  
  21. #include "pmwin.h"
  22. #include "pmgpi.h"
  23. #include "pmdlg.h"
  24. #include "pmhelp.h"
  25. #include "pmstdres.h"
  26.  
  27.  
  28. MRESULT PMWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  29. {
  30.     PMWin* win=NULL;
  31.     
  32.     if (msg==WM_CREATE) {
  33.         WinSetWindowPtr(hwnd,0,mp1);  // mp1=this pointer from WinCreateWindow, line 125
  34.         ((PMWin*)mp1)->hwnd=hwnd;     // PMWin::hwnd is set to hwnd
  35.     }
  36.  
  37.     win = (PMWin*)WinQueryWindowPtr(hwnd, 0);
  38.  
  39.     if (win) { // is there a C++ window object to process messages?
  40.         PMEvent event(msg,mp1,mp2,CHARMSG(&msg),MOUSEMSG(&msg)); // yes
  41.         MRESULT ret=win->msgProc(event);
  42.         if (msg==WM_DESTROY) delete win; // automatically destroy the C++ object il the window is destroyed
  43.         return ret;
  44.     }
  45.     return WinDefWindowProc(hwnd,msg,mp1,mp2); // no
  46. }
  47.  
  48.  
  49. /////////////////////////////////////////////////////////////////////////////
  50. // PMWin class
  51. // the constructors follow. Note that the constructors only fill in the
  52. // createArgs structure. The window is actually created by the createWin 
  53. // function
  54.  
  55. PMWin::PMWin(HAB iab)
  56. {
  57.     ab.PMAnchorBlock(iab);
  58.     createArgs=NULL;
  59.     hwnd=(HWND)0;
  60.     hwndFrame=(HWND)0;
  61.     oldproc=WinDefWindowProc; // for safety
  62. }
  63.  
  64. PMWin::PMWin(PCSZ classname,HAB iab)
  65. {
  66.     ab.PMAnchorBlock(iab);
  67.     createArgs=new WINCREATEARGS;
  68.     hwnd=(HWND)0;
  69.     hwndFrame=(HWND)0;
  70.     oldproc=WinDefWindowProc; 
  71.     
  72.     createArgs->hwndParent=HWND_DESKTOP;
  73.     createArgs->flStyle= WS_VISIBLE;
  74.     createArgs->flCreateFlags=(FCF_TITLEBAR | FCF_SYSMENU |
  75.                   FCF_SIZEBORDER | FCF_MINMAX |
  76.                   FCF_SHELLPOSITION | FCF_TASKLIST);
  77.     createArgs->pszClientClass=classname;
  78.     createArgs->pszTitle=NULL;
  79.     createArgs->styleClient=0L;
  80.     createArgs->hmod=(HMODULE)0;
  81.     createArgs->idResources=0;
  82.     createArgs->phwndClient=&hwnd;
  83.  
  84.     createArgs->hab=iab;
  85.     createArgs->pszClassName=classname;
  86.     createArgs->pfnWndProc=PMWndProc;
  87.     createArgs->flClassStyle=CS_SIZEREDRAW;
  88.     createArgs->cbWindowData=sizeof(this);
  89. }
  90.  
  91. PMWin::~PMWin()
  92. {
  93.     delete createArgs;
  94. }
  95.  
  96. // I'm using the two-phase method of window creation (frame and client)
  97. // beacuse I want to intercept the WM_CREATE message of the client window.
  98. // (see the PMWndProc functions at the beginning of this file)
  99.  
  100. BOOL PMWin::createWin()
  101. {
  102.     HWND newhwnd;
  103.     FRAMECDATA fcdata;
  104.  
  105.     CLASSINFO temp;
  106.     if (!WinQueryClassInfo(ab,createArgs->pszClassName,&temp)) {
  107.         BOOL ret=WinRegisterClass (createArgs->hab, createArgs->pszClassName, 
  108.             createArgs->pfnWndProc,createArgs->flClassStyle, createArgs->cbWindowData);
  109.         assert(ret);
  110.     }
  111.     
  112.     oldproc=WinDefWindowProc;
  113.  
  114.     fcdata.cb = sizeof(FRAMECDATA); // fill in the frame control data structure
  115.     fcdata.flCreateFlags = createArgs->flCreateFlags;
  116.     fcdata.hmodResources = createArgs->hmod;
  117.     fcdata.idResources   = createArgs->idResources;
  118.     
  119.     newhwnd = WinCreateWindow(createArgs->hwndParent,WC_FRAME,
  120.                             createArgs->pszTitle,0,
  121.                             0,0,0,0,    /* coordinates */ 0, // no owner 
  122.                             HWND_TOP,createArgs->idResources,&fcdata,NULL);
  123.     
  124.     assert(newhwnd);
  125.     hwndFrame=newhwnd;
  126.     newhwnd = WinCreateWindow(hwndFrame,createArgs->pszClassName,
  127.                             NULL,0,
  128.                             0,0,0,0,    /* coordinates */ 
  129.                             hwndFrame,
  130.                             HWND_TOP,FID_CLIENT,this,NULL);
  131.  
  132.     assert(newhwnd);
  133. //    hwnd=newhwnd; // not necessary, this->hwnd is set by PMWndProc
  134.     WinShowWindow(hwndFrame, TRUE);
  135.  
  136. //    BOOL ret=WinSetWindowPtr(hwnd, 0, this); // set the window user pointer to the pointer to this class. see PMWndProc
  137. //    assert(ret);
  138.     return hwnd;
  139. }
  140. //    newhwnd = WinCreateStdWindow (createArgs->hwndParent,createArgs->flStyle,
  141. //                &createArgs->flCreateFlags,createArgs->pszClientClass,
  142. //                createArgs->pszTitle,createArgs->styleClient,createArgs->hmod,
  143. //                createArgs->idResources,createArgs->phwndClient);
  144.  
  145. // a BOOL TRUE from dispachEvent means that we processed the message.
  146. MRESULT PMWin::msgProc(PMEvent &event)
  147. {
  148.     event.ret=MRFROMSHORT(FALSE);
  149.     // if we handle the message return the ret code, otherwise call the default message handler
  150.     return dispachEvent(event) ? event.ret : (*oldproc)(hwnd,event.msg,event.mp1,event.mp2);
  151. }
  152.  
  153. // dispach messages among the predefinde functions
  154. BOOL PMWin::dispachEvent(PMEvent & event)
  155. {
  156.     if ((event.msg>=WM_MOUSEFIRST && event.msg<=WM_MOUSELAST) ||
  157.             (event.msg>=WM_EXTMOUSEFIRST && event.msg<=WM_EXTMOUSELAST) ) {
  158.         return mouse(event);
  159.     } else if (event.msg>=HM_MSG_BASE && event.msg<=HM_CONTROL) {
  160.         return helpmsg(event);
  161.     } else
  162.         switch (event.msg) {
  163.         case WM_CREATE:
  164.             return create(event);
  165.         case WM_DESTROY:
  166.             return destroy();
  167.         case WM_SAVEAPPLICATION:
  168.             saveApplication();
  169.             return FALSE;
  170.         case WM_CHAR: 
  171.             return kbd(event);
  172.         case WM_COMMAND:
  173.             return command(SHORT1FROMMP (event.mp1),SHORT2FROMMP (event.mp2));
  174.         case WM_PAINT:
  175.             return paint();
  176.         case WM_ENABLE:
  177.             return enable();
  178.         case WM_SHOW:
  179.             return show();
  180.         case WM_MOVE:
  181.             return move();
  182.         case WM_SIZE:
  183.             return size(SHORT1FROMMP (event.mp2),SHORT2FROMMP (event.mp2));
  184.         case WM_ACTIVATE:
  185.             return activate();
  186.         case WM_SETFOCUS:
  187.             return setfocus();
  188.         case WM_HELP:
  189.             return help(event);
  190.         case WM_TIMER:
  191.             return timer();
  192.         case WM_CLOSE:
  193.             return closed();
  194.         case WM_PRESPARAMCHANGED:
  195.             invalidate(TRUE);
  196.             return TRUE;
  197.         case WM_INITMENU:
  198.             PMMenu* menu=new PMMenu(HWNDFROMMP(event.mp2));
  199.             return initmenu(SHORT1FROMMP(event.mp1),menu);
  200.             delete menu;
  201.         }               
  202.     return other(event);
  203. }
  204.  
  205. // predefinde message classes
  206. BOOL PMWin::create(PMEvent &)
  207. {
  208.     return FALSE;
  209. }
  210.  
  211. BOOL PMWin::destroy()
  212. {
  213.     return FALSE;
  214. }
  215.  
  216. // exception to the BOOL return value rool: PM requires that the default procedure always be called
  217. void PMWin::saveApplication()
  218. {
  219. }
  220.  
  221. BOOL PMWin::command(USHORT /*id*/,USHORT /*cmddev*/)  // nothandled
  222. {
  223.     return FALSE;
  224. }
  225.  
  226. BOOL PMWin::paint()
  227. {
  228.     return FALSE;
  229. }
  230.  
  231. BOOL PMWin::kbd(PMEvent &)
  232. {
  233.     return FALSE;
  234. }
  235.  
  236. BOOL PMWin::mouse(PMEvent &)
  237. {
  238.     return FALSE;
  239. }
  240.  
  241. BOOL PMWin::enable()
  242. {
  243.     return FALSE;
  244. }
  245. BOOL PMWin::show()    
  246. {
  247.     return FALSE;
  248. }
  249. BOOL PMWin::move()
  250. {
  251.     return FALSE;
  252. }
  253. BOOL PMWin::size(SHORT,SHORT)
  254. {
  255.     return FALSE;
  256. }
  257. BOOL PMWin::activate()
  258. {
  259.     return FALSE;
  260. }
  261. BOOL PMWin::setfocus()
  262. {
  263.     return FALSE;
  264. }
  265. BOOL PMWin::help(PMEvent &)
  266. {
  267.     return FALSE;
  268. }
  269. BOOL PMWin::helpmsg(PMEvent &)
  270. {
  271.     return FALSE;
  272. }
  273.  
  274. BOOL PMWin::timer()
  275. {
  276.     return FALSE;
  277. }
  278. BOOL PMWin::closed()
  279. {
  280.     return FALSE;
  281. }
  282.  
  283. BOOL PMWin::other(PMEvent &)
  284. {
  285.     return FALSE;
  286. }
  287.  
  288. MRESULT PMWin::close() 
  289.     return sendMsg(WM_CLOSE); 
  290. }
  291.  
  292. BOOL PMWin::destroyWin()
  293. {
  294.     return WinDestroyWindow(hwndFrame);
  295. }
  296.  
  297. BOOL PMWin::initmenu(SHORT /*id*/,PMMenu* /*menu*/)
  298. {
  299.     return FALSE;
  300. }
  301.  
  302.  
  303. /////////////////////////////////////////////////////////////////////////////
  304. // additional function
  305.  
  306. BOOL PMWin::ptInClient(PMPoint& pt)
  307. {
  308.     PMRect rcl;
  309.     rcl=this;
  310.     return WinPtInRect (ab, &rcl, &pt);
  311. }
  312.  
  313. /////////////////////////////////////////////////////////////////////////////
  314. // PMMain Window.
  315. // Adds automatic support to file handling, title bar text and menus and help
  316.  
  317. PMMainWin::PMMainWin(PCSZ classname,HAB ab,PMHelpWin *ihelpWin) : PMWin(classname,ab) 
  318.     helpWin=ihelpWin;
  319.     caption=NULL;
  320.     modified=FALSE;
  321.     filename[0]='\0';
  322.     fileFlags=PMMWFF_NOTHING;
  323.     fileNone="(none)"; fileUntitled="(untitled)";
  324.     flCaption=fsCaption=fnFilter=NULL;
  325. }
  326.  
  327.  
  328. // inline help support (this create uses the standard create function
  329. // and adds the automatic association to the help instance)
  330.  
  331. // supporto per help inline (usa tutto create della classe base, e aggiunge
  332. // l'associazione automatica alla finestra di help!)
  333.  
  334. BOOL PMMainWin::createWin()
  335. {
  336.     BOOL ret;
  337.     ret=PMWin::createWin();
  338.     if (helpWin) ret&=associateHelpInstance(helpWin->getHwnd());
  339.     return ret;
  340. }
  341.  
  342. BOOL PMMainWin::destroyWin()
  343. {
  344.     BOOL ret;
  345.     if (helpWin) {
  346.         ret=associateHelpInstance(NULLHANDLE);
  347.         ret&=PMWin::destroyWin();
  348.     } else ret=PMWin::destroyWin();
  349.     return ret;
  350. }
  351.  
  352. // automatically create a WindowPresSpace (WinBeginPaint) and pass it to 
  353. // the official paint function
  354. BOOL PMMainWin::paint() 
  355. {
  356.     PMWindowPresSpace ps(this);
  357.     return paint(ps);
  358. }
  359.  
  360. // the window wants to be closed. Ask to the user if it is possible (queryEnd)
  361. // and do it.
  362. BOOL PMMainWin::closed()
  363. {
  364.     if (queryEnd()) postMsg(WM_QUIT);
  365.     return TRUE;
  366. }
  367.  
  368. // used by queryEnd to ask the user if it is possible to close the file
  369. BOOL PMMainWin::fileKillConfirm()
  370. {
  371.     if (!modified) return TRUE;
  372.     char buf[160];
  373.     strcpy(buf,"Save Changes");
  374.     if (fileFlags==PMMWFF_FILE) {
  375.         strcat(buf," to ");
  376.         strcat(buf,filename);
  377.     }
  378.     strcat(buf,"?");
  379.     int ret=WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,(PCSZ)buf,(PCSZ)caption,0,MB_YESNOCANCEL|MB_ICONHAND|MB_APPLMODAL);
  380.     if (ret==MBID_YES) command(PMIDM_FILESAVE,0);
  381.     if (ret==MBID_CANCEL) return FALSE;
  382.     return TRUE;        
  383. }
  384.  
  385. // automatic title text setter. (automagically adds an * for modified files)
  386. void PMMainWin::setTitleCaption()
  387. {
  388.     char temp[256];
  389.     if (caption==NULL) temp[0]='\0';
  390.     else {
  391.         switch (fileFlags) {
  392.         case PMMWFF_NOTHING:
  393.             sprintf(temp,"%s",caption);
  394.             break;
  395.         case PMMWFF_NONE:
  396.             if (modified)
  397.                 sprintf(temp,"%s - *%s",caption,fileNone);
  398.             else
  399.                 sprintf(temp,"%s - %s",caption,fileNone);
  400.             break;
  401.         case PMMWFF_UNTITLED:
  402.             if (modified)
  403.                 sprintf(temp,"%s - *%s",caption,fileUntitled);
  404.             else
  405.                 sprintf(temp,"%s - %s",caption,fileUntitled);
  406.             break;
  407.         case PMMWFF_FILE:
  408.             if (modified)
  409.                 sprintf(temp,"%s - *%s",caption,filename);
  410.             else
  411.                 sprintf(temp,"%s - %s",caption,filename);
  412.             break;
  413.         }
  414.     }
  415.     setFrameWindowText(temp);
  416. }
  417.  
  418. // handle automatically menu selections.
  419. BOOL PMMainWin::command(USHORT id,USHORT /*cmddev*/)
  420. {
  421.     switch(id) {
  422.     case PMIDM_FILENEW:
  423.         if (fileKillConfirm()) {
  424.             if (fileNew()) { fileFlags=PMMWFF_UNTITLED; setModified(FALSE);    }
  425.         }
  426.         return TRUE;
  427.     case PMIDM_FILEOPEN: {
  428.         if (fileKillConfirm()) {
  429.             PMFileDialog fd(HWND_DESKTOP, hwnd, fnFilter, flCaption, "Open", FDS_OPEN_DIALOG);
  430.             int ret=fd.createWin();
  431.             if (ret==1) {
  432.                 FILEDLG fi=fd;
  433.                 if (fileOpen(fi.szFullFile)) { // if load clompleted successfully, change the caption
  434.                         strcpy(filename,fi.szFullFile);
  435.                     fileFlags=PMMWFF_FILE;
  436.                 }
  437.                 setModified(FALSE);
  438.             }
  439.         }
  440.         return TRUE;
  441.         }    
  442.     case PMIDM_FILESAVE: 
  443.         if (filename[0]) { // if there's already a filename, save the file
  444.             fileSave(filename);
  445.             setModified(FALSE);
  446.             return TRUE;
  447.         } // otherwise fallback to SAVEAS
  448.     case PMIDM_FILESAVEAS: {
  449.         PMFileDialog fd(HWND_DESKTOP, hwnd, fnFilter, fsCaption, "Save", FDS_SAVEAS_DIALOG);
  450.         int ret=fd.createWin();
  451.         if (ret==1) {
  452.             FILEDLG fi=fd;
  453.             if (fileSave(fi.szFullFile)) { // if correctly saved, change caption
  454.                 strcpy(filename,fi.szFullFile);
  455.                 fileFlags=PMMWFF_FILE;
  456.             }
  457.             setModified(FALSE);
  458.         }
  459.         return TRUE;
  460.         }    
  461.     case PMIDM_FILEPRINT :
  462.         filePrint();
  463.         return TRUE;
  464.     case PMIDM_FILEEXIT :
  465.         close();
  466.         return TRUE;
  467.  
  468.     case PMIDM_EDITUNDO :
  469.         editUndo();
  470.         return TRUE;
  471.     case PMIDM_EDITREDO :
  472.         editRedo();
  473.         return TRUE;
  474.     case PMIDM_EDITCUT :
  475.         editCut();
  476.         return TRUE;
  477.     case PMIDM_EDITCOPY :
  478.         editCopy();
  479.         return TRUE;
  480.     case PMIDM_EDITPASTE:
  481.         editPaste();
  482.         return TRUE;
  483.     case PMIDM_EDITCLEAR:
  484.         editClear();
  485.         return TRUE;
  486.     case PMIDM_EDITDUPLICATE :
  487.     case PMIDM_EDITSELECTALL :
  488.         return FALSE;
  489.  
  490.     case PMIDM_HELPINDEX :
  491.         helpWin->sendMsg(HM_HELP_INDEX);
  492.         return TRUE;
  493.     case PMIDM_HELPGENERAL :
  494.         helpWin->sendMsg(HM_EXT_HELP);
  495.         return TRUE;
  496.     case PMIDM_HELPUSING :
  497.         helpWin->sendMsg(HM_DISPLAY_HELP);
  498.         return TRUE;
  499.     case PMIDM_HELPKEYS :
  500.         helpWin->sendMsg(HM_KEYS_HELP);
  501.         return TRUE;
  502.  
  503.     case PMIDM_HELPABOUT :
  504.         WinDlgBox(HWND_DESKTOP,hwnd,(PFNWP)PMDlgProc,NULLHANDLE,PMDLG_ABOUT,NULL);
  505.         return TRUE;
  506.  
  507.     }
  508.     return FALSE;
  509. }
  510.  
  511.  
  512. /////////////////////////////////////////////////////////////////////////////
  513.  
  514. //static MRESULT PMSubclassWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  515. //{
  516. //    PMWin* win=NULL;
  517. //
  518. //    win = (PMWin*)WinQueryWindowPtr(hwnd, 0);
  519. //
  520. //    if (win) {
  521. //        PMEvent event(msg,mp1,mp2,CHARMSG(&msg),MOUSEMSG(&msg));
  522. //        MRESULT ret=win->msgProc(event);
  523. //        if (msg==WM_DESTROY) delete win; 
  524. //        return ret;
  525. //    }
  526. //    return WinDefWindowProc(hwnd,msg,mp1,mp2);
  527. //}
  528.  
  529. PMSubclassWin::PMSubclassWin(HWND hwndParent, PCSZ pszClass, PCSZ pszName,
  530.     ULONG flStyle, LONG x, LONG y, LONG cx, LONG cy, HWND hwndOwner,
  531.        HWND hwndInsertBehind, ULONG id, PVOID pCtlData, PVOID pPresParams) : PMWin(NULLHANDLE)
  532. {
  533.     createArgs=new WINCREATEARGS;
  534.     
  535.     createArgs->hwndParent=hwndParent;
  536.     createArgs->pszClientClass=pszClass;
  537.     createArgs->pszTitle=pszName;
  538.     createArgs->flStyle=flStyle;
  539.     createArgs->x=x;
  540.     createArgs->y=y;
  541.     createArgs->cx=cx;
  542.     createArgs->cy=cy;
  543.     createArgs->hwndOwner=hwndOwner;
  544.     createArgs->hwndInsertBehind=hwndInsertBehind;
  545.     createArgs->id=id;
  546.     createArgs->pCtlData=pCtlData;
  547.     createArgs->pPresParams=pPresParams;
  548.     
  549. }
  550.  
  551. PMSubclassWin::~PMSubclassWin()
  552. {
  553.     delete createArgs;
  554. }
  555.  
  556. BOOL PMSubclassWin::createWin()
  557. {
  558.     hwnd=WinCreateWindow(createArgs->hwndParent,
  559.                         createArgs->pszClientClass,
  560.                         createArgs->pszTitle,
  561.                         createArgs->flStyle,
  562.                         createArgs->x, createArgs->y,
  563.                         createArgs->cx, createArgs->cy,
  564.                         createArgs->hwndParent,
  565.                         createArgs->hwndInsertBehind,
  566.                         createArgs->id,
  567.                         createArgs->pCtlData,
  568.                         createArgs->pPresParams);
  569.     BOOL ret=WinSetWindowPtr(hwnd, 0, this);    // the first few messages (WM_CREATE...) are not sent to the massage handling routine
  570.     assert(ret);
  571.     ab.PMAnchorBlock( WinQueryAnchorBlock(hwnd) );
  572.     oldproc=WinSubclassWindow(hwnd,PMWndProc);            // PMWndProc gets active only at this point
  573.     return hwnd;
  574. }
  575.  
  576.  
  577. /////////////////////////////////////////////////////////////////////////////
  578. // Support functions:
  579. // DoAssert executes the assert macro code, displaying the offending condition, linenumber and source file
  580. // ErrBox shows a critical error box
  581. // MsgBox shows a message box (with optional sprintf-like parameter list)
  582.  
  583. void DoAssert(PCSZ msg,PCSZ file,unsigned line)
  584. {
  585.     char buf[160];
  586.     sprintf(buf,"at line %u in file %s: \"%s\"\nOK to continue, Cancel to close application.\n",line,file,msg);
  587.     if (WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,buf,"ASSERTION FAILURE",PMHLP_DLG_ASSERTFAIL,MB_OKCANCEL|MB_ICONHAND|MB_APPLMODAL|MB_HELP)!=MBID_OK) exit(-1);
  588. }
  589.  
  590. void ErrBox(PCSZ msg,...)
  591. {
  592.     char buf[256];
  593.  
  594.     va_list arg_ptr;
  595.     va_start(arg_ptr,msg);
  596.     vsprintf(buf,msg,arg_ptr);
  597.     va_end(arg_ptr);
  598.  
  599.     WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,buf,"Error!",PMHLP_DLG_ERRMSG,MB_CUACRITICAL|MB_APPLMODAL|MB_HELP);
  600. }
  601.  
  602. ULONG PMWin::msgBox(PCSZ title,PCSZ msg,...)
  603. {
  604.     char buf[256];
  605.  
  606.     va_list arg_ptr;
  607.     va_start(arg_ptr,msg);
  608.     vsprintf(buf,msg,arg_ptr);
  609.     va_end(arg_ptr);
  610.     
  611.     return WinMessageBox(HWND_DESKTOP,hwnd,buf,title,PMHLP_DLG_MSGWIN,MB_OK|MB_HELP);
  612. }
  613.  
  614. /*
  615.  * Local variables:
  616.  * compile-command: "dmake pmwin.o"
  617.  * end:
  618.  */
  619.