source: trunk/User Procedures/Nika/NI1_HDF5Browser.ipf @ 750

Last change on this file since 750 was 750, checked in by ilavsky, 4 years ago

Changed names, combined together and converted to rtGlobals=3 and UTF-8 encoding

File size: 108.4 KB
Line 
1#pragma TextEncoding = "UTF-8"
2#pragma rtGlobals=3             // Use modern global access method.
3//#pragma rtGlobals=1           // Use modern global access method.
4#pragma version = 1.01
5
6#include <WaveSelectorWidget>
7
8#pragma moduleName=NikaHDF5Browser
9
10//1.01 modified to compile when hdf5 xop is not available.
11// 1.0 initial release. Not working yet, but need to go ahead with release.
12//This is modified version of HDF5 Browser.ipf version 1.03 modified for use with Nika package by jan Ilavsky, January 2011
13// ilavsky@aps.anl.gov
14//this will provide limited functionality to provide user with ability to select for Nika which data to load and how. 
15
16//Menu "testing"
17//      "New HDF5 Browser", /Q, NI2_CreateNewHDF5Browser("Nika")
18//End
19
20//**********************
21//       this is the main function called by Nika or Irena to load general hdf file...
22
23Function/S NI2_LoadGeneralHDFFile(CalledFrom, fileName, PathName)
24        string CalledFrom,  fileName, PathName
25       
26        //CalledFrom ... "Nika" or "Irena"
27        Wave/Z/T GroupNames=$("root:Packages:"+CalledFrom+"HDF5Loader:GroupNames")
28        //this should contain list of data sets names to load form all selected hdf5 files...
29        if(!WaveExists(GroupNames))
30                NI2_CreateNewHDF5Browser(CalledFrom)
31                DoAlert 0, "Set Hdf options first"
32        endif
33        variable locFileID
34
35#if Exists("HDF5OpenFile")     
36        HDF5OpenFile/R/P=$(PathName) locFileID as fileName
37        //Need to create temp folder to handle the whole group of stuff here...
38        string oldDf=GetDataFolder (1)
39        setDataFolder root:Packages:
40        NewDataFolder/O/S root:Packages:TempHDFLoad
41        KillWaves/A/Z           //clean the folder up
42
43        variable i              //what if user wants more groups???
44        For(i=0;i<numpnts(GroupNames);i+=1)
45                HDF5LoadGroup /O /R /T /IMAG=1 :, locFileID, GroupNames[i]
46        endfor
47        string/g GroupPathsLoaded, dataFolderPathsLoaded, ObjectPathsLoaded
48       
49        GroupPathsLoaded                =       S_groupPaths
50        dataFolderPathsLoaded   =       S_dataFolderPaths
51        ObjectPathsLoaded               =        S_objectPaths
52        HDF5CloseFile locFileID
53        if(stringmatch(CalledFrom,"Nika"))
54                NI2_ParseNikaData()
55        else
56                abort "not finished Irena or other loading in NI1_HDF5Browser#NI2_LoadGeneralHDFFile"
57        endif
58       
59        return "LoadedWave"
60       
61#else
62        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
63#endif
64end
65
66 Function NI2_ParseNikaData()           //here we parse loaded groups for Nika. Notre - Nika expects ONE 2D image from the hdf file...
67        //need to create ONE "LoadedWave" in the Nika folder from what we have here...
68        //here is what is available:
69        setDataFolder root:Packages:TempHDFLoad
70        SVAR GroupPathsLoaded = root:Packages:TempHDFLoad:GroupPathsLoaded
71        SVAR dataFolderPathsLoaded=root:Packages:TempHDFLoad:dataFolderPathsLoaded
72        SVAR ObjectPathsLoaded=root:Packages:TempHDFLoad:ObjectPathsLoaded
73       
74        //for now assume only one group loaded for Nika.... If we will need more, we'll have to kwnowhat users want
75        //      string PathToData=stringfromList(0,GroupPathsLoaded,";")
76        //      PathToData = stringfromList(ItemsinList(PathToData, "/")-1, PathToData, "/")
77//      if(DataFolderExists(PathToData) )               //does the folder exist as expected?
78//              setDataFolder $(PathToData)
79//
80//
81//      else
82//              Abort "Parsing failed, send example of data to ilavsky@aps.anl.gov"
83//      endif
84        variable i
85        For(i=0;i<itemsInList(ObjectPathsLoaded,";");i+=1)
86                wave tempWv=$(stringFromList(i,ObjectPathsLoaded,";"))
87                if(dimsize(tempWv,1)>0 && dimsize(tempWv,2)==0) //thsi is 2 D wave
88                        //do something with this wave
89                       
90                        return 1
91                endif   
92        endfor
93        Abort "Parsing Nika data filed, not 2D wave found"
94       
95       
96end
97
98
99//*********************
100
101
102
103
104Structure NI2HDF5BrowserData
105        SVAR browserName
106
107        Wave/T groupsList
108        Wave/T groupAttributesList
109        Wave/T datasetsList
110        Wave/T datasetAttributesList
111       
112        Wave/T groupFullPaths
113        SVAR groupPath
114
115        SVAR hyperSelectionWavePath
116       
117        NVAR fileID
118        SVAR fileName
119        SVAR path
120        SVAR fullPath
121        NVAR readOnly
122       
123        SVAR datasetInfo                                                // Panel readout info for currently-selected dataset
124EndStructure
125
126static Function CreateHDF5BrowserGlobals(newBrowserName)
127        String newBrowserName
128       
129        // Create globals that apply to this browser window.
130       
131        String savedDataFolder = NI2_SetBrowserDataFolder(newBrowserName)
132       
133        String/G browserName = newBrowserName
134       
135        Make/O/T/N=0 groupsList
136        Make/O/T/N=0 groupAttributesList
137        Make/O/T/N=0 datasetsList
138        Make/O/T/N=0 datasetAttributesList
139
140        Make/O/T/N=0 groupFullPaths                                     // For each group, contains the corresponding full path to that group.
141        String/G groupPath = "/"                                                // Path to currently selected group
142
143        String/G hyperSelectionWavePath                 // Path to hyperselection wave, if any.
144       
145        Variable/G fileID = 0                                                   // 0 means no file is open for this browser.
146
147        String/G fileName = ""
148        String/G path = ""
149        String/G fullPath = ""
150        Variable/G readOnly = 1
151       
152        String/G datasetInfo
153       
154        SetDataFolder savedDataFolder
155End
156
157Function/S NI2_SetBrowserDataFolder(browserName)                // Pass "" for browserName to set to master browser folder.
158        String browserName
159       
160        String savedDataFolder = GetDataFolder(1)
161       
162        NewDataFolder/O/S root:Packages
163        NewDataFolder/O/S HDF5Browser
164       
165        if (strlen(browserName) > 0)
166                NewDataFolder/O/S $browserName
167        endif
168       
169        return savedDataFolder
170End
171
172static Function SetNI2HDF5BrowserData(browserName, bd)
173        String browserName
174        STRUCT NI2HDF5BrowserData &bd
175       
176        String savedDataFolder = NI2_SetBrowserDataFolder(browserName)
177       
178        // These statements set the structure fields to reference the corresponding waves and variables in the data folder.
179       
180        SVAR bd.browserName
181       
182        Wave/T bd.groupsList
183        Wave/T bd.groupAttributesList
184        Wave/T bd.datasetsList
185        Wave/T bd.datasetAttributesList
186       
187        Wave/T bd.groupFullPaths
188        SVAR bd.groupPath
189
190        SVAR bd.hyperSelectionWavePath
191       
192        NVAR bd.fileID
193        SVAR bd.fileName
194        SVAR bd.path
195        SVAR bd.fullPath
196        NVAR bd.readOnly
197       
198        SVAR bd.datasetInfo
199       
200        SetDataFolder savedDataFolder
201End
202
203static Function CountSlashes(item)
204        String item
205       
206        Variable slashes = 0
207        Variable pos = 0
208       
209        do
210                pos = strsearch(item, "/", pos)
211                if (pos < 0)
212                        break
213                endif
214                slashes += 1
215                pos += 1
216        while (1)
217
218        return slashes
219End
220
221Function/S NI2_GetGroupHierarchy(fileID, startPath, level, mode)
222        Variable fileID
223        String startPath
224        Variable level
225        Variable mode                   // 0 to just get group names; 1 to get full path to each group.
226
227        String result = ""
228
229        String indent = ""                      // Used only for mode 0.
230        Variable i, j
231
232#if Exists("HDF5ListGroup")     
233       
234        // This gives full hierarchy with full paths
235        HDF5ListGroup /F /R /TYPE=1 fileID, startPath
236        result = S_HDF5ListGroup
237       
238        if (mode == 0)                          // Want just names, not full paths
239                result = ""
240                Variable numItems = ItemsInList(S_HDF5ListGroup)
241                for(i=0; i<numItems; i+=1)
242                        String item = StringFromList(i, S_HDF5ListGroup)
243                        level = CountSlashes(item)
244                        indent = ""
245                        for(j=0; j<level; j+=1)
246                                indent += "    "
247                        endfor
248                        item = ParseFilePath(0, item, "/", 1, 0)                        // Get last element
249                        item = indent + item                                            // Prepend indentation
250                        result += item + ";"           
251                endfor 
252        endif
253       
254        return result
255#else
256        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
257#endif
258End
259
260static Function/S GetFileHierarchy(bd, mode)
261        STRUCT NI2HDF5BrowserData &bd
262        Variable mode                                           // 0 to just get group names; 1 to get full path to each group.
263       
264        if (bd.fileID == 0)                                                     // No file is open?
265                return ""
266        endif
267       
268        String hierarchy, rootName
269        if (mode == 1)
270                rootName = "/"                                  // For purposes of storing full path, use true root name
271        else
272                rootName = "root"                                       // For display purposes, use "root" as root name
273        endif
274        hierarchy = rootName + ";" + NI2_GetGroupHierarchy(bd.fileID, "/", 1, mode)
275       
276        return hierarchy
277End
278
279static Function ResetListSelections(bd, resetGroupsList, resetGroupAttributesList, resetDatasetsList, resetDatasetAttributesList)
280        STRUCT NI2HDF5BrowserData &bd
281        Variable resetGroupsList, resetGroupAttributesList, resetDatasetsList, resetDatasetAttributesList
282
283        if (resetGroupsList)
284                ListBox GroupsList, win=$bd.browserName, selRow=0
285        endif
286        if (resetGroupAttributesList)
287                ListBox GroupAttributesList, win=$bd.browserName, selRow=0
288        endif
289        if (resetDatasetsList)
290                ListBox DatasetsList, win=$bd.browserName, selRow=0
291        endif
292        if (resetDatasetAttributesList)
293                ListBox DatasetAttributesList, win=$bd.browserName, selRow=0
294        endif
295End
296
297Function/S NI2_HDF5GetObjectFullPath(groupPath, objectName)
298        String groupPath                                        // Path to parent group
299        String objectName                               // Name of dataset or group in parent group
300       
301        String fullPath
302       
303        if (CmpStr(groupPath, "/") == 0)
304                fullPath = "/" + objectName
305        else
306                fullPath = groupPath + "/" + objectName
307        endif
308       
309        return fullPath
310End
311
312Function/S NI2_GetTextPreviewString(tw)
313        Wave/T tw                                               // tw has already been flattened to 1D.
314       
315        String preview, temp
316        Variable len
317       
318        Variable row, numRows, totalLength
319       
320        totalLength = 0
321
322        numRows = numpnts(tw)
323        if (numRows == 0)
324                return ""
325        endif
326       
327        preview = ""
328        row = 0
329        do
330                temp = tw[row]
331                temp = ReplaceString("\r", temp, "<CR>", 1)     // Convert CR to "\r"
332                temp = ReplaceString("\n", temp, "<LF>", 1)     // Convert LF to "\n"
333
334                len = strlen(temp)
335                if (len > 128)
336                        if (numRows == 1)
337                                sprintf preview, "\"%s\" . . . (%d characters total)", temp[0,127], len
338                        else
339                                preview += " . . . <Strings too long to display here>"
340                        endif
341                       
342                        row = numRows                                                   // To prevent extra . . .
343                        break
344                endif
345
346                preview += "\"" + temp + "\""
347                row += 1
348                totalLength += len
349
350                if (row >= numRows)
351                        break
352                endif
353                if (totalLength >= 256)                         // Enough is enough
354                        break
355                endif
356
357                preview += ", "
358        while(1)
359
360        if (row < numRows)
361                preview += " . . ."
362        endif
363
364        return preview
365End
366
367Function/S NI2_GetNumericPreviewString(w)
368        Wave w                                                  // w has already been flattened to 1D.
369
370        String preview, temp
371       
372        Variable row, numRows, totalLength
373       
374        totalLength = 0
375
376        numRows = numpnts(w)
377        if (numRows == 0)
378                return ""
379        endif
380       
381        preview = ""
382        row = 0
383        do
384                sprintf temp, "%g", w[row]
385                preview += temp
386                row += 1
387                totalLength += strlen(temp)
388
389                if (row >= numRows)
390                        break
391                endif
392                if (totalLength >= 256)                                 // Enough is enough
393                        break
394                endif
395
396                preview += ", "
397        while(1)
398
399        if (row < numRows)
400                preview += " . . ."
401        endif
402       
403        return preview
404End
405
406Function/S NI2_GetPreviewString(locationID, objectType, di, fullPath, attributeName)
407        Variable locationID
408        Variable objectType                                             // 1 = group, 2 = dataset
409        STRUCT NI2_HDF5DataInfo &di
410        String fullPath                                                 // Full path to group or dataset
411        String attributeName                                    // "" if this is a dataset, not an attribute
412       
413        String value = "<Can't display here>"
414        String temp
415       
416        Variable rank = di.ndims
417        Variable dim, numElements
418       
419        if (rank == 0)
420                numElements = 1
421        else
422                numElements = di.dims[0]
423                for(dim=1; dim<rank; dim+=1)
424                        numElements *= di.dims[dim]             
425                endfor
426        endif
427
428#if Exists("HDF5LoadData")     
429        strswitch(di.datatype_class_str)
430                case "H5T_INTEGER":
431                case "H5T_FLOAT":
432                case "H5T_ENUM":
433                case "H5T_OPAQUE":
434                case "H5T_BITFIELD":
435                        if (numElements > 100)
436                                value = "<Too big to display here>"     // It would take too long to load.
437                                break
438                        endif
439                       
440                        HDF5LoadData /A=attributeName /N=tempNumericAttributeWave /O /Q /TYPE=(objectType) /VAR=0 /Z locationID, fullPath
441                        if (V_flag != 0)
442                                value = "ERROR!"
443                        else
444                                Wave tempNumericAttributeWave
445                               
446                                if (rank > 1)
447                                        // So we can treat multi-dimensional wave as one big row.
448                                        Redimension/N=(numElements)/E=1 tempNumericAttributeWave
449                                endif
450                               
451                                value = NI2_GetNumericPreviewString(tempNumericAttributeWave)
452                        endif
453                        KillWaves/Z tempNumericAttributeWave
454                        break                           
455
456                case "H5T_REFERENCE":
457                        if (numElements > 10)
458                                value = "<Too big to display here>"     // It would take too long to load.
459                                break
460                        endif
461               
462                        HDF5LoadData /A=attributeName /N=tempTextAttributeWave /O /Q /TYPE=(objectType) /VAR=0 /Z locationID, fullPath
463                        if (V_flag != 0)
464                                value = "ERROR!"
465                        else
466                                if (rank > 1)
467                                        wave tempTextAttributeWave
468                                        // So we can treat multi-dimensional wave as one big row.
469                                        Redimension/N=(numElements)/E=1 tempTextAttributeWave
470                                endif
471                               
472                                // Remove the prefix (e.g., "D:" for a dataset, which is there for
473                                // programmatic use but would confuse in a preview.
474                                Wave/T references = tempTextAttributeWave                       // Created by HDF5LoadData
475                                String tmp
476                                Variable npnts=numpnts(references), len
477                                Variable i
478                                for(i=0; i<npnts; i+=1)
479                                        tmp = references[i]
480                                        len = strlen(tmp)
481                                        references[i] = tmp[2,len]
482                                endfor
483                               
484                                value = NI2_GetTextPreviewString(references)
485                        endif
486                        KillWaves/Z tempTextAttributeWave
487                        break                           
488
489                case "H5T_STRING":
490                        if (numElements > 100)
491                                value = "<Too big to display here>"     // It would take too long to load.
492                                break
493                        endif
494               
495                        HDF5LoadData /A=attributeName /N=tempTextAttributeWave /O /Q /TYPE=(objectType) /VAR=0 /Z locationID, fullPath
496                        if (V_flag != 0)
497                                value = "ERROR!"
498                        else
499                                if (rank > 1)
500                                        // So we can treat multi-dimensional wave as one big row.
501                                        Redimension/N=(numElements)/E=1 tempTextAttributeWave
502                                endif
503                                value = NI2_GetTextPreviewString(tempTextAttributeWave)
504                        endif
505                        KillWaves/Z tempTextAttributeWave
506                        break                           
507                       
508                case "H5T_TIME":
509                case "H5T_COMPOUND":
510                case "H5T_VLEN":
511                case "H5T_ARRAY":
512                        value = "<Can't display this type here>"
513                        break
514        endswitch
515       
516        return value
517#else
518        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
519#endif
520End
521
522static Function FillDatasetsList(bd)
523        STRUCT NI2HDF5BrowserData &bd
524
525#if Exists("HDF5ListGroup")     
526        HDF5ListGroup /TYPE=2 bd.fileID, bd.groupPath
527        Variable numItemsInList = ItemsInList(S_HDF5ListGroup)
528       
529        if (numItemsInList == 0)
530                Redimension/N=0 bd.datasetsList
531        else
532                Redimension/N=(numItemsInList, 5) bd.datasetsList
533                SetDimLabel 1, 0, Dataset, bd.datasetsList
534                SetDimLabel 1, 1, Rank, bd.datasetsList
535                SetDimLabel 1, 2, 'Dim Sizes', bd.datasetsList
536                SetDimLabel 1, 3, Type, bd.datasetsList
537                SetDimLabel 1, 4, Value, bd.datasetsList
538                bd.datasetsList[][0] = StringFromList(p, S_HDF5ListGroup)
539       
540                String dataset
541                Variable i, numDatasets
542                numDatasets = ItemsInList(S_HDF5ListGroup)
543                for(i=0; i<numDatasets; i+=1)
544                        dataset = StringFromList(i, S_HDF5ListGroup)
545                        String fullPath = NI2_HDF5GetObjectFullPath(bd.groupPath, dataset)
546                        STRUCT NI2_HDF5DataInfo di
547                        NI2_InitHDF5DataInfo(di)                        // Set input fields.
548                        HDF5DatasetInfo(bd.fileID, fullPath, 0, di)
549                       
550                        Variable rank = di.ndims
551                        bd.datasetsList[i][1] = num2istr(rank)
552                       
553                        String dimsStr=""
554                        Variable dim
555                        for(dim=0; dim<rank; dim+=1)
556                                dimsStr += num2istr(di.dims[dim]) + ";"
557                        endfor
558                        bd.datasetsList[i][2] = dimsStr
559
560                        bd.datasetsList[i][3] = di.datatype_str
561
562                        String preview = NI2_GetPreviewString(bd.fileID, 2, di, fullPath, "")
563                        bd.datasetsList[i][4] = preview
564                endfor
565        endif
566#else
567        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
568#endif
569End
570               
571static Function FillGroupAttributesList(bd)
572        STRUCT NI2HDF5BrowserData &bd
573
574#if Exists("HDF5ListAttributes")       
575        Variable numAttributes = 0
576        String groupPath = bd.groupPath
577        if (strlen(groupPath) > 0)
578                HDF5ListAttributes/TYPE=1 bd.fileID, groupPath
579                numAttributes = ItemsInList(S_HDF5ListAttributes)
580        endif
581       
582        if (numAttributes == 0)
583                Redimension/N=0 bd.groupAttributesList
584        else
585                Redimension/N=(numAttributes, 5) bd.groupAttributesList
586                SetDimLabel 1, 0, Attribute,bd.groupAttributesList
587                SetDimLabel 1, 1, Rank,bd.groupAttributesList
588                SetDimLabel 1, 2, 'Dim Sizes',bd.groupAttributesList
589                SetDimLabel 1, 3, Type,bd.groupAttributesList
590                SetDimLabel 1, 4, Value,bd.groupAttributesList
591                bd.groupAttributesList[][0] = StringFromList(p, S_HDF5ListAttributes)
592       
593                String attribute
594                Variable i
595                for(i=0; i<numAttributes; i+=1)
596                        String attributeName
597                        attributeName = StringFromList(i, S_HDF5ListAttributes)
598                        attribute = attributeName
599
600                        STRUCT NI2_HDF5DataInfo di
601                        NI2_InitHDF5DataInfo(di)                        // Set input fields.
602                        HDF5AttributeInfo(bd.fileID, groupPath, 1, attribute, 0, di)
603                       
604                        Variable rank = di.ndims
605                        bd.groupAttributesList[i][1] = num2istr(rank)
606                       
607                        String dimsStr=""
608                        Variable dim
609                        for(dim=0; dim<rank; dim+=1)
610                                dimsStr += num2istr(di.dims[dim]) + ";"
611                        endfor
612                        bd.groupAttributesList[i][2] = dimsStr
613
614                        bd.groupAttributesList[i][3] = di.datatype_str
615
616                        String preview = NI2_GetPreviewString(bd.fileID, 1, di, groupPath, attributeName)
617                        bd.groupAttributesList[i][4] = preview
618                endfor
619        endif
620#else
621        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
622#endif
623End
624               
625static Function FillDatasetAttributesList(bd)
626        STRUCT NI2HDF5BrowserData &bd
627
628#if Exists("HDF5ListAttributes")       
629        Variable numAttributes = 0
630        String datasetPath = NI2_SelectedDatasetPath(bd)
631        if (strlen(datasetPath) > 0)
632                HDF5ListAttributes/TYPE=2 bd.fileID, datasetPath
633                numAttributes = ItemsInList(S_HDF5ListAttributes)
634        endif
635       
636        if (numAttributes == 0)
637                Redimension/N=0 bd.datasetAttributesList
638        else
639                Redimension/N=(numAttributes, 5) bd.datasetAttributesList
640                SetDimLabel 1, 0, Attribute,bd.datasetAttributesList
641                SetDimLabel 1, 1, Rank,bd.datasetAttributesList
642                SetDimLabel 1, 2, 'Dim Sizes',bd.datasetAttributesList
643                SetDimLabel 1, 3, Type,bd.datasetAttributesList
644                SetDimLabel 1, 4, Value,bd.datasetAttributesList
645                bd.datasetAttributesList[][0] = StringFromList(p, S_HDF5ListAttributes)
646       
647                String attribute
648                Variable i
649                for(i=0; i<numAttributes; i+=1)
650                        String attributeName
651                        attributeName = StringFromList(i, S_HDF5ListAttributes)
652                        attribute = attributeName
653
654                        STRUCT NI2_HDF5DataInfo di
655                        NI2_InitHDF5DataInfo(di)                        // Set input fields.
656                        HDF5AttributeInfo(bd.fileID, datasetPath, 2, attribute, 0, di)
657                       
658                        Variable rank = di.ndims
659                        bd.datasetAttributesList[i][1] = num2istr(rank)
660                       
661                        String dimsStr=""
662                        Variable dim
663                        for(dim=0; dim<rank; dim+=1)
664                                dimsStr += num2istr(di.dims[dim]) + ";"
665                        endfor
666                        bd.datasetAttributesList[i][2] = dimsStr
667
668                        bd.datasetAttributesList[i][3] = di.datatype_str
669
670                        String preview = NI2_GetPreviewString(bd.fileID, 2, di, datasetPath, attributeName)
671                        bd.datasetAttributesList[i][4] = preview
672                endfor
673        endif
674#else
675        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
676#endif
677End
678       
679static Function FillLists(bd)
680        STRUCT NI2HDF5BrowserData &bd
681
682        if (bd.fileID == 0)                                                     // No file is open?
683                Redimension/N=(0) bd.groupsList
684                Redimension/N=(0) bd.groupAttributesList
685                Redimension/N=(0) bd.datasetsList
686                Redimension/N=(0) bd.datasetAttributesList
687                return -1
688        endif
689       
690        Variable numItemsInList
691        String hierarchy
692       
693        // Show entire hierarchy in Groups list.
694        hierarchy = GetFileHierarchy(bd, 0)     
695        numItemsInList = ItemsInList(hierarchy)
696        Redimension/N=(numItemsInList) bd.groupsList
697        bd.groupsList = StringFromList(p, hierarchy)
698       
699        // The groupFullPaths wave stores the full path to each group
700        hierarchy = GetFileHierarchy(bd, 1)     
701        numItemsInList = ItemsInList(hierarchy)
702        Redimension/N=(numItemsInList) bd.groupFullPaths
703        bd.groupFullPaths = StringFromList(p, hierarchy)
704       
705        // Show datasets in current group in Datasets list.
706        FillDatasetsList(bd)
707       
708        // Show attributes of currently-selected group.
709        FillGroupAttributesList(bd)
710       
711        // Show attributes of currently-selected dataset.
712        FillDatasetAttributesList(bd)
713End
714
715Function/S NI2_SelectedGroupName(bd)
716        STRUCT NI2HDF5BrowserData &bd
717       
718        if (numpnts(bd.groupsList) == 0)
719                return ""
720        endif
721
722        ControlInfo/W=$bd.browserName GroupsList
723        Variable selRow = V_value
724        String groupName = bd.groupsList[selRow]
725
726        // Group names may have leading spaces at this point. The spaces are used to create
727        // indentation in the list to show the hierarchy. We must remove the leading spaces.
728        sscanf groupName, " %s", groupName
729
730        if (strlen(groupName) > 0)
731                return groupName
732        endif
733
734        return ""
735End
736
737Function/S NI2_SelectedGroupPath(bd)
738        STRUCT NI2HDF5BrowserData &bd
739       
740        String groupPath = bd.groupPath
741
742        return groupPath
743End
744
745Function/S NI2_SelectedDatasetName(bd)
746        STRUCT NI2HDF5BrowserData &bd
747       
748        if (numpnts(bd.datasetsList) == 0)
749                return ""
750        endif
751
752        ControlInfo/W=$bd.browserName DatasetsList
753        Variable selRow = V_value
754        String datasetName = bd.datasetsList[selRow][0]
755        if (strlen(datasetName) > 0)
756                return datasetName
757        endif
758
759        return ""
760End
761
762Function/S NI2_SelectedDatasetPath(bd)
763        STRUCT NI2HDF5BrowserData &bd
764
765        String datasetName = NI2_SelectedDatasetName(bd)
766        if (strlen(datasetName) == 0)
767                return ""                                               // Nothing is selected
768        endif
769       
770        String datasetPath = bd.groupPath
771        if (CmpStr(datasetPath[strlen(datasetPath)-1],"/") != 0)
772                datasetPath += "/"
773        endif
774        datasetPath += datasetName
775
776        return datasetPath
777End
778
779Function/S NI2_SelectedAttributeName(bd, isGroupAttribute)
780        STRUCT NI2HDF5BrowserData &bd
781        Variable isGroupAttribute
782
783        String controlName
784        if (isGroupAttribute)
785                controlName = "GroupAttributesList"
786                Wave/T list = bd.groupAttributesList
787        else
788                controlName = "DatasetAttributesList"
789                Wave/T list = bd.datasetAttributesList
790        endif
791       
792        if (numpnts(list) == 0)
793                return ""
794        endif
795
796        ControlInfo/W=$bd.browserName $controlName
797        Variable selRow = V_value
798        String attributeName = list[selRow][0]
799       
800        if (strlen(attributeName) > 0)
801                return attributeName
802        endif
803
804        return ""
805End
806
807Function/S NI2_SelectedAttributePath(bd, isGroupAttribute)
808        STRUCT NI2HDF5BrowserData &bd
809        Variable isGroupAttribute
810
811        String attributeName = NI2_SelectedAttributeName(bd, isGroupAttribute)
812        if (strlen(attributeName) == 0)
813                return ""                                               // Nothing is selected
814        endif
815       
816        String path
817        if (isGroupAttribute)
818                path = NI2_SelectedGroupPath(bd)
819        else
820                path = NI2_SelectedDatasetPath(bd)
821        endif
822       
823        path += "/" + attributeName
824
825        return path
826End
827
828StrConstant NI2_kLoadAllMembersString = "_Load_All_Members_"
829
830static Function SetMembersPopupMenu(bd)
831        STRUCT NI2HDF5BrowserData &bd
832       
833#if Exists("HDF5OpenFile")     
834        Variable hideMembers
835        String memberList
836
837        hideMembers = 1
838        memberList = NI2_kLoadAllMembersString + ";"
839       
840        if (bd.fileID != 0)                             // File is open for this browser?
841                String fullPath
842               
843                fullPath = NI2_SelectedDatasetPath(bd)
844                if (strlen(fullPath) > 0)
845                        STRUCT NI2_HDF5DataInfo di
846                        NI2_InitHDF5DataInfo(di)
847                        HDF5DatasetInfo(bd.fileID, fullPath, 0, di)
848                        if (CmpStr(di.datatype_class_str, "H5T_COMPOUND") == 0)
849                                hideMembers = 0
850                               
851                                ControlInfo /W=$bd.browserName Members
852                                Variable selectedItemNumber = V_Value
853                                STRUCT NI2_HDF5DatatypeInfo dti
854                                NI2_InitHDF5DatatypeInfo(dti)
855                                if (HDF5TypeInfo(bd.fileID, fullPath, "", "", 1, dti))
856                                        memberList += "HDF5TypeInfo Error!"
857                                else
858                                        memberList += dti.names
859                                        if (selectedItemNumber > dti.nmembers+1)        // +1 because of "_Load_All_Members_" item.
860                                                selectedItemNumber = 1          // Force menu selection to be in bounds.
861                                        endif
862                                endif
863                                PopupMenu Members, win=$bd.browserName, mode=selectedItemNumber
864                        endif
865                endif
866        endif
867       
868        PopupMenu Members, win=$bd.browserName, disable=hideMembers
869       
870        String cmd                              // What a pain. Can't use local variable with PopupMenu value=
871        sprintf cmd, "PopupMenu Members, win=%s, value=\"%s\"", bd.browserName, memberList
872        Execute cmd
873#else
874        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
875#endif
876End
877
878Function NI2_HDF5GetReadOnlySetting(browserName)
879        String browserName
880       
881        Variable result
882        ControlInfo /W=$browserName ReadOnly
883        result = V_value
884        return result
885End
886
887Function NI2_HDF5GetCompLoadInfo(bd, isCompound, compMode, memberName)
888        STRUCT NI2HDF5BrowserData &bd
889        Variable &isCompound                                    // Output: If non-zero, a compound dataset is selected.
890        Variable &compMode                                              // Output: Value suitable for passing to HDF5LoadData /COMP flag.
891        String &memberName                                              // Output: If "" load all members.
892       
893        isCompound = 0
894        memberName = ""
895       
896        ControlInfo /W=$bd.browserName Members
897        if (V_disable == 0)
898                isCompound = 1
899                memberName = S_value
900                if (CmpStr(memberName, NI2_kLoadAllMembersString) == 0)
901                        memberName = ""
902                endif
903        endif
904
905        compMode = isCompound && strlen(memberName)>0
906End
907
908Function NI2_HDF5GetTranspose2DSetting(browserName)
909        String browserName
910       
911        Variable result
912        ControlInfo /W=$browserName Transpose2DDatasets
913        result = V_value
914        return result
915End
916
917static Function HDF5GetLoadDatasetOptions(browserName, tableDisplayMode, graphDisplayMode)
918        String browserName
919        Variable &tableDisplayMode, &graphDisplayMode
920       
921        ControlInfo /W=$browserName DisplayInTable
922        tableDisplayMode = V_value - 1                                          // 0=no; 1=display; 2=append
923       
924        ControlInfo /W=$browserName DisplayInGraph
925        graphDisplayMode = V_value - 1                                          // 0=no; 1=display; 2=append
926       
927        return tableDisplayMode || graphDisplayMode
928End
929
930static Function MembersPopupProc(ctrlName,popNum,popStr) : PopupMenuControl
931        String ctrlName
932        Variable popNum
933        String popStr
934       
935        String browserName = NI2_HDF5GetTopBrowserName()
936       
937        STRUCT NI2HDF5BrowserData bd
938        SetNI2HDF5BrowserData(browserName, bd)
939       
940        NI2_HDF5DisplaySelectedDataset(bd)
941End
942
943static Function SetButtonStates(bd)
944        STRUCT NI2HDF5BrowserData &bd
945       
946        if (bd.fileID == 0)                             // No file is open for this browser?
947                Button CreateFile, win=$bd.browserName, disable=2               // Enable Create
948                Button OpenFile, win=$bd.browserName, disable=0         // Enable Open
949                Button CloseFile, win=$bd.browserName, disable=2                // Disable Close
950                Button LoadGroup, win=$bd.browserName, disable=2                // Disable Load Group
951                Button SaveDataFolder, win=$bd.browserName, disable=2   // Disable Save Data Folder
952                Button LoadDataset, win=$bd.browserName, disable=2      // Disable Load Dataset
953                Button SaveWaves, win=$bd.browserName, disable=2                // Disable Save Waves
954        else
955                Button CreateFile, win=$bd.browserName, disable=2               // Disable Create
956                Button OpenFile, win=$bd.browserName, disable=2         // Disable Open
957                Button CloseFile, win=$bd.browserName, disable=0                // Enable Close
958               
959                String groupName = NI2_SelectedGroupName(bd)
960                Variable code = strlen(groupName) > 0 ? 0:2
961                Button LoadGroup, win=$bd.browserName, disable=code     // Enable Load Group
962                code = bd.readOnly == 0 ? 0:2
963                Button SaveDataFolder, win=$bd.browserName, disable=code        // Enable Save Data Folder
964               
965                String datasetName = NI2_SelectedDatasetName(bd)
966                code = strlen(datasetName) > 0 ? 0:2
967                Button LoadDataset, win=$bd.browserName, disable=code   // Enable Load Dataset
968                code = bd.readOnly == 0 ? 0:2
969                Button SaveWaves, win=$bd.browserName, disable=code             // Enable Save Waves
970                //overwrite the code above...
971                Button LoadGroup, win=$bd.browserName, disable=2        // Disable Load Dataset
972                Button LoadDataset, win=$bd.browserName, disable=2      // Disable Load Dataset
973        endif
974        SetMembersPopupMenu(bd)
975        SetGraphButtonTitle(bd.browserName)
976        SetTableButtonTitle(bd.browserName)
977        SetDumpButtonTitle(bd.browserName)
978        SetVariable HyperSelectionWave, win=$bd.browserName, value= bd.hyperSelectionWavePath
979End
980
981static Function DrawFilePath(bd)
982        STRUCT NI2HDF5BrowserData &bd
983       
984        // TitleBox FilePath, win=$bd.browserName, title=bd.fullPath                    // This is limited to 63 characters.
985        TitleBox FilePath, win=$bd.browserName, variable=bd.fullPath
986End
987
988static Function DrawGroupPath(bd)
989        STRUCT NI2HDF5BrowserData &bd
990       
991        TitleBox GroupPath, win=$bd.browserName, variable=bd.groupPath
992End
993
994static Function DrawDatasetInfo(bd)
995        STRUCT NI2HDF5BrowserData &bd
996       
997        TitleBox Dataset, win=$bd.browserName, variable=bd.datasetInfo
998End
999
1000static Function UpdateAfterFileCreateOrOpen(isCreate, browserName, fileID, path, fileName)
1001        Variable isCreate
1002        String browserName
1003        Variable fileID
1004        String path, fileName
1005       
1006        STRUCT NI2HDF5BrowserData bd
1007        SetNI2HDF5BrowserData(browserName, bd)
1008
1009        ResetListSelections(bd, 1, 1, 1, 1)
1010
1011        bd.fileID = fileID
1012        bd.fileName = fileName
1013        bd.path = path
1014        bd.fullPath = path + fileName
1015        DrawFilePath(bd)
1016       
1017        bd.readOnly = isCreate ? 0 : NI2_HDF5GetReadOnlySetting(browserName)
1018
1019        bd.groupPath = "/"
1020        DrawGroupPath(bd)
1021
1022        SetButtonStates(bd)
1023
1024        FillLists(bd)
1025       
1026        NI2_UpdateAfterGroupSelected(bd, "/")
1027       
1028        String datasetName = NI2_SelectedDatasetName(bd)
1029        if (strlen(datasetName) > 0)
1030                NI2_SelectDataset(bd, datasetName)
1031        endif
1032End
1033
1034static Function CreateFileButtonProc(ctrlName) : ButtonControl
1035        String ctrlName
1036       
1037#if Exists("HDF5OpenFile")     
1038        String browserName = NI2_HDF5GetTopBrowserName()
1039
1040        STRUCT NI2HDF5BrowserData bd
1041        SetNI2HDF5BrowserData(browserName, bd)
1042       
1043        Variable fileID
1044       
1045        HDF5CreateFile /I /O fileID  as ""
1046        if (V_flag == 0)                // Create OK?
1047                UpdateAfterFileCreateOrOpen(1, browserName, fileID, S_path, S_fileName)
1048        endif
1049#else
1050        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
1051#endif
1052End
1053
1054static Function OpenFileButtonProc(ctrlName) : ButtonControl
1055        String ctrlName
1056       
1057#if Exists("HDF5OpenFile")     
1058        String browserName = NI2_HDF5GetTopBrowserName()
1059       
1060        Variable readOnly = NI2_HDF5GetReadOnlySetting(browserName)
1061       
1062        Variable locFileID
1063       
1064        if (readOnly)
1065                HDF5OpenFile/R locFileID as ""
1066        else
1067                HDF5OpenFile locFileID as ""
1068        endif
1069       
1070        if (V_flag == 0)                                        // Open OK?
1071                UpdateAfterFileCreateOrOpen(0, browserName, locFileID, S_path, S_fileName)
1072        endif
1073#else
1074        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
1075#endif
1076End
1077
1078// This detects if the file is no longer open, such as if you save the experiment, quit Igor and then reopen the experiment.
1079Function NI2_FileWasUnexpectedlyClosed(bd)
1080        STRUCT NI2HDF5BrowserData &bd
1081
1082#if Exists("HDF5OpenFile")     
1083        if (bd.fileID == 0)
1084                return 0                                // File is closed but not unexpectedly.
1085        endif
1086       
1087        HDF5ListAttributes/Q /TYPE=1 /Z bd.fileID , "/"         // Try to list the attributes of the root of the file.
1088        if (V_flag != 0)
1089                return 1                                                                // Error: Assume file was closed.
1090        endif
1091       
1092        return 0
1093#else
1094        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
1095#endif
1096End
1097
1098static Function FileWasClosed(bd)                       // Does cleanup after a file is closed.
1099        STRUCT NI2HDF5BrowserData &bd
1100
1101        bd.fileID = 0
1102        Redimension/N=(0) bd.groupFullPaths
1103        bd.groupPath = ""
1104        bd.fileName = ""
1105        bd.path = ""
1106        bd.fullPath = ""
1107        bd.datasetInfo = ""
1108
1109        DrawFilePath(bd)
1110        SetButtonStates(bd)
1111        FillLists(bd)
1112End
1113
1114static Function CloseFileButtonProc(ctrlName) : ButtonControl
1115        String ctrlName
1116       
1117#if Exists("HDF5OpenFile")     
1118        String browserName = NI2_HDF5GetTopBrowserName()
1119
1120        STRUCT NI2HDF5BrowserData bd
1121        SetNI2HDF5BrowserData(browserName, bd)
1122
1123        HDF5CloseFile bd.fileID
1124        CloseSavePanels()
1125        FileWasClosed(bd)
1126#else
1127        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
1128#endif
1129End
1130
1131static Function LoadDatasetButtonProc(ctrlName) : ButtonControl
1132        String ctrlName
1133
1134#if Exists("HDF5OpenFile")     
1135        String browserName
1136        STRUCT NI2HDF5BrowserData bd
1137       
1138        if(stringmatch(ctrlName,"AddGrouptoList"))
1139               
1140                browserName = NI2_HDF5GetTopBrowserName()
1141       
1142                SetNI2HDF5BrowserData(browserName, bd)
1143               
1144                Wave/T DataSetNames=$("root:Packages:"+NI2_GetHDF5LoaderLocString()+":DataSetNames")
1145                Wave/T GroupNames=$("root:Packages:"+NI2_GetHDF5LoaderLocString()+":GroupNames")
1146                SVAR BrowserNameFldr=$("root:Packages:"+NI2_GetHDF5LoaderLocString()+":BrowserNameFldr")
1147                Redimension/N=(numpnts(DataSetNames)+1) GroupNames
1148                GroupNames[numpnts(GroupNames)-1]=NI2_SelectedGroupPath(bd)
1149                BrowserNameFldr = NI2_HDF5GetTopBrowserName()
1150        elseif(stringmatch(ctrlName,"ClearDatasetList"))
1151                browserName = NI2_HDF5GetTopBrowserName()       
1152                SetNI2HDF5BrowserData(browserName, bd)         
1153                Wave/T DataSetNames=$("root:Packages:"+NI2_GetHDF5LoaderLocString()+":DataSetNames")
1154                Wave/T GroupNames=$("root:Packages:"+NI2_GetHDF5LoaderLocString()+":GroupNames")
1155                SVAR BrowserNameFldr=$("root:Packages:"+NI2_GetHDF5LoaderLocString()+":BrowserNameFldr")
1156                Redimension/N=(0) DataSetNames, GroupNames
1157//              DataSetNames[numpnts(DataSetNames)-1]=NI2_SelectedDatasetPath(bd)
1158                BrowserNameFldr = NI2_HDF5GetTopBrowserName()           
1159        else            //Old LoadDataset button...
1160       
1161                browserName = NI2_HDF5GetTopBrowserName()
1162       
1163                SetNI2HDF5BrowserData(browserName, bd)
1164               
1165                String datasetPath = NI2_SelectedDatasetPath(bd)
1166       
1167                String slabWaveStr = ""
1168                ControlInfo /W=$bd.browserName UseHyperSelection
1169                if (V_value)                                                            // Use Hyperselection is checked?
1170                        slabWaveStr = bd.hyperSelectionWavePath
1171                endif
1172                WAVE/Z slabWave = $slabWaveStr                  // It is OK if wave does not exist and slabWave is NULL. HDF5LoadData will simply ignore /SLAB.
1173               
1174                Variable isCompound
1175                String memberName
1176                Variable compMode
1177                NI2_HDF5GetCompLoadInfo(bd, isCompound, compMode, memberName)
1178       
1179                // If isFormalImage is true, we are loading an image written
1180                // according to the HDF5 Image and Palette Specification.
1181                Variable isFormalImage = 0
1182                if (!isCompound)
1183                        String savedDataFolder = NI2_SetBrowserDataFolder("")                   // tempClassAttribute goes in master HDF5Browser data folder
1184                        HDF5LoadData /Z /O /N=tempClassAttribute /A="CLASS" /Q /VAR=1 bd.fileID, datasetPath
1185                        if (V_flag == 0)
1186                                WAVE/T tempClassAttribute                       // HDF5LoadData will have created this string
1187                                if (CmpStr(tempClassAttribute[0],"IMAGE") == 0)
1188                                        isFormalImage = 1
1189                                endif   
1190                                KillWaves/Z tempClassAttribute
1191                        endif
1192                        SetDataFolder savedDataFolder
1193                endif
1194               
1195                Variable tableDisplayMode, graphDisplayMode             // 0=no; 1=display; 2=append
1196                HDF5GetLoadDatasetOptions(bd.browserName, tableDisplayMode, graphDisplayMode)
1197               
1198                if (isFormalImage)
1199                        HDF5LoadImage /O /GRPH=(graphDisplayMode) /T=(tableDisplayMode) bd.fileID, datasetPath
1200                else
1201                        Variable transpose2D = NI2_HDF5GetTranspose2DSetting(bd.browserName)
1202                        HDF5LoadData /O /SLAB=slabWave /TRAN=(transpose2D) /COMP={compMode,memberName} /GRPH=(graphDisplayMode) /T=(tableDisplayMode) bd.fileID, datasetPath
1203                endif
1204        endif
1205#else
1206        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
1207#endif
1208End
1209
1210static Function LoadGroupButtonProc(ctrlName) : ButtonControl
1211        String ctrlName
1212
1213#if Exists("HDF5OpenFile")     
1214        String browserName = NI2_HDF5GetTopBrowserName()
1215
1216        STRUCT NI2HDF5BrowserData bd
1217        SetNI2HDF5BrowserData(browserName, bd)
1218       
1219        String groupPath = NI2_SelectedGroupPath(bd)
1220
1221        ControlInfo /W=$bd.browserName LoadGroupsRecursively
1222        if (V_value)                                                                    // Use LoadGroupsRecursively is checked?
1223                HDF5LoadGroup /O /R /T /IMAG=1 :, bd.fileID, groupPath
1224        else
1225                HDF5LoadGroup /O /T /IMAG=1 :, bd.fileID, groupPath
1226        endif
1227
1228        // For debugging
1229        Variable numItems
1230        Variable debug = 1
1231        if (debug)
1232                Wave/Z/T groupPaths = root:groupPaths
1233                if (WaveExists(groupPaths))
1234                        numItems = ItemsInList(S_groupPaths)
1235                        Redimension/N=(numItems) groupPaths
1236                        groupPaths = StringFromList(p, S_groupPaths)
1237                endif
1238
1239                Wave/Z/T dataFolderPaths = root:dataFolderPaths
1240                if (WaveExists(dataFolderPaths))
1241                        numItems = ItemsInList(S_dataFolderPaths)
1242                        Redimension/N=(numItems) dataFolderPaths
1243                        dataFolderPaths = StringFromList(p, S_dataFolderPaths)
1244                endif
1245
1246                Wave/Z/T wavePaths = root:wavePaths
1247                if (WaveExists(wavePaths))
1248                        numItems = ItemsInList(S_objectPaths)
1249                        Redimension/N=(numItems) wavePaths
1250                        wavePaths = StringFromList(p, S_objectPaths)
1251                endif
1252        endif
1253#else
1254        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
1255#endif
1256End
1257
1258Function NI2_AttachListWaves(bd)
1259        STRUCT NI2HDF5BrowserData &bd
1260       
1261        ListBox GroupsList win=$bd.browserName, listWave=bd.groupsList
1262        ListBox GroupAttributesList win=$bd.browserName, listWave=bd.groupAttributesList
1263        ListBox DatasetsList win=$bd.browserName, listWave=bd.datasetsList
1264        ListBox DatasetAttributesList win=$bd.browserName, listWave=bd.datasetAttributesList
1265End
1266
1267Function NI2_HDF5BrowserPanelHook(infoStr)
1268        String infoStr
1269
1270#if Exists("HDF5OpenFile")     
1271        String browserName= StringByKey("WINDOW",infoStr)
1272        String event= StringByKey("EVENT",infoStr)
1273
1274        STRUCT NI2HDF5BrowserData bd
1275        SetNI2HDF5BrowserData(browserName, bd)
1276
1277        strswitch(event)
1278                case "activate":                                // We do not get this on Windows when the panel is first created.
1279                        // This detects if the file is no longer open, such as if you save the experiment, quit Igor and then reopen the experiment.
1280                        if (NI2_FileWasUnexpectedlyClosed(bd))
1281                                Printf "The file \"%s\" is no longer open.\r", bd.fileName
1282                                FileWasClosed(bd)
1283                        endif
1284                       
1285                        SetGraphButtonTitle(browserName)
1286                        SetTableButtonTitle(browserName)
1287                        SetDumpButtonTitle(browserName)
1288                        break
1289
1290                case "resize":
1291                        NI2_HDF5ResizeBrowser(browserName)
1292                        break
1293                       
1294                case "moved":                   // This message was added in Igor Pro 5.04B07.
1295                        // If this is the last HDF5 browser, save the browser window size and position.
1296                        if (strlen(NI2_HDF5GetIndxBrowserName(1)) == 0)
1297                                SetPrefWindowCoords(browserName)
1298                        endif
1299                        break
1300                       
1301                case "kill":
1302                        if (bd.fileID != 0)
1303                                HDF5CloseFile bd.fileID
1304                                bd.fileID = 0
1305                                CloseSavePanels()
1306                        endif
1307                        KillDataFolder root:Packages:HDF5Browser:$browserName
1308                        break
1309        endswitch
1310       
1311        return 0
1312#else
1313        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
1314#endif
1315End
1316
1317Function NI2_SelectDataset(bd, datasetName)
1318        STRUCT NI2HDF5BrowserData &bd
1319        String datasetName
1320
1321#if Exists("HDF5OpenFile")     
1322        String info
1323
1324        if (strlen(datasetName) == 0)
1325                info = ""
1326        else
1327                String fullPath
1328                fullPath = NI2_HDF5GetObjectFullPath(bd.groupPath, datasetName)
1329                STRUCT NI2_HDF5DataInfo di
1330                NI2_InitHDF5DataInfo(di)                        // Set input fields.
1331                HDF5DatasetInfo(bd.fileID, fullPath, 0, di)
1332                // Print s
1333                sprintf info, "%s, class=%s", datasetName, di.datatype_class_str
1334        endif
1335        bd.datasetInfo = info
1336        DrawDatasetInfo(bd)
1337        SetButtonStates(bd)
1338        FillDatasetAttributesList(bd)
1339#else
1340        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
1341#endif
1342End
1343
1344Function NI2_UpdateAfterGroupSelected(bd, fullGroupPath)
1345        STRUCT NI2HDF5BrowserData &bd
1346        String fullGroupPath
1347       
1348        Variable selectedGroupChanged = CmpStr(bd.groupPath, fullGroupPath) != 0
1349
1350        bd.groupPath = fullGroupPath
1351        DrawGroupPath(bd)
1352        FillGroupAttributesList(bd)
1353        FillDatasetsList(bd)
1354
1355        if (selectedGroupChanged)
1356                ResetListSelections(bd, 0, 1, 1, 1)
1357                String datasetName = ""
1358                if (numpnts(bd.datasetsList) > 0)
1359                        datasetName = bd.datasetsList[0][0]
1360                endif
1361                NI2_SelectDataset(bd, datasetName)
1362        endif
1363        SetButtonStates(bd)
1364End
1365
1366static Function GroupsListActionProc(s, bd) : ListboxControl
1367        STRUCT WMListboxAction &s
1368        STRUCT NI2HDF5BrowserData &bd
1369       
1370        String browserName = s.win
1371        Variable result = 0                                                                     // As of now, the return value must always be zero.
1372       
1373        switch(s.eventCode)
1374                case 4:                                         // Cell selection
1375                        String fullGroupPath = bd.groupFullPaths[s.row]
1376                        NI2_UpdateAfterGroupSelected(bd, fullGroupPath)
1377                        // Printf "Row=%d, column=%d, path=%s\r", s.row, s.col, fullGroupPath
1378                        if (NI2_HDF5BrowserDumpIsVisible())
1379                                NI2_HDF5DisplayDumpOfSelGroup(bd)
1380                        endif
1381                        break   
1382        endswitch
1383
1384        return result
1385End
1386
1387static Function GroupAttributesListActionProc(s, bd) : ListboxControl
1388        STRUCT WMListboxAction &s
1389        STRUCT NI2HDF5BrowserData &bd
1390       
1391        String browserName = s.win
1392        Variable result = 0                                                                     // As of now, the return value must always be zero.
1393       
1394        switch(s.eventCode)
1395                case 3:                                         // Double-click
1396                        break;
1397                       
1398                case 4:                                         // Cell selection
1399                        // Printf "Row=%d, column=%d\r", s.row, s.col
1400                        NI2_HDF5DisplaySelAttribute(bd,  1)                             // Update various windows if they are displayed
1401                        break   
1402        endswitch
1403
1404        return result
1405End
1406
1407Function NI2_HandleDatasetDoubleClick(s, bd)
1408        STRUCT WMListboxAction &s
1409        STRUCT NI2HDF5BrowserData &bd
1410
1411#if Exists("HDF5OpenFile")     
1412        String datasetPath = NI2_SelectedDatasetPath(bd)
1413        if (strlen(datasetPath) == 0)
1414                return -1
1415        endif
1416       
1417        STRUCT NI2_HDF5DataInfo di
1418        NI2_InitHDF5DataInfo(di)
1419        HDF5DatasetInfo(bd.fileID, datasetPath, 0, di)
1420        switch(di.datatype_class)
1421                default:
1422                        // Load dataset here.
1423                        break   
1424        endswitch
1425#else
1426        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
1427#endif
1428End
1429
1430static Function DatasetsListActionProc(s, bd) : ListboxControl
1431        STRUCT WMListboxAction &s
1432        STRUCT NI2HDF5BrowserData &bd
1433       
1434        String browserName = s.win
1435        Variable result = 0                                                                     // As of now, the return value must always be zero.
1436       
1437        switch(s.eventCode)
1438                case 3:                                         // Double-click
1439                        NI2_HandleDatasetDoubleClick(s, bd)
1440                        break;
1441                       
1442                case 4:                                         // Cell selection
1443                        String name = bd.datasetsList[s.row][0]
1444                        NI2_SelectDataset(bd, name)
1445                        // Printf "Row=%d, column=%d, name=%s\r", s.row, s.col, name
1446                        NI2_HDF5DisplaySelectedDataset(bd)                                      // Update various windows if they are displayed
1447                        break   
1448        endswitch
1449       
1450        return result
1451End
1452
1453static Function DatasetAttributesListActionProc(s, bd) : ListboxControl
1454        STRUCT WMListboxAction &s
1455        STRUCT NI2HDF5BrowserData &bd
1456       
1457        String browserName = s.win
1458        Variable result = 0                                                                     // As of now, the return value must always be zero.
1459       
1460        switch(s.eventCode)
1461                case 3:                                         // Double-click
1462                        break;
1463                       
1464                case 4:                                         // Cell selection
1465                        // Printf "Row=%d, column=%d\r", s.row, s.col
1466                        NI2_HDF5DisplaySelAttribute(bd,  0)                             // Update various windows if they are displayed
1467                        break   
1468        endswitch
1469       
1470        return result
1471End
1472
1473static Function ListBoxActionProc(s) : ListboxControl
1474        STRUCT WMListboxAction &s
1475
1476        String browserName = s.win
1477
1478        STRUCT NI2HDF5BrowserData bd
1479        SetNI2HDF5BrowserData(browserName, bd)
1480       
1481        Variable result = 0                                                     // As of now, the return value must always be zero.
1482       
1483        strswitch(s.ctrlName)
1484                case "GroupsList":
1485                        result = GroupsListActionProc(s, bd)
1486                        break
1487                       
1488                case "GroupAttributesList":
1489                        result = GroupAttributesListActionProc(s, bd)
1490                        break
1491                       
1492                case "DatasetsList":
1493                        result = DatasetsListActionProc(s, bd)
1494                        break
1495               
1496                case "DatasetAttributesList":
1497                        result = DatasetAttributesListActionProc(s, bd)
1498                        break
1499        endswitch
1500       
1501        return result
1502End
1503
1504static Function SetGraphButtonTitle(browserName)
1505        String browserName
1506       
1507        if (NI2_HDF5BrowserGraphIsVisible())
1508                Button Graph, win=$browserName, title="Hide Graph"
1509        else
1510                Button Graph, win=$browserName, title="Show Graph"
1511        endif   
1512End
1513
1514static Function GraphButtonProc(ctrlName) : ButtonControl
1515        String ctrlName
1516
1517        String browserName = NI2_HDF5GetTopBrowserName()
1518       
1519        if (NI2_HDF5BrowserGraphIsVisible())
1520                KillWIndow/Z HDF5BrowserGraph
1521        else
1522                NI2_HDF5CreateBrowserGraph()                            // Create if it does not exist.
1523        endif
1524        SetGraphButtonTitle(browserName)
1525End
1526
1527static Function SetTableButtonTitle(browserName)
1528        String browserName
1529       
1530        if (NI2_HDF5BrowserTableIsVisible())
1531                Button Table, win=$browserName, title="Hide Table"
1532        else
1533                Button Table, win=$browserName, title="Show Table"
1534        endif   
1535End
1536
1537static Function TableButtonProc(ctrlName) : ButtonControl
1538        String ctrlName
1539
1540        String browserName = NI2_HDF5GetTopBrowserName()
1541       
1542        if (NI2_HDF5BrowserTableIsVisible())
1543                KillWIndow/Z HDF5BrowserTable
1544        else
1545                NI2_HDF5CreateBrowserTable()                            // Create if it does not exist.
1546        endif
1547        SetTableButtonTitle(browserName)
1548End
1549
1550static Function SetDumpButtonTitle(browserName)
1551        String browserName
1552       
1553        if (NI2_HDF5BrowserDumpIsVisible())
1554                Button Dump, win=$browserName, title="Hide Dump"
1555        else
1556                Button Dump, win=$browserName, title="Show Dump"
1557        endif   
1558End
1559
1560static Function DumpButtonProc(ctrlName) : ButtonControl
1561        String ctrlName
1562
1563        String browserName = NI2_HDF5GetTopBrowserName()
1564       
1565        if (NI2_HDF5BrowserDumpIsVisible())
1566                Notebook HDF5DumpNotebook, visible=0
1567        else
1568                NI2_HDF5CreateDumpWindow()                                              // Create if it does not exist.
1569                DoWindow/F HDF5DumpNotebook                     // Show it.
1570        endif
1571        SetDumpButtonTitle(browserName)
1572End
1573
1574static Function HelpButtonProc(ctrlName) : ButtonControl
1575        String ctrlName
1576
1577        DisplayHelpTopic "The HDF5 Browser"
1578End
1579
1580static Function CreateHDF5BrowserPanel(browserName)
1581        String browserName
1582       
1583        Variable isMacintosh = 0
1584        if (CmpStr(IgorInfo(2),"Macintosh") == 0)
1585                isMacintosh = 1
1586        endif
1587
1588        // Determine panel size and position
1589        Variable left, top, right, bottom
1590        GetPrefWindowCoords("HDF5Browser", left, top, right, bottom)    // See if prefs set.
1591        if (right-left<200 || bottom-top<200)
1592                // These values are calculated to fill a typical 17 inch screen (832x624) on Macintosh.
1593                left = 5
1594                top = 50
1595                right = 1020
1596                bottom = 625
1597        endif
1598       
1599        Variable readOnly, loadGroupsRecursively, transpose2DDatasets
1600        GetPrefBrowserSettings(readOnly, loadGroupsRecursively, transpose2DDatasets)
1601
1602        NewPanel /W=(left, top, right, bottom)/K=1 as "HDF5 Browser"
1603       
1604        DoWindow/C $browserName
1605        DoWindow/T $browserName, browserName
1606       
1607        // This marks this control panel as an HDF5 browser.
1608        SetWindow kwTopWin, userdata(HDF5BrowserName)=browserName
1609       
1610        SetDrawLayer ProgBack
1611
1612        SetDrawEnv fstyle= 1
1613        DrawText 18,75,"File:"
1614
1615        SetDrawEnv fstyle= 1
1616        DrawText 18,103,"Selected Group:"
1617
1618        SetDrawEnv fstyle= 1
1619        DrawText 18,130,"Selected Dataset:"
1620
1621        TitleBox FilePath,pos={55,57},size={706,21}
1622
1623        left = isMacintosh ? 150 : 125
1624        TitleBox GroupPath,pos={left,86},size={658,20}
1625        TitleBox Dataset,pos={left,113},size={13,21}
1626
1627        CheckBox UseHyperSelection,pos={15,155},size={110,14},title="Use Hyperselection",value= 0
1628        CheckBox UseHyperSelection,help={"For experts only. Allows loading a subset of a dataset."}
1629
1630        SetVariable HyperSelectionWave,pos={140,155},size={340,16},title="Hyper Selection Wave:"
1631        SetVariable HyperSelectionWave,help={"Enter full path to wave containing hyperselection information. See HDF5LoadData /SLAB keyword help."}
1632
1633        CheckBox LoadGroupsRecursively,pos={15,181},size={137,14},title="Load Groups Recursively",value=loadGroupsRecursively
1634        CheckBox LoadGroupsRecursively,proc=HDF5BrowserPrefCheckboxProc,help={"When checked, the Load Group button loads subgroups."}
1635       
1636        Button CreateFile,pos={15,8},size={125,20},proc=NikaHDF5Browser#CreateFileButtonProc,title="Create HDF5 File", disable=1
1637        Button OpenFile,pos={159,8},size={125,20},proc=NikaHDF5Browser#OpenFileButtonProc,title="Open HDF5 File"
1638        Button CloseFile,pos={296,8},size={125,20},proc=NikaHDF5Browser#CloseFileButtonProc,title="Close HDF5 File"
1639        Button Help,pos={435,8},size={50,20},proc=NikaHDF5Browser#HelpButtonProc,title="Help"
1640        CheckBox ReadOnly,pos={186,32},size={68,14},title="Read Only",proc=HDF5BrowserPrefCheckboxProc,value=readOnly
1641
1642        // Start Preview
1643
1644        Button Graph,pos={556,27},size={90,20},proc=NikaHDF5Browser#GraphButtonProc,title="Show Graph"
1645        Button Graph help={"Shows or hides a graph which displays the last dataset or attribute that you selected."}
1646
1647        Button Table,pos={672,27},size={90,20},proc=NikaHDF5Browser#TableButtonProc,title="Show Table"
1648        Button Table help={"Shows or hides a table which displays the last dataset or attribute that you selected."}
1649
1650        Button Dump,pos={556,59},size={90,20},proc=NikaHDF5Browser#DumpButtonProc,title="Show Dump"
1651        Button Dump help={"Shows or hides a window which displays a dump of the last dataset or attribute that you selected."}
1652
1653        CheckBox ShowAttributesInDump,pos={653,71},size={100,14},title="Show Attributes In Dump"
1654        CheckBox ShowAttributesInDump help={"Check to display the dataset's attributes in the dump window."}
1655
1656        CheckBox ShowDataInDump,pos={653,56},size={114,14},title="Show Data In Dump"
1657        CheckBox ShowDataInDump,help={"Check to display data in the dump window. For large datasets this can take a long time."}
1658
1659        GroupBox PreviewOptions,pos={543,5},size={258,87},title="Preview Options"
1660
1661        // End Preview
1662
1663        TitleBox GroupsTitle,pos={15,230},size={50,16},disable=2,title="Groups",fSize=14
1664        TitleBox GroupsTitle,frame=0,fStyle=1
1665
1666        ListBox GroupsList,pos={15,250},size={306,170}, mode=2, proc=NikaHDF5Browser#ListBoxActionProc
1667        ListBox GroupsList,fSize=14
1668
1669        Button LoadGroup,pos={80,224},size={100,20},proc=NikaHDF5Browser#LoadGroupButtonProc,title="Load Group"
1670        Button LoadGroup,help={"Loads the currently selected group into the current data folder."}
1671
1672        Button SaveDataFolder,pos={194,224},size={120,20},proc=NikaHDF5Browser#SaveDataFolderButtonProc,title="Save Data Folder"
1673        Button SaveDataFolder,help={"Saves a data folder in the currently selected group. Available if the current HDF5 file is open for read/write."}
1674
1675        TitleBox GroupAttributesTitle,pos={15,435},size={111,16},disable=2,title="Group Attributes"
1676        TitleBox GroupAttributesTitle,fSize=14,frame=0,fStyle=1
1677
1678        ListBox GroupAttributesList,pos={15,455},size={306,109}, mode=2, proc=NikaHDF5Browser#ListBoxActionProc
1679        ListBox GroupAttributesList, widths={175,40,80,120,1000}, userColumnResize= 1   // userColumnResize requires Igor Pro 5.02.
1680        ListBox GroupAttributesList,fSize=14
1681
1682        TitleBox DatasetsTitle,pos={341,230},size={62,16},disable=2,title="Datasets"
1683        TitleBox DatasetsTitle,fSize=14,frame=0,fStyle=1
1684
1685        ListBox DatasetsList,pos={341,250},size={459,170}, mode=2, proc=NikaHDF5Browser#ListBoxActionProc
1686        ListBox DatasetsList, widths={175,40,80,120,1000}, userColumnResize= 1  // userColumnResize requires Igor Pro 5.02.
1687        ListBox DatasetsList,fSize=14
1688
1689        TitleBox DatasetAttributesTitle,pos={341,435},size={123,16},disable=2,title="Dataset Attributes"
1690        TitleBox DatasetAttributesTitle,fSize=14,frame=0,fStyle=1
1691
1692        ListBox DatasetAttributesList,pos={341,455},size={459,109}, mode=2, proc=NikaHDF5Browser#ListBoxActionProc
1693        ListBox DatasetAttributesList, widths={175,40,80,120,1000}, userColumnResize= 1 // userColumnResize requires Igor Pro 5.02.
1694        ListBox DatasetAttributesList,fSize=14
1695
1696
1697        Button LoadDataset,pos={415,224},size={100,20},proc=NikaHDF5Browser#LoadDatasetButtonProc,title="Load Dataset"
1698        Button LoadDataset,help={"Loads the currently selected dataset into the current data folder."}
1699
1700        Button SaveWaves,pos={529,224},size={100,20},proc=NikaHDF5Browser#SaveWavesButtonProc,title="Save Waves"
1701        Button SaveWaves,help={"Saves waves in the currently selected group. Available if the current HDF5 file is open for read/write."}
1702
1703        CheckBox Transpose2DDatasets,pos={189,181},size={130,14},title="Transpose 2D Datasets",value=transpose2DDatasets
1704        CheckBox Transpose2DDatasets,proc=HDF5BrowserPrefCheckboxProc,help={"When checked, 2D datasets are transposed so that Igor image plots will match HDFView."}
1705
1706        PopupMenu Members,pos={342,194},size={216,24},title="Members"
1707        PopupMenu Members,mode=1,value= #"\"Load All Members\""
1708        PopupMenu Members proc=HDF5Browser#MembersPopupProc
1709        PopupMenu Members,help={"Choose the compound member to preview or load."}
1710        //Nika stuff
1711//      Wave/O/N=0/T DataSetNames, GroupNames           //these are pointes to waht user wants to get into Igor...
1712        TitleBox NikaDataSetsTitle,pos={801,10},size={62,16},disable=2,title="Selected Datasets"
1713        TitleBox NikaDataSetsTitle,fSize=14,frame=0,fStyle=1
1714
1715        string tempWvName="root:Packages:"+NI2_GetHDF5LoaderLocString()+":GroupNames"
1716        ListBox NikaGroupList,pos={755,35},size={220,200}, mode=2, proc=NikaHDF5Browser#ListBoxActionProc
1717        ListBox NikaGroupList, widths={175,40,80,120,1000}, userColumnResize= 1 // userColumnResize requires Igor Pro 5.02.
1718        ListBox NikaGroupList,fSize=14, listwave=$(tempWvName)
1719
1720        Button AddGrouptoList,pos={600,200},size={150,20},proc=NikaHDF5Browser#LoadDatasetButtonProc,title="Add Group to Nika List"
1721        Button AddGrouptoList,help={"Loads the currently selected Group into the current data folder."}
1722        Button ClearDatasetList,pos={600,200},size={150,20},proc=NikaHDF5Browser#LoadDatasetButtonProc,title="Clear Nika List"
1723        Button ClearDatasetList,help={"Clears Nika list of selected datasets"}
1724
1725
1726        // Load Dataset Options
1727        PopupMenu DisplayInTable,pos={565,123},size={200,24},title="Table:"
1728        PopupMenu DisplayInTable,mode=2,value= #"\"No Table;Display in New Table;Append to Top Table\""
1729        PopupMenu DisplayInGraph,pos={563,154},size={203,24},title="Graph:"
1730        PopupMenu DisplayInGraph,mode=2,value= #"\"No Graph;Display in New Graph;Append to Top Graph\""
1731        GroupBox LoadDatasetOptions,pos={542,100},size={258,87},title="Load Dataset Options"
1732       
1733        NI2_HDF5ResizeBrowser(browserName)              // Needed because we used preferred browser size.
1734       
1735        SetWindow kwTopWin,hook=NI2_HDF5BrowserPanelHook
1736EndMacro
1737
1738
1739static Function/S NI2_GetHDF5LoaderLocString()
1740       
1741        string CurPanelName = WinName(0,64)
1742        if(stringmatch(CurPanelName[0,3],"Nika"))
1743                return "NikaHDF5Loader"
1744        elseif(stringmatch(CurPanelName[0,4],"Irena"))
1745                return "IrenaHDF5Loader"
1746        else
1747                return ""
1748        endif
1749end
1750
1751Function NI2_HDF5BrowserPrefCheckboxProc(ctrlName,checked) : CheckBoxControl
1752        String ctrlName
1753        Variable checked
1754       
1755        STRUCT NI2_HDF5BrowserPrefs prefs
1756       
1757        NI2_HDF5BrowserLoadPackagePrefs(prefs)
1758        strswitch(ctrlName)
1759                case "ReadOnly":
1760                        prefs.readOnly = checked
1761                        break
1762               
1763                case "LoadGroupsRecursively":
1764                        prefs.loadGroupsRecursively = checked
1765                        break
1766               
1767                case "Transpose2DDatasets":
1768                        prefs.transpose2DDatasets = checked
1769                        break
1770                       
1771                case "SaveGroupsRecursively":
1772                        prefs.saveGroupsRecursively = checked
1773                        break
1774                       
1775                case "IncludeIgorAttributes":
1776                        prefs.includeIgorAttributes = checked
1777                        break
1778               
1779        endswitch
1780        NI2_HDF5BrowserSavePackagePrefs(prefs)
1781End
1782
1783Function NI2_CreateNewHDF5Browser(WhereFrom)
1784        string WhereFrom                        //Nika or Irena
1785        if (Exists("HDF5LoadData") != 4)
1786                String message
1787                message = "The HDF5XOP is not activated. Please see the HDF5XOP Help file for instructions."
1788                DoAlert 0, message
1789                DisplayHelpTopic "HDF5XOP"
1790                return -1       
1791        endif
1792
1793        SetDataFolder root:
1794        NewDataFolder/O/S root:Packages
1795        NewDataFolder/O/S $(WhereFrom+"HDF5Loader")
1796        //Init here the Nika part...
1797        make/O/N=0/T DataSetNames, GroupNames           //these are pointes to waht user wants to get into Igor...
1798        String/g BrowserNameFldr
1799               
1800        String browserName = UniqueName(WhereFrom+"HDF5Browser", 9, 0)
1801        BrowserNameFldr = browserName           //thsi is where all teh otehr stuff will be....
1802       
1803        CreateHDF5BrowserGlobals(browserName)
1804       
1805        CreateHDF5BrowserPanel(browserName)
1806
1807        STRUCT NI2HDF5BrowserData bd
1808        SetNI2HDF5BrowserData(browserName, bd)
1809       
1810        NI2_AttachListWaves(bd)
1811        SetButtonStates(bd)
1812End
1813
1814Static Function IsHDF5Browser(name)
1815        String name                     // Name of a window
1816       
1817        if (WinType(name) != 7)
1818                return 0                                // Not a control panel window
1819        endif
1820       
1821        String data = GetUserData(name, "", "HDF5BrowserName")  // HDF5BrowserName property is set by CreateHDF5BrowserPanel
1822        if (CmpStr(data,name) == 0)                     // Is this an HDF5Browser?
1823                return 1
1824        endif
1825       
1826        return 0
1827End
1828
1829Function/S NI2_HDF5GetIndxBrowserName(index)
1830        Variable index
1831       
1832        if (index < 0)
1833                return ""                               // Bad index
1834        endif
1835       
1836        String panelName
1837       
1838        Variable i = 0
1839        do
1840                panelName = WinName(i, 64)
1841                if (strlen(panelName) == 0)
1842                        break
1843                endif
1844               
1845                if (IsHDF5Browser(panelName))           // Is this an HDF5Browser?
1846                        if (index == 0)
1847                                return panelName
1848                        endif
1849                        index -= 1
1850                endif
1851               
1852                i += 1
1853        while(1)
1854       
1855        return ""               // No HDF5 browser with that index
1856End
1857
1858Function/S NI2_HDF5GetTopBrowserName()
1859        String browserName = NI2_HDF5GetIndxBrowserName(0)
1860        return browserName
1861End
1862
1863Function NI2_HDF5AreAnyBrowsersOpen()
1864        String browserName = NI2_HDF5GetIndxBrowserName(0)
1865        if (strlen(browserName) > 0)
1866                return 1
1867        endif
1868        return 0
1869End
1870
1871// FixCloseHDF5FileButtons()
1872// If experiment was saved with an HDF5 file open, it is no longer open but the panel's Open
1873// and Close buttons will be out-of-sync. This fixes that.
1874static Function FixCloseHDF5FileButtons()
1875
1876        STRUCT NI2HDF5BrowserData bd
1877        String browserName
1878       
1879        Variable index = 0
1880        do
1881                browserName = NI2_HDF5GetIndxBrowserName(index)
1882                if (strlen(browserName) == 0)
1883                        break
1884                endif
1885               
1886                SetNI2HDF5BrowserData(browserName, bd)
1887                if (NI2_FileWasUnexpectedlyClosed(bd))
1888                        FileWasClosed(bd)
1889                endif
1890               
1891                index += 1
1892        while(1)
1893End
1894
1895static Function AfterFileOpenHook(refNum,file,pathName,type,creator,kind)
1896        Variable refNum,kind
1897        String file,pathName,type,creator
1898
1899        // DoAlert 0, "AfterFileOpenHook"               // For debugging
1900       
1901        switch (kind)
1902                case 1:                                                                         // Packed experiment
1903                case 2:                                                                         // Unpacked experiment
1904                        FixCloseHDF5FileButtons()               // If experiment was saved with an HDF5 file open, it is no longer open
1905                break
1906        endswitch
1907
1908        return 0
1909End
1910
1911// ************* Start of HDF5 Browser Display Routines ***************
1912
1913static Function WaveRank(w)
1914        Wave w
1915       
1916        Variable dimension
1917        for(dimension=3; dimension>=0; dimension-=1)
1918                if (DimSize(w, dimension) > 0)
1919                        return dimension+1
1920                endif
1921        endfor
1922       
1923        return 0
1924End
1925
1926// *** DISPLAY IN DUMP WINDOW ***
1927
1928Function NI2_HDF5BrowserDumpIsVisible() // Returns true if dump window exists and is visible.
1929                                                                        // Returns false if it does not exist or is invisible.
1930        DoWindow  HDF5DumpNotebook
1931        if (V_flag == 0)
1932                return 0                                        // Does not exist
1933        endif
1934       
1935        String name
1936        Variable index = 0
1937        do
1938                name = WinName(index, 16, 1)
1939                if (strlen(name) == 0)
1940                        return 0                                // Did not find HDF5DumpNotebook among visible notebooks.
1941                endif
1942                if (CmpStr(name, "HDF5DumpNotebook") == 0)
1943                        return 1                                // Found HDF5DumpNotebook among visible notebooks
1944                endif
1945                index += 1
1946        while(1)
1947       
1948        return 0                                                // This will never execute.                                                     
1949End
1950
1951Function NI2_HDF5BrowserDumpHook(infoStr)
1952        String infoStr
1953
1954        String event= StringByKey("EVENT",infoStr)
1955
1956        strswitch(event)
1957                case "activate":                                // We do not get this on Windows when the panel is first created.
1958                        break
1959                       
1960                case "resize":
1961                case "moved":                                   // This message was added in Igor Pro 5.04B07.
1962                        SetPrefWindowCoords("HDF5DumpNotebook")
1963                        break
1964        endswitch
1965       
1966        return 0
1967End
1968
1969Function NI2_HDF5CreateDumpWindow()
1970        DoWindow HDF5DumpNotebook
1971        if (V_flag == 0)
1972                Variable left, top, right, bottom
1973                GetPrefWindowCoords("HDF5DumpNotebook", left, top, right, bottom)
1974                if (right > left)                                                                                       // Were prefs ever set?
1975                        NewNotebook /F=0 /N=HDF5DumpNotebook/K=1 /W=(left, top, right, bottom)
1976                else
1977                        NewNotebook/F=0/N=HDF5DumpNotebook/K=1
1978                endif
1979                SetWindow HDF5DumpNotebook,hook=HDF5BrowserDumpHook
1980                if (NumType(FontSizeHeight("Courier New", 12, 0)) == 0)         // Courier New exists?
1981                        Notebook HDF5DumpNotebook font="Courier New", fSize=12
1982                endif
1983                Notebook HDF5DumpNotebook text="A dump will appear here when you click a dataset.\r"
1984        endif
1985End
1986
1987// CleanupDump(dump)
1988// Removes nulls, converts \n to CR, etc. Dump of NASA strings can contain lots of such "garbage".
1989// For an example, see the attribute /StructMetadata.O_GLOSDS in the NASA sample file MISRAERO.h5.
1990static Function/S CleanupDump(dump)
1991        String dump
1992       
1993        // Convert literal string "\000" to "". Null characters are represented in dump by "\000"
1994        dump = ReplaceString("\\000", dump, "", 1)
1995       
1996        // Convert literal string "\r\n" to CR
1997        dump = ReplaceString("\\r\\n", dump, "\r", 1)
1998       
1999        // Convert literal string "\r" to CR
2000        dump = ReplaceString("\\r", dump, "\r", 1)
2001       
2002        // Convert literal string "\n" to CR
2003        dump = ReplaceString("\\n", dump, "\r", 1)
2004       
2005        // Convert literal string "\t" to tab
2006        dump = ReplaceString("\\t", dump, "\t", 1)
2007       
2008        // Convert CRLF to CR
2009        dump = ReplaceString("\r\n", dump, "\r", 1)
2010       
2011        // Convert LF to CR
2012        dump = ReplaceString("\n", dump, "\r", 1)
2013       
2014        return dump
2015End
2016
2017Function NI2_HDF5DisplayDumpOfSelGroup(bd)
2018        STRUCT NI2HDF5BrowserData &bd
2019       
2020#if Exists("HDF5OpenFile")     
2021        String path = NI2_SelectedGroupPath(bd)
2022        if (strlen(path) == 0)
2023                return -1
2024        endif
2025       
2026        ControlInfo /W=$bd.browserName ShowAttributesInDump
2027        Variable showAttributes = V_value
2028        HDF5Dump /Q /H=1 /ATTR=(showAttributes) /G=path bd.fullPath                                     // This sets S_HDF5Dump.
2029        S_HDF5Dump = CleanupDump(S_HDF5Dump)   
2030       
2031        NI2_HDF5CreateDumpWindow()
2032       
2033        Notebook HDF5DumpNotebook selection={startOfFile, endOfFile}
2034        Notebook HDF5DumpNotebook text=S_HDF5Dump
2035        Notebook HDF5DumpNotebook selection={startOfFile, startOfFile}, findText={"",1}
2036#else
2037        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
2038#endif
2039End
2040
2041static Function DisplayDumpOfSelectedDataset(bd)
2042        STRUCT NI2HDF5BrowserData &bd
2043       
2044#if Exists("HDF5OpenFile")     
2045        String datasetPath = NI2_SelectedDatasetPath(bd)
2046        if (strlen(datasetPath) == 0)
2047                return -1
2048        endif
2049       
2050        ControlInfo /W=$bd.browserName ShowAttributesInDump
2051        Variable showAttributes = V_value
2052        ControlInfo /W=$bd.browserName ShowDataInDump
2053        Variable showData = V_value
2054        HDF5Dump /Q /ATTR=(showAttributes) /H=(!showData) /D=datasetPath bd.fullPath                    // This sets S_HDF5Dump.
2055        S_HDF5Dump = CleanupDump(S_HDF5Dump)   
2056       
2057        NI2_HDF5CreateDumpWindow()
2058       
2059        Notebook HDF5DumpNotebook selection={startOfFile, endOfFile}
2060        Notebook HDF5DumpNotebook text=S_HDF5Dump
2061        Notebook HDF5DumpNotebook selection={startOfFile, startOfFile}, findText={"",1}
2062#else
2063        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
2064#endif
2065End
2066
2067static Function DisplayDumpOfSelectedAttribute(bd, isGroupAttribute)
2068        STRUCT NI2HDF5BrowserData &bd
2069        Variable isGroupAttribute
2070       
2071#if Exists("HDF5OpenFile")     
2072        String path = NI2_SelectedAttributePath(bd, isGroupAttribute)
2073        if (strlen(path) == 0)
2074                return -1
2075        endif
2076       
2077        HDF5Dump /Q /A=path bd.fullPath                                 // This sets S_HDF5Dump.
2078        S_HDF5Dump = CleanupDump(S_HDF5Dump)   
2079       
2080        NI2_HDF5CreateDumpWindow()
2081       
2082        Notebook HDF5DumpNotebook selection={startOfFile, endOfFile}
2083        Notebook HDF5DumpNotebook text=S_HDF5Dump
2084        Notebook HDF5DumpNotebook selection={startOfFile, startOfFile}, findText={"",1}
2085#else
2086        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
2087#endif
2088End
2089
2090// *** DISPLAY IN GRAPH ***
2091
2092Function NI2_HDF5BrowserGraphIsVisible()                // Returns true if dump window exists and is visible.
2093                                                                                // Returns false if it does not exist or is invisible.
2094        DoWindow  HDF5BrowserGraph
2095        if (V_flag == 0)
2096                return 0                                        // Does not exist
2097        endif
2098       
2099        // Graphs are always visible so we don't need to check that.
2100       
2101        return 1                                                // This will never execute.                                                     
2102End
2103
2104Function NI2_HDF5BrowserGraphHook(infoStr)
2105        String infoStr
2106
2107        String event= StringByKey("EVENT",infoStr)
2108
2109        strswitch(event)
2110                case "activate":                                // We do not get this on Windows when the panel is first created.
2111                        break
2112                       
2113                case "resize":
2114                case "moved":                                   // This message was added in Igor Pro 5.04B07.
2115                        SetPrefWindowCoords("HDF5BrowserGraph")
2116                        break
2117        endswitch
2118       
2119        return 0
2120End
2121
2122Function NI2_HDF5CreateBrowserGraph()
2123        DoWindow HDF5BrowserGraph
2124        if (V_flag == 0)
2125                Variable left, top, right, bottom
2126                GetPrefWindowCoords("HDF5BrowserGraph", left, top, right, bottom)
2127                if (right > left)                                                                       // Were prefs ever set?
2128                        Display /K=1 /W=(left, top, right, bottom)
2129                else
2130                        Display /K=1
2131                endif
2132                DoWindow/C HDF5BrowserGraph
2133                SetWindow HDF5BrowserGraph,hook=HDF5BrowserGraphHook
2134        endif
2135End
2136
2137static Function SetImageLayer(ctrlName,varNum,varStr,varName) : SetVariableControl
2138        String ctrlName
2139        Variable varNum
2140        String varStr
2141        String varName
2142
2143        NVAR imageLayer = root:Packages:HDF5Browser:imageLayer
2144
2145        ModifyImage /W=HDF5BrowserGraph BrowserWave, plane=varNum
2146End
2147
2148static Function DisplayGraphOfSelectedData(bd, isAttribute, objectType, listOfWavesLoaded)              // The data is already loaded into waves specified by listOfWavesLoaded
2149        STRUCT NI2HDF5BrowserData &bd
2150        Variable isAttribute
2151        Variable objectType                             // Host object type of attribute. 1=group, 2=dataset
2152        String listOfWavesLoaded
2153
2154        NI2_HDF5CreateBrowserGraph()
2155
2156        String firstWaveLoaded = StringFromList(0, listOfWavesLoaded)
2157        Wave browserWave = root:Packages:HDF5Browser:$firstWaveLoaded
2158        Variable newDataIsText = WaveType(browserWave) == 0
2159
2160        Variable oldRank = 0, newRank = 0
2161        if (strlen(TraceNameList("HDF5BrowserGraph", ";", 1)) > 0)
2162                oldRank = 1                                     
2163        endif
2164        if (strlen(ImageNameList("HDF5BrowserGraph", ";")) > 0)
2165                oldRank = 2
2166        endif
2167        newRank = WaveRank(browserWave) // Will be zero for zero-point wave
2168
2169        String savedDataFolder = NI2_SetBrowserDataFolder("")                                   // browserWave goes in master HDF5Browser data folder
2170
2171        Variable displayedDimensionalityChanged = (oldRank <= 1) != (newRank <= 1)      // Switching between 1D and >1D ?
2172        Variable index, browserWaveIsDisplayed
2173        String waveLoaded, nameOfGraphWave
2174       
2175        // Remove any waves in graph not in listOfWavesLoaded or all waves if dimensionality changed
2176        index = 0
2177        do
2178                if (oldRank == 1)
2179                        Wave/Z graphWave = WaveRefIndexed("HDF5BrowserGraph", index, 1)
2180                        if (!WaveExists(graphWave))
2181                                break
2182                        endif
2183                        nameOfGraphWave = NameOfWave(graphWave)
2184                else
2185                        nameOfGraphWave = StringFromList(index, ImageNameList("HDF5BrowserGraph", ";" ))
2186                        if (strlen(nameOfGraphWave) == 0)
2187                                break
2188                        endif
2189                endif
2190               
2191                Variable waveIsInListOfLoadedWaves = WhichListItem(nameOfGraphWave, listOfWavesLoaded) >= 0
2192                if (displayedDimensionalityChanged || !waveIsInListOfLoadedWaves)
2193                        if (oldRank == 1)
2194                                RemoveFromGraph /W=HDF5BrowserGraph $nameOfGraphWave
2195                        endif
2196                        if (oldRank > 1)
2197                                RemoveImage /W=HDF5BrowserGraph $nameOfGraphWave
2198                        endif
2199                        index -= 1
2200                endif
2201                if (!waveIsInListOfLoadedWaves)
2202                        KillWaves/Z $nameOfGraphWave
2203                endif
2204               
2205                index += 1
2206        while(1)
2207
2208        // Append any waves to graph in listOfWavesLoaded
2209        index = 0
2210        do
2211                waveLoaded = StringFromList(index, listOfWavesLoaded)
2212                if (strlen(waveLoaded) == 0)
2213                        break
2214                endif
2215                Wave browserWave = root:Packages:HDF5Browser:$waveLoaded
2216                Variable browserWaveType = WaveType(browserWave)
2217                nameOfGraphWave = waveLoaded
2218
2219                CheckDisplayed /W=HDF5BrowserGraph browserWave
2220                browserWaveIsDisplayed = V_flag
2221               
2222                if (!newDataIsText)
2223                        if (browserWaveType != 0)                               // When loading compound data we can get a mix of numeric and non-numeric.
2224                                if (browserWaveIsDisplayed == 0)
2225                                        if (newRank <= 1)
2226                                                AppendToGraph /W=HDF5BrowserGraph browserWave
2227                                                if (displayedDimensionalityChanged)
2228                                                        SetAxis/A left
2229                                                endif
2230                                        else
2231                                                AppendImage /W=HDF5BrowserGraph browserWave
2232                                                if (displayedDimensionalityChanged)
2233                                                        SetAxis/A/R left                                                                        // Reverse left axis like NewImage does.
2234                                                endif
2235                                        endif
2236                                endif
2237       
2238                                if (newRank >= 2)
2239                                        NVAR formalImageType = root:Packages:HDF5Browser:formalImageType
2240                                        switch (formalImageType)                                                        // browserPalette would be created by HDF5LoadImage
2241                                                case 0:                                                                                         // Not a formal image.
2242                                                case 1:                                                                                         // No palette wave loaded.
2243                                                        ModifyImage /W=HDF5BrowserGraph $nameOfGraphWave ctab= {*,*,Grays,0}
2244                                                        break
2245                                                       
2246                                                case 2:                                                                                         // Palette wave was loaded.
2247                                                        Wave browserPalette = root:Packages:HDF5Browser:browserPalette
2248                                                        ModifyImage /W=HDF5BrowserGraph $nameOfGraphWave, cindex=browserPalette
2249                                                        break
2250                                        endswitch
2251                                endif
2252                        endif
2253                endif
2254
2255                if (newDataIsText)                                              // Display a snippet of the text wave.
2256                        if (browserWaveType == 0)               // When loading compound data we can get a mix of numeric and non-numeric.
2257                                String text
2258                                Wave/T w = root:Packages:HDF5Browser:$nameOfGraphWave
2259                                if (numpnts(w) > 0)
2260                                        text = CleanupDump(w[0])
2261                                else
2262                                        text = ""
2263                                endif
2264                                if ( (strlen(text) > 256) || (numpnts(w)>1) )
2265                                        text = "A snippet of the text:\r\r" + text[0,255]                       
2266                                endif                   
2267                                TextBox/C/N=browserTextbox/W=HDF5BrowserGraph/A=LT text
2268                        endif
2269                else
2270                        TextBox/K/N=browserTextbox/W=HDF5BrowserGraph
2271                endif
2272               
2273                index += 1
2274        while(1)
2275       
2276        // Show Image Layer control if displaying a stack of images
2277        Variable numDims = WaveDims(browserWave)
2278        Variable isStack = 0
2279        if (!newDataIsText && newRank>2)
2280                if (DimSize(browserWave,2) == 3)
2281                        // Igor assumes that this is an RGB wave using direct color.
2282                        if (numDims > 3)
2283                                isStack = 1                     // This is a stack of RGB images.
2284                        endif
2285                else
2286                        isStack = 1                     // This is a stack of indexed color images.
2287                endif
2288        endif
2289       
2290        if (isStack)
2291                Variable/G root:Packages:HDF5Browser:imageLayer = 0
2292                Variable dim, numLayers
2293               
2294                numLayers = 1
2295                for(dim=2; dim<numDims; dim+=1)
2296                        numLayers *= DimSize(browserWave, dim)
2297                endfor
2298               
2299                ControlBar/W=HDF5BrowserGraph 25
2300                SetVariable ImageLayer win=HDF5BrowserGraph, title="Layer",size={100,20},format="%d"
2301                SetVariable ImageLayer win=HDF5BrowserGraph, proc=HDF5Browser#SetImageLayer
2302                SetVariable ImageLayer win=HDF5BrowserGraph, value=imageLayer, limits={0,numLayers-1,1}
2303        else
2304                KillControl/W=HDF5BrowserGraph ImageLayer
2305                ControlBar/W=HDF5BrowserGraph 0
2306        endif
2307               
2308        String title = "HDF5 Preview - "
2309        if (isAttribute)
2310                title += NI2_SelectedAttributeName(bd, objectType==1)
2311        else
2312                title += NI2_SelectedDatasetName(bd)
2313        endif
2314        title = title[0,39]                             // Unfortunately titles are limited to 40 characters.
2315        DoWindow /T HDF5BrowserGraph, title
2316       
2317        SetDataFolder savedDataFolder
2318End
2319
2320// *** DISPLAY IN TABLE ***
2321
2322Function NI2_HDF5BrowserTableIsVisible()                // Returns true if dump window exists and is visible.
2323                                                                                // Returns false if it does not exist or is invisible.
2324        DoWindow  HDF5BrowserTable
2325        if (V_flag == 0)
2326                return 0                                        // Does not exist
2327        endif
2328       
2329        // Tables are always visible so we don't need to check that.
2330       
2331        return 1                                                // This will never execute.                                                     
2332End
2333
2334Function NI2_HDF5BrowserTableHook(infoStr)
2335        String infoStr
2336
2337        String event= StringByKey("EVENT",infoStr)
2338
2339        strswitch(event)
2340                case "activate":                                // We do not get this on Windows when the panel is first created.
2341                        break
2342                       
2343                case "resize":
2344                case "moved":                                   // This message was added in Igor Pro 5.04B07.
2345                        SetPrefWindowCoords("HDF5BrowserTable")
2346                        break
2347        endswitch
2348       
2349        return 0
2350End
2351
2352Function NI2_HDF5CreateBrowserTable()
2353        DoWindow HDF5BrowserTable
2354        if (V_flag == 0)
2355                Variable left, top, right, bottom
2356                GetPrefWindowCoords("HDF5BrowserTable", left, top, right, bottom)
2357                if (right > left)                                                                       // Were prefs ever set?
2358                        Edit /K=1 /W=(left, top, right, bottom)
2359                else
2360                        Edit /K=1
2361                endif
2362                DoWindow/C HDF5BrowserTable
2363                SetWindow HDF5BrowserTable,hook=HDF5BrowserTableHook
2364        endif
2365End
2366
2367static Function DisplayTableOfSelectedData(bd, isAttribute, objectType, listOfWavesLoaded)              // The data is already loaded into waves listed by listOfWavesLoaded
2368        STRUCT NI2HDF5BrowserData &bd
2369        Variable isAttribute
2370        Variable objectType                             // Host object type of attribute. 1=group, 2=dataset
2371        String listOfWavesLoaded
2372       
2373        NI2_HDF5CreateBrowserTable()
2374
2375        String waveLoaded
2376        Variable index
2377       
2378        // Remove any waves in table not in listOfWavesLoaded
2379        index = 0
2380        do
2381                Wave/Z tableWave = WaveRefIndexed("HDF5BrowserTable", index, 3)
2382                if (!WaveExists(tableWave))
2383                        break
2384                endif
2385               
2386                String nameOfTableWave = NameOfWave(tableWave)
2387                if (WhichListItem(nameOfTableWave, listOfWavesLoaded) < 0)
2388                        RemoveFromTable /W=HDF5BrowserTable tableWave
2389                        KillWaves/Z tableWave
2390                        index -= 1
2391                endif
2392               
2393                index += 1
2394        while(1)
2395
2396        // Append any waves to table in listOfWavesLoaded
2397        index = 0
2398        do
2399                waveLoaded = StringFromList(index, listOfWavesLoaded)
2400                if (strlen(waveLoaded) == 0)
2401                        break
2402                endif
2403                Wave browserWave = root:Packages:HDF5Browser:$waveLoaded
2404       
2405                CheckDisplayed /W=HDF5BrowserTable browserWave
2406                if (V_flag == 0)
2407                        AppendToTable /W=HDF5BrowserTable browserWave
2408                endif
2409               
2410                index += 1
2411        while(1)
2412
2413        String title = "HDF5 Preview - "
2414        if (isAttribute)
2415                title += NI2_SelectedAttributeName(bd, objectType==1)
2416        else
2417                title += NI2_SelectedDatasetName(bd)
2418        endif
2419        title = title[0,39]                             // Unfortunately titles are limited to 40 characters.
2420        DoWindow /T HDF5BrowserTable, title
2421End
2422
2423static Function RemoveFromGraphAndTable(w)
2424        Wave w
2425       
2426        String name = NameOfWave(w)
2427       
2428        if (NI2_HDF5BrowserGraphIsVisible())
2429                CheckDisplayed /W=HDF5BrowserGraph w
2430                if (V_flag != 0)
2431                        Variable isImage = strlen(ImageNameList("HDF5BrowserGraph", ";")) > 0
2432                        if (isImage)
2433                                RemoveImage /W=HDF5BrowserGraph $name
2434                        else
2435                                RemoveFromGraph /W=HDF5BrowserGraph $name
2436                        endif
2437                endif
2438        endif
2439       
2440        if (NI2_HDF5BrowserTableIsVisible())
2441                CheckDisplayed /W=HDF5BrowserTable w
2442                if (V_flag != 0)
2443                        RemoveFromTable /W=HDF5BrowserTable w
2444                endif
2445        endif
2446End
2447
2448//      KillConflictingBrowserWaves(new_class_str, enumMode)
2449//      "Conflicting" means that a wave is text and we are going
2450// to use the same name to load a numeric wave or vice versa.
2451// This function removes conflicting waves from the browser graph
2452// and table and then kills them.
2453static Function KillConflictingBrowserWaves(new_class_str, enumMode)
2454        String new_class_str                            // Class of data we are about to load.
2455        Variable enumMode
2456
2457        String browserDF = "root:Packages:HDF5Browser"
2458        Variable numWaves = CountObjects(browserDF, 1)
2459        Variable i
2460
2461        for(i=0; i<numWaves; i+=1)
2462                String name = GetIndexedObjName(browserDF, 1, i)
2463                if (strlen(name)>=11 && CmpStr(name[0,11],"browserWave")==0)    // Is this a browser display wave?
2464                        Wave w = $(browserDF + ":" + name)
2465
2466                        Variable oldDataIsText
2467                        oldDataIsText = WaveType(w) == 0
2468       
2469                        Variable newDataIsText
2470                       
2471                        newDataIsText = 0
2472                        strswitch(new_class_str)
2473                                case "H5T_STRING":
2474                                case "H5T_REFERENCE":
2475                                        newDataIsText = 1
2476                                        break
2477                                       
2478                                case "H5T_ENUM":
2479                                        if (enumMode == 1)
2480                                                newDataIsText = 1
2481                                        endif
2482                                        break
2483                                       
2484                                case "H5T_COMPOUND":
2485                                        // If we are loading all members of a compound dataset
2486                                        // at this point we need to know the class of the compound
2487                                        // member corresponding to the current wave. However, this
2488                                        // is very complicated to do and I decided not to attempt it.
2489                                        // The result is that, if we have an existing brower_<member>
2490                                        // wave whose type is string and we try to load a numeric wave
2491                                        // with the same name, HDF5LoadWave will get a "Can't overwrite
2492                                        // text with numeric and vice versa" error.
2493                                        break
2494                        endswitch
2495                       
2496                        if (newDataIsText != oldDataIsText)
2497                                RemoveFromGraphAndTable(w)
2498                                KillWaves w
2499                                // Printf "Killed %s\r", name
2500                        endif
2501                endif
2502        endfor
2503End
2504
2505static Function LoadSelectedDataForDisplay(bd, isAttribute, objectType, listOfWavesLoaded, errorMessage)
2506        STRUCT NI2HDF5BrowserData &bd
2507        Variable isAttribute
2508        Variable objectType                                     // Host object type of attribute. 1=group, 2=dataset
2509        String &listOfWavesLoaded               // Output: List of waves loaded.
2510        String &errorMessage                            // Output: Error message or ""
2511       
2512#if Exists("HDF5OpenFile")     
2513        Variable err = 0
2514        errorMessage = ""
2515
2516        Wave/Z browserWave = root:Packages:HDF5Browser:browserWave
2517       
2518        String path
2519        if (isAttribute)
2520                if (objectType == 1)
2521                        path = NI2_SelectedGroupPath(bd)
2522                else
2523                        path = NI2_SelectedDatasetPath(bd)
2524                endif
2525        else
2526                path = NI2_SelectedDatasetPath(bd)
2527        endif
2528        if (strlen(path) == 0)
2529                return -1
2530        endif
2531       
2532        String attributeName = ""
2533        if (isAttribute)
2534                attributeName = NI2_SelectedAttributeName(bd, objectType==1)
2535                if (strlen(attributeName) == 0)
2536                        return -1
2537                endif
2538        endif
2539
2540        STRUCT NI2_HDF5DataInfo di
2541        NI2_InitHDF5DataInfo(di)
2542        if (isAttribute)
2543                HDF5AttributeInfo(bd.fileID, path, objectType, attributeName, 0, di)
2544        else
2545                HDF5DatasetInfo(bd.fileID, path, 0, di)
2546        endif
2547       
2548        String datatype_class_str = di.datatype_class_str
2549
2550        STRUCT NI2_HDF5DatatypeInfo dti
2551        NI2_InitHDF5DatatypeInfo(dti)                                   // Sets input fields.
2552
2553        Variable isCompound = 0
2554        Variable compMode = 0
2555        String memberName = ""
2556        if (!isAttribute)                       // We support viewing members only for datasets, not for attributes.
2557                NI2_HDF5GetCompLoadInfo(bd, isCompound, compMode, memberName)
2558        endif
2559       
2560        if (compMode != 0)                      // Are we loading a member of a compound datatype?
2561                err = HDF5TypeInfo(bd.fileID, path, "", memberName, 1, dti)
2562                if (err != 0)
2563                        return err
2564                endif
2565                datatype_class_str = dti.type_class_str
2566        else
2567                // If array, we need to know about the base datatype of the array
2568                if (CmpStr(datatype_class_str, "H5T_ARRAY") == 0)
2569                        if (isAttribute)
2570                                HDF5AttributeInfo(bd.fileID, path, objectType, attributeName, 2, di)            // 2 means get info on base datatype of array
2571                        else
2572                                HDF5DatasetInfo(bd.fileID, path, 2, di) // 2 means get info on base datatype of array
2573                        endif
2574                endif
2575                datatype_class_str = di.datatype_class_str
2576        endif
2577
2578        err = NI2_HDF5CheckDataClass(datatype_class_str, errorMessage)
2579        if (err != 0)
2580                if (WaveExists(browserWave))
2581                        Redimension/N=(0) browserWave           // Don't leave browserWave around which a user might
2582                endif                                                                                           // think goes with the selected item in the control panel
2583                return err
2584        endif
2585       
2586        String savedDataFolder = NI2_SetBrowserDataFolder("")                                   // tempClassAttribute goes in master HDF5Browser data folder
2587       
2588        // If isFormalImage is true, we are loading an image written
2589        // according to the HDF5 Image and Palette Specification.
2590        Variable isFormalImage = 0
2591        if (!isAttribute && !isCompound)
2592                HDF5LoadData /Z /O /N=tempClassAttribute /A="CLASS" /Q /VAR=1 bd.fileID, path
2593                if (V_flag == 0)
2594                        WAVE/T tempClassAttribute                       // HDF5LoadData will have created this string
2595                        if (CmpStr(tempClassAttribute[0],"IMAGE") == 0)
2596                                isFormalImage = 1
2597                        endif   
2598                        KillWaves/Z tempClassAttribute
2599                endif
2600        endif
2601
2602        SetDataFolder savedDataFolder
2603       
2604        Variable enumMode = 1                                           // 0: Load enum into numeric wave; 1: load enum into text wave.
2605       
2606        // If browserWave exists and we are switching from a text wave to a numeric wave
2607        // or vice-versa then we must remove browserWave from the graph and table and
2608        // kill it. Otherwise we will get an error when HDF5LoadData tries to overwrite
2609        // a text wave with numeric or vice versa.
2610        KillConflictingBrowserWaves(datatype_class_str, enumMode)       // Also removes them from graph and table.
2611       
2612        savedDataFolder = NI2_SetBrowserDataFolder("")                                  // browser waves go in master HDF5Browser data folder
2613
2614        String slabWaveStr = ""
2615        ControlInfo /W=$bd.browserName UseHyperSelection
2616        if (V_value)                                                            // Use Hyperselection is checked?
2617                slabWaveStr = bd.hyperSelectionWavePath
2618        endif
2619        WAVE/Z slabWave = $slabWaveStr                  // It is OK if wave does not exist and slabWave is NULL. HDF5LoadData will simply ignore /SLAB.
2620
2621        if (isFormalImage)
2622                String browserPaletteName = "browserPalette"
2623                HDF5LoadImage /O /N=browserWave /PALN=browserPaletteName /Q bd.fileID, path
2624                Variable/G formalImageType = 1          // Means formal image with no palette
2625                listOfWavesLoaded = StringFromList(0, S_waveNames)      // We count only the image as loaded, not the palette.
2626                if (WhichListItem(browserPaletteName, S_waveNames) >= 0)
2627                        formalImageType = 2                                     // Means formal image with palette
2628                endif
2629        else
2630                KillWaves/Z browserPalette
2631
2632                Variable transpose2D = NI2_HDF5GetTranspose2DSetting(bd.browserName)
2633
2634                // Note that when loading all members of a compound dataset this
2635                // will create a family of waves named browserWave_<member>.
2636                // Also when loading a VLEN dataset it will create a family of
2637                // waves named browserWave<digit>. Otherwise it just creates
2638                // one wave named browserWave.
2639                HDF5LoadData /O /IGOR=-1 /N=browserWave /COMP={compMode,memberName} /TRAN=(transpose2D) /A=attributeName /TYPE=(objectType) /ENUM=(enumMode) /SLAB=slabWave /Q /VAR=0 bd.fileID, path
2640
2641                Variable/G formalImageType = 0          // Not a formal image
2642                listOfWavesLoaded = S_waveNames
2643        endif
2644       
2645        errorMessage = GetRTErrMessage()
2646        err = GetRTError(1)
2647
2648        SetDataFolder savedDataFolder
2649        return err
2650#else
2651        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
2652#endif
2653End
2654
2655Function NI2_HDF5DisplaySelectedDataset(bd)
2656        STRUCT NI2HDF5BrowserData &bd
2657
2658        if (NI2_HDF5BrowserDumpIsVisible())
2659                DisplayDumpOfSelectedDataset(bd)
2660        endif
2661
2662        String listOfWavesLoaded = ""   
2663       
2664        Variable needToLoadData = NI2_HDF5BrowserGraphIsVisible() || NI2_HDF5BrowserTableIsVisible()
2665        if (needToLoadData)
2666                String errorMessage
2667                if (LoadSelectedDataForDisplay(bd, 0, 2, listOfWavesLoaded, errorMessage) != 0)
2668                        DoAlert 0, errorMessage
2669                        return -1
2670                endif
2671        endif
2672
2673        if (NI2_HDF5BrowserGraphIsVisible())
2674                DisplayGraphOfSelectedData(bd, 0, 1, listOfWavesLoaded)
2675        endif
2676
2677        if (NI2_HDF5BrowserTableIsVisible())
2678                DisplayTableOfSelectedData(bd, 0, 1, listOfWavesLoaded)
2679        endif
2680End
2681
2682Function NI2_HDF5DisplaySelAttribute(bd,  isGroupAttribute)
2683        STRUCT NI2HDF5BrowserData &bd
2684        Variable isGroupAttribute
2685       
2686        if (NI2_HDF5BrowserDumpIsVisible())
2687                DisplayDumpOfSelectedAttribute(bd, isGroupAttribute)
2688        endif
2689       
2690        Variable objectType = isGroupAttribute ? 1:2
2691       
2692        String listOfWavesLoaded = ""
2693       
2694        Variable needToLoadData = NI2_HDF5BrowserGraphIsVisible() || NI2_HDF5BrowserTableIsVisible()
2695        if (needToLoadData)
2696                String errorMessage
2697                if (LoadSelectedDataForDisplay(bd, 1, objectType, listOfWavesLoaded, errorMessage))
2698                        DoAlert 0, errorMessage
2699                        return -1
2700                endif
2701        endif
2702
2703        if (NI2_HDF5BrowserGraphIsVisible())
2704                DisplayGraphOfSelectedData(bd, 1, objectType, listOfWavesLoaded)
2705        endif
2706
2707        if (NI2_HDF5BrowserTableIsVisible())
2708                DisplayTableOfSelectedData(bd, 1, objectType, listOfWavesLoaded)
2709        endif
2710End
2711
2712// ************* End of HDF5 Browser Display Routines ***************
2713
2714// ************* Start of HDF5 Browser Resize Routines ***************
2715
2716static Function MinWindowSize(winName,minwidth,minheight)
2717        String winName
2718        Variable minwidth,minheight
2719
2720        GetWindow $winName wsize
2721        Variable width= max(V_right-V_left,minwidth)
2722        Variable height= max(V_bottom-V_top,minheight)
2723        MoveWindow/W=$winName V_left, V_top, V_left+width, V_top+height
2724End
2725
2726Function NI2_PositionControlRelative(panelName, control, masterControl, xMode, yMode, dx, dy)
2727        String panelName
2728        String control, masterControl           // Positions control relative to masterControl.
2729        Variable xMode          // 0 = relative to master left, 1 = relative to master right, 2 = do not set x position.
2730        Variable yMode          // 0 = relative to master top, 1 = relative to master bottom, 2 = do not set y position.
2731        Variable dx, dy
2732       
2733        ControlInfo/W=$panelName $masterControl
2734        Variable masterLeft = V_left, masterTop = V_top
2735        Variable masterRight = masterLeft+V_width, masterBottom=masterTop+V_height
2736       
2737        ControlInfo/W=$panelName $control
2738        Variable controlLeft = V_left, controlTop = V_top
2739       
2740        Variable left, top
2741       
2742        switch(xMode)
2743                case 0:
2744                        left = masterLeft + dx
2745                        break
2746       
2747                case 1:
2748                        left = masterRight + dx
2749                        break
2750               
2751                case 2:
2752                        left = controlLeft
2753                        break
2754        endswitch
2755       
2756        switch(yMode)
2757                case 0:
2758                        top = masterTop + dy
2759                        break
2760       
2761                case 1:
2762                        top = masterBottom + dy
2763                        break
2764               
2765                case 2:
2766                        top = controlTop
2767                        break
2768        endswitch
2769
2770        ModifyControl $control, win=$panelName, pos={left, top}
2771End
2772
2773Function NI2_OffsetControls(panelName, controlList, dx, dy)
2774        String panelName
2775        String controlList      // Semicolon-separated list of control names
2776        Variable dx, dy
2777       
2778        String name
2779        Variable index = 0
2780        do
2781                name = StringFromList(index, controlList)
2782                if (strlen(name) == 0)
2783                        break
2784                endif
2785               
2786                ControlInfo/W=$panelName $name
2787                ModifyControl $name, win=$panelName, pos={V_left+dx,V_top+dy}
2788
2789                index += 1
2790        while(1)
2791End
2792
2793Function NI2_HDF5ResizeBrowser(browserName)
2794        String browserName
2795
2796        Variable statusCode= 0
2797
2798        String win = browserName
2799
2800        GetWindow $browserName wsizeDC
2801        Variable winLeft=V_left, winTop=V_top, winRight=V_right, winBottom=V_bottom
2802        Variable winWidth = winRight - winLeft, winHeight = winBottom - winTop
2803
2804        if (winWidth<800 || winHeight<500)
2805                return 0                                                // Too small.
2806        endif
2807       
2808        // Set preferred browser window size. We would like to also do this
2809        // when the browser is moved without resizing but we get no message
2810        // from Igor when a window is moved.
2811        SetPrefWindowCoords(browserName)
2812       
2813        Variable leftBorder=15, hSpaceBetweenLists=20, rightBorder=15
2814        Variable listsTop, vSpaceBetweenLists = 30, bottomBorder = 10
2815       
2816        ControlInfo/W=$browserName GroupsList
2817        listsTop = V_top
2818       
2819        Variable hSpaceForLists = winRight - winLeft - leftBorder - hSpaceBetweenLists - rightBorder
2820        Variable vSpaceForLists = winBottom - listsTop - vSpaceBetweenLists - bottomBorder
2821       
2822        Variable groupListsWidth = .3 * hSpaceForLists
2823        Variable datasetListsWidth = .45 * hSpaceForLists
2824        Variable NikaListsWidth = .23 * hSpaceForLists
2825       
2826        Variable groupListsHeight = .65 * vSpaceForLists
2827        Variable attributeListsHeight = .35 * vSpaceForLists
2828       
2829        Variable left, top
2830       
2831        // Set Groups list coordinates
2832        left = leftBorder
2833        top = listsTop
2834        ListBox GroupsList, win=$browserName, pos={left, top}, size={groupListsWidth, groupListsHeight}
2835       
2836        // Set Group Attributes list coordinates
2837        left = leftBorder
2838        top = listsTop + groupListsHeight + vSpaceBetweenLists
2839        ListBox GroupAttributesList, win=$browserName, pos={left, top}, size={groupListsWidth, attributeListsHeight}
2840       
2841        top -= 20
2842        TitleBox GroupAttributesTitle, win=$browserName, pos={left, top}
2843
2844        // Remember where DatasetsList is. it is used to position other control.
2845        ControlInfo/W=$browserName DatasetsList
2846        Variable oldDatasetsListRight = V_Left + V_Width
2847       
2848        // Set Datasets list coordinates
2849        left = leftBorder + groupListsWidth + hSpaceBetweenLists
2850        top = listsTop
2851        ListBox DatasetsList, win=$browserName, pos={left, top}, size={datasetListsWidth, groupListsHeight}
2852       
2853        // Determine how DatasetsList right edge changed. This is used to position other control.
2854        ControlInfo/W=$browserName DatasetsList
2855        Variable changeInDatsetsListRight = (V_Left + V_Width) - oldDatasetsListRight
2856       
2857        // Set Datasets Title
2858        top -= 20
2859        TitleBox DatasetsTitle, win=$browserName, pos={left, top}
2860       
2861        // Set Load Dataset Button
2862        NI2_PositionControlRelative(browserName, "LoadDataset", "DatasetsTitle", 1, 2, 20, 0)
2863//      NI2_PositionControlRelative(browserName, "AddGrouptoList", "DatasetsTitle", 1, 2, 0, -20)
2864
2865
2866        // Set Save Waves Button
2867        NI2_PositionControlRelative(browserName, "SaveWaves", "LoadDataset", 1, 2, 20, 0)
2868
2869        // Set Members popup menu
2870        NI2_PositionControlRelative(browserName, "Members", "DatasetsTitle", 0, 2, 0, 0)
2871       
2872        // Set Dataset Attributes list coordinates
2873        left = leftBorder + groupListsWidth + hSpaceBetweenLists
2874        top = listsTop + groupListsHeight + vSpaceBetweenLists
2875        ListBox DatasetAttributesList, win=$browserName, pos={left, top}, size={datasetListsWidth, attributeListsHeight}
2876       
2877        top -= 20
2878        TitleBox DatasetAttributesTitle, win=$browserName, pos={left, top}
2879
2880        // Set Preview Options
2881        String list = "PreviewOptions;Graph;Table;Dump;ShowAttributesInDump;ShowDataInDump;"
2882        NI2_OffsetControls(browserName, list, changeInDatsetsListRight, 0)
2883
2884        // Set Load Dataset Options
2885        list = "LoadDatasetOptions;DisplayInTable;DisplayInGraph;"
2886        NI2_OffsetControls(browserName, list, changeInDatsetsListRight, 0)
2887
2888        //Set Nika controls
2889        // Set Datasets list coordinates
2890        left = leftBorder + groupListsWidth + hSpaceBetweenLists+datasetListsWidth+hSpaceBetweenLists
2891        top = listsTop
2892
2893        ListBox NikaGroupList, win=$browserName, pos={left, top}, size={NikaListsWidth, groupListsHeight}
2894        // Set Load Dataset Button
2895        NI2_PositionControlRelative(browserName, "NikaDataSetsTitle", "NikaGroupList", 0, 0, 30, -20)
2896        Button AddGrouptoList,win=$browserName, pos={left, top-150}, size={NikaListsWidth, 20}
2897        Button ClearDatasetList,win=$browserName, pos={left, top-100}, size={NikaListsWidth, 20}
2898
2899//      NI2_PositionControlRelative(browserName, "NikaDataSetsTitle", "NikaGroupList", 0, 0, 30, -20)
2900
2901//      Wave/O/N=0/T DataSetNames, GroupNames           //these are pointes to waht user wants to get into Igor...
2902//      TitleBox NikaDataSetsTitle,pos={801,10},size={62,16},disable=2,title="Selected Datasets"
2903       
2904       
2905        statusCode=1
2906
2907        return statusCode       // 0 if nothing done, else 1 or 2
2908End
2909
2910// ************* End of HDF5 Browser Resize Routines ***************
2911
2912// ************* Start of HDF5 Browser Prefs Routines ***************
2913
2914// HDF5 Browser preferences are stored on disk in the Packages directory
2915// in Igor's preferences directory. They are temporarily loaded into an
2916// HDF5BrowserPrefs data structure but are not stored permanently in memory.
2917
2918// When a preference value has to be changed, the prefs data structure
2919// is loaded into memory, the value is changed and the data structure is
2920// immediately written back out to disk.
2921// To see where prefs data is changed, search for HDF5BrowserSavePackagePrefs.
2922
2923static StrConstant kPackageName = "NikaHDF5Browser"     // NOTE: Package name must be distinctive!
2924static Constant kCurrentPrefsVersion = 100                      // Changes to hundreds digit means incompatible prefs
2925static StrConstant kPrefFileName = "Preferences.bin"
2926static Constant kPrefRecordID = 0
2927
2928Structure NI2_HDF5BrowserPrefs
2929        uint32 prefsVersion                     // Preferences structure version number. 100 means 1.00.
2930
2931        // Preview graph location in points. 0 means default.
2932        float graphLeft
2933        float graphTop
2934        float graphRight
2935        float graphBottom
2936               
2937        // Preview table location in points. 0 means default.
2938        float tableLeft
2939        float tableTop
2940        float tableRight
2941        float tableBottom
2942               
2943        // Dump notebook location in points. 0 means default.
2944        float dumpLeft
2945        float dumpTop
2946        float dumpRight
2947        float dumpBottom
2948                       
2949        // Save Waves and Save Data Folder panel location in pixels. 0 means default.
2950        float savePanelLeft
2951        float savePanelTop
2952        float savePanelRight
2953        float savePanelBottom
2954
2955        // HDF5 browser location in points. 0 means default.
2956        float browserLeft
2957        float browserTop
2958        float browserRight
2959        float browserBottom
2960
2961        // Overall prefs
2962        uchar readOnly                                          // Open file read only
2963        uchar reservedOverall[15]
2964       
2965        // Load prefs
2966        uchar loadGroupsRecursively             // Controls Load Group button
2967        uchar transpose2DDatasets               // Controls Load Dataset and Load Group buttons
2968        uchar reservedLoad[14]
2969       
2970        // Save prefs
2971        uchar saveGroupsRecursively             // Affects Save Data Folder
2972        uchar includeIgorAttributes             // Affects Save Waves and Save Data Folder
2973        uchar reservedSave[14]
2974
2975        uint32 reserved[100]    // Reserved for future use
2976EndStructure
2977
2978Function NI2_HDF5BrowserLoadPackagePrefs(prefs)
2979        STRUCT NI2_HDF5BrowserPrefs &prefs
2980
2981        Variable i
2982
2983        // This loads preferences from disk if they exist on disk.
2984        LoadPackagePreferences kPackageName, kPrefFileName, kPrefRecordID, prefs
2985
2986        // If prefs not loaded or not valid, initialize them.
2987        if (V_flag!=0 || prefs.prefsVersion!=kCurrentPrefsVersion)
2988                prefs.prefsVersion = kCurrentPrefsVersion
2989               
2990                prefs.graphLeft = 0
2991                prefs.graphTop = 0
2992                prefs.graphRight = 0
2993                prefs.graphBottom = 0
2994                       
2995                // Preview table location in points. 0 means default.
2996                prefs.tableLeft = 0
2997                prefs.tableTop = 0
2998                prefs.tableRight = 0
2999                prefs.tableBottom = 0
3000                       
3001                // Dump notebook location in points. 0 means default.
3002                prefs.dumpLeft = 0
3003                prefs.dumpTop = 0
3004                prefs.dumpRight = 0
3005                prefs.dumpBottom = 0
3006                               
3007                // Save Waves and Save Data Folder panel location in pixels. 0 means default.
3008                prefs.savePanelLeft = 0
3009                prefs.savePanelTop = 0
3010                prefs.savePanelRight = 0
3011                prefs.savePanelBottom = 0
3012       
3013                // HDF5 browser location in points. 0 means default.
3014                prefs.browserLeft = 0
3015                prefs.browserTop = 0
3016                prefs.browserRight = 0
3017                prefs.browserBottom = 0
3018       
3019                // Overall prefs
3020                prefs.readOnly = 1
3021                for(i=0; i<15; i+=1)
3022                        prefs.reservedOverall[i] = 0
3023                endfor
3024               
3025                // Load prefs
3026                prefs.loadGroupsRecursively = 1
3027                prefs.transpose2DDatasets = 0
3028                for(i=0; i<14; i+=1)
3029                        prefs.reservedLoad[i] = 0
3030                endfor
3031               
3032                // Save prefs
3033                prefs.saveGroupsRecursively = 1
3034                prefs.includeIgorAttributes = 1
3035                for(i=0; i<14; i+=1)
3036                        prefs.reservedSave[i] = 0
3037                endfor
3038       
3039                for(i=0; i<100; i+=1)
3040                        prefs.reserved[i] = 0
3041                endfor
3042               
3043                NI2_HDF5BrowserSavePackagePrefs(prefs)          // Create default prefs file.
3044        endif
3045End
3046
3047Function NI2_HDF5BrowserSavePackagePrefs(prefs)
3048        STRUCT NI2_HDF5BrowserPrefs &prefs
3049
3050        SavePackagePreferences kPackageName, kPrefFileName, kPrefRecordID, prefs
3051End
3052
3053static Function GetPrefWindowCoords(windowName, left, top, right, bottom)
3054        String windowName
3055        Variable &left, &top, &right, &bottom
3056
3057        STRUCT NI2_HDF5BrowserPrefs prefs
3058       
3059        NI2_HDF5BrowserLoadPackagePrefs(prefs)
3060       
3061        strswitch(windowName)
3062                case "HDF5BrowserGraph":
3063                        left = prefs.graphLeft
3064                        top = prefs.graphTop
3065                        right = prefs.graphRight
3066                        bottom = prefs.graphBottom
3067                        break
3068               
3069                case "HDF5BrowserTable":
3070                        left = prefs.tableLeft
3071                        top = prefs.tableTop
3072                        right = prefs.tableRight
3073                        bottom = prefs.tableBottom
3074                        break
3075               
3076                case "HDF5DumpNotebook":
3077                        left = prefs.dumpLeft
3078                        top = prefs.dumpTop
3079                        right = prefs.dumpRight
3080                        bottom = prefs.dumpBottom
3081                        break
3082               
3083                case "HDF5SaveWavesPanel":
3084                case "HDF5SaveDataFolderPanel":
3085                        left = prefs.savePanelLeft
3086                        top = prefs.savePanelTop
3087                        right = prefs.savePanelRight
3088                        bottom = prefs.savePanelBottom
3089                        break
3090
3091                default:                // We want to get preferred coords for a new HDF5 browser.
3092                        left = prefs.browserLeft
3093                        top = prefs.browserTop
3094                        right = prefs.browserRight
3095                        bottom = prefs.browserBottom
3096                        break
3097        endswitch
3098End
3099
3100static Function SetPrefWindowCoords(windowName)
3101        String windowName
3102       
3103        STRUCT NI2_HDF5BrowserPrefs prefs
3104       
3105        NI2_HDF5BrowserLoadPackagePrefs(prefs)
3106       
3107        GetWindow $windowName wSize
3108
3109        // NewPanel uses device coordinates. We therefore need to scale from
3110        // points (returned by GetWindow) to device units for windows created
3111        // by NewPanel.
3112        Variable scale = ScreenResolution / 72
3113       
3114        strswitch(windowName)
3115                case "HDF5BrowserGraph":
3116                        prefs.graphLeft = V_Left
3117                        prefs.graphTop = V_Top
3118                        prefs.graphRight = V_Right
3119                        prefs.graphBottom = V_Bottom
3120                        break
3121               
3122                case "HDF5BrowserTable":
3123                        prefs.tableLeft = V_left
3124                        prefs.tableTop = V_top
3125                        prefs.tableRight = V_Right
3126                        prefs.tableBottom = V_Bottom
3127                        break
3128               
3129                case "HDF5DumpNotebook":
3130                        prefs.dumpLeft = V_left
3131                        prefs.dumpTop = V_top
3132                        prefs.dumpRight = V_Right
3133                        prefs.dumpBottom = V_Bottom
3134                        break
3135                       
3136                case "HDF5SaveWavesPanel":
3137                case "HDF5SaveDataFolderPanel":
3138                        prefs.savePanelLeft = V_left * scale
3139                        prefs.savePanelTop = V_top * scale
3140                        prefs.savePanelRight = V_Right * scale
3141                        prefs.savePanelBottom = V_Bottom * scale
3142                        break
3143
3144                default:                // We want to set preferred coords for a new HDF5 browser.
3145                        prefs.browserLeft = V_left * scale
3146                        prefs.browserTop = V_top * scale
3147                        prefs.browserRight = V_Right * scale
3148                        prefs.browserBottom = V_Bottom * scale
3149                        break
3150        endswitch
3151
3152        NI2_HDF5BrowserSavePackagePrefs(prefs)
3153End
3154
3155static Function GetPrefBrowserSettings(readOnly, loadGroupsRecursively, transpose2DDatasets)
3156        Variable &readOnly
3157        Variable &loadGroupsRecursively
3158        Variable &transpose2DDatasets
3159
3160        STRUCT NI2_HDF5BrowserPrefs prefs
3161       
3162        NI2_HDF5BrowserLoadPackagePrefs(prefs)
3163
3164        readOnly = prefs.readOnly
3165        loadGroupsRecursively = prefs.loadGroupsRecursively
3166        transpose2DDatasets = prefs.transpose2DDatasets
3167End
3168
3169// ************* End of HDF5 Browser Prefs Routines ***************
3170
3171// ************* Start of HDF5 Utility Routines ***************
3172
3173Function NI2_HDF5CheckDataClass(dataClassStr, errorMessage)
3174        String dataClassStr
3175        String &errorMessage
3176       
3177        Variable err = 0
3178        errorMessage = ""
3179
3180        strswitch(dataClassStr)
3181                case "H5T_TIME":
3182                        errorMessage = "HDF5XOP does not support data of class H5T_TIME."
3183                        err = -1
3184                        break
3185        endswitch
3186
3187        return err
3188End
3189
3190Function NI2_HDF5MakeHyperslabWave(path, numRows)
3191        String path                     // Path to wave. e.g., "root:slab"
3192        Variable numRows
3193       
3194        Make /O /N=(numRows,4) $path
3195        Wave slab = $path
3196        slab = 1                                                                // Set all elements to 1.
3197
3198        Variable row
3199        String dimLabel
3200        for(row=0; row<numRows; row+=1)
3201                sprintf dimLabel, "Dimension %d", row           // HR, 060206: Fixed setting of row dimension labels.
3202                SetDimLabel 0, row, $dimLabel, slab
3203        endfor
3204        SetDimLabel 1, 0, Start, slab
3205        SetDimLabel 1, 1, Stride, slab
3206        SetDimLabel 1, 2, Count, slab
3207        SetDimLabel 1, 3, Block, slab
3208End
3209
3210Constant NI2_H5S_MAX_RANK = 32
3211
3212Constant NI2_kHDF5DataInfoVersion = 1000                // 1000 means 1.000.
3213
3214Structure NI2_HDF5DataInfo                                      // Use with HDF5DatasetInfo and HDF5AttributeInfo functions
3215        // Input fields (inputs to HDF5 XOP)
3216        uint32 version                                                  // Must be set to kHDF5DataInfoVersion
3217        char structName[16]                                             // Must be "HDF5DataInfo".
3218
3219        // Output fields (outputs from HDF5 XOP)
3220        double datatype_class;                          // e.g., H5T_INTEGER, H5T_FLOAT.
3221        char datatype_class_str[32];            // String with class spelled out. e.g., "H5T_INTEGER", "H5T_FLOAT".
3222        double datatype_size;                                   // Size in bytes of one element.
3223        double datatype_sign;                                   // H5T_SGN_NONE (unsigned), H5T_SGN_2 (signed), H5T_SGN_ERROR (this type does not have a sign, i.e., it is not an integer type).
3224        double datatype_order;                          // H5T_ORDER_LE, H5T_ORDER_BE, H5T_ORDER_VAX
3225        char datatype_str[64];                          // Human-readable string, e.g., "16-bit unsigned integer"
3226        double dataspace_type;                          // H5S_NO_CLASS (-1), H5S_SCALAR (0), H5S_SIMPLE (1), H5S_COMPLEX (2).
3227        double ndims;                                                           // Zero for H5S_SCALAR. Number of dimensions in the dataset for H5S_SIMPLE.
3228        double dims[NI2_H5S_MAX_RANK];                  // Size of each dimension.
3229        double maxdims[NI2_H5S_MAX_RANK];               // Maximum size of each dimension.
3230EndStructure
3231
3232Function NI2_InitHDF5DataInfo(di)                               // Sets input fields.
3233        STRUCT NI2_HDF5DataInfo &di
3234       
3235        // HDF5XOP uses these fields to make sure the structure passed in to it is compatible.
3236        di.version = NI2_kHDF5DataInfoVersion
3237        di.structName = "HDF5DataInfo"
3238End
3239
3240//      HDF5DatasetRank(locationID, name)
3241//      Returns rank or zero in event of error.
3242Function NI2_HDF5DatasetRank(locationID, name)
3243        Variable locationID
3244        String name
3245
3246#if Exists("HDF5OpenFile")     
3247        STRUCT NI2_HDF5DataInfo di
3248        NI2_InitHDF5DataInfo(di)                        // Set input fields.
3249
3250        Variable err = HDF5DatasetInfo(locationID, name, 1, di)
3251        if (err != 0)
3252                return 0
3253        endif
3254        Variable rank = di.ndims
3255        return rank
3256#else
3257        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
3258#endif
3259End
3260
3261//      HDF5AttributeRank(locationID, name)
3262//      Returns rank or zero in event of error.
3263Function NI2_HDF5AttributeRank(locationID, objectName, objectType, attributeName)
3264        Variable locationID
3265        String objectName
3266        Variable objectType
3267        String attributeName
3268
3269#if Exists("HDF5OpenFile")     
3270        STRUCT NI2_HDF5DataInfo di
3271        NI2_InitHDF5DataInfo(di)                        // Set input fields.
3272
3273        Variable err = HDF5AttributeInfo(locationID, objectName, objectType, attributeName, 1, di)
3274        if (err != 0)
3275                return 0
3276        endif
3277        Variable rank = di.ndims
3278        return rank
3279#else
3280        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
3281#endif
3282End
3283
3284Constant NI2_kHDF5DatatypeInfoVersion = 1000            // 1000 means 1.000.
3285Structure  NI2_HDF5DatatypeInfo                                                 // Use with HDF5TypeInfo functions
3286        // Input fields (inputs to HDF5 XOP)
3287        uint32 version                                  // Structure version. Used for backward compatibility.
3288        char structName[32]                             // Must be "HDF5DatatypeInfo". Used to prevent passing wrong structure to XFUNC.
3289       
3290        // Output fields (outputs from HDF5 XOP)
3291        double type_class                               // e.g., H5T_INTEGER, H5T_FLOAT.
3292        char type_class_str[32]         // String with class spelled out. e.g., "H5T_INTEGER", "H5T_FLOAT".
3293        double size                                             // Size in bytes of one element.
3294        double sign                                             // H5T_SGN_NONE (unsigned), H5T_SGN_2 (signed), H5T_SGN_ERROR (this type does not have a sign, i.e., it is not an integer type).
3295        double order                                            // H5T_ORDER_LE, H5T_ORDER_BE, H5T_ORDER_VAX, H5T_ORDER_ERROR (this type does not have an order).
3296        double cset                                             // Currently must be H5T_CSET_ASCII (0).
3297        double strpad                                           // H5T_str_t: H5T_STR_ERROR (-1), H5T_STR_NULLTERM (0), H5T_STR_NULLPAD (1), H5T_STR_SPACEPAD (2)
3298        double nmembers                                 // For enum or compound datatypes only, number of members.
3299        String names                                            // For enum or compound datatypes only, semicolon-separated list of enum names.
3300        int32 values[100]                               // For enum datatype only, list of enum values. For compound datatype, list of classes.
3301        String opaque_tag                               // For opaque datatypes only, tag name.
3302EndStructure
3303
3304Function NI2_InitHDF5DatatypeInfo(dti)                  // Sets input fields.
3305        STRUCT NI2_HDF5DatatypeInfo &dti
3306       
3307        // HDF5XOP uses these fields to make sure the structure passed in to it is compatible.
3308        dti.version = NI2_kHDF5DatatypeInfoVersion
3309        dti.structName = "NI2_HDF5DatatypeInfo"
3310End
3311
3312// ************* End of HDF5 Utility Routines ***************
3313
3314// ************* Start of HDF5 Save Routines ***************
3315
3316static Function StringsAreEqual(str1, str2)     // Case sensitive
3317        String str1, str2
3318       
3319        Variable len1=strlen(str1), len2=strlen(str2)
3320        if (len1 != len2)
3321                return 0
3322        endif
3323       
3324        Variable i
3325        for(i=0; i<len1; i+=1)
3326                if (char2num(str1[i]) != char2num(str2[i]))
3327                        return 0
3328                endif
3329        endfor
3330       
3331        return 1
3332End
3333
3334static Function/S GetUnquotedLeafName(path)
3335        String path                     // Path to data folder or wave
3336       
3337        String name
3338        name = ParseFilePath(0, path, ":", 1, 0)                // Just the name without path.
3339       
3340        // Remove single quotes if present
3341        if (CmpStr(name[0],"'") == 0)
3342                Variable len = strlen(name)
3343                name = name[1,len-2]   
3344        endif
3345       
3346        return name
3347End
3348
3349static Function HaveObjectNameConflict(listOfObjectsInGroup, listOfObjectsToBeSaved, conflictingObjectName)
3350        String listOfObjectsInGroup                     // Semicolon-separated list of all types of objects in selected group or list of datasets in selected group.
3351        String listOfObjectsToBeSaved           // Semicolon-separated list of names of objects about to be saved in the HDF5 file.
3352        String &conflictingObjectName
3353       
3354        conflictingObjectName = ""
3355       
3356        Variable i, j
3357        Variable numObjectsInList, numObjectsToBeSaved
3358       
3359        numObjectsInList = ItemsInList(listOfObjectsInGroup)
3360        numObjectsToBeSaved = ItemsInList(listOfObjectsToBeSaved)
3361        for(i=0; i<numObjectsToBeSaved; i+=1)
3362                String objectToBeSavedName = StringFromList(i, listOfObjectsToBeSaved)
3363                objectToBeSavedName = GetUnquotedLeafName(objectToBeSavedName)          // Just the name without path.
3364                if (CmpStr(objectToBeSavedName, "root") == 0)
3365                        objectToBeSavedName = IgorInfo(1)               // Use name of current experiment instead of "root".
3366                endif
3367                for(j=0; j<numObjectsInList; j+=1)
3368                        String groupObjectName = StringFromList(j, listOfObjectsInGroup)
3369                        if (StringsAreEqual(groupObjectName,objectToBeSavedName))
3370                                conflictingObjectName = groupObjectName
3371                                return 1
3372                        endif
3373                endfor
3374        endfor
3375
3376        return 0
3377End
3378
3379static Function SaveButtonProc(ctrlName) : ButtonControl
3380        String ctrlName
3381       
3382#if Exists("HDF5OpenFile")     
3383        String panelName = WinName(0, 64)
3384        String message
3385       
3386        String list = WS_SelectedObjectsList(panelName, "SelectorList")
3387        if (strlen(list) == 0)
3388                strswitch(panelName)
3389                        case "HDF5SaveWavesPanel":
3390                                DoAlert 0, "You must select one or more waves to save first."
3391                                break
3392                               
3393                        case "HDF5SaveDataFolderPanel":
3394                                DoAlert 0, "You must select a data folder to save first."
3395                                break
3396                endswitch
3397                return -1
3398        endif
3399       
3400        String browserName = NI2_HDF5GetTopBrowserName()
3401        if (strlen(browserName) == 0)
3402                return -1                                                               // HDF5 Browser was killed.
3403        endif
3404
3405        STRUCT NI2HDF5BrowserData bd
3406        SetNI2HDF5BrowserData(browserName, bd)
3407       
3408        // Get list of all types of objects in the selected group (including named datasets and links)
3409        HDF5ListGroup /TYPE=15 bd.fileID, bd.groupPath
3410        String listOfObjectsInSelectedGroup = S_HDF5ListGroup
3411
3412        Variable haveConflict
3413        String conflictingObjectName
3414        haveConflict = HaveObjectNameConflict(listOfObjectsInSelectedGroup,list,conflictingObjectName)
3415        if (haveConflict)
3416                sprintf message, "The name '%s' is in use.\r\rOverwrite objects with conflicting names?", conflictingObjectName
3417                DoAlert 1, message
3418                if (V_flag != 1)
3419                        return -1
3420                endif
3421        endif
3422
3423        ControlInfo /W=$panelName IncludeIgorAttributes
3424        Variable igorAttributesMask = V_value ? -1 : 0
3425        Variable varMode = V_value ? 1 : 0
3426       
3427        String groupPath = NI2_SelectedGroupPath(bd)    // Currently selected group
3428        String newGroupPath = ""                                                        // Name of group we created, if any.
3429       
3430        Variable index = 0
3431        do
3432                String item = StringFromList(index, list)
3433                if (strlen(item) == 0)
3434                        break                                                                   // No more waves
3435                endif
3436               
3437                strswitch(panelName)
3438                        case "HDF5SaveWavesPanel":
3439                                Wave w = $item
3440                                String datasetPath = NI2_HDF5GetObjectFullPath(groupPath, NameOfWave(w))
3441                                HDF5SaveData /IGOR=(igorAttributesMask) /O w, bd.fileID, datasetPath
3442                                break
3443                               
3444                        case "HDF5SaveDataFolderPanel":
3445                                String dfName = ParseFilePath(0, item, ":", 1, 0)               // Just the data folder name without path.
3446                                if (CmpStr(item, "root") == 0)
3447                                        dfName = IgorInfo(1)            // Use name of current experiment instead of "root".
3448                                endif
3449                                newGroupPath = NI2_HDF5GetObjectFullPath(groupPath, dfName)
3450
3451                                ControlInfo/W=$panelName SaveGroupsRecursively
3452                                if (V_value)
3453                                        HDF5SaveGroup /IGOR=(igorAttributesMask) /VAR=(varMode) /O /R /T=dfName $item, bd.fileID, groupPath
3454                                else
3455                                        HDF5SaveGroup /IGOR=(igorAttributesMask) /VAR=(varMode) /O /T=dfName $item, bd.fileID, groupPath
3456                                endif
3457                                break
3458                endswitch
3459               
3460                if (V_flag != 0)
3461                        break                                                                   // Save error.
3462                endif
3463               
3464                index += 1
3465        while(1)
3466       
3467        strswitch(panelName)
3468                case "HDF5SaveWavesPanel":
3469                        FillDatasetsList(bd)
3470                        FillDatasetAttributesList(bd)
3471                        SetButtonStates(bd)                             // Needed to set Load Dataset button if we go from 0 datasets to >0 datasets.
3472                        break
3473                       
3474                case "HDF5SaveDataFolderPanel":
3475                        if (strlen(newGroupPath) > 0)
3476                                FillLists(bd)
3477                        endif
3478                        break
3479        endswitch
3480#else
3481        Abort "Hdf5 xop is not found. Reinstall xops using one of the Installers or link the hdf5.xop from Igor distribution to your Igor extensions folder"
3482#endif
3483End
3484
3485static Function DoneButtonProc(ctrlName) : ButtonControl
3486        String ctrlName
3487
3488        String panelName = WinName(0, 64)
3489        KillWIndow/Z $panelName
3490End
3491
3492static Function GetPrefSavePanelSettings(saveGroupsRecursively, includeIgorAttributes)
3493        Variable &saveGroupsRecursively
3494        Variable &includeIgorAttributes
3495
3496        STRUCT NI2_HDF5BrowserPrefs prefs
3497       
3498        NI2_HDF5BrowserLoadPackagePrefs(prefs)
3499
3500        saveGroupsRecursively = prefs.saveGroupsRecursively
3501        includeIgorAttributes = prefs.includeIgorAttributes
3502End
3503
3504static Function SetPrefSavePanelSettings(panelName)
3505        String panelName
3506       
3507        STRUCT NI2_HDF5BrowserPrefs prefs
3508       
3509        NI2_HDF5BrowserLoadPackagePrefs(prefs)
3510       
3511        strswitch(panelName)
3512                case "HDF5SaveWavesPanel":
3513                        ControlInfo/W=$panelName IncludeIgorAttributes
3514                        prefs.includeIgorAttributes = V_value
3515                        break
3516       
3517                case "HDF5SaveDataFolderPanel":
3518                        ControlInfo/W=$panelName SaveGroupsRecursively
3519                        prefs.saveGroupsRecursively = V_value
3520                        ControlInfo/W=$panelName IncludeIgorAttributes
3521                        prefs.includeIgorAttributes = V_value
3522                        break
3523        endswitch
3524
3525        NI2_HDF5BrowserSavePackagePrefs(prefs)
3526End
3527
3528static Function SetSaveButtonState(panelName)
3529        String panelName
3530       
3531        String selection = WS_SelectedObjectsList(panelName, "SelectorList")
3532        Variable code = strlen(selection) > 0 ? 0:2
3533        Button Save, win=$panelName, disable=code       
3534End
3535
3536Function NI2_HDF5SaveWavesPanelHook(infoStr)
3537        String infoStr
3538       
3539        String panelName = "NI2_HDF5SaveWavesPanel"
3540
3541        String event= StringByKey("EVENT",infoStr)
3542
3543        strswitch(event)
3544                case "activate":                                // We do not get this on Windows when the panel is first created.
3545                        SetSaveButtonState(panelName)
3546                        break
3547                       
3548                case "resize":
3549                case "moved":                                   // This message was added in Igor Pro 5.04B07.
3550                        SetPrefWindowCoords(panelName)
3551                        break
3552        endswitch
3553       
3554        return 0
3555End
3556
3557Function NI2_HDF5SaveDataFolderPanelHook(infoStr)
3558        String infoStr
3559       
3560        String panelName = "NI2_HDF5SaveDataFolderPanel"
3561
3562        String event= StringByKey("EVENT",infoStr)
3563
3564        strswitch(event)
3565                case "activate":                                // We do not get this on Windows when the panel is first created.
3566                        SetSaveButtonState(panelName)
3567                        break
3568                       
3569                case "resize":
3570                case "moved":                                   // This message was added in Igor Pro 5.04B07.
3571                        SetPrefWindowCoords(panelName)
3572                        break
3573        endswitch
3574       
3575        return 0
3576End
3577
3578static Function DisplaySaveWavesPanel()
3579        String panelName = "NI2_HDF5SaveWavesPanel"
3580
3581        DoWindow/F $panelName
3582        if (V_flag == 0)
3583                Variable left, top, right, bottom
3584                GetPrefWindowCoords(panelName, left, top, right, bottom)        // See if prefs set.
3585                if (right-left<200 || bottom-top<200)
3586                        left = 200
3587                        top = 100
3588                        right = 584
3589                        bottom = 632
3590                endif
3591               
3592                Variable recursive, includeIgorAttributes
3593                GetPrefSavePanelSettings(recursive, includeIgorAttributes)
3594
3595                Variable showWhat = WMWS_Waves
3596                NewPanel /W=(left,top,right,bottom) /N=$panelName /K=1 as "Save Waves as HDF5 Datasets"
3597                TitleBox ListTitle,pos={20,16},size={163,16},title="Select Wave(s) to Save as Datasets"
3598                TitleBox ListTitle,fSize=14,frame=0,fStyle=1
3599                ListBox SelectorList,pos={16,48},size={350,390},mode=4  // Multiple disjoint selection allowed.
3600                MakeListIntoWaveSelector(panelName, "SelectorList", content=showWhat)
3601                WS_SetNotificationProc(panelName, "SelectorList", "SelectorNotification", isExtendedProc=1)
3602                Button Save,pos={46,457},size={100,20},proc=HDF5Browser#SaveButtonProc,title="Save"
3603                Button Done,pos={226,457},size={100,20},proc=HDF5Browser#DoneButtonProc,title="Done"
3604                CheckBox IncludeIgorAttributes,pos={36,488},size={121,14},title="Include Igor Attributes"
3605                CheckBox IncludeIgorAttributes,proc=HDF5BrowserPrefCheckboxProc,help={"When checked, attributes are written so that wave properties can be recreated when loading back into Igor."}
3606                CheckBox IncludeIgorAttributes,value=includeIgorAttributes
3607                SetSaveButtonState(panelName)
3608                SetWindow kwTopWin,hook=NI2_HDF5SaveWavesPanelHook
3609        endif
3610End
3611
3612static Function DisplaySaveDataFolderPanel()
3613        String panelName = "HDF5SaveDataFolderPanel"
3614
3615        DoWindow/F $panelName
3616        if (V_flag == 0)
3617                Variable left, top, right, bottom
3618                GetPrefWindowCoords(panelName, left, top, right, bottom)        // See if prefs set.
3619                if (right-left<200 || bottom-top<200)
3620                        left = 200
3621                        top = 100
3622                        right = 584
3623                        bottom = 632
3624                endif
3625               
3626                Variable recursive, includeIgorAttributes
3627                GetPrefSavePanelSettings(recursive, includeIgorAttributes)
3628
3629                Variable showWhat = WMWS_DataFolders
3630                NewPanel /W=(left,top,right,bottom) /N=$panelName /K=1 as "Save Data Folder as HDF5 Group"
3631                TitleBox ListTitle,pos={20,16},size={163,16},title="Select Data Folder to Save as Group"
3632                TitleBox ListTitle,fSize=14,frame=0,fStyle=1
3633                ListBox SelectorList,pos={16,48},size={350,390},mode=1          // Single selection only.
3634                MakeListIntoWaveSelector(panelName, "SelectorList", content=showWhat)
3635                WS_SetNotificationProc(panelName, "SelectorList", "SelectorNotification", isExtendedProc=1)
3636                Button Save,pos={46,457},size={100,20},proc=HDF5Browser#SaveButtonProc,title="Save"
3637                Button Done,pos={226,457},size={100,20},proc=HDF5Browser#DoneButtonProc,title="Done"
3638                CheckBox SaveGroupsRecursively,pos={44,485},size={66,14},title="Save Groups Recursive",value=recursive
3639                CheckBox SaveGroupsRecursively,proc=HDF5BrowserPrefCheckboxProc,help={"When checked, sub-data folders are recursively saved."}
3640                CheckBox IncludeIgorAttributes,pos={44,507},size={121,14},title="Include Igor Attributes"
3641                CheckBox IncludeIgorAttributes,proc=HDF5BrowserPrefCheckboxProc,help={"When checked, attributes are written so that wave properties can be recreated when loading back into Igor."}
3642                CheckBox IncludeIgorAttributes,value=includeIgorAttributes
3643                SetSaveButtonState(panelName)
3644                SetWindow kwTopWin,hook=NI2_HDF5SaveDataFolderPanelHook
3645        endif
3646End
3647
3648Function NI2_SelectorNotification(SelectedItem, EventCode, panelName, controlName)
3649        String SelectedItem
3650        Variable EventCode
3651        String panelName
3652        String controlName
3653       
3654        // Printf "Panel=%s, Control=%s, Event code=%d, selection=\"%s\"\r", panelName, controlName, eventCode, selectedItem
3655
3656        switch(eventCode)
3657                case WMWS_DoubleClick:
3658                                break
3659       
3660                case WMWS_FolderOpened:                         // Selection is emptied when folder is opened.
3661                case WMWS_FolderClosed:                         // Selection is emptied when folder is opened.
3662                case WMWS_SelectionChanged:
3663                case WMWS_SelectionChangedShift:
3664                        SetSaveButtonState(panelName)
3665                        break
3666        endswitch
3667End
3668
3669static Function SaveWavesButtonProc(ctrlName) : ButtonControl
3670        String ctrlName
3671       
3672        KillWIndow/Z HDF5SaveDataFolderPanel    // One save panel open at a time.
3673        DisplaySaveWavesPanel()
3674       
3675        return 0
3676End
3677
3678static Function SaveDataFolderButtonProc(ctrlName) : ButtonControl
3679        String ctrlName
3680       
3681        KillWIndow/Z HDF5SaveWavesPanel // One save panel open at a time.
3682        DisplaySaveDataFolderPanel()
3683       
3684        return 0
3685End
3686
3687static Function CloseSavePanels()
3688        KillWIndow/Z HDF5SaveWavesPanel
3689        KillWIndow/Z HDF5SaveDataFolderPanel
3690End
3691
3692static Function HDF5SaveWavesPanelIsVisible()
3693        DoWindow HDF5SaveWavesPanel
3694        return V_flag
3695End
3696
3697static Function HDF5SaveDFPanelIsVisible()
3698        DoWindow HDF5SaveDataFolderPanel
3699        return V_flag
3700End
3701
3702// ************* End of HDF5 Save Routines ***************
Note: See TracBrowser for help on using the repository browser.