1 /*
2  *
3  *  Copyright (C) 1998-2017, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module:  dcmpstat
15  *
16  *  Author:  Joerg Riesmeier
17  *
18  *  Purpose: Classes for caching of the image database (Header/Source)
19  *
20  */
21 
22 
23 #ifndef DVCACHE_H
24 #define DVCACHE_H
25 
26 #include "dcmtk/config/osconfig.h"
27 
28 #include "dcmtk/ofstd/oflist.h"
29 #include "dcmtk/ofstd/ofstring.h"
30 #include "dcmtk/dcmqrdb/dcmqrdbi.h"  /* for DVIFhierarchyStatus */
31 
32 
33 /*--------------------*
34  *  type definitions  *
35  *--------------------*/
36 
37 /** describes the different types of instances stored in the database
38  */
39 enum DVPSInstanceType
40 {
41     /// image object
42     DVPSI_image,
43     /// presentation state object
44     DVPSI_presentationState,
45     /// structured reporting document
46     DVPSI_structuredReport,
47     /// stored print object
48     DVPSI_storedPrint,
49     /// hardcopy grayscale object
50     DVPSI_hardcopyGrayscale
51 };
52 
53 
54 /*---------------------*
55  *  class declaration  *
56  *---------------------*/
57 
58 /** A class to handle an instance cache (list of items).
59  *  This is the lowest level in the hierarchical cache structure.
60  *  Images are handled as well as presentation states. This class
61  *  is used by DVSeriesCache.
62  */
63 class DCMTK_DCMPSTAT_EXPORT DVInstanceCache
64 {
65 
66  public:
67 
68     /** Internal structure defining the list items.
69      */
70     struct DCMTK_DCMPSTAT_EXPORT ItemStruct
71     {
72         /** Constructor.
73          *  sets internal member variables.
74          *
75          ** @param  uid       unique identifier
76          *  @param  pos       file position in index file
77          *  @param  status    review status
78          *  @param  type      type of instance
79          *  @param  size      image size (in bytes)
80          *  @param  filename  filename of instance
81          */
ItemStructItemStruct82         ItemStruct(const OFString &uid,
83                    const int pos,
84                    const DVIFhierarchyStatus status,
85                    const DVPSInstanceType type,
86                    const int size,
87                    const OFString &filename)
88           : UID(uid),
89             Pos(pos),
90             Status(status),
91             Type(type),
92             ImageSize(size),
93             Filename(filename),
94             Checked(OFFalse),
95             Description(),
96             Label(),
97             List()
98         {}
99 
100         /// instance UID
101         OFString UID;
102         /// position in the index file
103         int Pos;
104         /// review status
105         DVIFhierarchyStatus Status;
106         /// type of instance
107         DVPSInstanceType Type;
108         /// image size (in bytes)
109         int ImageSize;
110         /// filename of instance
111         OFString Filename;
112         /// status flag to avoid double checking of referencing pstates
113         OFBool Checked;
114         /// instance description
115         OFString Description;
116         /// instance label
117         OFString Label;
118         /// list of referencing pstates
119         OFList<ItemStruct *> List;
120     };
121 
122     /** Constructor
123      */
DVInstanceCache()124     DVInstanceCache()
125       : List(),
126         Iterator(),
127         OldIterator()
128     {
129         Iterator = OldIterator = List.end();
130     }
131 
132     /** Destructor
133      */
~DVInstanceCache()134     virtual ~DVInstanceCache()
135     {
136         clear();
137     }
138 
139     /** reset all member variables to initial state.
140      *  delete all list items.
141      */
clear()142     inline void clear()
143     {
144         Iterator = List.begin();
145         OFListIterator(ItemStruct *) last = List.end();
146         while (Iterator != last)
147         {
148             delete (*Iterator);
149             Iterator = List.erase(Iterator);
150         }
151         List.clear();
152         Iterator = OldIterator = List.end();
153     }
154 
155     /** checks whether instance cache is empty
156      *
157      ** @result OFTrue if cache is empty, OFFalse otherwise
158      */
empty()159     inline OFBool empty() const
160     {
161         return List.empty();
162     }
163 
164     /** gets number of cache entries
165      *
166      ** @return number of cache entries
167      */
getCount()168     inline Uint32 getCount() const
169     {
170         return OFstatic_cast(Uint32, List.size());
171     }
172 
173     /** sets internal cursor to specified position in cache list
174      *
175      ** @param  idx  index position in cache list (starting with 0)
176      *
177      ** @return OFTrue if successful, OFFalse if 'idx' is invalid
178      */
gotoItem(Uint32 idx)179     inline OFBool gotoItem(Uint32 idx)
180     {
181         OFBool result = OFFalse;
182         Iterator = List.begin();
183         OFListIterator(ItemStruct *) last = List.end();
184         while (Iterator != last)
185         {
186             if (idx == 0)
187             {
188                 result = OFTrue;
189                 break;
190             }
191             idx--;
192             ++Iterator;
193         }
194         return result;
195     }
196 
197     /** sets internal cursor to first position in cache list
198      *
199      ** @return OFTrue if successful, OFFalse if list is empty
200      */
gotoFirst()201     inline OFBool gotoFirst()
202     {
203         OldIterator = Iterator;
204         Iterator = List.begin();
205         return (Iterator != List.end());
206     }
207 
208     /** sets internal cursor to next position in cache list
209      *
210      ** @return OFTrue if successful, OFFalse if new position is invalid
211      */
gotoNext()212     inline OFBool gotoNext()
213     {
214         OFListIterator(ItemStruct *) last = List.end();
215         if (Iterator != last)
216             Iterator++;
217         return (Iterator != last);
218     }
219 
220     /** sets internal cursor to last visited position in cache list
221      *
222      ** @return OFTrue if successful,
223      *          OFFalse if last visited position was invalid or the last one in the list
224      */
reset()225     inline OFBool reset()
226     {
227         OFBool result = OFFalse;
228         OFListIterator(ItemStruct *) last = List.end();
229         if (OldIterator != last)
230         {
231             Iterator = OldIterator;
232             OldIterator = last;
233             result = OFTrue;
234         }
235         return result;
236     }
237 
238     /** checks whether an item with the specified UID exists in the cache list
239      *
240      ** @param  uid  UID which should be checked
241      *
242      ** @return OFTrue if such an item exists, OFFalse otherwise
243      */
isElem(const OFString & uid)244     inline OFBool isElem(const OFString &uid)
245     {
246         OFBool result = OFFalse;
247         Iterator = List.begin();
248         OFListIterator(ItemStruct *) last = List.end();
249         while (Iterator != last)
250         {
251             const ItemStruct *item = (*Iterator);
252             if (item != NULL)
253             {
254                 if (item->UID == uid)
255                 {
256                     result = OFTrue;
257                     break;
258                 }
259             }
260             ++Iterator;
261         }
262         return result;
263     }
264 
265     /** gets the file position of the current (selected) instance
266      *
267      ** @return file position if successful, 0 otherwise
268      */
getPos()269     inline int getPos() const
270     {
271         const ItemStruct *item = getItem();
272         return (item != NULL) ? item->Pos : 0;
273     }
274 
275     /** gets review status of the current (selected) instance
276      *
277      ** @return hierarchical status code if successful, 'isNew' otherwise
278      */
getStatus()279     inline DVIFhierarchyStatus getStatus() const
280     {
281         const ItemStruct *item = getItem();
282         return (item != NULL) ? item->Status : DVIF_objectIsNew;
283     }
284 
285     /** gets type of the instance
286      *
287      ** @return type of instance
288      */
getType()289     inline DVPSInstanceType getType() const
290     {
291         const ItemStruct *item = getItem();
292         return (item != NULL) ? item->Type : DVPSI_image;
293     }
294 
295     /** gets image size of current (selected) instance
296      *
297      ** @return image size in bytes if successful, 0 otherwise
298      */
getImageSize()299     inline int getImageSize() const
300     {
301         const ItemStruct *item = getItem();
302         return (item != NULL) ? item->ImageSize : 0;
303     }
304 
305     /** gets filename of current (selected) instance
306      *
307      ** @return filename if successful, NULL otherwise
308      */
getFilename()309     inline const char *getFilename() const
310     {
311         const ItemStruct *item = getItem();
312         return (item != NULL) ? item->Filename.c_str() : (const char *)NULL;
313     }
314 
315     /** gets reference to current (selected) instance
316      *
317      ** @return pointer to ItemStruct if successful, NULL otherwise
318      */
getItem()319     inline ItemStruct *getItem() const
320     {
321         OFListConstIterator(ItemStruct *) it = Iterator;
322         return (it != List.end()) ? (*Iterator) : (ItemStruct *)NULL;
323     }
324 
325     /** adds a new item to the cache list.
326      *  sets internal cursor to new position.
327      *
328      ** @param  uid       unique identifier
329      *  @param  pos       file position in index file
330      *  @param  status    review status
331      *  @param  type      type of instance
332      *  @param  size      image size (in bytes)
333      *  @param  filename  filename of instance
334      */
addItem(const OFString & uid,const int pos,const DVIFhierarchyStatus status,const DVPSInstanceType type,const int size,const OFString & filename)335     inline void addItem(const OFString &uid,
336                         const int pos,
337                         const DVIFhierarchyStatus status,
338                         const DVPSInstanceType type,
339                         const int size,
340                         const OFString &filename)
341     {
342         ItemStruct *item = new ItemStruct(uid, pos, status, type, size, filename);
343         List.push_back(item);
344         Iterator = --List.end();                // set to new position
345     }
346 
347     /** updates hierarchical/review status for all list items.
348      *
349      ** @return resulting review status (summary of all items)
350      */
updateStatus()351     inline DVIFhierarchyStatus updateStatus()
352     {
353         OFListIterator(ItemStruct *) first = List.begin();
354         OFListIterator(ItemStruct *) last = List.end();
355         OFListIterator(ItemStruct *) iter = first;
356         DVIFhierarchyStatus status = DVIF_objectIsNew;
357         while (iter != last)
358         {
359             ItemStruct *item = (*iter);
360             if (item != NULL)
361             {
362                 switch (item->Status)
363                 {
364                     case DVIF_objectIsNew:
365                         if (status == DVIF_objectIsNotNew)
366                             status = DVIF_objectContainsNewSubobjects;
367                         break;
368                     case DVIF_objectIsNotNew:
369                     case DVIF_objectContainsNewSubobjects:
370                         if (iter == first)
371                             status = DVIF_objectIsNotNew;
372                         else if (status == DVIF_objectIsNew)
373                             status = DVIF_objectContainsNewSubobjects;
374                         break;
375                 }
376             }
377             ++iter;
378         }
379         return status;
380     }
381 
382 
383  protected:
384 
385     /// list of instances
386     OFList<ItemStruct *> List;
387     /// internal cursor to current (selected) list item
388     OFListIterator(ItemStruct *) Iterator;
389     /// last visited position in item list
390     OFListIterator(ItemStruct *) OldIterator;
391 };
392 
393 
394 /* ------------------------------ */
395 
396 
397 /** A class to handle a series cache (list of items).
398  *  This is the middle level in the hierarchical cache structure.
399  *  This class is used by DVStudyCache. The internal structure
400  *  is a list of DVInstanceCache.
401  */
402 class DCMTK_DCMPSTAT_EXPORT DVSeriesCache
403 {
404 
405  public:
406 
407     /** Internal structure defining the list items.
408      */
409     struct DCMTK_DCMPSTAT_EXPORT ItemStruct
410     {
411         /** Constructor.
412          *  sets internal member variables.
413          *
414          ** @param  uid     unique identifier
415          *  @param  status  review status (optional)
416          *  @param  type    type of series
417          */
418         ItemStruct(const OFString &uid,
419                    const DVIFhierarchyStatus status = DVIF_objectIsNew,
420                    const DVPSInstanceType type = DVPSI_image)
UIDItemStruct421           : UID(uid),
422             Status(status),
423             Type(type),
424             List()
425         {}
426 
427         /// instance UID
428         OFString UID;
429         /// review status for the series
430         DVIFhierarchyStatus Status;
431         /// type of all instances within this series
432         DVPSInstanceType Type;
433         /// list of instances within this series
434         DVInstanceCache List;
435     };
436 
437     /** Constructor.
438      */
DVSeriesCache()439     DVSeriesCache()
440       : List(),
441         Iterator(),
442         OldIterator()
443     {
444         Iterator = OldIterator = List.end();
445     }
446 
447     /** Destructor
448      */
~DVSeriesCache()449     virtual ~DVSeriesCache()
450     {
451         clear();
452     }
453 
454     /** reset all member variables to initial state
455      *  delete all list items.
456      */
clear()457     inline void clear()
458     {
459         Iterator = List.begin();
460         OFListIterator(ItemStruct *) last = List.end();
461         while (Iterator != last)
462         {
463             delete (*Iterator);
464             Iterator = List.erase(Iterator);
465         }
466         List.clear();
467         Iterator = OldIterator = List.end();
468     }
469 
470     /** checks whether instance cache is empty
471      *
472      ** @return OFTrue if cache is empty, OFFalse otherwise
473      */
empty()474     inline OFBool empty() const
475     {
476         return List.empty();
477     }
478 
479     /** gets number of cache entries
480      *
481      ** @return number of cache entries
482      */
getCount()483     inline Uint32 getCount() const
484     {
485         return OFstatic_cast(Uint32, List.size());
486     }
487 
488     /** sets internal cursor to specified position in cache list
489      *
490      ** @param  idx  index position in cache list (starting with 0)
491      *
492      ** @return OFTrue if successful, OFFalse if 'idx' is invalid
493      */
gotoItem(Uint32 idx)494     inline OFBool gotoItem(Uint32 idx)
495     {
496         OFBool result = OFFalse;
497         Iterator = List.begin();
498         OFListIterator(ItemStruct *) last = List.end();
499         while (Iterator != last)
500         {
501             if (idx == 0)
502             {
503                 result = OFTrue;
504                 break;
505             }
506             idx--;
507             ++Iterator;
508         }
509         return result;
510     }
511 
512     /** sets internal cursor to first position in cache list
513      *
514      ** @return OFTrue if successful, OFFalse if list is empty
515      */
gotoFirst()516     inline OFBool gotoFirst()
517     {
518         OldIterator = Iterator;
519         Iterator = List.begin();
520         return (Iterator != List.end());
521     }
522 
523     /** sets internal cursor to next position in cache list
524      *
525      ** @return OFTrue if successful, OFFalse if new position is invalid
526      */
gotoNext()527     inline OFBool gotoNext()
528     {
529         OFListIterator(ItemStruct *) last = List.end();
530         if (Iterator != last)
531             Iterator++;
532         return (Iterator != last);
533     }
534 
535     /** sets internal cursor to last visited position in cache list
536      *
537      ** @return OFTrue if successful,
538      *          OFFalse if last visited position was invalid or the last one in the list
539      */
reset()540     inline OFBool reset()
541     {
542         OFBool result = OFFalse;
543         OFListIterator(ItemStruct *) last = List.end();
544         if (OldIterator != last)
545         {
546             Iterator = OldIterator;
547             OldIterator = last;
548             result = OFTrue;
549         }
550         return result;
551     }
552 
553     /** checks whether an item with the specified UID exists in the cache list
554      *
555      ** @param  uid  UID which should be checked
556      *
557      ** @return OFTrue if such an item exists, OFFalse otherwise
558      */
isElem(const OFString & uid)559     inline OFBool isElem(const OFString &uid)
560     {
561         OFBool result = OFFalse;
562         Iterator = List.begin();
563         OFListIterator(ItemStruct *) last = List.end();
564         while (Iterator != last)
565         {
566             const ItemStruct *item = (*Iterator);
567             if (item != NULL)
568             {
569                 if (item->UID == uid)
570                 {
571                     result = OFTrue;
572                     break;
573                 }
574             }
575             ++Iterator;
576         }
577         return result;
578     }
579 
580     /** gets review status of the current (selected) series
581      *
582      ** @return hierarchical status code if successful, 'isNew' otherwise
583      */
getStatus()584     inline DVIFhierarchyStatus getStatus() const
585     {
586         const ItemStruct *item = getItem();
587         return (item != NULL) ? item->Status : DVIF_objectIsNew;
588     }
589 
590     /** gets type of all instances in the series
591      *
592      ** @return type of all instances
593      */
getType()594     inline DVPSInstanceType getType() const
595     {
596         const ItemStruct *item = getItem();
597         return (item != NULL) ? item->Type : DVPSI_image;
598     }
599 
600     /** gets reference to current (selected) series
601      *
602      ** @return pointer to ItemStruct if successful, NULL otherwise
603      */
getItem()604     inline ItemStruct *getItem() const
605     {
606         OFListConstIterator(ItemStruct *) it = Iterator;
607         return (it != List.end()) ? (*Iterator) : (ItemStruct *)NULL;
608     }
609 
610     /** adds a new item to the cache list.
611      *  sets internal cursor to new position.
612      *
613      ** @param  uid       unique identifier
614      *  @param  status    review status (optional)
615      */
616     inline void addItem(const OFString &uid,
617                         const DVIFhierarchyStatus status = DVIF_objectIsNew)
618     {
619         ItemStruct *item = new ItemStruct(uid, status);
620         List.push_back(item);
621         Iterator = --List.end();                // set to new position
622     }
623 
624     /** updates hierarchical/review status for all list items.
625      *
626      ** @return resulting review status (summary of all items)
627      */
updateStatus()628     inline DVIFhierarchyStatus updateStatus()
629     {
630         OFListIterator(ItemStruct *) first = List.begin();
631         OFListIterator(ItemStruct *) last = List.end();
632         OFListIterator(ItemStruct *) iter = first;
633         DVIFhierarchyStatus status = DVIF_objectIsNew;
634         while (iter != last)
635         {
636             ItemStruct *item = (*iter);
637             if (item != NULL)
638             {
639                 item->Status = item->List.updateStatus();
640                 switch (item->Status)
641                 {
642                     case DVIF_objectIsNew:
643                         if (status == DVIF_objectIsNotNew)
644                             status = DVIF_objectContainsNewSubobjects;
645                         break;
646                     case DVIF_objectIsNotNew:
647                         if (iter == first)
648                             status = DVIF_objectIsNotNew;
649                         else if (status == DVIF_objectIsNew)
650                             status = DVIF_objectContainsNewSubobjects;
651                         break;
652                     case DVIF_objectContainsNewSubobjects:
653                         status  = DVIF_objectContainsNewSubobjects;
654                         break;
655                 }
656             }
657             ++iter;
658         }
659         return status;
660     }
661 
662 
663  protected:
664 
665     /// list of series
666     OFList<ItemStruct *> List;
667     /// internal cursor to current (selected) list item
668     OFListIterator(ItemStruct *) Iterator;
669     /// last visited position in item list
670     OFListIterator(ItemStruct *) OldIterator;
671 };
672 
673 
674 /* ------------------------------ */
675 
676 
677 /** A class to handle a study cache (list of items).
678  *  This is the highest level in the hierarchical cache structure.
679  *  This class is used by DVInterface. The internal structure
680  *  is a list of DVSeriesCache.
681  */
682 class DCMTK_DCMPSTAT_EXPORT DVStudyCache
683 {
684 
685  public:
686 
687     /** Internal structure defining the list items.
688      */
689     struct DCMTK_DCMPSTAT_EXPORT ItemStruct
690     {
691         /** Constructor.
692          *  sets internal member variables.
693          *
694          ** @param  uid       unique identifier
695          *  @param  status    review status (optional)
696          */
697         ItemStruct(const OFString &uid,
698                    const DVIFhierarchyStatus status = DVIF_objectIsNew)
UIDItemStruct699           : UID(uid),
700             Status(status),
701             List()
702         {}
703 
704         /// instance UID
705         OFString UID;
706         /// review status for the series
707         DVIFhierarchyStatus Status;
708         /// list of series within this study
709         DVSeriesCache List;
710     };
711 
712     /** Constructor.
713      */
DVStudyCache()714     DVStudyCache()
715       : List(),
716         Iterator()
717     {
718         Iterator = List.end();
719     }
720 
721     /** Destructor
722      */
~DVStudyCache()723     virtual ~DVStudyCache()
724     {
725         clear();
726     }
727 
728     /** reset all member variables to initial state
729      *  delete all list items.
730      */
clear()731     inline void clear()
732     {
733         Iterator = List.begin();
734         OFListIterator(ItemStruct *) last = List.end();
735         while (Iterator != last)
736         {
737             delete (*Iterator);
738             Iterator = List.erase(Iterator);
739         }
740         List.clear();
741         Iterator = List.end();
742     }
743 
744     /** checks whether study cache is empty
745      *
746      ** @return OFTrue if cache is empty, OFFalse otherwise
747      */
empty()748     inline OFBool empty() const
749     {
750         return List.empty();
751     }
752 
753     /** gets number of cache entries
754      *
755      ** @return number of cache entries
756      */
getCount()757     inline Uint32 getCount() const
758     {
759         return OFstatic_cast(Uint32, List.size());
760     }
761 
762     /** sets internal cursor to specified position in cache list
763      *
764      ** @param  idx  index position in cache list (starting with 0)
765      *
766      ** @return OFTrue if successful, OFFalse if 'idx' is invalid
767      */
gotoItem(Uint32 idx)768     inline OFBool gotoItem(Uint32 idx)
769     {
770         OFBool result = OFFalse;
771         Iterator = List.begin();
772         OFListIterator(ItemStruct *) last = List.end();
773         while (Iterator != last)
774         {
775             if (idx == 0)
776             {
777                 result = OFTrue;
778                 break;
779             }
780             idx--;
781             ++Iterator;
782         }
783         return result;
784     }
785 
786     /** sets internal cursor to first position in cache list
787      *
788      ** @return OFTrue if successful, OFFalse if list is empty
789      */
gotoFirst()790     inline OFBool gotoFirst()
791     {
792         //OldIterator = Iterator;
793         Iterator = List.begin();
794         return (Iterator != List.end());
795     }
796 
797     /** sets internal cursor to next position in cache list
798      *
799      ** @return OFTrue if successful, OFFalse if new position is invalid
800      */
gotoNext()801     inline OFBool gotoNext()
802     {
803         OFListIterator(ItemStruct *) last = List.end();
804         if (Iterator != last)
805             Iterator++;
806         return (Iterator != last);
807     }
808 
809     /** checks whether an item with the specified UID exists in the cache list
810      *
811      ** @param  uid  UID which should be checked
812      *
813      ** @return OFTrue if such an item exists, OFFalse otherwise
814      */
isElem(const OFString & uid)815     inline OFBool isElem(const OFString &uid)
816     {
817         OFBool result = OFFalse;
818         Iterator = List.begin();
819         OFListIterator(ItemStruct *) last = List.end();
820         while (Iterator != last)
821         {
822             const ItemStruct *item = (*Iterator);
823             if (item != NULL)
824             {
825                 if (item->UID == uid)
826                 {
827                     result= OFTrue;
828                     break;
829                 }
830             }
831             ++Iterator;
832         }
833         return result;
834     }
835 
836     /** gets review status of the current (selected) sstudy
837      *
838      ** @return hierarchical status code if successful, 'isNew' otherwise
839      */
getStatus()840     inline DVIFhierarchyStatus getStatus() const
841     {
842         const ItemStruct *item = getItem();
843         return (item != NULL) ? item->Status : DVIF_objectIsNew;
844     }
845 
846     /** gets reference to current (selected) study
847      *
848      ** @return pointer to ItemStruct if successful, NULL otherwise
849      */
getItem()850     inline ItemStruct *getItem() const
851     {
852         OFListConstIterator(ItemStruct *) it = Iterator;
853         return (it != List.end()) ? (*Iterator) : (ItemStruct *)NULL;
854     }
855 
856     /** adds a new item to the cache list.
857      *  sets internal cursor to new position.
858      *
859      ** @param  uid       unique identifier
860      *  @param  status    review status (optional)
861      */
862     inline void addItem(const OFString &uid,
863                         const DVIFhierarchyStatus status = DVIF_objectIsNew)
864     {
865         ItemStruct *item = new ItemStruct(uid, status);
866         List.push_back(item);
867         Iterator = --List.end();                // set to new position
868     }
869 
870     /** updates hierarchical/review status for all list items.
871      *
872      ** @return resulting review status (summary of all items)
873      */
updateStatus()874     inline void updateStatus()
875     {
876         OFListIterator(ItemStruct *) iter = List.begin();
877         OFListIterator(ItemStruct *) last = List.end();
878         while (iter != last)
879         {
880             ItemStruct *item = (*iter);
881             if (item != NULL)
882                 item->Status = item->List.updateStatus();
883             ++iter;
884         }
885     }
886 
887 
888  protected:
889 
890     /// list of studies
891     OFList<ItemStruct *> List;
892     /// internal cursor to current (selected) list item
893     OFListIterator(ItemStruct *) Iterator;
894 };
895 
896 
897 #endif
898