1eee29733addb81ec70e01cfcc152f1252ef5e7e
[openafs.git] / src / WINNT / afssvrmgr / display.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 extern "C" {
11 #include <afs/param.h>
12 #include <afs/stds.h>
13 }
14
15 #include "svrmgr.h"
16 #include "display.h"
17 #include "dispguts.h"
18 #include "svr_window.h"
19 #include "svr_col.h"
20 #include "svc_col.h"
21 #include "agg_col.h"
22 #include "set_col.h"
23 #include "propcache.h"
24
25
26 /*
27  * DEFINITIONS ________________________________________________________________
28  *
29  */
30
31 #define cREALLOC_DISPLAYQUEUE  128
32
33 #define cUPDATE_THREADS_MAX      4
34
35
36 /*
37  * VARIABLES __________________________________________________________________
38  *
39  */
40
41 static size_t cDisplayQueueActive = 0;
42 static size_t cDisplayQueue = 0;
43 static size_t cUpdateThreadsActive = 0;
44 static DISPLAYREQUEST *aDisplayQueue = NULL;
45 static CRITICAL_SECTION *pcsDisplayQueue = NULL;
46
47 static DISPLAYREQUEST drActiveSERVERS;
48 static DISPLAYREQUEST drActiveSERVICES;
49 static DISPLAYREQUEST drActiveAGGREGATES;
50 static DISPLAYREQUEST drActiveFILESETS;
51 static DISPLAYREQUEST drActiveSERVERWINDOW;
52
53 static struct {
54    HWND hWnd;
55    WORD actOnDone;
56    LPIDENT lpiSelectOnDone;
57 } *aWindowActOnDone = NULL;
58 static size_t cWindowActOnDone = 0;
59 static CRITICAL_SECTION *pcsWindowActOnDone = NULL;
60
61
62 /*
63  * PROTOTYPES _________________________________________________________________
64  *
65  */
66
67 DWORD WINAPI DisplayQueue_ThreadProc (PVOID lp);
68
69 BOOL DisplayQueueFilter (size_t idqVictim, size_t idqKiller);
70
71
72 /*
73  * ROUTINES ___________________________________________________________________
74  *
75  */
76
77 BOOL CALLBACK GetItemText (HWND hList, LPFLN_GETITEMTEXT_PARAMS pfln, DWORD dwCookie)
78
79    LPVIEWINFO lpvi = (LPVIEWINFO)dwCookie;
80    LPIDENT lpi = (LPIDENT)(pfln->item.lParam);
81    LPTSTR psz = NULL;
82
83    if (pfln->item.icol < (int)lpvi->nColsShown)
84       {
85       size_t iCol = lpvi->aColumns[ pfln->item.icol ];
86
87       BOOL fShowServerName = !Server_GetServerForChild (GetParent(hList));
88
89       if (lpi != NULL)
90          {
91          DISPLAYTARGET dt = dtSERVERS;
92          if (lpvi == &gr.viewSet)
93             dt = dtFILESETS;
94          else if (lpvi == &gr.viewSvc)
95             dt = dtSERVICES;
96          else if ((lpvi == &gr.viewAgg) || (lpvi == &gr.viewAggMove) || (lpvi == &gr.viewAggCreate) || (lpvi == &gr.viewAggRestore))
97             dt = dtAGGREGATES;
98          else if (lpvi == &gr.viewRep)
99             dt = dtREPLICAS;
100
101          switch (dt)
102             {
103             case dtSERVERS:
104                if (lpi->fIsServer())
105                   psz = Server_GetColumnText (lpi, (SERVERCOLUMN)iCol);
106                break;
107
108             case dtSERVICES:
109                if (lpi->fIsService())
110                   psz = Services_GetColumnText (lpi, (SERVICECOLUMN)iCol, fShowServerName);
111                break;
112
113             case dtAGGREGATES:
114                if (lpi->fIsAggregate())
115                   psz = Aggregates_GetColumnText (lpi, (AGGREGATECOLUMN)iCol, fShowServerName);
116                break;
117
118             case dtFILESETS:
119                if (lpi->fIsFileset())
120                   psz = Filesets_GetColumnText (lpi, (FILESETCOLUMN)iCol, fShowServerName);
121                else if (lpi->fIsAggregate() && (pfln->item.icol == setcolNAME))
122                   psz = Aggregates_GetColumnText (lpi, aggcolNAME, FALSE);
123                else if (lpi->fIsServer() && (pfln->item.icol == setcolNAME))
124                   psz = Server_GetColumnText (lpi, svrcolNAME);
125                break;
126
127             case dtREPLICAS:
128                if (lpi->fIsFileset())
129                   psz = Replicas_GetColumnText (lpi, (REPLICACOLUMN)iCol);
130                break;
131             }
132          }
133       }
134
135    lstrcpy (pfln->item.pszText, (psz) ? psz : TEXT(""));
136    return TRUE;
137 }
138
139
140 void Display_AddActOnDone (HWND hWnd, WORD wAct, LPIDENT lpiSelectOnDone)
141 {
142    if (pcsWindowActOnDone == NULL)
143       {
144       pcsWindowActOnDone = New (CRITICAL_SECTION);
145       InitializeCriticalSection (pcsWindowActOnDone);
146       }
147    EnterCriticalSection (pcsWindowActOnDone);
148
149    for (size_t ii = 0; ii < cWindowActOnDone; ++ii)
150       {
151       if (aWindowActOnDone[ ii ].hWnd == hWnd)
152          break;
153       }
154    if (ii == cWindowActOnDone)
155       {
156       for (ii = 0; ii < cWindowActOnDone; ++ii)
157          {
158          if (aWindowActOnDone[ ii ].hWnd == 0)
159             break;
160          }
161       }
162    if (ii == cWindowActOnDone)
163       {
164       (void)REALLOC( aWindowActOnDone, cWindowActOnDone, 1+ii, 1 );
165       }
166    if (ii < cWindowActOnDone)
167       {
168       aWindowActOnDone[ ii ].hWnd = hWnd;
169       aWindowActOnDone[ ii ].actOnDone |= wAct;
170       if (!aWindowActOnDone[ ii ].lpiSelectOnDone)
171          aWindowActOnDone[ ii ].lpiSelectOnDone = lpiSelectOnDone;
172       }
173
174    LeaveCriticalSection (pcsWindowActOnDone);
175 }
176
177
178 WORD Display_FreeActOnDone (HWND hWnd, LPIDENT *plpiSelectOnDone)
179 {
180    WORD wAct = 0;
181
182    if (pcsWindowActOnDone == NULL)
183       {
184       pcsWindowActOnDone = New (CRITICAL_SECTION);
185       InitializeCriticalSection (pcsWindowActOnDone);
186       }
187    EnterCriticalSection (pcsWindowActOnDone);
188
189    for (size_t ii = 0; ii < cWindowActOnDone; ++ii)
190       {
191       if (aWindowActOnDone[ ii ].hWnd == hWnd)
192          {
193          wAct = aWindowActOnDone[ ii ].actOnDone;
194          if (!(*plpiSelectOnDone))
195             *plpiSelectOnDone = aWindowActOnDone[ ii ].lpiSelectOnDone;
196
197          aWindowActOnDone[ ii ].actOnDone = 0;
198          aWindowActOnDone[ ii ].hWnd = 0;
199          aWindowActOnDone[ ii ].lpiSelectOnDone = 0;
200          break;
201          }
202       }
203
204    LeaveCriticalSection (pcsWindowActOnDone);
205    return wAct;
206 }
207
208
209 void UpdateDisplay (LPDISPLAYREQUEST pdr, BOOL fWait)
210 {
211    BOOL fRunBeforeReturning = FALSE;
212
213    if (!ASSERT( pdr && pdr->hChild ))
214       return;
215    if (pdr->lpiNotify && pdr->lpiNotify->fIsCell())
216       pdr->lpiNotify = NULL;
217
218    if (pcsDisplayQueue == NULL)
219       {
220       pcsDisplayQueue = New (CRITICAL_SECTION);
221       InitializeCriticalSection (pcsDisplayQueue);
222       memset (&drActiveSERVERS, 0x00, sizeof(DISPLAYREQUEST));
223       memset (&drActiveSERVICES, 0x00, sizeof(DISPLAYREQUEST));
224       memset (&drActiveAGGREGATES, 0x00, sizeof(DISPLAYREQUEST));
225       memset (&drActiveFILESETS, 0x00, sizeof(DISPLAYREQUEST));
226       memset (&drActiveSERVERWINDOW, 0x00, sizeof(DISPLAYREQUEST));
227       }
228
229    EnterCriticalSection (pcsDisplayQueue);
230
231    for (size_t idq = 0; idq < cDisplayQueue; ++idq)
232       {
233       if (!aDisplayQueue[idq].hChild)
234          break;
235       }
236    if (idq == cDisplayQueue)
237       {
238       (void)REALLOC (aDisplayQueue, cDisplayQueue, 1+idq, cREALLOC_DISPLAYQUEUE);
239       }
240    if (idq < cDisplayQueue)
241       {
242       memcpy (&aDisplayQueue[idq], pdr, sizeof(DISPLAYREQUEST));
243
244       // Filter the display queue--for instance, if there's a request
245       // to update all filesets, we don't need to file a request to
246       // update an individual fileset.  Likewise, if we're about to
247       // file a request to update all filesets, nix all existing update-
248       // this-fileset and update-filesets-on-this-server requests.
249       //
250       for (size_t idqKiller = 0; idqKiller < cDisplayQueue; ++idqKiller)
251          {
252          if (DisplayQueueFilter (idq, idqKiller))
253             {
254             aDisplayQueue[idq].hChild = 0;
255             break;
256             }
257          }
258
259       // Hmmmm...even if there is no request in the queue which lets us
260       // kill this request, there may be a request actively being serviced
261       // *right now* which does so.  Test for that case too.
262       //
263       if (aDisplayQueue[idq].hChild)
264          {
265          if (DisplayQueueFilter (idq, (size_t)-1))
266             aDisplayQueue[idq].hChild = 0;
267          }
268
269       // Did the new request make it through all those tests?  If so,
270       // see if we can remove any other entries because of this one.
271       // Then initiate a thread to actually do the work if there isn't one.
272       //
273       if (aDisplayQueue[idq].hChild)
274          {
275          for (size_t idqVictim = 0; idqVictim < cDisplayQueue; ++idqVictim)
276             {
277             if (DisplayQueueFilter (idqVictim, idq))
278                {
279                InterlockedDecrementByWindow (aDisplayQueue[idqVictim].hChild);
280                aDisplayQueue[idqVictim].hChild = 0;
281                --cDisplayQueueActive;
282                }
283             }
284
285          if (aDisplayQueue[idq].hChild)
286             {
287             InterlockedIncrementByWindow (aDisplayQueue[ idq ].hChild);
288
289             if (fWait)
290                {
291                aDisplayQueue[idq].hChild = NULL; // we'll handle this one.
292                fRunBeforeReturning = TRUE;       // (remember to do so)
293                }
294             else if ((++cDisplayQueueActive) >= 1)
295                {
296                if (cUpdateThreadsActive < cUPDATE_THREADS_MAX)
297                   {
298                   ++cUpdateThreadsActive;
299                   StartThread (DisplayQueue_ThreadProc, 0);
300                   }
301                }
302             }
303          }
304       }
305
306    LeaveCriticalSection (pcsDisplayQueue);
307
308    if (fRunBeforeReturning)
309       {
310       DisplayQueue_ThreadProc (pdr);
311       }
312 }
313
314
315 DWORD WINAPI DisplayQueue_ThreadProc (PVOID lp)
316 {
317    LPDISPLAYREQUEST pdr = (LPDISPLAYREQUEST)lp;
318    LPDISPLAYREQUEST pdrActive = NULL;
319
320    Main_StartWorking();
321
322    do {
323       DISPLAYREQUEST dr;
324
325       EnterCriticalSection (pcsDisplayQueue);
326       if (pdr)
327          {
328          memcpy (&dr, pdr, sizeof(DISPLAYREQUEST));
329          }
330       else
331          {
332          for (size_t idq = 0; idq < cDisplayQueue; ++idq)
333             {
334             if (aDisplayQueue[idq].hChild)
335                {
336                memcpy (&dr, &aDisplayQueue[idq], sizeof(DISPLAYREQUEST));
337                memset (&aDisplayQueue[idq], 0x00, sizeof(DISPLAYREQUEST));
338                --cDisplayQueueActive;
339                break;
340                }
341             }
342          if (idq == cDisplayQueue)
343             {
344             if (!pdr) // Are we losing a background thread?
345                --cUpdateThreadsActive;
346             LeaveCriticalSection (pcsDisplayQueue);
347             break;
348             }
349          }
350
351       switch (dr.dt)
352          {
353          case dtSERVERS:       pdrActive = &drActiveSERVERS;       break;
354          case dtSERVICES:      pdrActive = &drActiveSERVICES;      break;
355          case dtAGGREGATES:    pdrActive = &drActiveAGGREGATES;    break;
356          case dtFILESETS:      pdrActive = &drActiveFILESETS;      break;
357          case dtSERVERWINDOW:  pdrActive = &drActiveSERVERWINDOW;  break;
358          }
359       if (pdrActive)
360          memcpy (pdrActive, &dr, sizeof(DISPLAYREQUEST));
361
362       LeaveCriticalSection (pcsDisplayQueue);
363
364       AfsClass_Enter();
365
366       switch (dr.dt)
367          {
368          case dtCELL:
369             Display_Cell_Internal (&dr);
370             break;
371
372          case dtSERVERS:
373             Display_Servers_Internal (&dr);
374             break;
375
376          case dtSERVICES:
377             Display_Services_Internal (&dr);
378             break;
379
380          case dtAGGREGATES:
381             Display_Aggregates_Internal (&dr);
382             break;
383
384          case dtFILESETS:
385             Display_Filesets_Internal (&dr);
386             break;
387
388          case dtREPLICAS:
389             Display_Replicas_Internal (&dr);
390             break;
391
392          case dtSERVERWINDOW:
393             Display_ServerWindow_Internal (&dr);
394             break;
395          }
396
397       EnterCriticalSection (pcsDisplayQueue);
398       LONG dw = InterlockedDecrementByWindow (dr.hChild);
399       if (pdrActive)
400          memset (pdrActive, 0x00, sizeof(DISPLAYREQUEST));
401       LeaveCriticalSection (pcsDisplayQueue);
402
403       if (dw == 0)
404          {
405          WORD actOnDone = dr.actOnDone;
406          LPIDENT lpiSelectOnDone = dr.lpiToSelect;
407          if (dr.hList)
408             {
409             actOnDone |= Display_FreeActOnDone (dr.hList, &lpiSelectOnDone);
410             }
411
412          if ((actOnDone & ACT_ENDCHANGE) && dr.hList)
413             {
414             if (dr.fList)
415                FL_EndChange (dr.hList, (LPARAM)lpiSelectOnDone);
416             else // must be a combobox
417                CB_EndChange (dr.hList, (LPARAM)lpiSelectOnDone);
418             }
419
420          if ((actOnDone & ACT_UNCOVER) && dr.hList)
421             AfsAppLib_Uncover (dr.hList);
422
423          if (actOnDone & ACT_SELPREVIEW)
424             {
425             LPIDENT lpiOld = Server_GetServer (SERVERWINDOW_PREVIEWPANE);
426             LPIDENT lpiNew = (LPIDENT)FL_GetSelectedData (GetDlgItem (g.hMain, IDC_SERVERS));
427             if (lpiOld != lpiNew)
428                Server_SelectServer (SERVERWINDOW_PREVIEWPANE, lpiNew, TRUE);
429             }
430          }
431       else if (dr.hList)
432          {
433          Display_AddActOnDone (dr.hList, dr.actOnDone, dr.lpiToSelect);
434          }
435
436       AfsClass_Leave();
437
438       } while (!lp);  // if given one task to do, stop; otherwise, loop forever
439
440    Main_StopWorking();
441
442    return 0;
443 }
444
445
446 BOOL DisplayQueueFilter (size_t idqVictim, size_t idqKiller)
447 {
448    if (idqVictim == idqKiller)
449       return FALSE;
450
451    LPDISPLAYREQUEST pdrKiller = (idqKiller == (size_t)-1) ? NULL : &aDisplayQueue[ idqKiller ];
452    LPDISPLAYREQUEST pdrVictim = &aDisplayQueue[ idqVictim ];
453
454    // if there's currently an operation in progress for this window,
455    // we may have just been asked to filter out a new request based on
456    // what's being done now.  {idqKiller==-1} signifies this case.
457    //
458    if (pdrKiller == NULL) // was idqKiller==-1 etc?
459       {
460       switch (pdrVictim->dt)
461          {
462          case dtSERVERS:
463             pdrKiller = &drActiveSERVERS;
464             break;
465
466          case dtSERVICES:
467             pdrKiller = &drActiveSERVICES;
468             break;
469
470          case dtAGGREGATES:
471             pdrKiller = &drActiveAGGREGATES;
472             break;
473
474          case dtFILESETS:
475             pdrKiller = &drActiveFILESETS;
476             break;
477
478          case dtSERVERWINDOW:
479             pdrKiller = &drActiveSERVERWINDOW;
480             break;
481          }
482
483       if (!pdrKiller)
484          return FALSE;
485       }
486
487    if ( (pdrVictim->dt == pdrKiller->dt) &&
488         (pdrVictim->hChild == pdrKiller->hChild) )
489       {
490       // only some windows are subject to this filtering.
491       //
492       switch (pdrVictim->dt)
493          {
494          case dtCELL:
495          case dtREPLICAS:
496             return FALSE; // don't bother filtering these.
497
498          case dtSERVERWINDOW:
499             return TRUE;  // update svr window twice?  why?
500          }
501
502       // if the new request talks about displaying information for a different
503       // server, the user must have selected or deselected a server in the
504       // list. we'll keep the new request.
505       //
506       if (pdrKiller->lpiServer != pdrVictim->lpiServer)
507          return FALSE;
508
509       // if pdrKiller is told to update everything, then all other requests
510       // are unnecessary.
511       //
512       if (!pdrKiller->lpiNotify || pdrKiller->lpiNotify->fIsCell())
513          return TRUE;
514
515       // if pdrVictim is told to update everything, then we'll always bow to it.
516       //
517       if (!pdrVictim->lpiNotify || pdrVictim->lpiNotify->fIsCell())
518          return FALSE;
519
520       // kill any duplicate request to update a particular object.
521       //
522       if (pdrVictim->lpiNotify == pdrKiller->lpiNotify)
523          return TRUE;
524
525       // kill any request to update a service or aggregate or fileset,
526       // if updating the entire associated server.
527       //
528       if ( (pdrKiller->lpiNotify->fIsServer()) &&
529            (pdrVictim->lpiNotify->GetServer() == pdrKiller->lpiNotify) )
530          return TRUE;
531
532       // kill any request to update a fileset, if updating the entire
533       // associated aggregate.
534       //
535       if ( (pdrKiller->lpiNotify->fIsAggregate()) &&
536            (pdrVictim->lpiNotify->fIsFileset()) &&
537            (pdrVictim->lpiNotify->GetAggregate() == pdrKiller->lpiNotify) )
538          return TRUE;
539       }
540
541    // hmmm...guess we need this request after all.
542    //
543    return FALSE;
544 }
545
546
547 void UpdateDisplay_Cell (BOOL fWait)
548 {
549    DISPLAYREQUEST dr;
550    memset (&dr, 0x00, sizeof(dr));
551    dr.dt = dtCELL;
552    dr.hChild = GetDlgItem (g.hMain, IDC_CELL);
553    UpdateDisplay (&dr, fWait);
554 }
555
556 void UpdateDisplay_Servers (BOOL fWait, LPIDENT lpiNotify, ULONG status)
557 {
558    DISPLAYREQUEST dr;
559    memset (&dr, 0x00, sizeof(dr));
560    dr.dt = dtSERVERS;
561    dr.hChild = GetDlgItem (g.hMain, IDC_SERVERS);
562    dr.lpiNotify = lpiNotify;
563    dr.status = status;
564    dr.fList = TRUE;
565    UpdateDisplay (&dr, fWait);
566 }
567
568 void UpdateDisplay_Services (BOOL fWait, HWND hChild, LPIDENT lpiNotify, ULONG status)
569 {
570    DISPLAYREQUEST dr;
571    memset (&dr, 0x00, sizeof(dr));
572    dr.dt = dtSERVICES;
573    dr.hChild = hChild;
574    dr.lpiNotify = lpiNotify;
575    dr.status = status;
576    dr.fList = TRUE;
577    UpdateDisplay (&dr, fWait);
578 }
579
580 void UpdateDisplay_Aggregates (BOOL fWait, HWND hListOrCombo, LPIDENT lpiNotify, ULONG status, LPIDENT lpiServer, LPIDENT lpiToSelect, LPVIEWINFO lpvi)
581 {
582    DISPLAYREQUEST dr;
583    memset (&dr, 0x00, sizeof(dr));
584    dr.dt = dtAGGREGATES;
585    dr.hList = hListOrCombo;
586    dr.hChild = GetParent (hListOrCombo);
587    dr.lpiNotify = lpiNotify;
588    dr.status = status;
589    dr.lpiServer = (lpiServer == NULL) ? Server_GetServerForChild (dr.hChild) : lpiServer;
590    dr.lpiToSelect = lpiToSelect;
591    dr.lpvi = lpvi;
592    UpdateDisplay (&dr, fWait);
593 }
594
595 void UpdateDisplay_Filesets (BOOL fWait, HWND hListOrCombo, LPIDENT lpiNotify, ULONG status, LPIDENT lpiServer, LPIDENT lpiAggregate, LPIDENT lpiToSelect)
596 {
597    DISPLAYREQUEST dr;
598    memset (&dr, 0x00, sizeof(dr));
599    dr.dt = dtFILESETS;
600    dr.hList = hListOrCombo;
601    dr.hChild = GetParent (hListOrCombo);
602    dr.lpiNotify = lpiNotify;
603    dr.status = status;
604    dr.lpiServer = (lpiServer == NULL) ? Server_GetServerForChild (dr.hChild) : lpiServer;
605    dr.lpiAggregate = lpiAggregate;
606    dr.lpiToSelect = lpiToSelect;
607    UpdateDisplay (&dr, fWait);
608 }
609
610 void UpdateDisplay_Replicas (BOOL fWait, HWND hList, LPIDENT lpiRW, LPIDENT lpiRO)
611 {
612    DISPLAYREQUEST dr;
613    memset (&dr, 0x00, sizeof(dr));
614    dr.dt = dtREPLICAS;
615    dr.hChild = GetParent (hList);
616    dr.hList = hList;
617    dr.lpiNotify = lpiRW;
618    dr.lpiToSelect = lpiRO;
619    dr.fList = TRUE;
620    UpdateDisplay (&dr, fWait);
621 }
622
623
624 void UpdateDisplay_ServerWindow (BOOL fWait, LPIDENT lpiServer)
625 {
626    // First, if there is a dedicated server window out there, update it.
627    //
628    HWND hServer;
629    if ((hServer = PropCache_Search (pcSERVER, lpiServer)) != NULL)
630       {
631       DISPLAYREQUEST dr;
632       memset (&dr, 0x00, sizeof(dr));
633       dr.dt = dtSERVERWINDOW;
634       dr.hChild = hServer;
635       dr.lpiServer = lpiServer;
636
637       UpdateDisplay (&dr, fWait);
638       }
639
640    // Second, if the preview pane is visible and showing this server,
641    // update it too.
642    //
643    if (gr.fPreview)
644       {
645       LPIDENT lpiPreview = Server_GetServer (g.hMain);
646       if ((lpiPreview == NULL) || (lpiPreview == lpiServer))
647          {
648          DISPLAYREQUEST dr;
649          memset (&dr, 0x00, sizeof(dr));
650          dr.dt = dtSERVERWINDOW;
651          dr.hChild = g.hMain;
652          dr.lpiServer = lpiPreview;
653
654          UpdateDisplay (&dr, fWait);
655          }
656       }
657 }
658
659
660 void UpdateDisplay_SetIconView (BOOL fWait, HWND hDialog, LPICONVIEW piv, ICONVIEW ivNew)
661 {
662    *piv = ivNew;
663
664    if (piv == &gr.ivSvr)
665       {
666       UpdateDisplay_Servers (fWait, NULL, 0);
667       }
668    else
669       {
670       LPIDENT lpi = Server_GetServer (hDialog);
671       UpdateDisplay_ServerWindow (fWait, lpi);
672       }
673 }
674
675
676 ICONVIEW Display_GetServerIconView (void)
677 {
678    LONG lvs;
679
680    if (gr.fPreview && !gr.fVert)
681       lvs = gr.diHorz.viewSvr.lvsView;
682    else
683       lvs = gr.diVert.viewSvr.lvsView;
684
685
686    if (lvs != FLS_VIEW_LIST)
687       return ivONEICON;
688
689    return gr.ivSvr;
690 }
691
692
693 BOOL HandleColumnNotify (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp, LPVIEWINFO pvi)
694 {
695    if (msg == WM_NOTIFY)
696       {
697       HWND hList = GetDlgItem (hDlg, ((LPNMHDR)lp)->idFrom);
698       if (fIsFastList (hList))
699          {
700          switch (((LPNMHDR)lp)->code)
701             { 
702             case FLN_COLUMNRESIZE:
703                FL_StoreView (hList, pvi);
704                return TRUE;
705
706             case FLN_COLUMNCLICK:
707                LPFLN_COLUMNCLICK_PARAMS pp = (LPFLN_COLUMNCLICK_PARAMS)lp;
708
709                int iCol;
710                BOOL fRev;
711                FastList_GetSortStyle (hList, &iCol, &fRev);
712
713                if (iCol == pp->icol)
714                   FastList_SetSortStyle (hList, iCol, !fRev);
715                else
716                   FastList_SetSortStyle (hList, pp->icol, FALSE);
717
718                FL_StoreView (hList, pvi);
719                return TRUE;
720             }
721          }
722       }
723
724    return FALSE;
725 }
726