#pragma rtGlobals=3		// Use modern global access method and strict wave access.
#pragma IgorVersion = 6.1
#pragma ModuleName = PearlDataExplorer
#pragma version = 1.50
#include "pearl-area-import"
#include "pearl-area-profiles"
#include "pearl-area-display"
#include "pearl-compat"
#include "pearl-pshell-import"
#if exists("MFR_OpenResultFile")
#include "pearl-matrix-import"
#endif

// copyright (c) 2013-16 Paul Scherrer Institut
//
// Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//   http:///www.apache.org/licenses/LICENSE-2.0

/// @file
/// @brief preview and import panel for PEARL data
/// @ingroup ArpesPackage
///
///
/// preview and import panel for PEARL data:
/// @arg area detector (HDF5) files from scienta analyser and prosilica cameras (if HDF5.xop is installed).
/// @arg igor text files from s-scans and otf-scans.
/// @arg pshell (HDF5) data files (if HDF5.xop is installed).
/// @arg matrix STM files (if MatrixFileReader.xop is installed).

/// @namespace PearlDataExplorer
/// @brief preview and import panel for PEARL data
///
/// PearlDataExplorer is declared in @ref pearl-data-explorer.ipf.

static strconstant package_name = "pearl_explorer"
static strconstant package_path = "root:packages:pearl_explorer:"

static strconstant ks_filematch_adh5 = "*.h5"
static strconstant ks_filematch_pshell = "psh*.h5"
static strconstant ks_filematch_itx = "*.itx"
static strconstant ks_filematch_mtrx = "*_mtrx"

function pearl_data_explorer()
	init_package()
	load_prefs()
	execute /q/z "PearlDataExplorer()"
end

/// initialize the global variables of the data explorer.
///
/// initializes the global variables and data folder for this procedure file
/// must be called once before the panel is created
/// warning: this function overwrites previous values
static function init_package()

	dfref savefolder = GetDataFolderDFR()
	SetDataFolder root:
	newdatafolder /o/s packages
	newdatafolder /o/s $package_name
	if (exists("v_InitPanelDone") == 2)
	 	SetDataFolder savefolder
		return 0
	endif
	
	make /o/n=0/t wtFiles
	make /o/n=0/i wSelectedFiles,wSelectedDatasets
	make /o/n=0/t wtDatasets
	make /o/n=0/t wtPositioners,wtDetectors
	make /o/n=0/i wSelectedPositioners,wSelectedDetectors
	
	make /o/n=(1,1) preview_image // preview 2D data
	make /o/n=0 preview_trace // preview 1D data
	make /o/n=0/t attr_names, attr_values, attr_filter, attr_filter_summary

	// persistent strings and variables. persistent = saved in preferences
	string /g s_filepath = "" // directory path to be listed
	string /g s_hdf_options = "" // recently used HDF5 load options
	string /g s_reduction_params = "" // recently used reduction parameters
	string /g s_preview_pvs = "" // semicolon-separated list of EPICS PVs to display in preview.
		// the list items can contain wildcards for StringMatch
	s_preview_pvs = "*OP:CURRENT*;*Stats*Total*;*KEITHLEY*READOUT;*CADC*"

	// non-persistent strings and variables
	string /g s_preview_file = "" // file or folder name of the current preview
	string /g s_preview_source = "" // data source, e.g. EPICS channel name, of the current preview
	string /g s_profiles_graph = "" // window name of the current preview if the data is two-dimensional
	string /g s_preview_trace_graph = "" // window name of the current preview if the data is one-dimensional
	string /g s_file_info = "" // description of selected file
	
	variable/g v_InitPanelDone = 1
	
 	SetDataFolder savefolder
end

/// save persistent package data to the preferences file.
///
/// this function is called when the user clicks the corresponding button.
/// the data saved in the file are: data file path, attributes filter
///
static function save_prefs()
	dfref saveDF = GetDataFolderDFR()
	dfref df = $package_path
	if (DataFolderRefStatus(df) == 1)
		string fullPath = SpecialDirPath("Packages", 0, 0, 0)
		fullPath += package_name
		NewPath/O/C/Q tempPackagePrefsPath, fullPath
		fullPath += ":preferences.pxp"
		SetDataFolder df
		string objects = "attr_filter;attr_filter_summary;s_filepath;s_hdf_options;s_reduction_params;s_preview_pvs"
		SaveData /O /Q /J=objects fullPath
		KillPath/Z tempPackagePrefsPath
	endif
	SetDataFolder saveDF
end

static function load_prefs()
	// loads persistent package data from the preferences file
	// the preferences file is an Igor packed experiment file in a special preferences folder
	dfref saveDF = GetDataFolderDFR()

	variable result = -1
	setdatafolder root:
	NewDataFolder /O/S packages
	NewDataFolder /O/S $package_name
	dfref packageDF = GetDataFolderDFR()
	string fullPath = SpecialDirPath("Packages", 0, 0, 0)
	fullPath += package_name

	GetFileFolderInfo /Q /Z fullPath
	if (V_Flag == 0) // Disk directory exists?
		fullPath += ":preferences.pxp"
		GetFileFolderInfo /Q /Z fullPath
		if (V_Flag == 0) // Preference file exist?
			LoadData /O /R /Q fullPath
			result = 0
		endif
	endif
	
	if (result == 0)
		svar /sdfr=packageDF filepath = s_filepath
		NewPath /O/Z pearl_explorer_filepath, filepath
		update_filelist()
		update_datasets()
	endif
	
 	SetDataFolder saveDF
	return result
end

/// check whether a file can be imported by this module.
///
/// the file type is determined by the extension of the file name.
///
/// @return file type
///	@arg 0	not a recognized file type
///	@arg 1	PShell file (HDF5, name starts with psh_)
///	@arg 2	area detector HDF5 file
///	@arg 3	Igor text (itx) file
///	@arg 4	Matrix STM file (*_mtrx)
///
static function pearl_file_type(filename)
	string filename
	
	if (StringMatch(filename, ks_filematch_pshell))
		return 1
	elseif (StringMatch(filename, ks_filematch_adh5))
		return 2
	elseif (StringMatch(filename, ks_filematch_itx))
		return 3
#if exists("MFR_OpenResultFile")
	elseif (StringMatch(filename, ks_filematch_mtrx))
		return 4
#endif
	else
		return 0
	endif
end

/// read a list of PEARL files from the file system
///
/// wtFiles and wSelectedFiles in the package data folder are updated.
/// only files for which pearl_file_type() returns non-zero are listed.
///
static function update_filelist()
	dfref saveDF = GetDataFolderDFR()

	string all_files
	wave /t wtFiles = $(package_path + "wtFiles")
	wave wSelectedFiles = $(package_path + "wSelectedFiles")
	variable nn
	
	PathInfo pearl_explorer_filepath
	if (v_flag == 1)
		all_files = IndexedFile(pearl_explorer_filepath, -1, "????")
		nn = ItemsInList(all_files)
	else
		all_files = ""
		nn = 0
	endif
	
	make /n=(nn) /t /free wtAllFiles
	wtAllFiles = StringFromList(p, all_files)
	Extract /o /t wtAllFiles, wtFiles, pearl_file_type(wtAllFiles[p])
	Sort /A /R wtFiles, wtFiles
	
	redimension /n=(numpnts(wtFiles)) wSelectedFiles
	wSelectedFiles = 0
	
	setdatafolder saveDF
end

static function update_datasets()
	// updates the list of imported datasets.
	// a dataset means any top-level data folder
	// which includes a string variable named pearl_explorer_import.
	dfref saveDF = GetDataFolderDFR()

	setdatafolder root:
	dfref rootdf = GetDataFolderDFR()
	setdatafolder $package_path
	dfref privatedf = GetDataFolderDFR()
	
	wave /t wtDatasets
	wave wSelectedDatasets
	variable maxdf = CountObjectsDFR(rootdf, 4)
	redimension /n=(maxdf) wtDatasets
	
	variable idf = 0
	variable ndf = 0
	string sdf
	
	do
		sdf = GetIndexedObjNameDFR(rootdf, 4, idf)
		if (strlen(sdf) >= 1)
			setdatafolder rootdf
			setdatafolder $sdf
			svar /z importer = pearl_explorer_import
			if (svar_exists(importer))
				wtDatasets[ndf] = sdf
				ndf += 1
			endif
		else
			break
		endif
		idf += 1
	while(1)
	
	redimension /n=(ndf) wtDatasets, wSelectedDatasets
	wSelectedDatasets = 0
	sort wtDatasets, wtDatasets
	
	setdatafolder saveDF
end

static function preview_file(filename)
	string filename
	
	dfref saveDF = GetDataFolderDFR()
	
	variable ft = pearl_file_type(filename)
	switch(ft)
		case 1:
			wave /z image = preview_pshell_file(filename)
			break
		case 2:
			wave /z image = preview_hdf_file(filename)
			break
		case 3:
			wave /z image = preview_itx_file(filename)
			break
		case 4:
			wave /z image = preview_mtrx_file(filename)
			break
		default:
			wave /z image = $""
	endswitch
	
	if (WaveExists(image))
		string graphname = show_preview_graph(image)
		// preset ELOG panel - if available
		if (exists("PearlElog#set_panel_attributes") == 6)
			string cmd
			sprintf cmd, "PearlElog#set_panel_attributes(\"\", \"File=%s\")", ParseFilePath(0, filename, ":", 1, 0)
			execute /Q/Z cmd
			if (strlen(graphname) > 0)
				sprintf cmd, "PearlElog#set_panel_graphs(\"\", \"%s\")", graphname
				execute /Q/Z cmd
			endif
		endif
	endif
	
	setdatafolder saveDF
	return 0
end

/// load the preview of a PShell HDF5 file.
///
/// the preview is an arbitrary detector image extracted from the file, see adh5_load_preview().
/// the preview is loaded to the preview_image wave in the pear_explorer data folder.
///
/// the s_file_info string is updated with information about the scan dimensions.
///
/// @param	filename		name of a file in the directory specified by the pearl_explorer_filepath path object.
///
/// @return	wave reference of the preview image
///
static function /wave preview_pshell_file(filename)
	string filename

	dfref saveDF = GetDataFolderDFR()

	setdatafolder $package_path
	dfref previewDF = GetDataFolderDFR()
	svar s_preview_file
	svar s_preview_source
	svar /z s_file_info
	if (! svar_exists(s_file_info))
		string /g s_file_info
	endif
	
	dfref tempDF = NewFreeDataFolder()
	setdatafolder tempDF
	string dataname
	dataname = psh5_load_preview("pearl_explorer_filepath", filename)
	
	s_preview_file = filename
	s_preview_source = ""

	wave /z data = $dataname
	if (waveexists(data))
		duplicate /o data, previewDF:preview_image
	else
		print "no data found in file " + filename
	endif
	
	if (strlen(s_preview_file) > 0)
		s_file_info = psh5_load_info("pearl_explorer_filepath", filename)
	else
		s_file_info = ""
	endif
	
	dfref attrDF = tempDF:attr
	if (DataFolderRefStatus(attrDF))
		preview_attributes(attrDF)
	endif
	
	setdatafolder saveDF
	wave /z /sdfr=previewDF preview_image
	return preview_image
end

/// load the preview of a PEARL HDF5 file.
///
/// the preview is an arbitrary detector image extracted from the file, see adh5_load_preview().
/// the preview is loaded to the preview_image wave in the pear_explorer data folder.
///
/// the s_file_info string is updated with information about the scan dimensions.
///
/// @param	filename		name of a file in the directory specified by the pearl_explorer_filepath path object.
///
/// @return	wave reference of the preview image
///
static function /wave preview_hdf_file(filename)
	string filename

	dfref saveDF = GetDataFolderDFR()
	setdatafolder $package_path
	svar s_preview_file
	svar s_preview_source
	adh5_load_preview("preview_image", "pearl_explorer_filepath", filename)
	s_preview_file = filename
	s_preview_source = ""
	wave /z preview_image
	
	svar /z s_file_info
	if (! svar_exists(s_file_info))
		string /g s_file_info
	endif
	if (strlen(s_preview_file) > 0)
		s_file_info = adh5_load_info("pearl_explorer_filepath", filename)
	else
		s_file_info = ""
	endif
	
	if (DataFolderExists("attr"))
		setdatafolder attr
		preview_attributes(GetDataFolderDFR())
		setdatafolder ::
	endif
	
	setdatafolder saveDF
	return preview_image
end

/// load the preview of a general ITX file.
///
/// the function is designed for PEARL OTF and EPICS scan data converted from MDA files.
/// the function picks the first wave whose PV note matches one from the global string s_preview_pvs
/// (see @ref preview_datafolder and @ref init_package).
///
/// the preview is loaded to the preview_image wave in the pearl_explorer data folder.
/// the s_file_info string is updated with information about the scan dimensions.
///
/// @note: the ITX files should load their waves into the current data folder (a "free" data folder).
/// some early versions of PEARL ITX data files created a data folder of their own.
/// both ways are allowed, while the first one is preferred.
/// on return, the current data folder must point to either the original free folder or the newly created one.
///
/// @param	filename		name of a file in the directory specified by the pearl_explorer_filepath path object.
///
/// @return	wave reference of the preview trace.
///			empty wave reference if the function failed.
///
static function /wave preview_itx_file(filename)
	string filename

	dfref saveDF = GetDataFolderDFR()
	setdatafolder $package_path
	svar s_preview_file
	svar s_preview_source
	wave preview_image

	dfref dataDF = newfreedatafolder()
	setdatafolder dataDF
	LoadWave /t/p=pearl_explorer_filepath/q filename
	s_preview_file = s_filename
	s_preview_source = ""
	
	preview_datafolder()
	preview_attributes(dataDF, include_datawaves=0)
	
	setdatafolder saveDF
	return preview_image
end

/// load the preview of a Matrix STM file.
///
/// the preview is loaded to the preview_image wave in the pearl_explorer data folder.
///
/// the s_file_info string is updated with information about the scan dimensions.
///
/// this function requires the MatrixFileReader.xop and pearl-matrix-import.ipf to be loaded.
/// otherwise it will return an empty wave reference.
///
/// @param	filename		name of a file in the directory specified by the pearl_explorer_filepath path object.
///
/// @return	wave reference of the preview image.
///			empty wave reference if the function failed.
///
static function /wave preview_mtrx_file(filename)
	string filename

#if exists("MFR_OpenResultFile")
	dfref saveDF = GetDataFolderDFR()
	setdatafolder $package_path
	variable /g V_MatrixFileReaderOverwrite = 1
	variable /g V_MatrixFileReaderFolder = 0
	variable /g V_MatrixFileReaderDouble = 0
	svar s_preview_file
	svar s_preview_source
	string datanames
	string dataname
	datanames = mtrx_load_preview("preview", "pearl_explorer_filepath", filename)
	if (strlen(datanames) > 0)
		s_preview_file = filename

		dataname = StringFromList(0, datanames)
		wave data = $dataname
		duplicate /o $dataname, preview_image
		s_preview_source = StringByKey("Dataset", note(data), "=", "\r")
		
		svar /z s_file_info
		if (svar_exists(s_file_info))
			s_file_info = ""
		endif
		
		variable i
		variable n = ItemsInList(datanames)
		string s
		for (i = 0; i < n; i += 1)
			s = StringFromList(i, datanames)
			killwaves /z $s
		endfor
	endif
	wave /z preview_image
	setdatafolder saveDF
#else
	wave /z preview_image = $""
#endif
	return preview_image
end

static function extract_preview_image(data, preview)
	// extracts a preview image from a wave of arbitrary dimension
	wave data
	wave preview

	variable z1, z2

	// extract image
	switch (WaveDims(data))
		case 1:
			redimension /n=(numpnts(data)) preview
			preview = data[p]
			break
		case 2:
			redimension /n=(dimsize(data, 0), dimsize(data, 1)) preview
			preview = data
			break
		case 3:
			redimension /n=(dimsize(data, 0), dimsize(data, 1)) preview
			z1 = floor(DimSize(data, 2) / 2)
			z2 = z1
			wave slab = ad_extract_slab(data, nan, nan, nan, nan, z1, z2, "", pscale=1)
			preview = slab
			break
		case 4:
			// not implemented
	endswitch
	
	switch (WaveDims(data))
		case 4:
		case 3:
		case 2:
			setscale /p y dimoffset(data, 1), dimdelta(data, 1), waveunits(data, 1), preview
		case 1:
			setscale /p x dimoffset(data, 0), dimdelta(data, 0), waveunits(data, 0), preview
			setscale d 0, 0, waveunits(data, -1), preview
	endswitch
end

static function preview_dataset(datasetname)
	string datasetname // name of a data folder under root
	
	dfref saveDF = GetDataFolderDFR()

	if (!DataFolderExists("root:" + datasetname))
		return -1
	endif
	setdatafolder root:
	setdatafolder $datasetname
	dfref datadf = GetDataFolderDFR()
	wave /z data

	setdatafolder $package_path
	svar s_preview_file
	svar s_preview_source
	wave preview_image
	if (WaveExists(data))
		s_preview_file = datasetname
		s_preview_source = ""
		extract_preview_image(data, preview_image)
		show_preview_graph(preview_image)
	else
		preview_image = nan
		s_preview_file = datasetname
		setdatafolder datadf
		preview_datafolder()
		show_preview_graph(preview_image)
	endif

	// attributes
	setdatafolder datadf
	if (DataFolderExists("attr"))
		setdatafolder attr
		preview_attributes(GetDataFolderDFR())
	else
		preview_attributes(GetDataFolderDFR(), include_datawaves=0)
	endif
	
	setdatafolder saveDF
	return 0
end

static function preview_datafolder()
	// preview data in the current data folder
	dfref saveDF = GetDataFolderDFR()

	setdatafolder $package_path
	svar s_preview_file
	svar s_preview_source
	svar s_preview_pvs
	wave preview_image

	setdatafolder saveDF
	
	// select a wave to display
	// consider only double-precision waves, i.e. ignore text and other special waves
	// filter by matching PV name to s_preview_pvs
	string d_names = WaveList("*", ";", "DP:1")
	variable nw = ItemsInList(d_names, ";")
	variable npv = ItemsInList(s_preview_pvs, ";")
	variable iw, ipv
	string wname, wnote, pv_name, pv_match
	for (iw = 0; iw < nw; iw += 1)
		wname = StringFromList(iw, d_names, ";")
		wnote = note($wname)
		pv_name = StringByKey("PV", wnote, "=", "\r")
		// find matching data wave by PV name
		for (ipv = 0; ipv < npv; ipv += 1)
			pv_match = StringFromList(ipv, s_preview_pvs)
			if (StringMatch(pv_name, pv_match))
				wave data = $wname
				s_preview_source = pv_name
				extract_preview_image(data, preview_image)
				preview_setscale_x(data, preview_image)
				npv = 0
				nw = 0
			endif
		endfor
	endfor
	
	setdatafolder saveDF
end

static function preview_setscale_x(data, preview)
	// sets the approximate x scale of OTF data.
	// requires an Axis1 tag with name of x wave in the wave note.
	// if any of these conditions is true, the function does not change the scaling:
	// 1) Axis1 tag or referenced wave is missing.
	// 2) preview wave is not set to point scaling.
	// 3) x wave is not monotonic (90% of the steps in the same direction).
	wave data
	wave preview
	
	if ((DimOffset(preview, 0) == 0) && (DimDelta(preview, 0) == 1))
		string xname = StringByKey("Axis1", note(data), "=", "\r")
		wave /z xwave = $xname
		if (WaveExists(xwave))
			// check for monotonicity
			variable monotonic = 0
			duplicate /free xwave, xdiff
			differentiate /p xwave /D=xdiff
			duplicate /free xdiff, xflag
			xflag = xdiff > 0
			monotonic = sum(xflag) > numpnts(xwave) * 0.9
			xflag = xdiff < 0
			monotonic = monotonic || (sum(xflag) > numpnts(xwave) * 0.9)
			if (monotonic)
				setscale /i x xwave[0], xwave[numpnts(xwave)-1], waveunits(xwave, -1), preview
			endif
		endif
	endif
end

static function preview_attributes(attr_folder, [dest_folder, attr_filter, include_datawaves, include_infowaves])
	// copies the first elements of attributes in the specified folder to the preview waves
	// by default, all existing attributes are copied
	// if a text wave attr_filter exists in the pear_explorer folder, only the attributes referenced therein are copied
	// to set up a filter, duplicate the attr_names wave of a template dataset, and remove unwanted items
	dfref attr_folder	// data folder which contains the attribute waves
	dfref dest_folder	// destination folder. the output is written to the attr_names and attr_values waves
		// default = package folder
	wave /t attr_filter // list of attributes allowed in the output
		// default = use attr_filter of package folder
	variable include_datawaves // 1 (default) = include data waves (any numeric wave which has a PV=name note)
		// 0 = don't include attributes from data waves
	variable include_infowaves // 1 (default) = include attributes from info waves (IN, ID, IV, IU)
		// 0 = don't include attributes from info waves
	
	dfref saveDF = GetDataFolderDFR()
	setdatafolder $package_path
	
	if (ParamIsDefault(dest_folder))
		dest_folder = GetDataFolderDFR()	// package folder
	endif
	if (ParamIsDefault(attr_filter))
		wave /t /z attr_filter
	endif
	if (ParamIsDefault(include_datawaves))
		include_datawaves = 1
	endif
	if (ParamIsDefault(include_infowaves))
		include_infowaves = 1
	endif
	
	setdatafolder dest_folder
	wave /t /z attr_names, attr_values
	if (!WaveExists(attr_names) || !WaveExists(attr_values))
		make /n=(1) /o /t attr_names, attr_values
	endif
	attr_names = ""
	attr_values = ""
	
	setdatafolder attr_folder
	wave /t /z IN
	wave /t /z ID
	wave /t /z IV
	wave /t /z IU

	// compile list of attributes
	variable nattr	// destination attributes
	variable iattr
	variable ninfo // info wave elements
	variable iinfo
	variable nw	// attribute waves
	variable iw
	string sw
	string ss

	if (WaveExists(IN) && include_infowaves)
		ninfo = numpnts(IN)
	else
		ninfo = 0
	endif
	if (include_datawaves)
		string waves = WaveList("*", ";", "")
		string exceptions = "ID;IN;IU;IV"
		waves = RemoveFromList(exceptions, waves)
		nw = ItemsInList(waves, ";")
	else
		nw = 0
	endif
	
	if (WaveExists(attr_filter) && (numpnts(attr_filter) >= 1))
		nattr = numpnts(attr_filter)
		redimension /n=(nattr)  attr_names
		attr_names = attr_filter
	else
		if(ninfo > 0)
			redimension /n=(ninfo) attr_names
			attr_names = SelectString(strlen(ID[p]) >= 0, IN[p], ID[p])	// use ID unless empty
		endif
		
		nattr = ninfo + nw
		iattr = ninfo
		redimension /n=(nattr) attr_names
		for (iw = 0; iw < nw; iw +=1 )
			sw = StringFromList(iw, waves, ";")
			ss = StringByKey("PV", note($sw), "=", "\r")
			FindValue /text=sw attr_names
			if ((v_value < 0) && (strlen(ss) >= 0))
				attr_names[iattr] = sw
				iattr += 1
			endif
		endfor
		nattr = iattr
	endif
	redimension /n=(nattr)  attr_names, attr_values
	sort attr_names, attr_names
	
	// look up attribute values
	for (iattr = 0; iattr < nattr; iattr += 1)
		sw = attr_names[iattr]
		// try info waves
		if (ninfo > 0)
			FindValue /text=sw ID
			if (v_value >= 0)
				attr_values[iattr] = IV[v_value]
			endif
			FindValue /text=sw IN
			if (v_value >= 0)
				attr_values[iattr] = IV[v_value]
			endif
		endif
		
		// override from attribute wave if existent
		if (nw > 0)
			switch (WaveType($sw, 1))
				case 1: // numeric
					wave /z w = $sw
					if (WaveExists(w) && (numpnts(w) >= 1))
						sprintf ss, "%g", w[0]
						attr_values[iattr] = ss
					endif
					break
				case 2: // text
					wave /t/z wt = $sw
					if (WaveExists(wt) && (numpnts(wt) >= 1))
						attr_values[iattr] = wt[0]
					endif
					break
			endswitch
		endif
	endfor
	
	setdatafolder saveDF
end

static function display_dataset(datasetname)
	// displays the graph of a loaded dataset in its own window
	string datasetname // name of a data folder under root
	
	dfref saveDF = GetDataFolderDFR()

	if (!DataFolderExists("root:" + datasetname))
		return -1
	endif
	setdatafolder root:
	setdatafolder $datasetname
	dfref datadf = GetDataFolderDFR()
	wave /z data
	if (!WaveExists(data))
		wave /z data = data1
	endif

	if (WaveExists(data))
		switch(WaveDims(data))
			case 2:
				ad_display_profiles(data)
				break
			case 3:
				ad_display_slice(data)
				ad_brick_slicer(data)
				break
		endswitch
	endif

	setdatafolder saveDF
	return 0
end

function test_attributes_notebook()
	dfref df = GetDataFolderDFR()
	wave /t /sdfr=df attr_names
	wave /t /sdfr=df attr_values
	attributes_notebook(attr_names, attr_values, GetDataFolder(0))
end

static function attributes_notebook(attr_names, attr_values, title)
	wave /t attr_names
	wave /t attr_values
	string title
	
	dfref saveDF = GetDataFolderDFR()
	setdatafolder $package_path
	wave /t/z attr_filter, attr_filter_summary

	string name = PearlCleanupName("nb_" + title[0,27])
	if (WinType(name) == 5)
		Notebook $name selection={startOfFile, endOfFile}	
		Notebook $name text=""
	else
		NewNotebook /F=1 /K=1 /N=$name as title
		GetWindow $name wsize
		v_right = v_left + 260
		v_bottom = v_top + 360
		MoveWindow /W=$name v_left, v_top, v_right, v_bottom
		Notebook $name tabs={2*72}
	endif

	// summary
	if (WaveExists(attr_filter_summary) && (numpnts(attr_filter_summary) >= 1))
		notebook $name fStyle=1, text="Summary\r\r"
		notebook $name fStyle=0
		notebook_add_attributes(name, attr_filter_summary, attr_names, attr_values)
		notebook $name text="\r"
	endif

	// all attributes
	notebook $name fStyle=1, text="All Attributes\r\r"
	notebook $name fStyle=0
	notebook_add_attributes(name, $"", attr_names, attr_values)
	notebook $name selection={startOfFile,startOfFile}, findText={"",1}
	
	setdatafolder saveDF
end

static function notebook_add_attributes(notebook_name, attr_filter, attr_names, attr_values)
	string notebook_name
	wave /t /z attr_filter
	wave /t attr_names
	wave /t attr_values

	variable nw = numpnts(attr_names)
	variable iw
	string sw
	string ss

	variable do_filter = WaveExists(attr_filter)
	
	for (iw = 0; iw < nw; iw += 1)
		if (do_filter)
			sw = attr_names[iw]
			FindValue /text=sw attr_filter
		else
			v_value = 0
		endif
		if (v_value >= 0)
			sprintf ss, "%s\t%s\r", attr_names[iw], attr_values[iw]
			notebook $notebook_name text=ss
		endif
	endfor
end

static function /s show_preview_graph(data, [xdata])
	// displays a preview of one- or two-dimensional data
	wave data // data to be displayed. must either one-dimensional or two-dimensional
	wave xdata // positions on x axis
	
	dfref saveDF = GetDataFolderDFR()
	setdatafolder $package_path
	
	svar s_profiles_graph
	svar s_preview_file
	svar s_preview_source
	svar s_preview_trace_graph
	
	if ((strlen(s_profiles_graph) > 0) && (WinType(s_profiles_graph) == 1))
		KillWindow $s_profiles_graph
	endif
	if ((strlen(s_preview_trace_graph) > 0) && (WinType(s_preview_trace_graph) == 1))
		KillWindow $s_preview_trace_graph
	endif

	string graphname
	if (wavedims(data) == 2)
		s_profiles_graph = ad_display_profiles(data)
		ModifyGraph /w=$s_profiles_graph /z wbRGB=(48640,56832,60160)
		graphname = s_profiles_graph
	elseif (wavedims(data) == 1)
		duplicate /o data, preview_trace
		if (!ParamIsDefault(xdata))
			duplicate /o xdata, preview_trace_x
		else
			duplicate /o data, preview_trace_x
			preview_trace_x = x
			setscale d 0, 0, WaveUnits(data, 0), preview_trace_x
		endif
		s_preview_trace_graph = display_preview_trace(preview_trace_x, preview_trace)
		ModifyGraph /w=$s_preview_trace_graph wbRGB=(48640,56832,60160)
		graphname = s_preview_trace_graph
	else
		return ""
	endif
	
	string title = "Preview " + s_preview_file
	if (strlen(s_preview_source) > 0)
		title = title + " (" + s_preview_source[0,31] + ")"
	endif
	dowindow /f/t $graphname, title
	
	setdatafolder saveDF
	return graphname
end

static function /s display_preview_trace(xtrace, ytrace)
	wave xtrace
	wave ytrace
	
	display /n=pearl_explorer_1d /k=1 ytrace vs xtrace as "Preview"
	string graphname = s_name
	ModifyGraph /w=$graphname rgb[0]=(0,0,0)
	ModifyGraph /w=$graphname grid=2
	ModifyGraph /w=$graphname mirror=1
	ModifyGraph /w=$graphname minor=1
	ModifyGraph /w=$graphname axThick=0.5
	ModifyGraph /w=$graphname gridRGB=(52224,52224,52224)
	ModifyGraph /w=$graphname gridHair=0
	ModifyGraph /w=$graphname tick=0
	ModifyGraph /w=$graphname btLen=4
	
	// axis labels	
	string labels = note(ytrace)
	string lab
	lab = StringByKey("AxisLabelX", labels, "=", "\r")
	if (!strlen(lab))
		lab = "X"
	endif
	Label /w=$graphname bottom lab + " (\\U)"
	lab = StringByKey("AxisLabelD", labels, "=", "\r")
	if (!strlen(lab))
		lab = "value"
	endif
	Label /w=$graphname left lab + " (\\U)"
	
	return s_name
end

static function load_selected_files([options])
	string options
	
	dfref saveDF = GetDataFolderDFR()
	setdatafolder $package_path
	
	wave wSelectedFiles
	wave/t wtFiles
	variable nn = numpnts(wSelectedFiles)
	variable ii
	for (ii = 0; ii < nn; ii += 1)
		if (wSelectedFiles[ii])
			if (ParamIsDefault(options))
				load_file(wtFiles[ii])
			else
				load_file(wtFiles[ii], options=options)
			endif
		endif
	endfor
	
	update_datasets()
	setdatafolder saveDF
end

static function load_file(filename, [options])
	string filename
	string options
	
	dfref saveDF = GetDataFolderDFR()

	variable ft = pearl_file_type(filename)
	switch(ft)
		case 1:
			if (ParamIsDefault(options))
				load_pshell_file(filename)
			else
				load_pshell_file(filename, options=options)
			endif
			break
		case 2:
			if (ParamIsDefault(options))
				load_hdf_file(filename)
			else
				load_hdf_file(filename, options=options)
			endif
			break
		case 3:
			load_itx_file(filename)
			break
		case 4:
			load_mtrx_file(filename)
			break
		default:
			break
	endswitch

	setdatafolder saveDF
end

static function prompt_hdf_options(options)
	string &options

	string mode = StringByKey("mode", options, ":", ";")
	string reduction_func = StringByKey("reduction_func", options, ":", ";")
	
	string modes = "load_reduced"
	string reduction_functions = adh5_list_reduction_funcs()
	
	if (strlen(mode) == 0)
		mode = StringFromList(0, modes, ";")
	endif
	if (strlen(reduction_func) == 0)
		reduction_func = StringFromList(0, reduction_functions, ";")
	endif
	
	prompt mode, "Mode", popup, modes
	prompt reduction_func, "Reduction Function", popup, reduction_functions
	doprompt "HDF5 Loading Options", mode, reduction_func

	if (v_flag == 0)
		options = ReplaceStringByKey("mode", options, mode, ":", ";")
		options = ReplaceStringByKey("reduction_func", options, reduction_func, ":", ";")
	endif
	return v_flag // 0 = OK, 1 = cancel
end

/// prototype for prompting for processing function parameters.
///
/// the function should prompt the user for function parameters,
/// and update the param argument if the user clicked OK.
/// returns 0 if the user clicked OK, 1 if the user cancelled.
///	
/// prompt functions must have the same name as the corresponding reduction function
/// with the prefix "prompt_".
/// be aware of the limited length of function names in Igor.
///
/// this function is a prototype. it does nothing but returns OK.
///
function prompt_default_process(param)
	string &param
	
	return 0
end

function prompt_func_params(func_name, func_param)
	string func_name
	string &func_param
	
	string prompt_name = "prompt_" + func_name
	if (exists(prompt_name) == 6)
		funcref prompt_default_process prompt_func = $prompt_name
		return prompt_func(func_param)
	else
		// ignore missing prompt function
		return 0
	endif
end

static function /df load_pshell_file(filename, [options])
	string filename
	string options

	dfref saveDF = GetDataFolderDFR()
	string nickname = ad_suggest_foldername(filename)
	string loaded_filename = ""
	
	if (ParamIsDefault(options))
		loaded_filename = psh5_load_complete(nickname, "pearl_explorer_filepath", filename)
	else
		if (strlen(options) == 0)
			svar pref_options = $(package_path + "s_hdf_options")
			options = pref_options
			if (prompt_hdf_options(options) == 0)
				// OK
				pref_options = options
			else
				// cancel
				options = ""
			endif
		endif
		
		string mode = StringByKey("mode", options, ":", ";")
		
		strswitch(mode)
			case "load_reduced":
				string reduction_func = StringByKey("reduction_func", options, ":", ";")
				svar pref_params = $(package_path + "s_reduction_params")
				string reduction_params = pref_params
				if (prompt_func_params(reduction_func, reduction_params) == 0)
					pref_params = reduction_params
					print reduction_func, reduction_params
					psh5_load_reduced(nickname, "pearl_explorer_filepath", filename, $reduction_func, reduction_params)
					svar s_filepath
					loaded_filename = s_filepath
				endif
				break
		endswitch
	endif

	dfref dataDF	
	if (strlen(loaded_filename) > 0)
		setdatafolder $("root:" + nickname)
		dataDF = GetDataFolderDFR()
		string /g pearl_explorer_import = "load_pshell_file"
	endif
	
	setdatafolder saveDF
	return dataDF
end

static function /df load_hdf_file(filename, [options])
	string filename
	string options

	dfref saveDF = GetDataFolderDFR()
	string nickname = ad_suggest_foldername(filename)
	string loaded_filename = ""
	
	if (ParamIsDefault(options))
		loaded_filename = adh5_load_complete(nickname, "pearl_explorer_filepath", filename)
	else
		if (strlen(options) == 0)
			svar pref_options = $(package_path + "s_hdf_options")
			options = pref_options
			if (prompt_hdf_options(options) == 0)
				// OK
				pref_options = options
			else
				// cancel
				options = ""
			endif
		endif
		
		string mode = StringByKey("mode", options, ":", ";")
		
		strswitch(mode)
			case "load_reduced":
				string reduction_func = StringByKey("reduction_func", options, ":", ";")
				svar pref_params = $(package_path + "s_reduction_params")
				string reduction_params = pref_params
				if (prompt_func_params(reduction_func, reduction_params) == 0)
					pref_params = reduction_params
					print reduction_func, reduction_params
					loaded_filename = adh5_load_reduced(nickname, "pearl_explorer_filepath", filename, $reduction_func, reduction_params)
				endif
				break
		endswitch
	endif

	dfref dataDF	
	if (strlen(loaded_filename) > 0)
		setdatafolder $("root:" + nickname)
		dataDF = GetDataFolderDFR()
		string /g pearl_explorer_import = "load_hdf_file"
	endif
	
	setdatafolder saveDF
	return dataDF
end

static function /df load_itx_file(filename, [options])
	string filename
	string options

	dfref saveDF = GetDataFolderDFR()
	string nickname = itx_suggest_foldername(filename)

	if (ParamIsDefault(options))
		options = ""
	endif
	
	setdatafolder root:
	newdatafolder /s/o $("root:" + nickname)
	dfref dataDF = GetDataFolderDFR()
	
	// note: some versions of PEARL data files save data to a new data folder,
	// and leave the newly created folder as the current folder.
	// the free data folder is used by those files which don't create their own data folder.
	// this is the new recommended behaviour

	LoadWave /t/p=pearl_explorer_filepath/q filename
	svar waves = s_wavenames
	dfref actDF = GetDataFolderDFR()
	if (v_flag > 0)
		string /g pearl_explorer_import = "load_itx_file"
	endif
	
	if (!DataFolderRefsEqual(actDF, dataDF))
		// the file created its own data folder.
		// let's kill the pre-allocated folder
		setdatafolder dataDF
		if (ItemsInList(WaveList("*", ";", ""), ";") == 0)
			killdatafolder /z dataDF
		endif
	endif
	
	setdatafolder saveDF
	return actDF
end

/// load a matrix (STM) data file
///
///
static function /df load_mtrx_file(filename, [options])
	string filename
	string options

	dfref saveDF = GetDataFolderDFR()
	dfref dataDF = $""

#if exists("MFR_OpenResultFile")
	setdatafolder root:
	string datasets = ""
	datasets = mtrx_load_file("pearl_explorer_filepath", filename)
	if (strlen(datasets) > 0)
		string /g pearl_explorer_import = "load_mtrx_file"
		string s1 = StringFromList(0, datasets)
		wave w1 = $s1
		dataDF = GetWavesDataFolderDFR(w1)
	endif
#endif
	
	setdatafolder saveDF
	return dataDF
end

function /s itx_suggest_foldername(filename, [ignoredate,sourcename,unique])
	// suggests the name of a data folder based on a file name
	// if the file name follows the naming convention source-date-index.extension,
	// the function tries to generate the nick name as source_date_index.
	// otherwise it's just a cleaned up version of the file name.
	string filename // file name, including extension. can also include a folder path (which is ignored)
		// the extension is currently ignored, but may be used later to select the parent folder
	variable ignoredate // if non-zero, the nick name will not include the date part
		// defaults to zero
	string sourcename // nick name of the data source
		// the function tries to detect the source from the file name
		// this option can be used to override auto-detection
		// allowed values: sscan, otf
	variable unique // if non-zero, the resulting name is made a unique data folder name in the current data folder
		// defaults to zero
	
	if (ParamIsDefault(ignoredate))
		ignoredate = 0
	endif
	if (ParamIsDefault(unique))
		unique = 0
	endif
	
	string basename = ParseFilePath(3, filename, ":", 0, 0)
	string extension = ParseFilePath(4, filename, ":", 0, 0)
	string nickname
	
	string autosource
	if (strsearch(basename, "X03DA_PC", 0, 2) >= 0)
		autosource = "sscan"
		basename = ReplaceString("_", basename, "-")
		ignoredate = 1
	elseif (strsearch(basename, "otf", 0, 2) >= 0)
		autosource = "otf"
	endif
	if (ParamIsDefault(sourcename))
		sourcename = autosource
	endif
	
	variable nparts = ItemsInList(basename, "-")
	if (nparts >= 3)
		string datepart = StringFromList(nparts - 2, basename, "-")
		string indexpart = StringFromList(nparts - 1, basename, "-")
		if (ignoredate)
			sprintf nickname, "%s_%s", sourcename, indexpart
		else
			sprintf nickname, "%s_%s_%s", sourcename, datepart, indexpart
		endif
	else
		nickname = PearlCleanupName(basename)
	endif
	
	if (unique && CheckName(nickname, 11))
		nickname = UniqueName(nickname + "_", 11, 0)
	endif
	
	return nickname
end

Window PearlDataExplorer() : Panel
	PauseUpdate; Silent 1		// building window...
	NewPanel /K=1 /W=(800,0,1530,444) as "PEARL Data Explorer"
	ModifyPanel cbRGB=(48640,56832,60160)
	
	GroupBox gb_filepath,pos={8,4},size={224,52},title="file system folder"
	TitleBox tb_filepath,pos={20,24},size={174,20},frame=2
	TitleBox tb_filepath,variable=root:packages:pearl_explorer:s_filepath,fixedSize=1
	Button b_browse_filepath,pos={200,24},size={20,20},proc=PearlDataExplorer#bp_browse_filepath,title="..."
	Button b_browse_filepath,fColor=(65280,48896,32768)
	
	GroupBox gb_prefs,pos={240,4},size={58,52},title="prefs",help={"explorer package preferences"}
	Button b_save_prefs,pos={252,20},size={32,17},proc=PearlDataExplorer#bp_save_prefs,title="save"
	Button b_save_prefs,help={"save preferences of the data explorer package (data file path, attributes filter)"}
	Button b_save_prefs,fColor=(65280,48896,32768)
	Button b_load_prefs,pos={252,36},size={32,17},proc=PearlDataExplorer#bp_load_prefs,title="load"
	Button b_load_prefs,help={"load preferences of the data explorer package"}
	Button b_load_prefs,fColor=(65280,48896,32768)
	
	GroupBox gb_filelist,pos={8,64},size={224,372},title="data files"
	ListBox lb_files,pos={20,84},size={200,212},proc=PearlDataExplorer#lbp_filelist
	ListBox lb_files,listWave=root:packages:pearl_explorer:wtFiles
	ListBox lb_files,selWave=root:packages:pearl_explorer:wSelectedFiles,row= 11,mode= 4
	TitleBox tb_file_info,pos={20,300},size={198,78},frame=2,fixedSize=1
	TitleBox tb_file_info,variable= root:packages:pearl_explorer:s_file_info

	Button b_update_filelist,pos={20,386},size={60,20},proc=PearlDataExplorer#bp_update_filelist,title="update list"
	Button b_update_filelist,fColor=(65280,48896,32768)
	CheckBox cb_file_preview,pos={84,390},size={60,20},title="preview"
	CheckBox cb_file_preview,help={"enable/disable automatic preview window when selecting a data file"}
	CheckBox cb_file_preview,value=1
	Button b_file_prev,pos={176,386},size={20,20},proc=PearlDataExplorer#bp_file_prev,title="\\W646"
	Button b_file_prev,help={"previous file"}
	Button b_file_prev,fColor=(65280,48896,32768)
	Button b_file_next,pos={200,386},size={20,20},proc=PearlDataExplorer#bp_file_next,title="\\W649"
	Button b_file_next,help={"next file"}
	Button b_file_next,fColor=(65280,48896,32768)

	Button b_load_files,pos={20,410},size={76,20},proc=PearlDataExplorer#bp_load_files,title="load complete"
	Button b_load_files,help={"load the complete contents from the selected files"}
	Button b_load_files,fColor=(65280,48896,32768)
	Button b_load_files_opt,pos={100,410},size={76,20},proc=PearlDataExplorer#bp_load_files_opt,title="load reduced"
	Button b_load_files_opt,help={"load data from the selected files with options (reduced dimensions)"}
	Button b_load_files_opt,fColor=(65280,48896,32768)

	// datasets group	
	GroupBox gb_datasets,pos={240,64},size={224,372},title="datasets"
	ListBox lb_datasets,pos={252,84},size={200,300},proc=PearlDataExplorer#lbp_datasets,help={"list of loaded datasets"}
	ListBox lb_datasets,listWave=root:packages:pearl_explorer:wtDatasets
	ListBox lb_datasets,selWave=root:packages:pearl_explorer:wSelectedDatasets,mode= 1
	ListBox lb_datasets,selRow= -1

	Button b_update_datasets,pos={252,386},size={60,20},proc=PearlDataExplorer#bp_update_datasets,title="update list"
	Button b_update_datasets,help={"update the list of datasets"}
	Button b_update_datasets,fColor=(65280,48896,32768)
	CheckBox cb_dataset_preview,pos={316,390},size={60,20},title="preview"
	CheckBox cb_dataset_preview,help={"enable/disable automatic preview window when selecting a dataset"}
	CheckBox cb_dataset_preview,value=0
	Button b_dataset_prev,pos={408,386},size={20,20},proc=PearlDataExplorer#bp_dataset_prev,title="\\W646"
	Button b_dataset_prev,help={"goto previous dataset"}
	Button b_dataset_prev,fColor=(65280,48896,32768)
	Button b_dataset_next,pos={432,386},size={20,20},proc=PearlDataExplorer#bp_dataset_next,title="\\W649"
	Button b_dataset_next,help={"goto next dataset"}
	Button b_dataset_next,fColor=(65280,48896,32768)
	
	Button b_dataset_folder,pos={252,410},size={50,20},proc=PearlDataExplorer#bp_dataset_folder,title="goto DF"
	Button b_dataset_folder,help={"set the current data folder of the selected dataset"}
	Button b_dataset_folder,fColor=(65280,48896,32768)
	Button b_dataset_display,pos={306,410},size={50,20},proc=PearlDataExplorer#bp_dataset_display,title="display"
	Button b_dataset_display,help={"display the selected dataset in its own window"}
	Button b_dataset_display,fColor=(65280,48896,32768)

	GroupBox gb_preview,pos={472,4},size={250,52},title="preview"
	TitleBox tb_preview_file,pos={484,24},size={226,20},frame=2
	TitleBox tb_preview_file,variable=root:packages:pearl_explorer:s_preview_file,fixedSize=1

	GroupBox gb_attributes,pos={472,64},size={250,372},title="attributes"
	Button b_attr_notebook,pos={484,386},size={60,20},proc=PearlDataExplorer#bp_attr_notebook,title="notebook"
	Button b_attr_notebook,help={"show attribute list in a notebook"}
	Button b_attr_notebook,fColor=(65280,48896,32768)

	String fldrSav0= GetDataFolder(1)
	SetDataFolder root:packages:pearl_explorer:
	Edit/W=(484,84,710,384)/HOST=#  attr_names,attr_values
	ModifyTable format(Point)=1,width(Point)=0,width(attr_names)=103,width(attr_values)=103
	ModifyTable statsArea=85
	SetDataFolder fldrSav0
	RenameWindow #,T0
	SetActiveSubwindow ##
EndMacro

static function bp_load_prefs(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			load_prefs()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_save_prefs(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			save_prefs()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_browse_filepath(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	dfref saveDF = GetDataFolderDFR()

	switch( ba.eventCode )
		case 2: // mouse up
			PathInfo /S pearl_explorer_filepath
			NewPath /M="select data file folder" /O/Z pearl_explorer_filepath
			if (v_flag == 0)
				PathInfo /S pearl_explorer_filepath
				svar filepath = $(package_path + "s_filepath")
				filepath = s_path
				update_filelist()
			endif
			break
		case -1: // control being killed
			break
	endswitch

	setdatafolder saveDF
	return 0
End

static function bp_update_filelist(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			update_filelist()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_load_files(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			load_selected_files()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_load_files_opt(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			load_selected_files(options="")
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_file_next(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	dfref saveDF = GetDataFolderDFR()
	
	switch( ba.eventCode )
		case 2: // mouse up
			setdatafolder $package_path
			wave /t wtFiles
			wave wSelectedFiles
			FindValue /i=1 wSelectedFiles
			v_value += 1
			if (v_value >= numpnts(wtFiles))
				v_value = min(numpnts(wtFiles) - 1, 0)
			endif
			wSelectedFiles = p == v_value
			if (v_value >= 0)
				variable ifile = v_value
				ControlInfo /W=PearlDataExplorer cb_file_preview
				if (v_value)
					preview_file(wtFiles[ifile])
				endif
			endif
			break
		case -1: // control being killed
			break
	endswitch

	setdatafolder saveDF
	return 0
End

static function bp_file_prev(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	dfref saveDF = GetDataFolderDFR()
	
	switch( ba.eventCode )
		case 2: // mouse up
			setdatafolder $package_path
			wave /t wtFiles
			wave wSelectedFiles
			FindValue /i=1 wSelectedFiles
			v_value -= 1
			if (v_value < 0)
				v_value = numpnts(wtFiles) - 1
			endif
			wSelectedFiles = p == v_value
			if (v_value >= 0)
				variable ifile = v_value
				ControlInfo /W=PearlDataExplorer cb_file_preview
				if (v_value)
					preview_file(wtFiles[ifile])
				endif
			endif
			break
		case -1: // control being killed
			break
	endswitch

	setdatafolder saveDF
	return 0
End

static function lbp_filelist(lba) : ListBoxControl
	STRUCT WMListboxAction &lba

	Variable row = lba.row
	Variable col = lba.col
	WAVE/T/Z listWave = lba.listWave
	WAVE/Z selWave = lba.selWave

	switch( lba.eventCode )
		case -1: // control being killed
			break
		case 1: // mouse down
			if (selWave[row])
				ControlInfo /W=PearlDataExplorer cb_file_preview
				if (v_value)
					preview_file(listWave[row])
				endif
			endif
			break
		case 3: // double click
			break
		case 4: // cell selection
		case 5: // cell selection plus shift key
			break
		case 6: // begin edit
			break
		case 7: // finish edit
			break
		case 13: // checkbox clicked (Igor 6.2 or later)
			break
	endswitch

	return 0
End

static function bp_update_datasets(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			update_datasets()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_dataset_folder(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			ControlInfo /W=PearlDataExplorer lb_datasets
			if (v_value >= 0)
				setdatafolder $package_path
				wave /t wtDatasets
				string dataset = wtDatasets[v_value]
				string cmd
				sprintf cmd, "setdatafolder root:%s", PossiblyQuoteName(dataset)
				execute /q /z cmd
				cmd = "setdatafolder :scan1"
				execute /q /z cmd
				sprintf cmd, "setdatafolder %s", GetDataFolder(1)
				print cmd
			endif
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_dataset_display(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			ControlInfo /W=PearlDataExplorer lb_datasets
			if (v_value >= 0)
				setdatafolder $package_path
				wave /t wtDatasets
				string dataset = wtDatasets[v_value]
				display_dataset(dataset)
			endif
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_dataset_next(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			ControlInfo /W=PearlDataExplorer lb_datasets
			wave /t wtDatasets = $(s_datafolder + s_value)
			v_value += 1
			if (v_value >= numpnts(wtDatasets))
				v_value = min(0, numpnts(wtDatasets) - 1)
			endif
			ListBox lb_datasets win=PearlDataExplorer, selRow=v_value
			if (v_value >= 0)
				variable ids = v_value
				ControlInfo /W=PearlDataExplorer cb_dataset_preview
				if (v_value)
					preview_dataset(wtDatasets[ids])
				endif
			endif
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_dataset_prev(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			ControlInfo /W=PearlDataExplorer lb_datasets
			wave /t wtDatasets = $(s_datafolder + s_value)
			v_value -= 1
			if (v_value < 0)
				v_value = max(-1, numpnts(wtDatasets) - 1)
			endif
			ListBox lb_datasets win=PearlDataExplorer, selRow=v_value
			if (v_value >= 0)
				variable ids = v_value
				ControlInfo /W=PearlDataExplorer cb_dataset_preview
				if (v_value)
					preview_dataset(wtDatasets[ids])
				endif
			endif
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function lbp_datasets(lba) : ListBoxControl
	STRUCT WMListboxAction &lba

	Variable row = lba.row
	Variable col = lba.col
	WAVE/T/Z listWave = lba.listWave
	WAVE/Z selWave = lba.selWave

	switch( lba.eventCode )
		case -1: // control being killed
			break
		case 1: // mouse down
			if (row >= 0)
				ControlInfo /W=PearlDataExplorer cb_dataset_preview
				if (v_value)
					preview_dataset(listWave[row])
				endif
			endif
			break
		case 3: // double click
			break
		case 4: // cell selection
		case 5: // cell selection plus shift key
			break
		case 6: // begin edit
			break
		case 7: // finish edit
			break
		case 13: // checkbox clicked (Igor 6.2 or later)
			break
	endswitch

	return 0
End

static function bp_attr_notebook(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	dfref saveDF = GetDataFolderDFR()

	switch( ba.eventCode )
		case 2: // mouse up
			setdatafolder $package_path
			svar s_preview_file
			wave /t /z attr_names
			wave /t /z attr_values
			if (WaveExists(attr_names))
				attributes_notebook(attr_names, attr_values, s_preview_file)
			endif
			break
		case -1: // control being killed
			break
	endswitch

	setdatafolder saveDF
	return 0
End
