/**************************************************************

chart_dlg.cpp (C-Munipack project)
The 'Plot chart' dialog
Copyright (C) 2008 David Motl, dmotl@volny.cz

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

**************************************************************/

#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "utils.h"
#include "main.h"
#include "ctxhelp.h"
#include "configuration.h"
#include "export_aavso.h"
#include "config.h"

struct tAAVSOPageData
{
	CAavsoExportTab *tab;
	bool skip;
	tAAVSOPageData(CAavsoExportTab *t):tab(t), skip(false) {}
};

static tAAVSOPageData *MakePage(CAavsoExportTab *tab)
{
	return new tAAVSOPageData(tab);
}

static void DeletePage(tAAVSOPageData *data, gpointer *user_data)
{
	delete data->tab;
}

tAavsoExportParams::tAavsoExportParams():star_id(NULL), obs_code(NULL), obs_type(NULL), chart(NULL), 
	filter(NULL), notes(NULL), kname(NULL) 
{
	memset(cname, 0, CNAME_MAX*sizeof(gchar*));
	memset(cmag, 0, CNAME_MAX*sizeof(gchar*));
}

tAavsoExportParams::~tAavsoExportParams()
{
	g_free(star_id);
	g_free(obs_code);
	g_free(obs_type);
	g_free(chart);
	g_free(filter);
	g_free(notes);
	for (int i=0; i<CNAME_MAX; i++) {	
		g_free(cname[i]);
		g_free(cmag[i]);
	}
	g_free(kname);
}

static void UpdateString(gchar *&str, const gchar *value)
{
	if (StrCmp0(str, value)) {
		g_free(str);
		if (value && *value!='\0') 
			str = g_strdup(value);
		else 
			str = NULL;
	}
}

//--------------------------   AAVSO Export   -------------------------------

//
// Constructor
//
CAavsoExportDlg::CAavsoExportDlg(GtkWindow *pParent):m_Pages(NULL), m_heliocentric(false),
	m_haveCheckStar(false), m_ncomp(0)
{
	m_pDlg = gtk_dialog_new_with_buttons("Export light curve in AAVSO format", pParent, 
		(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 
		GTK_STOCK_GO_BACK, GTK_RESPONSE_NO, GTK_STOCK_GO_FORWARD, GTK_RESPONSE_YES, 
		GTK_STOCK_SAVE, GTK_RESPONSE_OK, GTK_STOCK_HELP, GTK_RESPONSE_HELP, NULL);
	gtk_dialog_widget_standard_tooltips(GTK_DIALOG(m_pDlg));
	gtk_window_set_position(GTK_WINDOW(m_pDlg), GTK_WIN_POS_CENTER);
	m_pSaveBtn = get_dialog_widget_by_response(GTK_DIALOG(m_pDlg), GTK_RESPONSE_OK);
	m_pCancelBtn = get_dialog_widget_by_response(GTK_DIALOG(m_pDlg), GTK_RESPONSE_CANCEL);
	m_pBackBtn = get_dialog_widget_by_response(GTK_DIALOG(m_pDlg), GTK_RESPONSE_NO);
	gtk_widget_set_tooltip_text(m_pBackBtn, "Go back to the previous page");
	m_pNextBtn = get_dialog_widget_by_response(GTK_DIALOG(m_pDlg), GTK_RESPONSE_YES);
	gtk_widget_set_tooltip_text(m_pNextBtn, "Continue to the next page");
	g_signal_connect(G_OBJECT(m_pDlg), "response", G_CALLBACK(response_dialog), this);

	// Dialog icon
	gchar *icon = get_icon_file("lightcurve");
	gtk_window_set_icon(GTK_WINDOW(m_pDlg), gdk_pixbuf_new_from_file(icon, NULL));
	g_free(icon);

	// Page 1: Observation data / Variable star
	m_Pages = g_slist_append(m_Pages, MakePage(new CAavsoExportInitTab(this)));

	// Page 2: Comparison star
	m_Pages = g_slist_append(m_Pages, MakePage(new CAavsoExportCompTab(this)));

	// Page 3: Comparison stars (ensemble photometry)
	m_Pages = g_slist_append(m_Pages, MakePage(new CAavsoExportEnsembleTab(this)));

	// Page 4: Check star
	m_Pages = g_slist_append(m_Pages, MakePage(new CAavsoExportCheckTab(this)));

	// Notebook
	m_pTabs = gtk_notebook_new();
	gtk_widget_set_size_request(m_pTabs, 512, 320);
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(m_pDlg)->vbox), m_pTabs);
	gtk_container_set_border_width(GTK_CONTAINER(m_pTabs), 8);
	gtk_notebook_set_show_border(GTK_NOTEBOOK(m_pTabs), FALSE);
	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(m_pTabs), FALSE);
	for (GSList *ptr=m_Pages; ptr!=NULL; ptr=ptr->next) 
		gtk_notebook_append_page(GTK_NOTEBOOK(m_pTabs), ((tAAVSOPageData*)ptr->data)->tab->Widget(), NULL);

	gtk_widget_show_all(GTK_DIALOG(m_pDlg)->vbox);
}

CAavsoExportDlg::~CAavsoExportDlg()
{
	gtk_widget_destroy(m_pDlg);
	g_slist_foreach(m_Pages, (GFunc)DeletePage, NULL);
	g_slist_free(m_Pages);
}

void CAavsoExportDlg::LoadParams(void)
{
	// Variable star designation
	gchar *star_id = g_Project->GetStr("AAVSO", "STAR_ID");
	if (!star_id || *star_id=='\0') {
		const CObjectCoords *obj = g_Project->ObjectCoords();
		if (obj->Name())
			star_id = g_strdup(obj->Name());
	}
	UpdateString(m_params.star_id, star_id);
	g_free(star_id);

	// Observer code
	gchar *obs_code = g_Project->GetStr("AAVSO", "OBS_CODE");
	if (!obs_code || *obs_code=='\0') 
		obs_code = CConfig::GetStr("AAVSO", "OBS_CODE");
	UpdateString(m_params.obs_code, obs_code);
	g_free(obs_code);

	// Observation type
	gchar *obs_type = g_Project->GetStr("AAVSO", "OBS_TYPE");
	if (!obs_type || *obs_type=='\0') 
		obs_type = CConfig::GetStr("AAVSO", "OBS_TYPE");
	UpdateString(m_params.obs_type, obs_type);
	g_free(obs_type);

	// Filter
	gchar *filter = g_Project->GetStr("AAVSO", "FILTER");
	UpdateString(m_params.filter, filter);
	g_free(filter);

	// Chart
	gchar *chart = g_Project->GetStr("AAVSO", "CHART");
	UpdateString(m_params.chart, chart);
	g_free(chart);

	// Notes
	gchar *notes = g_Project->GetStr("AAVSO", "NOTES");
	UpdateString(m_params.notes, notes);
	g_free(notes);

	int starId[CNAME_MAX];
	int ncomp = m_selection.GetStarList(CMPACK_SELECT_COMP, starId, CNAME_MAX);
	for (int i=0; i<CNAME_MAX; i++) {
		char key[256];

		// Comparison star designation
		if (i==0) 
			strcpy(key, "CNAME");
		else
			sprintf(key, "CNAME%d", i+1);
		gchar *cname = g_Project->GetStr("AAVSO", key);
		if ((!cname || cname[0]=='\0') && (i<ncomp)) {
			const gchar *tag = m_tags.caption(starId[i]);
			if (tag)
				cname = g_strdup(tag);
		}
		UpdateString(m_params.cname[i], cname);
		g_free(cname);

		// Comparison star magnitude
		if (i==0)
			strcpy(key, "CMAG");
		else
			sprintf(key, "CMAG%d", i+1);
		gchar *cmag = g_Project->GetStr("AAVSO", key);
		UpdateString(m_params.cmag[i], cmag);
		g_free(cmag);
	}

	// Check star designation
	int ncheck = m_selection.GetStarList(CMPACK_SELECT_CHECK, starId, CNAME_MAX);
	gchar *kname = g_Project->GetStr("AAVSO", "KNAME");
	if ((!kname || kname[0]=='\0') && (ncheck>0)) {
		const gchar *tag = m_tags.caption(starId[0]);
		if (tag)
			kname = g_strdup(tag);
	}
	UpdateString(m_params.kname, kname);
	g_free(kname);
}

void CAavsoExportDlg::SaveParams(void)
{
	g_Project->SetStr("AAVSO", "STAR_ID", m_params.star_id);
	g_Project->SetStr("AAVSO", "OBS_CODE", m_params.obs_code);
	CConfig::SetStr("AAVSO", "OBS_CODE", m_params.obs_code);
	g_Project->SetStr("AAVSO", "OBS_TYPE", m_params.obs_type);
	CConfig::SetStr("AAVSO", "OBS_TYPE", m_params.obs_type);
	g_Project->SetStr("AAVSO", "FILTER", m_params.filter);
	g_Project->SetStr("AAVSO", "CHART", m_params.chart);
	g_Project->SetStr("AAVSO", "NOTES", m_params.notes);
	for (int i=0; i<CNAME_MAX; i++) {
		char key[256];

		if (i==0) 
			strcpy(key, "CNAME");
		else
			sprintf(key, "CNAME%d", i+1);
		g_Project->SetStr("AAVSO", key, m_params.cname[i]);

		if (i==0)
			strcpy(key, "CMAG");
		else
			sprintf(key, "CMAG%d", i+1);
		g_Project->SetStr("AAVSO", key, m_params.cmag[i]);
	}
	g_Project->SetStr("AAVSO", "KNAME", m_params.kname);
}

bool CAavsoExportDlg::Execute(const gchar *filename, const CTable &table, const CSelection &stars, 
	const CTags &tags, bool heliocentric, GError **error)
{
	m_selection = stars;
	m_tags = tags;
	m_ncomp = stars.CountStars(CMPACK_SELECT_COMP);
	m_haveCheckStar = stars.CountStars(CMPACK_SELECT_CHECK)>0;
	m_heliocentric = heliocentric;

	// Check parameters
	if (m_ncomp==0) {
		set_error(error, "Select at least one comparison star");
		return false;
	}
	if (stars.CountStars(CMPACK_SELECT_COMP) > CNAME_MAX) {
		char msg[256];
		sprintf(msg, "Select no more than %d comparison stars", CNAME_MAX);
		set_error(error, msg);
		return false;
	}
	if (m_ncomp>1 && !m_haveCheckStar) {
		set_error(error, "Select at least one check star");
		return false;
	}

	// Read parameters from the project
	LoadParams();

	// Initialize controls
	GoToFirstPage();
	if (gtk_dialog_run(GTK_DIALOG(m_pDlg)) != GTK_RESPONSE_OK) {
		gtk_widget_hide(m_pDlg);
		return false;
	}
	gtk_widget_hide(m_pDlg);

	// Save parameters to the project
	SaveParams();
	return SaveFile(filename, table, error);
}

void CAavsoExportDlg::response_dialog(GtkDialog *pDlg, gint response_id, CAavsoExportDlg *pMe)
{
	if (!pMe->OnResponseDialog(response_id)) 
		g_signal_stop_emission_by_name(pDlg, "response");
}

bool CAavsoExportDlg::OnResponseDialog(int response_id)
{
	switch (response_id)
	{
	case GTK_RESPONSE_OK:
		// Accept the dialog
		return LeavePageQuery(response_id);
	case GTK_RESPONSE_YES:
		// Go to next page
		if (LeavePageQuery(response_id))
			GoForward();
		return false;
	case GTK_RESPONSE_NO:
		// Go to previous page
		if (LeavePageQuery(response_id))
			GoBackward();
		return false;
	case GTK_RESPONSE_HELP:
		// Show context help
		g_MainWnd->ShowHelp(GTK_WINDOW(m_pDlg), IDH_AAVSO_EXPORT);
		return false;
	}
	return true;
}

void CAavsoExportDlg::GoToFirstPage(void)
{
	GoToPage(0);
}

void CAavsoExportDlg::GoForward(void)
{
	int page = gtk_notebook_get_current_page(GTK_NOTEBOOK(m_pTabs));
	switch (page)
	{
	case 0:
		// Go to 'comparison star' page or 'ensemble comp. stars' page
		GoToPage(m_ncomp<=1 ? 1 : 2);
		break;
	case 1:
	case 2:
		// Go to the 'check star' page
		if (m_haveCheckStar) {
			GoToPage(3);
			break;
		}
		break;
	default:
		break;
	}
}

void CAavsoExportDlg::GoBackward()
{
	int page = gtk_notebook_get_current_page(GTK_NOTEBOOK(m_pTabs));
	switch (page)
	{
	case 3:
		// Go to 'comparison star' page or 'ensemble comp. stars' page
		GoToPage(m_ncomp<=1 ? 1 : 2);
		break;
	case 1:
	case 2:
		// Go to 'observation' page
		GoToPage(0);
		break;
	default:
		// Do nothing
		break;
	}
}

void CAavsoExportDlg::GoToPage(int page)
{
	switch (page)
	{
	case 0:
		// Go to 'observation' page
		gtk_widget_hide(m_pBackBtn);
		gtk_widget_hide(m_pSaveBtn);
		gtk_widget_show(m_pNextBtn);
		break;
	case 1:
	case 2:
		// Go to 'comparison star' page
		gtk_widget_show(m_pBackBtn);
		if (m_haveCheckStar) {
			gtk_widget_hide(m_pSaveBtn);
			gtk_widget_show(m_pNextBtn);
		} else {
			gtk_widget_show(m_pSaveBtn);
			gtk_widget_hide(m_pNextBtn);
		}
		break;
	case 3:
		// Go to 'check star' page
		gtk_widget_show(m_pBackBtn);
		gtk_widget_show(m_pSaveBtn);
		gtk_widget_hide(m_pNextBtn);
		break;
	}
	UpdateTab(page);
	gtk_notebook_set_current_page(GTK_NOTEBOOK(m_pTabs), page);
}

bool CAavsoExportDlg::LeavePageQuery(int response_id)
{
	int page = gtk_notebook_get_current_page(GTK_NOTEBOOK(m_pTabs));
	if (page>=0) {
		CAavsoExportTab *tab = ((tAAVSOPageData*)g_slist_nth_data(m_Pages, page))->tab;
		if (response_id==GTK_RESPONSE_OK || response_id==GTK_RESPONSE_YES) {
			if (!tab->Check())
				return false;
		}
		tab->Apply();
		SaveParams();
	}
	return true;
}

void CAavsoExportDlg::UpdateTab(int page)
{
	((tAAVSOPageData*)g_slist_nth_data(m_Pages, page))->tab->Update();
}

bool CAavsoExportDlg::SaveFile(const gchar *filepath, const CTable &table, GError **error)
{
	g_assert(filepath != NULL);

	FILE *f = open_file(filepath, "w");
	if (!f) {
		set_error(error, "Error when creating the file", filepath, CMPACK_ERR_OPEN_ERROR);
		return false;
	}

	CTable tmp;
	tmp.MakeCopy(table);

	CCSVWriter *writer = new CCSVWriter(f);
	ExportTable(*writer, tmp);
	delete writer;
	fclose(f);
	return true;
}

void CAavsoExportDlg::ExportTable(CCSVWriter &f, CTable &table)
{
	char buf[256];
	int jd_column = -1, amass_column = -1, mag_column = -1, err_column = -1, cmag_column = -1, kmag_column = -1;
	int jd_prec = JD_PREC, mag_prec = 2, amass_prec = 3;

	// Meta data
	f.SetSaveHeader(true);
	f.SetHeaderFormat('#', ',');
	f.SetMetaFormat('#', '=');
	f.AddMeta("TYPE", "EXTENDED");
	if (m_params.obs_code)
		f.AddMeta("OBSCODE", m_params.obs_code);
	sprintf(buf, "%s %s", CMAKE_PROJECT_NAME, VERSION);
	f.AddMeta("SOFTWARE", buf);
	f.AddMeta("DELIM", ",");
	f.AddMeta("DATE", !m_heliocentric ? "JD" : "HJD");
	if (m_params.obs_type)
		f.AddMeta("OBSTYPE", m_params.obs_type);

	// Table header
	f.AddColumn("STARID","na");
	f.AddColumn("DATE","na");
	f.AddColumn("MAGNITUDE","na");
	f.AddColumn("MAGERR","na");
	f.AddColumn("FILTER","na");
	f.AddColumn("TRANS","NO");
	f.AddColumn("MTYPE","STD");
	f.AddColumn("CNAME",(m_ncomp<=1 ? "na" : "ENSEMBLE"));
	f.AddColumn("CMAG","na");
	f.AddColumn("KNAME","na");
	f.AddColumn("KMAG","na");
	f.AddColumn("AIRMASS","na");
	f.AddColumn("GROUP","na");
	f.AddColumn("CHART","na");
	f.AddColumn("NOTES","na");

	CChannels *cx = table.ChannelsX();
	for (int i=0; i<cx->Count(); i++) {
		CChannel *ch = table.ChannelsX()->Get(i);
		if ((ch->Info()==CChannel::DATA_JD && !m_heliocentric) || (ch->Info()==CChannel::DATA_JD_HEL && m_heliocentric)) {
			jd_column = ch->Column();
			jd_prec = ch->Precision();
			break;
		}
	}

	CChannels *cy = table.ChannelsY();
	for (int i=0; i<cy->Count(); i++) {
		CChannel *ch = table.ChannelsY()->Get(i);
		switch (ch->Info())
		{
		case CChannel::DATA_VCMAG:
			if (strcmp(ch->Name(), "V-C")==0 || strcmp(ch->Name(), "V1-C")==0) {
				mag_column = ch->Column();
				err_column = ch->ColumnU();
				mag_prec = ch->Precision();
			}
			break;
		case CChannel::DATA_CMAG:
			if (strcmp(ch->Name(), "C")==0 || strcmp(ch->Name(), "C1")==0) 
				cmag_column = ch->Column();
			break;
		case CChannel::DATA_KMAG:
			if (m_haveCheckStar && strcmp(ch->Name(), "K")==0 || strcmp(ch->Name(), "K1")==0)
				kmag_column = ch->Column();
			break;
		case CChannel::DATA_AIRMASS:
			if (strcmp(ch->Name(), "AIRMASS")==0) {
				amass_column = ch->Column();
				amass_prec = ch->Precision();
			}
			break;
		default:
			break;
		}
	}

	// Table data
	if (m_ncomp==1) {
		// Observation with single comparison star
		// Standard magnitude of a comparison star (mag)
		double mag_offset = atof(m_params.cmag[0]);
		// Check star is optional
		bool ok = table.Rewind();
		while (ok) {
			double date = 0, magnitude = 0, error = 0;
			double kmag_value = 0, cmag_value = 0, airmass = 0;
			bool show = table.GetDbl(jd_column, &date);
			if (amass_column>=0)
				table.GetDbl(amass_column, &airmass);
			show = show & table.GetDbl(mag_column, &magnitude) && table.GetDbl(err_column, &error);
			show = show & table.GetDbl(cmag_column, &cmag_value);
			bool show_kmag = table.GetDbl(kmag_column, &kmag_value);
			if (show) {
				f.Append();
				f.SetStr(0, m_params.star_id);
				f.SetDbl(1, date, jd_prec);
				f.SetDbl(2, magnitude + mag_offset, mag_prec);
				f.SetDbl(3, error, mag_prec);
				if (m_params.filter && *m_params.filter!='\0')
					f.SetStr(4, m_params.filter);
				f.SetStr(7, m_params.cname[0]);
				f.SetDbl(8, cmag_value, mag_prec);
				if (m_params.kname && *m_params.kname!='\0') 
					f.SetStr(9, m_params.kname);
				if (show_kmag) {
					// Standard photometry: Instrumental magnitude of the check star!!!
					f.SetDbl(10, kmag_value, mag_prec);
				}
				if (airmass > 0)
					f.SetDbl(11, airmass, amass_prec);
				if (m_params.chart && *m_params.chart!='\0')
					f.SetStr(13, m_params.chart);
				if (m_params.notes && *m_params.notes!='\0')
					f.SetStr(14, m_params.notes);
			}
			ok = table.Next();
		}
	} else 
	if (m_ncomp>1) {
		// Ensemble photometry with multiple comparison stars
		// Standard magnitude of an ensemble comparison
		double mag_offset = 0;
		for (int i=0; i<m_ncomp; i++) 
			mag_offset += pow(10.0, -0.4*atof(m_params.cmag[i]));
		mag_offset = -2.5*log10(mag_offset/m_ncomp);
		// Check star is mandatory
		bool ok = table.Rewind();
		while (ok) {
			double date = 0, magnitude = 0, error = 0;
			double kmag_value = 0, cmag_value = 0, airmass = 0;
			bool show = table.GetDbl(jd_column, &date);
			if (amass_column>=0)
				table.GetDbl(amass_column, &airmass);
			show = show & table.GetDbl(mag_column, &magnitude) && table.GetDbl(err_column, &error);
			show = show & table.GetDbl(cmag_column, &cmag_value) && table.GetDbl(kmag_column, &kmag_value);
			if (show) {
				f.Append();
				f.SetStr(0, m_params.star_id);
				f.SetDbl(1, date, jd_prec);
				f.SetDbl(2, magnitude + mag_offset, mag_prec);
				f.SetDbl(3, error, mag_prec);
				if (m_params.filter && *m_params.filter!='\0')
					f.SetStr(4, m_params.filter);
				f.SetStr(9, m_params.kname);
				// Ensemble photometry: Standardized magnitude of the check star!!!
				f.SetDbl(10, kmag_value - cmag_value + mag_offset, mag_prec);	
				if (airmass > 0)
					f.SetDbl(11, airmass, amass_prec);
				if (m_params.chart && *m_params.chart!='\0')
					f.SetStr(13, m_params.chart);
				if (m_params.notes && *m_params.notes!='\0')
					f.SetStr(14, m_params.notes);
			}
			ok = table.Next();
		}
	}
}

//---------------------   TABS (COMMON) ------------------------

//
// Constructor - common controls for all pages
//
CAavsoExportTab::CAavsoExportTab(CAavsoExportDlg *parent, const gchar *caption):m_pParent(parent) 
{
	m_tab = gtk_vbox_new(FALSE, 8);

	// Title
	m_title = gtk_label_new(NULL);

	char buf[512];
	sprintf(buf, "<span size=\"xx-large\" weight=\"bold\">%s</span>", caption);
	gtk_label_set_markup(GTK_LABEL(m_title), buf);

	gtk_misc_set_alignment(GTK_MISC(m_title), 0.5, 0.5);
	gtk_box_pack_start(GTK_BOX(m_tab), m_title, FALSE, TRUE, 0);
}

//---------------------   TAB - OBSERVATION DATA ------------------------

//
// Constructor
//
CAavsoExportInitTab::CAavsoExportInitTab(CAavsoExportDlg *parent):CAavsoExportTab(parent, "Observation")
{
	GtkWidget *label;

	GtkWidget *box = gtk_table_new(7, 5, FALSE);
	gtk_box_pack_start(GTK_BOX(Widget()), box, TRUE, TRUE, 0);
	gtk_table_set_row_spacings(GTK_TABLE(box), 8);
	gtk_table_set_col_spacings(GTK_TABLE(box), 8);
	gtk_table_attach(GTK_TABLE(box), gtk_label_new(NULL), 0, 1, 0, 6, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
	gtk_table_attach(GTK_TABLE(box), gtk_label_new(NULL), 4, 5, 0, 6, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);

	label = gtk_label_new("Star designation");
	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
	m_StarId = gtk_entry_new_with_max_length(32);
	gtk_widget_set_tooltip_text(m_StarId, "AAVSO Designation, the AAVSO Name or the AAVSO Unique Identifier");
	gtk_table_attach(GTK_TABLE(box), m_StarId, 2, 3, 0, 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);

	label = gtk_label_new("Observer code");
	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), label, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
	m_ObsCode = gtk_entry_new_with_max_length(4);
	gtk_widget_set_tooltip_text(m_ObsCode, "The official AAVSO Observer Code for the observer, e.g. XYZ");
	gtk_table_attach(GTK_TABLE(box), m_ObsCode, 2, 3, 1, 2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);

	label = gtk_label_new("Observation type");
	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), label, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
	m_ObsType = gtk_combo_box_entry_new_text();
	gtk_combo_box_append_text(GTK_COMBO_BOX(m_ObsType), "CCD");
	gtk_combo_box_append_text(GTK_COMBO_BOX(m_ObsType), "DSLR");
	gtk_widget_set_tooltip_text(m_ObsType, "Type of the observation");
	gtk_table_attach(GTK_TABLE(box), m_ObsType, 2, 3, 2, 3, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);

	label = gtk_label_new("Filter");
	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), label, 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
	m_Filter = gtk_entry_new();
	gtk_widget_set_tooltip_text(m_Filter, "The filter used for the observation");
	gtk_table_attach(GTK_TABLE(box), m_Filter, 2, 3, 3, 4, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
	m_SelectFilterBtn = gtk_button_new_with_label("Browse");
	gtk_widget_set_tooltip_text(m_SelectFilterBtn, "Click the \"Browse\" button to show a list of filters");
	g_signal_connect(G_OBJECT(m_SelectFilterBtn), "clicked", G_CALLBACK(button_clicked), this);
	gtk_table_attach(GTK_TABLE(box), m_SelectFilterBtn, 3, 4, 3, 4, (GtkAttachOptions)(GTK_FILL | GTK_SHRINK), GTK_FILL, 0, 0);

	label = gtk_label_new("Chart - sequence ID");
	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), label, 1, 2, 4, 5, GTK_FILL, GTK_FILL, 0, 0);
	m_Chart = gtk_entry_new_with_max_length(20);
	gtk_widget_set_tooltip_text(m_Chart, "The sequence ID you will find in red at the bottom of the photometry table. (Max. 20 characters)");
	gtk_table_attach(GTK_TABLE(box), m_Chart, 2, 3, 4, 5, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);

	label = gtk_label_new("Notes");
	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), label, 1, 2, 5, 6, GTK_FILL, GTK_FILL, 0, 0);
	m_Notes = gtk_entry_new_with_max_length(100);
	gtk_widget_set_tooltip_text(m_Notes, "Comments or notes about the observation. (Max. 100 characters)");
	gtk_table_attach(GTK_TABLE(box), m_Notes, 2, 3, 5, 6, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
}

//
// Update contents of the controls
//
void CAavsoExportInitTab::Update()
{
	tAavsoExportParams *params = m_pParent->Params();

	if (params->star_id)
		gtk_entry_set_text(GTK_ENTRY(m_StarId), params->star_id);
	else
		gtk_entry_set_text(GTK_ENTRY(m_StarId), "");
	if (params->obs_code)
		gtk_entry_set_text(GTK_ENTRY(m_ObsCode), params->obs_code);
	else
		gtk_entry_set_text(GTK_ENTRY(m_ObsCode), "");
	if (params->obs_type)
		gtk_entry_set_text(GTK_ENTRY(GTK_BIN(m_ObsType)->child), params->obs_type);
	else
		gtk_entry_set_text(GTK_ENTRY(GTK_BIN(m_ObsType)->child), "");
	if (params->filter)
		gtk_entry_set_text(GTK_ENTRY(m_Filter), params->filter);
	else
		gtk_entry_set_text(GTK_ENTRY(m_Filter), "");
	if (params->chart)
		gtk_entry_set_text(GTK_ENTRY(m_Chart), params->chart);
	else
		gtk_entry_set_text(GTK_ENTRY(m_Chart), "");
	if (params->notes)
		gtk_entry_set_text(GTK_ENTRY(m_Notes), params->notes);
	else
		gtk_entry_set_text(GTK_ENTRY(m_Notes), "");
}


//
// Validate the fields
//
bool CAavsoExportInitTab::Check()
{
	const gchar *star_id = gtk_entry_get_text(GTK_ENTRY(m_StarId));
	if (strlen(star_id)<=0) {
		ShowError(Window(), "Please, fill in the star identifier");
		gtk_widget_grab_focus(m_StarId);
		return false;
	}
	
	const gchar *obs_code = gtk_entry_get_text(GTK_ENTRY(m_ObsCode));
	if (strlen(obs_code)<=0) {
		ShowError(Window(), "Please, fill in the observer code");
		gtk_widget_grab_focus(m_ObsCode);
		return false;
	}
	if (strspn(obs_code, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != strlen(obs_code)) {
		ShowError(Window(), "The value is not a valid AAVSO Observer Code");
		gtk_widget_grab_focus(m_ObsCode);
		return false;
	}		

	const gchar *obs_type = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(m_ObsType)->child));
	if (strlen(obs_type)<=0) {
		ShowError(Window(), "Please, choose the observation type");
		gtk_widget_grab_focus(m_ObsType);
		return false;
	}
	if (strspn(obs_type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != strlen(obs_type)) {
		ShowError(Window(), "The value is not a valid observation type");
		gtk_widget_grab_focus(m_ObsType);
		return false;
	}		

	const gchar *filter = gtk_entry_get_text(GTK_ENTRY(m_Filter));
	if (strlen(filter)<=0) {
		ShowError(Window(), "Please, fill in the filter");
		gtk_widget_grab_focus(m_Filter);
		return false;
	}
	if (strspn(filter, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != strlen(filter)) {
		ShowError(Window(), "The value is not a valid filter");
		gtk_widget_grab_focus(m_Filter);
		return false;
	}		

	const gchar *chart = gtk_entry_get_text(GTK_ENTRY(m_Chart));
	if (strlen(chart)<=0) {
		ShowError(Window(), "Please, fill in the chart - sequence ID");
		gtk_widget_grab_focus(m_Chart);
		return false;
	}
	if (strspn(chart, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-") != strlen(chart)) {
		ShowError(Window(), "The value is not a valid AAVSO Chart Identifier");
		gtk_widget_grab_focus(m_Chart);
		return false;
	}		

	return true;
}


//
// Save values from controls
//
void CAavsoExportInitTab::Apply()
{
	tAavsoExportParams *params = m_pParent->Params();

	UpdateString(params->star_id, gtk_entry_get_text(GTK_ENTRY(m_StarId)));
	UpdateString(params->obs_code, gtk_entry_get_text(GTK_ENTRY(m_ObsCode)));
	UpdateString(params->obs_type, gtk_entry_get_text(GTK_ENTRY(GTK_BIN(m_ObsType)->child)));
	UpdateString(params->filter, gtk_entry_get_text(GTK_ENTRY(m_Filter)));
	UpdateString(params->chart, gtk_entry_get_text(GTK_ENTRY(m_Chart)));
	UpdateString(params->notes, gtk_entry_get_text(GTK_ENTRY(m_Notes)));
}


//
// Button click handler
//
void CAavsoExportInitTab::button_clicked(GtkWidget *button, CAavsoExportInitTab *pDlg)
{
	pDlg->OnButtonClicked(button);
}

void CAavsoExportInitTab::OnButtonClicked(GtkWidget *pBtn)
{
	if (pBtn == m_SelectFilterBtn) {
		const gchar *obs_type = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(m_ObsType)->child));
		bool ccd = strcmp(obs_type, "CCD")==0, dslr = strcmp(obs_type, "DSLR")==0;
		if (!ccd && !dslr) {
			ShowError(Window(), "Please, select the observation type first.");
			return;
		}
		AavsoExportSelectFilterDlg dlg(Window(), ccd, dslr);
		gchar *str = dlg.Execute(gtk_entry_get_text(GTK_ENTRY(m_Filter)));
		if (str) {
			gtk_entry_set_text(GTK_ENTRY(m_Filter), str);
			g_free(str);
		}
	}
}

CAavsoExportCompTab::CAavsoExportCompTab(CAavsoExportDlg *parent):CAavsoExportTab(parent, "Comparison star")
{
	GtkWidget *box = gtk_table_new(2, 5, FALSE);
	gtk_box_pack_start(GTK_BOX(Widget()), box, TRUE, TRUE, 0);
	gtk_table_set_row_spacings(GTK_TABLE(box), 8);
	gtk_table_set_col_spacings(GTK_TABLE(box), 8);
	gtk_table_attach(GTK_TABLE(box), gtk_label_new(NULL), 0, 1, 0, 6, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
	gtk_table_attach(GTK_TABLE(box), gtk_label_new(NULL), 4, 5, 0, 6, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);

	m_label1 = gtk_label_new("Star designation");
	gtk_misc_set_alignment(GTK_MISC(m_label1), 1.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), m_label1, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
	m_StarId = gtk_entry_new_with_max_length(32);
	gtk_widget_set_tooltip_text(m_StarId, "Comparison star name or label");
	gtk_table_attach(GTK_TABLE(box), m_StarId, 2, 3, 0, 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);

	m_label2 = gtk_label_new("Magnitude");
	gtk_misc_set_alignment(GTK_MISC(m_label2), 1.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), m_label2, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
	m_Mag = gtk_entry_new_with_max_length(32);
	gtk_widget_set_tooltip_text(m_Mag, "Reference magnitude of the comparison star");
	gtk_table_attach(GTK_TABLE(box), m_Mag, 2, 3, 1, 2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
}

//
// Update contents of the controls
//
void CAavsoExportCompTab::Update()
{
	tAavsoExportParams *params = m_pParent->Params();

	if (params->cname[0])
		gtk_entry_set_text(GTK_ENTRY(m_StarId), params->cname[0]);
	else
		gtk_entry_set_text(GTK_ENTRY(m_StarId), "");
	gtk_widget_set_sensitive(GTK_WIDGET(m_label1), TRUE);
	gtk_widget_set_sensitive(GTK_WIDGET(m_StarId), TRUE);
	if (params->cmag[0])
		gtk_entry_set_text(GTK_ENTRY(m_Mag), params->cmag[0]);
	else
		gtk_entry_set_text(GTK_ENTRY(m_Mag), "");
	gtk_widget_set_sensitive(GTK_WIDGET(m_label2), TRUE);
	gtk_widget_set_sensitive(GTK_WIDGET(m_Mag), TRUE);
}


//
// Validate the fields
//
bool CAavsoExportCompTab::Check()
{
	const gchar *star_id = gtk_entry_get_text(GTK_ENTRY(m_StarId));
	if (strlen(star_id)<=0) {
		ShowError(Window(), "Please, fill in the star identifier");
		gtk_widget_grab_focus(m_StarId);
		return false;
	}
	
	const gchar *mag = gtk_entry_get_text(GTK_ENTRY(m_Mag));
	if (strlen(mag)<=0) {
		ShowError(Window(), "Please, fill in the magnitude");
		gtk_widget_grab_focus(m_Mag);
		return false;
	}

	return true;
}


//
// Save values from controls
//
void CAavsoExportCompTab::Apply()
{
	tAavsoExportParams *params = m_pParent->Params();

	UpdateString(params->cname[0], gtk_entry_get_text(GTK_ENTRY(m_StarId)));
	UpdateString(params->cmag[0], gtk_entry_get_text(GTK_ENTRY(m_Mag)));
}

CAavsoExportEnsembleTab::CAavsoExportEnsembleTab(CAavsoExportDlg *parent):CAavsoExportTab(parent, "Comparison stars")
{
	GtkWidget *scr_wnd = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scr_wnd), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_box_pack_start(GTK_BOX(Widget()), scr_wnd, TRUE, TRUE, 0);

	GtkWidget *box = gtk_table_new(CNAME_MAX+2, 5, FALSE);
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scr_wnd), box);
	gtk_table_set_row_spacings(GTK_TABLE(box), 8);
	gtk_table_set_col_spacings(GTK_TABLE(box), 8);
	gtk_table_attach(GTK_TABLE(box), gtk_label_new(NULL), 0, 1, 0, CNAME_MAX+1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
	gtk_table_attach(GTK_TABLE(box), gtk_label_new(NULL), 4, 5, 0, CNAME_MAX+1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
	gtk_table_attach(GTK_TABLE(box), gtk_label_new(NULL), 0, 5, CNAME_MAX+1, CNAME_MAX+2, GTK_FILL, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), 0, 0);

	m_Header[0] = gtk_label_new("Star designation");
	gtk_misc_set_alignment(GTK_MISC(m_Header[0]), 0.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), m_Header[0], 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0);

	m_Header[1] = gtk_label_new("Magnitude");
	gtk_misc_set_alignment(GTK_MISC(m_Header[1]), 0.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), m_Header[1], 3, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0);

	for (int i=0; i<CNAME_MAX; i++) {
		m_RowLabel[i] = gtk_label_new("comp #XX");
		gtk_misc_set_alignment(GTK_MISC(m_RowLabel[i]), 1.0, 0.5);
		gtk_table_attach(GTK_TABLE(box), m_RowLabel[i], 1, 2, i+1, i+2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);

		m_StarId[i] = gtk_entry_new_with_max_length(32);
		gtk_widget_set_tooltip_text(m_StarId[i], "Comparison star name or label");
		gtk_table_attach(GTK_TABLE(box), m_StarId[i], 2, 3, i+1, i+2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);

		m_Mag[i] = gtk_entry_new_with_max_length(32);
		gtk_widget_set_tooltip_text(m_Mag[i], "Instrumental magnitude of the comparison star");
		gtk_table_attach(GTK_TABLE(box), m_Mag[i], 3, 4, i+1, i+2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
	}
}

//
// Update contents of the controls
//
void CAavsoExportEnsembleTab::Update()
{
	tAavsoExportParams *params = m_pParent->Params();
	CSelection *stars = m_pParent->Selection();

	int starId[CNAME_MAX];
	stars->GetStarList(CMPACK_SELECT_COMP, starId, CNAME_MAX);

	for (int i=0; i<CNAME_MAX; i++) {
		if (i < m_pParent->NComp()) {
			gtk_label_set_text(GTK_LABEL(m_RowLabel[i]), stars->Caption(starId[i]));
			gtk_entry_set_text(GTK_ENTRY(m_StarId[i]), params->cname[i]);
			gtk_entry_set_text(GTK_ENTRY(m_Mag[i]), params->cmag[i]);

			gtk_widget_show(GTK_WIDGET(m_RowLabel[i]));
			gtk_widget_show(GTK_WIDGET(m_StarId[i]));
			gtk_widget_show(GTK_WIDGET(m_Mag[i]));
		} else {
			gtk_widget_hide(GTK_WIDGET(m_RowLabel[i]));
			gtk_widget_hide(GTK_WIDGET(m_StarId[i]));
			gtk_widget_hide(GTK_WIDGET(m_Mag[i]));
		}
	}
}


//
// Validate the fields
//
bool CAavsoExportEnsembleTab::Check()
{
	for (int i=0; i<m_pParent->NComp(); i++) {
		const gchar *star_id = gtk_entry_get_text(GTK_ENTRY(m_StarId[i]));
		if (strlen(star_id)<=0) {
			ShowError(Window(), "Please, fill in the star identifier");
			gtk_widget_grab_focus(m_StarId[i]);
			return false;
		}
	
		const gchar *mag = gtk_entry_get_text(GTK_ENTRY(m_Mag[i]));
		if (strlen(mag)<=0) {
			ShowError(Window(), "Please, fill in the magnitude");
			gtk_widget_grab_focus(m_Mag[i]);
			return false;
		}
	}

	return true;
}


//
// Save values from controls
//
void CAavsoExportEnsembleTab::Apply()
{
	tAavsoExportParams *params = m_pParent->Params();

	for (int i=0; i<m_pParent->NComp(); i++) {
		UpdateString(params->cname[i], gtk_entry_get_text(GTK_ENTRY(m_StarId[i])));
		UpdateString(params->cmag[i], gtk_entry_get_text(GTK_ENTRY(m_Mag[i])));
	}
}

CAavsoExportCheckTab::CAavsoExportCheckTab(CAavsoExportDlg *parent):CAavsoExportTab(parent, "Check star")
{
	GtkWidget *box = gtk_table_new(2, 5, FALSE);
	gtk_box_pack_start(GTK_BOX(Widget()), box, TRUE, TRUE, 0);
	gtk_table_set_row_spacings(GTK_TABLE(box), 8);
	gtk_table_set_col_spacings(GTK_TABLE(box), 8);
	gtk_table_attach(GTK_TABLE(box), gtk_label_new(NULL), 0, 1, 0, 6, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
	gtk_table_attach(GTK_TABLE(box), gtk_label_new(NULL), 4, 5, 0, 6, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);

	GtkWidget *label = gtk_label_new("Star designation");
	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	gtk_table_attach(GTK_TABLE(box), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
	m_StarId = gtk_entry_new_with_max_length(32);
	gtk_widget_set_tooltip_text(m_StarId, "Check star name or label.");
	gtk_table_attach(GTK_TABLE(box), m_StarId, 2, 3, 0, 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), GTK_FILL, 0, 0);
}


//
// Update contents of the controls
//
void CAavsoExportCheckTab::Update()
{
	tAavsoExportParams *params = m_pParent->Params();

	if (params->kname)
		gtk_entry_set_text(GTK_ENTRY(m_StarId), params->kname);
	else
		gtk_entry_set_text(GTK_ENTRY(m_StarId), "");
}


//
// Validate the fields
//
bool CAavsoExportCheckTab::Check()
{
	const gchar *star_id = gtk_entry_get_text(GTK_ENTRY(m_StarId));
	if (strlen(star_id)<=0) {
		ShowError(Window(), "Please, fill in the star identifier");
		gtk_widget_grab_focus(m_StarId);
		return false;
	}
	
	return true;
}


//
// Save values from controls
//
void CAavsoExportCheckTab::Apply()
{
	tAavsoExportParams *params = m_pParent->Params();

	UpdateString(params->kname, gtk_entry_get_text(GTK_ENTRY(m_StarId)));
}

//----------------------------   SELECT FILTER DIALOG   ----------------------------

enum tColumnId
{
	COL_NAME,
	COL_DESCRIPTION,
	NCOLS
};

const static struct tTreeViewColumn {
	const char *caption;		// Column name
	int column;					// Model column index
	double align;
} ListColumns[] = {
	{ "Filter",			COL_NAME },
	{ "Designation",	COL_DESCRIPTION },
	{ NULL }
};

const static struct tTreeViewRow {
	const char *name, *description;
	bool ccd, dslr;
} ListRows[] = {
	{ "U",		"Johnson U", true },
	{ "B",		"Johnson B", true },
	{ "V",		"Johnson V", true },
	{ "R",		"Cousins R", true },
	{ "I",		"Cousins I", true },
	{ "J",		"NIR 1.2 micron", true },
	{ "H",		"NIR 1.6 micron", true },
	{ "K",		"NIR 2.2 micron", true },
	{ "TG",		"DSLR or color CCD green channel", false, true },
	{ "TB",		"DSLR or color CCD blue channel", false, true },
	{ "TR",		"DSLR or color CCD red channel", false, true },
	{ "CV",		"Clear (unfiltered) using V-band comp star magnitudes (this is more common than CR)", true },
	{ "CR",		"Clear (unfiltered) using R-band comp star magnitudes", true },
	{ "SZ",		"Sloan z (formerly in database as Z)", true },
	{ "SU",		"Sloan u", true },
	{ "SG",		"Sloan g", true },
	{ "SR",		"Sloan r", true },
	{ "SI",		"Sloan i", true },
	{ "STU",	"Stromgren u", true },
	{ "STV",	"Stromgren v", true },
	{ "STB",	"Stromgren b", true },
	{ "STY",	"Stromgren y", true },
	{ "STHBW",	"Stromgren Hbw", true },
	{ "STHBN",	"Stromgren Hbn", true },
	{ "MA",		"Optec Wing A", true },
	{ "MB",		"Optec Wing B", true },
	{ "MI",		"Optec Wing C", true },
	{ NULL }
};

//
// Constructor
//
AavsoExportSelectFilterDlg::AavsoExportSelectFilterDlg(GtkWindow *pParent, bool ccd, bool dslr):m_SelPath(NULL)
{
	GtkWidget *vbox, *scrwnd;
	GtkTreeSelection *selection;

	// Dialog with buttons
	m_pDlg = gtk_dialog_new_with_buttons("Select filter", pParent, 
		(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
		GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
	gtk_dialog_widget_standard_tooltips(GTK_DIALOG(m_pDlg));
	gtk_window_set_position(GTK_WINDOW(m_pDlg), GTK_WIN_POS_CENTER);
	g_signal_connect(G_OBJECT(m_pDlg), "response", G_CALLBACK(response_dialog), this);

	// Dialog icon
	gchar *icon = get_icon_file("muniwin");
	gtk_window_set_icon(GTK_WINDOW(m_pDlg), gdk_pixbuf_new_from_file(icon, NULL));
	g_free(icon);

	// Dialog layout
	vbox = gtk_vbox_new(FALSE, 4);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(m_pDlg)->vbox), vbox, TRUE, TRUE, 0);
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
	gtk_widget_set_size_request(vbox, 520, 560);

	// Search result
	m_FilterList = gtk_list_store_new(NCOLS, G_TYPE_STRING, G_TYPE_STRING);
	for (int i=0; ListRows[i].name!=NULL; i++) {
		if ((ListRows[i].ccd && ccd) || (ListRows[i].dslr && dslr)) {
			GtkTreeIter iter;
			gtk_list_store_append(m_FilterList, &iter);
			gtk_list_store_set(m_FilterList, &iter, COL_NAME, ListRows[i].name, COL_DESCRIPTION, ListRows[i].description, -1);
		}
	}

	// User catalog
	m_ListView = gtk_tree_view_new();
	gtk_widget_set_tooltip_text(m_ListView, "List of the standard AAVSO filters");
	for (int i=0; ListColumns[i].caption!=NULL; i++) {
		GtkTreeViewColumn *col = gtk_tree_view_column_new();
		// Set column name and alignment
		gtk_tree_view_column_set_title(col, ListColumns[i].caption);
		gtk_tree_view_append_column(GTK_TREE_VIEW(m_ListView), col);
		// Add text renderer
		GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
		gtk_tree_view_column_pack_start(col, renderer, TRUE);
		g_object_set(renderer, "xalign", ListColumns[i].align, NULL);
		gtk_tree_view_column_add_attribute(col, renderer, "text", ListColumns[i].column);
	}
	gtk_tree_view_set_model(GTK_TREE_VIEW(m_ListView), GTK_TREE_MODEL(m_FilterList));
	gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(m_ListView), true);
	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(m_ListView), TRUE);
	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_ListView));
	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
	g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(selection_changed), this);
	scrwnd = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwnd),
		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwnd), 
		GTK_SHADOW_ETCHED_IN);
	gtk_container_add(GTK_CONTAINER(scrwnd), m_ListView);
	gtk_box_pack_start(GTK_BOX(vbox), scrwnd, TRUE, TRUE, 0);

	gtk_widget_show_all(GTK_DIALOG(m_pDlg)->vbox);
}

//
// Destructor
//
AavsoExportSelectFilterDlg::~AavsoExportSelectFilterDlg()
{
	gtk_tree_path_free(m_SelPath);
	g_object_unref(m_FilterList);
	gtk_widget_destroy(m_pDlg);
}


//
// Execute the dialog
//
gchar *AavsoExportSelectFilterDlg::Execute(const gchar *defaultValue)
{
	Select(defaultValue);
	if (gtk_dialog_run(GTK_DIALOG(m_pDlg)) != GTK_RESPONSE_ACCEPT) {
		gtk_widget_hide(m_pDlg);
		return NULL;
	}
	gtk_widget_hide(m_pDlg);

	GtkTreeIter iter;
	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_ListView));
	if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) 
		return NULL;

	gchar *name = NULL;
	gtk_tree_model_get(GTK_TREE_MODEL(m_FilterList), &iter, COL_NAME, &name, -1);
	return name;
}


//
// Select item by name
//
void AavsoExportSelectFilterDlg::Select(const gchar *name)
{
	GtkTreeIter iter;
	int ok = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_FilterList), &iter);
	while (ok) {
		gchar *n;
		gtk_tree_model_get(GTK_TREE_MODEL(m_FilterList), &iter, COL_NAME, &n, -1);
		if (StrCmp0(n, name)==0) {
			GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_ListView));
			gtk_tree_selection_unselect_all(selection);
			gtk_tree_selection_select_iter(selection, &iter);
			g_free(n);
			return;
		}
		g_free(n);
		ok = gtk_tree_model_iter_next(GTK_TREE_MODEL(m_FilterList), &iter);
	}
	UpdateControls();
}

//
// TreeView selection changed
//
void AavsoExportSelectFilterDlg::selection_changed(GtkTreeSelection *selection, AavsoExportSelectFilterDlg *pMe)
{
	pMe->OnSelectionChanged(selection);
}

void AavsoExportSelectFilterDlg::OnSelectionChanged(GtkTreeSelection *selection)
{
	GtkTreeModel *model;

	GList *list = gtk_tree_selection_get_selected_rows(selection, &model);
	if (list) {
		if (model == GTK_TREE_MODEL(m_FilterList)) {
			gtk_tree_path_free(m_SelPath);
			m_SelPath = gtk_tree_path_copy((GtkTreePath*)(list->data));
		}
		g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
		g_list_free(list);
	} else {
		if (model == GTK_TREE_MODEL(m_FilterList)) {
			gtk_tree_path_free(m_SelPath);
			m_SelPath = NULL;
		}
	}
	UpdateControls();
}


//
// Enable and disable controls
//
void AavsoExportSelectFilterDlg::UpdateControls(void)
{
	GtkTreeSelection *pSel = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_ListView));
	GtkWidget *ok_btn = get_dialog_widget_by_response(GTK_DIALOG(m_pDlg), GTK_RESPONSE_ACCEPT);
	gtk_widget_set_sensitive(ok_btn, gtk_tree_selection_count_selected_rows(pSel)>0);
}


//
// Handle dialog response
//
void AavsoExportSelectFilterDlg::response_dialog(GtkDialog *pDlg, gint response_id, AavsoExportSelectFilterDlg *pMe)
{
	if (!pMe->OnResponseDialog(response_id))
		g_signal_stop_emission_by_name(pDlg, "response");
}

bool AavsoExportSelectFilterDlg::OnResponseDialog(gint response_id)
{
	switch (response_id)
	{
	case GTK_RESPONSE_ACCEPT:
		{
			GtkTreeSelection *pSel = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_ListView));
			return gtk_tree_selection_count_selected_rows(pSel)>0;
		}
		break;
	}
	return true;
}
