home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Exec 3 / CD_Magazyn_EXEC_nr_3.iso / Internet / Strony_WWW / Opus4.x / DOpus414JRsrc.lha / DirectoryOpus4 / Program / buffers.c < prev    next >
C/C++ Source or Header  |  2000-03-24  |  18KB  |  610 lines

  1. /*
  2.  
  3. Directory Opus 4
  4. Original GPL release version 4.12
  5. Copyright 1993-2000 Jonathan Potter
  6.  
  7. This program is free software; you can redistribute it and/or
  8. modify it under the terms of the GNU General Public License
  9. as published by the Free Software Foundation; either version 2
  10. of the License, or (at your option) any later version.
  11.  
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. GNU General Public License for more details.
  16.  
  17. You should have received a copy of the GNU General Public License
  18. along with this program; if not, write to the Free Software
  19. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  20.  
  21. All users of Directory Opus 4 (including versions distributed
  22. under the GPL) are entitled to upgrade to the latest version of
  23. Directory Opus version 5 at a reduced price. Please see
  24. http://www.gpsoft.com.au for more information.
  25.  
  26. The release of Directory Opus 4 under the GPL in NO WAY affects
  27. the existing commercial status of Directory Opus 5.
  28.  
  29. */
  30.  
  31. #include "dopus.h"
  32.  
  33. /* Allocate or free directory buffers as necessary to achieve desired
  34.    buffer count in each window */
  35.  
  36. void allocdirbuffers(newnum)
  37. int newnum;
  38. {
  39.     int win,a,num;
  40.     struct DirectoryWindow *dir,*next;
  41.  
  42.     for (win=0;win<2;win++) {
  43.         if (data_buffer_count[win]>newnum) {
  44.             dir=dopus_firstwin[win];
  45.             for (a=0;a<newnum;a++) dir=dir->next;
  46.             dir->last->next=dopus_firstwin[win];
  47.             dopus_firstwin[win]->last=dir->last;
  48.             num=data_buffer_count[win];
  49.             for (a=newnum;a<num;a++) {
  50.                 next=dir->next;
  51.                 freedir(dir,-1);
  52.                 if (dopus_curwin[win]==dir) dopus_curwin[win]=dopus_firstwin[win];
  53.                 FreeMem(dir,sizeof(struct DirectoryWindow));
  54.                 if ((--data_buffer_count[win])<1) break;
  55.                 dir=next;
  56.             }
  57.         }
  58.         else if (data_buffer_count[win]<newnum) {
  59.             dir=dopus_firstwin[win];
  60.             for (a=0;a<data_buffer_count[win]-1;a++) dir=dir->next;
  61.             for (a=data_buffer_count[win];a<newnum;a++) {
  62.                 if (next=AllocMem(sizeof(struct DirectoryWindow),MEMF_CLEAR)) {
  63.                     if (!dir) {
  64.                         dopus_firstwin[win]=next;
  65.                         dopus_curwin[win]=next;
  66.                         dir=next;
  67.                     }
  68.                     dir->next=next;
  69.                     next->last=dir;
  70.                     next->next=dopus_firstwin[win];
  71.                     dopus_firstwin[win]->last=next;
  72.                     next->number=a;
  73.                     next->hlen=145;
  74.                     ++data_buffer_count[win];
  75.                     dir=next;
  76.                 }
  77.             }
  78.         }
  79.     }
  80. }
  81.  
  82. /* Searches both buffer lists for a given directory, and displays it in
  83.    the active window if found (copying it over if necessary) */
  84.  
  85. bringinbuffer(dirbuf,win,read)
  86. char *dirbuf;
  87. int win,read;
  88. {
  89.     int a,otherwin;
  90.     struct DirectoryWindow *dir;
  91.  
  92.     if (status_iconified || !dirbuf) return(0);
  93.     checkcurrentbuffer(win);
  94.     if (!(findbuffer(dirbuf,win,0,0))) {
  95.         otherwin=1-data_active_window;
  96.         dir=dopus_firstwin[otherwin];
  97.         for (a=0;a<data_buffer_count[otherwin];a++,dir=dir->next) {
  98.             if ((LStrCmpI(dirbuf,dir->directory))==0) {
  99.                 incrementbuf(data_active_window,1,0);
  100.                 copydirwin(dir,dopus_curwin[data_active_window],data_active_window);
  101.                 check_old_buffer(data_active_window);
  102.                 return(1);
  103.             }
  104.         }
  105.         if (read) {
  106.             strcpy(str_pathbuffer[data_active_window],dirbuf);
  107.             startgetdir(data_active_window,SGDFLAGS_CANMOVEEMPTY);
  108.         }
  109.         else return(0);
  110.     }
  111.     return(1);
  112. }
  113.  
  114. /* Searches forward from the current buffer to find an empty one, or one
  115.    with the same name (it looks first for one with the same name).
  116.    If a buffer is not found, it uses the current buffer */
  117.  
  118. void findemptybuffer(win)
  119. int win;
  120. {
  121.     struct DirectoryWindow *dir;
  122.     int a,ok=0;
  123.     char fullbuf[256];
  124.  
  125.     expand_path(str_pathbuffer[win],fullbuf);
  126.     checkdir(str_pathbuffer[win],NULL);
  127.     checkdir(fullbuf,NULL);
  128.  
  129.     dir=dopus_curwin[win];
  130.     for (a=0;a<data_buffer_count[win];a++) {
  131.         if (LStrCmpI(dir->directory,str_pathbuffer[win])==0 ||
  132.             LStrCmpI(dir->directory,fullbuf)==0) {
  133.             ok=1;
  134.             break;
  135.         }
  136.         dir=dir->next;
  137.     }
  138.     if (!ok) {
  139.         dir=dopus_curwin[win];
  140.         for (a=0;a<data_buffer_count[win];a++) {
  141.             if (!dir->directory[0]) break;
  142.             dir=dir->next;
  143.         }
  144.     }
  145.     dopus_curwin[win]=dir;
  146. }
  147.  
  148. /* Sets up and reads a new directory */
  149.  
  150. void startgetdir(win,flags)
  151. int win,flags;
  152. {
  153.     checkcurrentbuffer(win);
  154.  
  155.     if (!(config->dirflags&DIRFLAGS_CHECKBUFS && (flags&SGDFLAGS_CANCHECKBUFS)) ||
  156.         !(findbuffer(str_pathbuffer[win],win,1,0))) {
  157.         if ((config->dirflags&DIRFLAGS_FINDEMPTY) && (flags&SGDFLAGS_CANMOVEEMPTY))
  158.             findemptybuffer(win);
  159.         if (str_pathbuffer[win][0]==0) expand_path("",str_pathbuffer[win]);
  160.         checkdir(str_pathbuffer[win],&path_strgadget[win]);
  161.         strcpy(dopus_curwin[win]->directory,str_pathbuffer[win]);
  162.         getdir(dopus_curwin[win],win,(flags&SGDFLAGS_REREADINGOLD));
  163.     }
  164.     seename(win);
  165.     refreshwindow(win,0);
  166. }
  167.  
  168. /* Moves forward or backward a buffer and refreshes display */
  169.  
  170. void incrementbuf(win,dir,show)
  171. int win,dir,show;
  172. {
  173.     advancebuf(win,dir);
  174.     strcpy(str_pathbuffer[win],dopus_curwin[win]->directory);
  175.     checkdir(str_pathbuffer[win],&path_strgadget[win]);
  176.     checksize(win);
  177.     check_old_buffer(win);
  178.     if (show) {
  179.         seename(win);
  180.         refreshwindow(win,1);
  181.         doselinfo(win);
  182.     }
  183.     startnotify(win);
  184. }
  185.  
  186. /* Copies the contents of one window to another */
  187.  
  188. void copydirwin(sourcewin,destwin,dest)
  189. struct DirectoryWindow *sourcewin,*destwin;
  190. int dest;
  191. {
  192.     struct Directory *copy;
  193.  
  194.     if (status_iconified) return;
  195.     busy();
  196.     freedir(destwin,dest);
  197.     copy=sourcewin->firstentry;
  198.     strcpy(destwin->realdevice,sourcewin->realdevice);
  199.     while (copy) {
  200.         if (!(addfile(destwin,dest,copy->name,copy->size,copy->type,©->date,
  201.             copy->comment,copy->protection,copy->subtype,FALSE,copy->dispstr,NULL,
  202.             copy->owner_id,copy->group_id))) break;
  203.         copy=copy->next;
  204.     }
  205.     strcpy(str_pathbuffer[dest],sourcewin->directory);
  206.     strcpy(destwin->directory,sourcewin->directory);
  207.     strcpy(destwin->diskname,sourcewin->diskname);
  208.     destwin->hlen=sourcewin->hlen;
  209.     destwin->flags=sourcewin->flags;
  210.     copy_datestamp(&sourcewin->dirstamp,&destwin->dirstamp);
  211.     refreshwindow(dest,1);
  212.     checkdir(str_pathbuffer[dest],&path_strgadget[dest]);
  213.     checksize(dest);
  214.     last_selected_entry=NULL;
  215.     okay();
  216.     unbusy();
  217. }
  218.  
  219. /* Swaps the contents of the two windows */
  220.  
  221. void swapdirwin()
  222. {
  223.     int tmp;
  224.     struct DirectoryWindow tempwin;
  225.  
  226.     if (status_iconified) return;
  227.  
  228.     CopyMem((char *)dopus_curwin[0],(char *)&tempwin,sizeof(struct DirectoryWindow));
  229.     tempwin.next=dopus_curwin[1]->next; tempwin.number=dopus_curwin[1]->number;
  230.     tempwin.last=dopus_curwin[1]->last;
  231.     dopus_curwin[1]->next=dopus_curwin[0]->next; dopus_curwin[1]->number=dopus_curwin[0]->number;
  232.     dopus_curwin[1]->last=dopus_curwin[0]->last;
  233.     CopyMem((char *)dopus_curwin[1],(char *)dopus_curwin[0],sizeof(struct DirectoryWindow));
  234.     CopyMem((char *)&tempwin,(char *)dopus_curwin[1],sizeof(struct DirectoryWindow));
  235.  
  236.     for (tmp=0;tmp<2;tmp++) {
  237.         strcpy(str_pathbuffer[tmp],dopus_curwin[tmp]->directory);
  238.         refreshwindow(tmp,1);
  239.         checkdir(str_pathbuffer[tmp],&path_strgadget[tmp]);
  240.         checksize(tmp);
  241.     }
  242.     okay();
  243. }
  244.  
  245. /* Moves forwards or backwards a directory */
  246.  
  247. void advancebuf(win,dir)
  248. int win,dir;
  249. {
  250.     int a;
  251.  
  252.     endnotify(win);
  253.     if (!checkcurrentbuffer(win)) {
  254.         if (dir>0) {
  255.             for (a=0;a<dir;a++) dopus_curwin[win]=dopus_curwin[win]->next;
  256.         }
  257.         else {
  258.             for (a=dir;a<0;a++) dopus_curwin[win]=dopus_curwin[win]->last;
  259.         }
  260.     }
  261. }
  262.  
  263. /* Clears the contents of all unshown buffers */
  264.  
  265. void clearbuffers()
  266. {
  267.     int a,win;
  268.     struct DirectoryWindow *dir;
  269.  
  270.     busy();
  271.     for (win=0;win<2;win++) {
  272.         dir=dopus_firstwin[win];
  273.         for (a=0;a<data_buffer_count[win];a++) {
  274.             if (dopus_curwin[win]!=dir) {
  275.                 freedir(dir,-1);
  276.                 dir->directory[0]=0;
  277.             }
  278.             dir=dir->next;
  279.         }
  280.     }
  281.     unbusy();
  282.     dostatustext(globstring[STR_BUFFERS_CLEARED]);
  283. }
  284.  
  285. /* Searches backwards for a named buffer and moves to it if found */
  286.  
  287. struct DirectoryWindow *findbuffer(dirbuf,win,canchecklocks,onlyreturn)
  288. char *dirbuf;
  289. int win,canchecklocks,onlyreturn;
  290. {
  291.     int a,founddir=0,ret=0,try,checklocks=0;
  292.     struct DirectoryWindow *dir;
  293.     BPTR lock=0,testlock;
  294.     struct FileInfoBlock __aligned fblock;
  295.     char tempbuf[300];
  296.  
  297.     if (status_iconified) return(NULL);
  298.     dir=dopus_curwin[win];
  299.     main_proc->pr_WindowPtr=(APTR)-1;
  300.  
  301.     strcpy(tempbuf,dirbuf);
  302.  
  303.     if (config->dirflags&DIRFLAGS_EXPANDPATHS) {
  304.         canchecklocks=0;
  305.         expand_path(dirbuf,tempbuf);
  306.     }
  307.  
  308.     TackOn(tempbuf,NULL,300);
  309.  
  310.     for (try=0;try<2;try++) {
  311.         if (checklocks && !(lock=Lock(tempbuf,ACCESS_READ))) break;
  312.  
  313.         for (a=0;a<data_buffer_count[win];a++,dir=dir->last) {
  314.             if (dir->directory[0]) {
  315.                 if (checklocks &&
  316.                     (testlock=Lock(dir->directory,ACCESS_READ))) {
  317.                     if (CompareLock(lock,testlock)==LOCK_SAME) founddir=1;
  318.                     UnLock(testlock);
  319.                     if (!founddir) continue;
  320.                 }
  321.                 if (founddir || (LStrCmpI(tempbuf,dir->directory))==0) {
  322.                     if (!(lockandexamine(tempbuf,&fblock)) ||
  323.                         (CompareDate(&fblock.fib_Date,&dir->dirstamp)!=0)) continue;
  324.                     if (!onlyreturn) go_to_buffer(win,dir);
  325.                     ret=1;
  326.                     break;
  327.                 }
  328.             }
  329.         }
  330.         if (ret) break;
  331.         if (canchecklocks) checklocks=1;
  332.         else break;
  333.     }
  334.     if (lock) UnLock(lock);
  335.     if (config->errorflags&ERROR_ENABLE_DOS) main_proc->pr_WindowPtr=(APTR)Window;
  336.     return((ret)?dir:NULL);
  337. }
  338.  
  339. /* Checks all buffers for a pathname and changes it to the new
  340.    pathname if found */
  341.  
  342. void renamebuffers(old,new)
  343. char *old,*new;
  344. {
  345.     int a,win;
  346.     struct DirectoryWindow *dir;
  347.  
  348.     for (win=0;win<2;win++) {
  349.         dir=dopus_firstwin[win];
  350.         for (a=0;a<data_buffer_count[win];a++) {
  351.             if (replacepart(dir->directory,old,new) && dopus_curwin[win]==dir) {
  352.                 strcpy(str_pathbuffer[win],dir->directory);
  353.                 checkdir(str_pathbuffer[win],&path_strgadget[win]);
  354.             }
  355.             dir=dir->next;
  356.         }
  357.     }
  358. }
  359.  
  360. replacepart(string,old,new)
  361. char *string,*old,*new;
  362. {
  363.     int oldlen,stringlen;
  364.     char tempbuf[256];
  365.  
  366.     oldlen=strlen(old);
  367.     if (LStrnCmpI(string,old,oldlen)) return(0);
  368.     if ((stringlen=strlen(string))>oldlen) {
  369.         stringlen-=oldlen;
  370.         strncpy(tempbuf,&string[oldlen],stringlen);
  371.         tempbuf[stringlen]=0;
  372.     }
  373.     else stringlen=0;
  374.     strcpy(string,new);
  375.     if (stringlen>0) StrConcat(string,tempbuf,256);
  376.     return(1);
  377. }
  378.  
  379. /* Generates a list of all available buffers in a window */
  380.  
  381. void dolistbuffers(destwin)
  382. int destwin;
  383. {
  384.     int a,b,c,win;
  385.     struct DateStamp ds;
  386.     struct DirectoryWindow *dir;
  387.     char name[2];
  388.  
  389.     if (status_iconified) return;
  390.     makespecialdir(destwin,globstring[STR_BUFFER_LIST_TITLE]);
  391.  
  392.     busy(); b=scrdata_dispwin_nchars[destwin]; name[0]=0;
  393.     DateStamp(&ds);
  394.     for (win=0;win<2;win++) {
  395.         dir=dopus_firstwin[win];
  396.         for (a=0;a<data_buffer_count[win];a++) {
  397.             if (dir->directory[0]) {
  398.                 addfile(dopus_curwin[destwin],destwin,name,0,
  399.                     ENTRY_CUSTOM,&ds,dir->directory,0,
  400.                     CUSTOMENTRY_BUFFERLIST,FALSE,dir->directory,NULL,0,0);
  401.                 if ((c=strlen(dir->directory))>b) b=c;
  402.             }
  403.             dir=dir->next;
  404.         }
  405.     }
  406.     dopus_curwin[destwin]->hlen=b;
  407.     refreshwindow(destwin,3);
  408.     unbusy();
  409.     okay();
  410. }
  411.  
  412. /* Check if buffer is a special buffer, and return to normal if so */
  413.  
  414. checkcurrentbuffer(win)
  415. int win;
  416. {
  417.     if (dopus_curwin[win]==dopus_specialwin[win]) {
  418.         dopus_curwin[win]=dopus_oldwin[win];
  419.         return(1);
  420.     }
  421.     return(0);
  422. }
  423.  
  424. /* Send message to custom entry handler */
  425.  
  426. void userentrymessage(dir,entry,type)
  427. struct DirectoryWindow *dir;
  428. struct Directory *entry;
  429. int type;
  430. {
  431.     struct RexxMsg *msg,*reply;
  432.     struct MsgPort *port;
  433.     int fail=0;
  434.  
  435.     /* If no customhandler installed or rexx library is not there, return */
  436.     if (!dir->custhandler[0] || !RexxSysBase) return;
  437.  
  438.     /* Attempt to create the message packet */
  439.     if (!(msg=CreateRexxMsg(general_port,NULL,str_arexx_portname))) fail=1;
  440.     else {
  441.         /* Set message type (and argument count of 4) */
  442.         msg->rm_Action=RXFUNC|4;
  443.  
  444.         /* Fill out argument pointers.
  445.  
  446.            Arg0   = operation type
  447.            Arg1   = entry number
  448.            Arg2   = entry text
  449.            Arg3   = userdata          */
  450.  
  451.         msg->rm_Args[0]=(STRPTR)type;
  452.         msg->rm_Args[1]=entry->name;
  453.         msg->rm_Args[2]=entry->comment;
  454.         msg->rm_Args[3]=(STRPTR)entry->protection;
  455.  
  456.         /* Fill in the message correctly.
  457.            Arguments are integer/string/string/integer
  458.                         1       0      0      1       = 9 */
  459.  
  460.         if (!(FillRexxMsg(msg,4,9))) fail=1;
  461.     }
  462.  
  463.     /* If we failed to create the message properly, release it and return */
  464.     if (fail) {
  465.         if (msg) DeleteRexxMsg(msg);
  466.         doerror(103);
  467.         return;
  468.     }
  469.  
  470.     /* Forbid() to find the port */
  471.     Forbid();
  472.     if (!(port=FindPort(dir->custhandler))) {
  473.         char message[200];
  474.  
  475.         /* Port was not out there; put up an error requester */
  476.         lsprintf(message,globstring[STR_CUSTPORT_NOT_FOUND],dir->custhandler);
  477.         simplerequest(message,str_okaystring,NULL);
  478.  
  479.         /* Clear message and return */
  480.         ClearRexxMsg(msg,4);
  481.         DeleteRexxMsg(msg);
  482.         Permit();
  483.         return;
  484.     }
  485.  
  486.     /* Turn busy pointer on and send the message */
  487.     busy();
  488.     PutMsg(port,(struct Message *)msg);
  489.  
  490.     Permit();
  491.     SetSignal(0,INPUTSIG_ABORT);
  492.  
  493.     FOREVER {
  494.         /* If abort sequence hit, break out immediately. The message is now
  495.            lost to us, we can never free it */
  496.         if ((Wait(1<<general_port->mp_SigBit|INPUTSIG_ABORT))&INPUTSIG_ABORT) {
  497.             status_haveaborted=status_justabort=0;
  498.             break;
  499.         }
  500.  
  501.         /* If message has been replied, free it and break out */
  502.         if (reply=(struct RexxMsg *)GetMsg(general_port)) {
  503.             ClearRexxMsg(msg,4);
  504.             DeleteRexxMsg(msg);
  505.             break;
  506.         }
  507.     }
  508.     unbusy();
  509.     okay();
  510. }
  511.  
  512. /* Opens the "special" dir in the window for a custom list */
  513.  
  514. void makespecialdir(win,title)
  515. int win;
  516. char *title;
  517. {
  518.     freedir(dopus_specialwin[win],win);
  519.     if (dopus_curwin[win]!=dopus_specialwin[win]) {
  520.         dopus_oldwin[win]=dopus_curwin[win];
  521.         dopus_curwin[win]=dopus_specialwin[win];
  522.     }
  523.     strcpy(dopus_curwin[win]->diskname,title);
  524.     str_pathbuffer[win][0]=0;
  525.     checkdir(str_pathbuffer[win],&path_strgadget[win]);
  526.     freedir(dopus_curwin[win],win);
  527.     dopus_curwin[win]->diskfree=dopus_curwin[win]->disktot=dopus_curwin[win]->diskblock=-1;
  528. }
  529.  
  530. /* Checks buffers to see if they need re-reading */
  531.  
  532. void check_old_buffer(win)
  533. int win;
  534. {
  535.     int reread=0;
  536.  
  537.     if (config->dirflags&DIRFLAGS_REREADOLD && dopus_curwin[win]->directory[0]) {
  538.         if (dopus_curwin[win]->firstentry &&
  539.             (dopus_curwin[win]->firstentry->type==ENTRY_CUSTOM ||
  540.             dopus_curwin[win]->firstentry->type==ENTRY_DEVICE)) return;
  541.         else {
  542.             struct FileInfoBlock __aligned testinfo;
  543.  
  544.             main_proc->pr_WindowPtr=(APTR)-1;
  545.             if (lockandexamine(dopus_curwin[win]->directory,&testinfo)) {
  546.                 if (CompareDate(&dopus_curwin[win]->dirstamp,&testinfo.fib_Date)<0)
  547.                     reread=1;
  548.                 else if (!(config->dirflags&DIRFLAGS_EXPANDPATHS) &&
  549.                     dopus_curwin[win]->volumename[0]) {
  550.  
  551.                     char rootname[256];
  552.  
  553.                     strcpy(rootname,dopus_curwin[win]->directory);
  554.                     if (getroot(rootname,NULL) &&
  555.                         (strcmp(rootname,dopus_curwin[win]->volumename))!=0)
  556.                         reread=1;
  557.                 }
  558.             }
  559.             if (config->errorflags&ERROR_ENABLE_DOS) main_proc->pr_WindowPtr=(APTR)Window;
  560.         }
  561.         if (reread) {
  562.             struct DirWindowPars notifypars;
  563.  
  564.             notifypars.reselection_list=NULL;
  565.             makereselect(¬ifypars,win);
  566.             startgetdir(win,SGDFLAGS_REREADINGOLD);
  567.             doreselect(¬ifypars,0);
  568.             makereselect(¬ifypars,-1);
  569.         }
  570.     }
  571. }
  572.  
  573. /* Refresh the display of a window */
  574.  
  575. void refreshwindow(win,type)
  576. int win,type;
  577. {
  578.     if (win>-1) {
  579.         if (dopus_curwin[win]->offset>dopus_curwin[win]->total-scrdata_dispwin_lines)
  580.             dopus_curwin[win]->offset=dopus_curwin[win]->total-scrdata_dispwin_lines;
  581.         if (dopus_curwin[win]->offset<0)
  582.             dopus_curwin[win]->offset=0;
  583.  
  584.         if (type&1) {
  585.             fixprop(win);
  586.             fixhorizprop(win);
  587.         }
  588.         if (type&2) displayname(win,1);
  589.         dopus_curwin[win]->oldoff=dopus_curwin[win]->oldhoff=-1;
  590.         displaydir(win);
  591.     }
  592. }
  593.  
  594. /* Move to a buffer */
  595.  
  596. void go_to_buffer(win,dir)
  597. int win;
  598. struct DirectoryWindow *dir;
  599. {
  600.     endnotify(win);
  601.     dopus_curwin[win]=dir;
  602.     strcpy(str_pathbuffer[win],dir->directory);
  603.     checkdir(str_pathbuffer[win],&path_strgadget[win]);
  604.     checksize(win);
  605.     check_old_buffer(win);
  606.     refreshwindow(win,1);
  607.     doselinfo(win);
  608.     startnotify(win);
  609. }
  610.