Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / WINNT / afsusrmgr / display.cpp
1 extern "C" {
2 #include <afs/param.h>
3 #include <afs/stds.h>
4 }
5
6 #include "TaAfsUsrMgr.h"
7 #include "usr_col.h"
8 #include "grp_col.h"
9 #include "mch_col.h"
10
11
12 /*
13  * ANIMATED ICON ______________________________________________________________
14  *
15  */
16
17 static DWORD l_cReqAnimation = 0;
18
19 void Display_StartWorking (void)
20 {
21    if ((++l_cReqAnimation) == 1)
22       {
23       AfsAppLib_StartAnimation (GetDlgItem (g.hMain, IDC_ANIM));
24       }
25 }
26
27
28 void Display_StopWorking (void)
29 {
30    if (!l_cReqAnimation || !(--l_cReqAnimation))
31       {
32       AfsAppLib_StopAnimation (GetDlgItem (g.hMain, IDC_ANIM));
33       }
34 }
35
36
37 /*
38  * USER/GROUP LISTS ___________________________________________________________
39  *
40  */
41
42 void Display_PopulateList (void)
43 {
44    switch (Display_GetActiveTab())
45       {
46       case ttUSERS:
47          Display_PopulateUserList();
48          break;
49
50       case ttGROUPS:
51          Display_PopulateGroupList();
52          break;
53
54       case ttMACHINES:
55          Display_PopulateMachineList();
56          break;
57       }
58 }
59
60
61 void Display_PopulateUserList (void)
62 {
63    if (g.idCell)
64       {
65       HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
66       HWND hList = GetDlgItem (hDlg, IDC_USERS_LIST);
67       if (IsWindow (hList))
68          {
69          TCHAR szQuerying[ cchRESOURCE ];
70          GetString (szQuerying, IDS_QUERYING_LONG);
71          SetDlgItemText (hDlg, IDC_USERS_TITLE, szQuerying);
72
73          Display_StartWorking();
74          GetDlgItemText (hDlg, IDC_USERS_PATTERN, g.szPatternUsers, cchNAME);
75          StartTask (taskUPD_USERS, g.hMain);
76          }
77       }
78 }
79
80
81 void Display_PopulateGroupList (void)
82 {
83    if (g.idCell)
84       {
85       HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
86       HWND hList = GetDlgItem (hDlg, IDC_GROUPS_LIST);
87       if (IsWindow (hList))
88          {
89          TCHAR szQuerying[ cchRESOURCE ];
90          GetString (szQuerying, IDS_QUERYING_LONG);
91          SetDlgItemText (hDlg, IDC_GROUPS_TITLE, szQuerying);
92
93          Display_StartWorking();
94          GetDlgItemText (hDlg, IDC_GROUPS_PATTERN, g.szPatternGroups, cchNAME);
95          StartTask (taskUPD_GROUPS, g.hMain);
96          }
97       }
98 }
99
100
101 void Display_PopulateMachineList (void)
102 {
103    if (g.idCell)
104       {
105       HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
106       HWND hList = GetDlgItem (hDlg, IDC_MACHINES_LIST);
107       if (IsWindow (hList))
108          {
109          TCHAR szQuerying[ cchRESOURCE ];
110          GetString (szQuerying, IDS_QUERYING_LONG);
111          SetDlgItemText (hDlg, IDC_MACHINES_TITLE, szQuerying);
112
113          Display_StartWorking();
114          GetDlgItemText (hDlg, IDC_MACHINES_PATTERN, g.szPatternMachines, cchNAME);
115          StartTask (taskUPD_MACHINES, g.hMain);
116          }
117       }
118 }
119
120
121 void Display_OnEndTask_UpdUsers (LPTASKPACKET ptp)
122 {
123    HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
124    HWND hList = GetDlgItem (hDlg, IDC_USERS_LIST);
125    if (IsWindow (hList) && !lstrcmpi (TASKDATA(ptp)->szPattern, g.szPatternUsers))
126       {
127       FastList_Begin (hList);
128
129       // Update the title above the list to indicate what we're showing
130       //
131       TCHAR szCell[ cchRESOURCE ];
132       asc_CellNameGet_Fast (g.idClient, g.idCell, szCell);
133
134       LPTSTR pszTitle = FormatString ((TASKDATA(ptp)->szPattern[0] || (gr.SearchUsers.SearchType != SEARCH_NO_LIMITATIONS)) ? IDS_USERS_PATTERN : IDS_USERS_ALL, TEXT("%s"), szCell);
135       SetDlgItemText (hDlg, IDC_USERS_TITLE, pszTitle);
136       FreeString (pszTitle);
137
138       // For faster access, we'll want to use a hashlist to deal with
139       // the items in our ASIDLIST (right now it's just a flat array).
140       // This lets us remove duplicates--which is no big deal because
141       // there shouldn't be any anyway--but more importantly it lets
142       // us instantly determine if a particular ASID is in the list
143       // (the asc_AsidListTest function is O(n), and we need O(1)).
144       //
145       LPHASHLIST pAsidList = New (HASHLIST);
146       if (TASKDATA(ptp)->pAsidList)
147          {
148          for (size_t iAsid = 0; iAsid < TASKDATA(ptp)->pAsidList->cEntries; ++iAsid)
149             pAsidList->AddUnique ((PVOID)(TASKDATA(ptp)->pAsidList->aEntries[ iAsid ].idObject));
150          }
151
152       // Delete any items which are currently in the FastList but
153       // which aren't in our AsidList.
154       //
155       HLISTITEM hItemNext;
156       for (HLISTITEM hItem = FastList_FindFirst (hList); hItem; hItem = hItemNext)
157          {
158          hItemNext = FastList_FindNext (hList, hItem);
159          ASID idObject = (ASID)FastList_GetItemParam (hList, hItem);
160          if (!pAsidList->fIsInList ((PVOID)idObject))
161             FastList_RemoveItem (hList, hItem);
162          }
163
164       // Add items for any entries which are in our AsidList but aren't
165       // currently in the FastList.
166       //
167       DWORD dwStyle = GetWindowLong (hList, GWL_STYLE);
168
169       for (LPENUM pEnum = pAsidList->FindFirst(); pEnum; pEnum = pEnum->FindNext())
170          {
171          ASID idObject = (ASID)( pEnum->GetObject() );
172
173          HLISTITEM hItem;
174          if ((hItem = FastList_FindItem (hList, (LPARAM)idObject)) == NULL)
175             {
176             FASTLISTADDITEM ai;
177             memset (&ai, 0x00, sizeof(ai));
178             Display_GetImageIcons (dwStyle, gr.ivUsr, idObject, imageUSER, IMAGE_NOIMAGE, &ai.iFirstImage, &ai.iSecondImage);
179             ai.lParam = (LPARAM)idObject;
180             hItem = FastList_AddItem (hList, &ai);
181             }
182          }
183
184       Delete (pAsidList);
185       FastList_End (hList);
186       }
187
188    Display_StopWorking();
189 }
190
191
192 void Display_OnEndTask_UpdGroups (LPTASKPACKET ptp)
193 {
194    HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
195    HWND hList = GetDlgItem (hDlg, IDC_GROUPS_LIST);
196    if (IsWindow (hList) && !lstrcmpi (TASKDATA(ptp)->szPattern, g.szPatternGroups))
197       {
198       FastList_Begin (hList);
199
200       // Update the title above the list to indicate what we're showing
201       //
202       TCHAR szCell[ cchRESOURCE ];
203       asc_CellNameGet_Fast (g.idClient, g.idCell, szCell);
204
205       LPTSTR pszTitle = FormatString ((TASKDATA(ptp)->szPattern[0]) ? IDS_GROUPS_PATTERN : IDS_GROUPS_ALL, TEXT("%s"), szCell);
206       SetDlgItemText (hDlg, IDC_GROUPS_TITLE, pszTitle);
207       FreeString (pszTitle);
208
209       // For faster access, we'll want to use a hashlist to deal with
210       // the items in our ASIDLIST (right now it's just a flat array).
211       // This lets us remove duplicates--which is no big deal because
212       // there shouldn't be any anyway--but more importantly it lets
213       // us instantly determine if a particular ASID is in the list
214       // (the asc_AsidListTest function is O(n), and we need O(1)).
215       //
216       LPHASHLIST pAsidList = New (HASHLIST);
217       if (TASKDATA(ptp)->pAsidList)
218          {
219          for (size_t iAsid = 0; iAsid < TASKDATA(ptp)->pAsidList->cEntries; ++iAsid)
220             pAsidList->AddUnique ((PVOID)(TASKDATA(ptp)->pAsidList->aEntries[ iAsid ].idObject));
221          }
222
223       // Delete any items which are currently in the FastList but
224       // which aren't in our AsidList.
225       //
226       HLISTITEM hItemNext;
227       for (HLISTITEM hItem = FastList_FindFirst (hList); hItem; hItem = hItemNext)
228          {
229          hItemNext = FastList_FindNext (hList, hItem);
230          ASID idObject = (ASID)FastList_GetItemParam (hList, hItem);
231          if (!pAsidList->fIsInList ((PVOID)idObject))
232             FastList_RemoveItem (hList, hItem);
233          }
234
235       // Add items for any entries which are in our AsidList but aren't
236       // currently in the FastList.
237       //
238       DWORD dwStyle = GetWindowLong (hList, GWL_STYLE);
239
240       for (LPENUM pEnum = pAsidList->FindFirst(); pEnum; pEnum = pEnum->FindNext())
241          {
242          ASID idObject = (ASID)( pEnum->GetObject() );
243
244          HLISTITEM hItem;
245          if ((hItem = FastList_FindItem (hList, (LPARAM)idObject)) == NULL)
246             {
247             FASTLISTADDITEM ai;
248             memset (&ai, 0x00, sizeof(ai));
249             Display_GetImageIcons (dwStyle, gr.ivGrp, idObject, imageGROUP, IMAGE_NOIMAGE, &ai.iFirstImage, &ai.iSecondImage);
250             ai.lParam = (LPARAM)idObject;
251             hItem = FastList_AddItem (hList, &ai);
252             }
253          }
254
255       Delete (pAsidList);
256       FastList_End (hList);
257       }
258
259    Display_StopWorking();
260 }
261
262
263 void Display_OnEndTask_UpdMachines (LPTASKPACKET ptp)
264 {
265    HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
266    HWND hList = GetDlgItem (hDlg, IDC_MACHINES_LIST);
267    if (IsWindow (hList) && !lstrcmpi (TASKDATA(ptp)->szPattern, g.szPatternMachines))
268       {
269       FastList_Begin (hList);
270
271       // Update the title above the list to indicate what we're showing
272       //
273       TCHAR szCell[ cchRESOURCE ];
274       asc_CellNameGet_Fast (g.idClient, g.idCell, szCell);
275
276       LPTSTR pszTitle = FormatString ((TASKDATA(ptp)->szPattern[0]) ? IDS_MACHINES_PATTERN : IDS_MACHINES_ALL, TEXT("%s"), szCell);
277       SetDlgItemText (hDlg, IDC_MACHINES_TITLE, pszTitle);
278       FreeString (pszTitle);
279
280       // For faster access, we'll want to use a hashlist to deal with
281       // the items in our ASIDLIST (right now it's just a flat array).
282       // This lets us remove duplicates--which is no big deal because
283       // there shouldn't be any anyway--but more importantly it lets
284       // us instantly determine if a particular ASID is in the list
285       // (the asc_AsidListTest function is O(n), and we need O(1)).
286       //
287       LPHASHLIST pAsidList = New (HASHLIST);
288       if (TASKDATA(ptp)->pAsidList)
289          {
290          for (size_t iAsid = 0; iAsid < TASKDATA(ptp)->pAsidList->cEntries; ++iAsid)
291             pAsidList->AddUnique ((PVOID)(TASKDATA(ptp)->pAsidList->aEntries[ iAsid ].idObject));
292          }
293
294       // Delete any items which are currently in the FastList but
295       // which aren't in our AsidList.
296       //
297       HLISTITEM hItemNext;
298       for (HLISTITEM hItem = FastList_FindFirst (hList); hItem; hItem = hItemNext)
299          {
300          hItemNext = FastList_FindNext (hList, hItem);
301          ASID idObject = (ASID)FastList_GetItemParam (hList, hItem);
302          if (!pAsidList->fIsInList ((PVOID)idObject))
303             FastList_RemoveItem (hList, hItem);
304          }
305
306       // Add items for any entries which are in our AsidList but aren't
307       // currently in the FastList.
308       //
309       DWORD dwStyle = GetWindowLong (hList, GWL_STYLE);
310
311       for (LPENUM pEnum = pAsidList->FindFirst(); pEnum; pEnum = pEnum->FindNext())
312          {
313          ASID idObject = (ASID)( pEnum->GetObject() );
314
315          HLISTITEM hItem;
316          if ((hItem = FastList_FindItem (hList, (LPARAM)idObject)) == NULL)
317             {
318             FASTLISTADDITEM ai;
319             memset (&ai, 0x00, sizeof(ai));
320             Display_GetImageIcons (dwStyle, gr.ivMch, idObject, imageSERVER, IMAGE_NOIMAGE, &ai.iFirstImage, &ai.iSecondImage);
321             ai.lParam = (LPARAM)idObject;
322             hItem = FastList_AddItem (hList, &ai);
323             }
324          }
325
326       Delete (pAsidList);
327       FastList_End (hList);
328       }
329
330    Display_StopWorking();
331 }
332
333
334 void Display_RefreshView (LPVIEWINFO pviNew, ICONVIEW ivNew)
335 {
336    // Find the current VIEWINFO and ICONVIEW settings
337    //
338    HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
339
340    HWND hList;
341    LPVIEWINFO pviOld;
342    ICONVIEW *pivOld;
343    switch (Display_GetActiveTab())
344       {
345       case ttUSERS:
346          pivOld = &gr.ivUsr;
347          pviOld = &gr.viewUsr;
348          hList = GetDlgItem (hDlg, IDC_USERS_LIST);
349          break;
350
351       case ttGROUPS:
352          pivOld = &gr.ivGrp;
353          pviOld = &gr.viewGrp;
354          hList = GetDlgItem (hDlg, IDC_GROUPS_LIST);
355          break;
356
357       case ttMACHINES:
358          pivOld = &gr.ivMch;
359          pviOld = &gr.viewMch;
360          hList = GetDlgItem (hDlg, IDC_MACHINES_LIST);
361          break;
362       }
363
364    if (IsWindow(hList))
365       {
366       FastList_Begin (hList);
367
368       // If the VIEWINFO state has changed, fix it. This will change between
369       // large icons, small icons and details, as well as correct the currently-
370       // displayed columns to match what's in the new VIEWINFO structure.
371       //
372       BOOL fChangedLayouts = FALSE;
373
374       if (memcmp (pviOld, pviNew, sizeof(VIEWINFO)))
375          {
376          FL_RestoreView (hList, pviNew);
377          fChangedLayouts = ((pviOld->lvsView & FLS_VIEW_MASK) != (pviNew->lvsView & FLS_VIEW_MASK)) ? TRUE : FALSE;
378          memcpy (pviOld, pviNew, sizeof(VIEWINFO));
379          }
380
381       // If the ICONVIEW state has changed, fix all items to show just
382       // the appropriate icons. We'll also have to do this if we just changed
383       // from details/small/large icons to another layout (that's what
384       // fChangedLayouts indicates)
385       //
386       if ((*pivOld != ivNew) || (fChangedLayouts))
387          {
388          DWORD dwStyle = GetWindowLong (hList, GWL_STYLE);
389
390          HLISTITEM hItem = NULL;
391          while ((hItem = FastList_FindNext (hList, hItem)) != NULL)
392             {
393             ASID idObject = (ASID)FastList_GetItemParam (hList, hItem);
394
395             int iFirstImage;
396             int iSecondImage;
397             if (pviOld == &gr.viewUsr)
398                Display_GetImageIcons (dwStyle, ivNew, idObject, imageUSER, IMAGE_NOIMAGE, &iFirstImage, &iSecondImage);
399             else if (pviOld == &gr.viewGrp)
400                Display_GetImageIcons (dwStyle, ivNew, idObject, imageGROUP, IMAGE_NOIMAGE, &iFirstImage, &iSecondImage);
401             else
402                Display_GetImageIcons (dwStyle, ivNew, idObject, imageSERVER, IMAGE_NOIMAGE, &iFirstImage, &iSecondImage);
403
404             FastList_SetItemFirstImage (hList, hItem, iFirstImage);
405             FastList_SetItemSecondImage (hList, hItem, iSecondImage);
406             }
407
408          *pivOld = ivNew;
409          }
410
411       FastList_End (hList);
412       }
413 }
414
415
416 void Display_RefreshView_Fast (void)
417 {
418    HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
419    HWND hList = GetDlgItem (hDlg, IDC_GROUPS_LIST);
420    if (!IsWindow (hList))
421       hList = GetDlgItem (hDlg, IDC_USERS_LIST);
422    if (!IsWindow (hList))
423       hList = GetDlgItem (hDlg, IDC_MACHINES_LIST);
424    if (IsWindow (hList))
425       {
426       RECT rWindow;
427       GetClientRect (hList, &rWindow);
428       InvalidateRect (hList, &rWindow, FALSE);
429       UpdateWindow (hList);
430       }
431 }
432
433
434 void Display_SelectAll (void)
435 {
436    HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
437    HWND hList = GetDlgItem (hDlg, IDC_GROUPS_LIST);
438    if (!IsWindow (hList))
439       hList = GetDlgItem (hDlg, IDC_USERS_LIST);
440    if (!IsWindow (hList))
441       hList = GetDlgItem (hDlg, IDC_MACHINES_LIST);
442    if (IsWindow (hList))
443       {
444       // Select all items in the list
445       //
446       FastList_SelectAll (hList);
447
448       // Simulate a FLN_ITEMSELECT notification (since we changed
449       // the selection programmatically, no notification will have been
450       // sent).
451       //
452       FLN_ITEMSELECT_PARAMS fln;
453       fln.hdr.hwndFrom = hList;
454       fln.hdr.idFrom = GetWindowLong (hList, GWL_ID);
455       fln.hdr.code = FLN_ITEMSELECT;
456       fln.hItem = FastList_FindFirstSelected (hList);
457       SendMessage (GetParent (hList), WM_NOTIFY, (WPARAM)fln.hdr.idFrom, (LPARAM)&fln);
458       }
459 }
460
461
462 LPASIDLIST Display_GetSelectedList (void)
463 {
464    LPASIDLIST pAsidList = NULL;
465
466    HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
467    HWND hList = GetDlgItem (hDlg, IDC_GROUPS_LIST);
468    if (!IsWindow (hList))
469       hList = GetDlgItem (hDlg, IDC_USERS_LIST);
470    if (!IsWindow (hList))
471       hList = GetDlgItem (hDlg, IDC_MACHINES_LIST);
472    if (IsWindow (hList))
473       {
474       if (asc_AsidListCreate (&pAsidList))
475          {
476          for (HLISTITEM hItem = FastList_FindFirstSelected (hList);
477               hItem != NULL;
478               hItem = FastList_FindNextSelected (hList, hItem))
479             {
480             ASID idObject = (ASID)FastList_GetItemParam (hList, hItem);
481             if (idObject)
482                asc_AsidListAddEntry (&pAsidList, idObject, 0);
483             }
484          }
485       }
486
487    return pAsidList;
488 }
489
490
491 size_t Display_GetSelectedCount (void)
492 {
493    size_t cSelected = 0;
494
495    HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
496    HWND hList = GetDlgItem (hDlg, IDC_GROUPS_LIST);
497    if (!IsWindow (hList))
498       hList = GetDlgItem (hDlg, IDC_USERS_LIST);
499    if (!IsWindow (hList))
500       hList = GetDlgItem (hDlg, IDC_MACHINES_LIST);
501    if (IsWindow (hList))
502       {
503       for (HLISTITEM hItem = FastList_FindFirstSelected (hList);
504            hItem != NULL;
505            hItem = FastList_FindNextSelected (hList, hItem))
506          {
507          ++cSelected;
508          }
509       }
510
511    return cSelected;
512 }
513
514
515 TABTYPE Display_GetActiveTab (void)
516 {
517    HWND hDlg = GetTabChild (GetDlgItem (g.hMain, IDC_TAB));
518    if (IsWindow (GetDlgItem (hDlg, IDC_GROUPS_LIST)))
519       return ttGROUPS;
520    if (IsWindow (GetDlgItem (hDlg, IDC_MACHINES_LIST)))
521       return ttMACHINES;
522    return ttUSERS;
523 }
524
525
526 /*
527  * ROUTINES ___________________________________________________________________
528  *
529  */
530
531 BOOL Display_HandleColumnNotify (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp, LPVIEWINFO pvi)
532 {
533    if (msg == WM_NOTIFY)
534       {
535       HWND hList = GetDlgItem (hDlg, ((LPNMHDR)lp)->idFrom);
536       if (fIsFastList (hList))
537          {
538          switch (((LPNMHDR)lp)->code)
539             { 
540             case FLN_COLUMNRESIZE:
541                FL_StoreView (hList, pvi);
542                return TRUE;
543
544             case FLN_COLUMNCLICK:
545                LPFLN_COLUMNCLICK_PARAMS pp = (LPFLN_COLUMNCLICK_PARAMS)lp;
546
547                int iCol;
548                BOOL fRev;
549                FastList_GetSortStyle (hList, &iCol, &fRev);
550
551                if (iCol == pp->icol)
552                   FastList_SetSortStyle (hList, iCol, !fRev);
553                else
554                   FastList_SetSortStyle (hList, pp->icol, FALSE);
555
556                FL_StoreView (hList, pvi);
557                return TRUE;
558             }
559          }
560       }
561
562    return FALSE;
563 }
564
565
566 BOOL CALLBACK Display_GetItemText (HWND hList, LPFLN_GETITEMTEXT_PARAMS pfln, DWORD dwCookie)
567
568    LPVIEWINFO lpvi = (LPVIEWINFO)dwCookie;
569    ASID idObject = (ASID)(pfln->item.lParam);
570
571    pfln->item.pszText[0] = TEXT('\0');
572
573    if ((idObject) && (pfln->item.icol < (int)lpvi->nColsShown))
574       {
575       size_t iCol = lpvi->aColumns[ pfln->item.icol ];
576
577       if (lpvi == &gr.viewUsr)
578          {
579          User_GetColumn (idObject, (USERCOLUMN)iCol, pfln->item.pszText, NULL, NULL, NULL);
580          }
581       else if (lpvi == &gr.viewGrp)
582          {
583          Group_GetColumn (idObject, (GROUPCOLUMN)iCol, pfln->item.pszText, NULL, NULL, NULL);
584          }
585       else if (lpvi == &gr.viewMch)
586          {
587          Machine_GetColumn (idObject, (MACHINECOLUMN)iCol, pfln->item.pszText, NULL, NULL, NULL);
588          }
589       }
590
591    return TRUE;
592 }
593
594
595 void Display_GetImageIcons (DWORD dwStyle, ICONVIEW iv, ASID idObject, int iImageNormal, int iImageAlert, int *piFirstImage, int *piSecondImage)
596 {
597    BOOL fAlert = FALSE;
598
599    if ((dwStyle & FLS_VIEW_MASK) != FLS_VIEW_LIST)
600       iv = ivONEICON;
601
602    switch (iv)
603       {
604       case ivTWOICONS:
605          *piFirstImage = iImageNormal;
606          *piSecondImage = (fAlert) ? iImageAlert : IMAGE_BLANKIMAGE;
607          break;
608
609       case ivONEICON:
610          *piFirstImage = (fAlert) ? iImageAlert : iImageNormal;
611          *piSecondImage = IMAGE_NOIMAGE;
612          break;
613
614       case ivSTATUS:
615          *piFirstImage = (fAlert) ? iImageAlert : IMAGE_BLANKIMAGE;
616          *piSecondImage = IMAGE_NOIMAGE;
617          break;
618       }
619 }
620