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

varfind.cpp (C-Munipack project)
Widget for the VarFind dialog
Copyright (C) 2011 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 "varfind.h"
#include "configuration.h"
#include "configuration.h"
#include "export_dlgs.h"
#include "lightcurve_dlg.h"
#include "main.h"
#include "proc_classes.h"
#include "utils.h"
#include "ctxhelp.h"
#include "choosestars_dlg.h"
#include "varcat.h"

#define MIN_MAG_RANGE 0.05
#define MAX_MAG_RANGE 20.0

enum tCommandId
{
	CMD_HIDE = 100,
	CMD_DELETE,
	CMD_PREVIEW,
	CMD_FRAMEINFO,
	CMD_COPY_WCS
};

static const CPopupMenu::tPopupMenuItem LCGraphContextMenu[] = {
	{ CPopupMenu::MB_ITEM, CMD_PREVIEW,		"_Show frame" },
	{ CPopupMenu::MB_ITEM, CMD_FRAMEINFO,	"_Show properties" },
	{ CPopupMenu::MB_SEPARATOR },
	{ CPopupMenu::MB_ITEM, CMD_HIDE,		"_Delete frames from data set" },
	{ CPopupMenu::MB_ITEM, CMD_DELETE,		"_Remove frames from project" },
	{ CPopupMenu::MB_END }
};

static const CPopupMenu::tPopupMenuItem MDGraphContextMenu[] = {
	{ CPopupMenu::MB_ITEM, CMD_HIDE,		"_Delete objects from data set" },
	{ CPopupMenu::MB_ITEM, CMD_COPY_WCS,	"Copy _WCS coordinates" },
	{ CPopupMenu::MB_END }
};

static const CPopupMenu::tPopupMenuItem ChartMenu[] = {
	{ CPopupMenu::MB_ITEM, CMD_COPY_WCS,	"Copy _WCS coordinates" },
	{ CPopupMenu::MB_END }
};

//-------------------------   LIST OF CATALOGS   --------------------------------

enum tCatalogId
{
	GCVS,
	NSV,
	NSVS,
	VSX,
	CATALOG_COUNT
};

static const struct tCatalogInfo {
	const gchar *caption;
	const gchar *id;
	int par_use, par_path;
	int defaultColor;
	VarCat::tCatalogFilter filter;
} Catalogs[CATALOG_COUNT] = {
	{ "GCVS", "GCVS", CConfig::SEARCH_GCVS, CConfig::GCVS_PATH, 0xE6BE00, VarCat::GCVS },
	{ "NSV", "NSV", CConfig::SEARCH_NSV,  CConfig::NSV_PATH, 0xE6BE00, VarCat::NSV },
	{ "NSVS", "NSVS", CConfig::SEARCH_NSVS, CConfig::NSVS_PATH, 0xE6BE00, VarCat::NSVS },
	{ "VSX", "VSX", CConfig::SEARCH_VSX, CConfig::VSX_PATH, 0xCC00CC, VarCat::VSX }
};

//-----------------------------------   HELPER FUNCTIONS   ---------------------------------------------

static GtkToolItem *new_tool_button(GtkWidget *toolbar, const gchar *icon_name, const gchar *tooltip)
{
	GtkToolItem *tbtn = gtk_tool_button_new(NULL, NULL);
	if (icon_name && icon_name[0] != '\0') {
		gchar *tmp = (gchar*)g_malloc(strlen(icon_name) + 7);
		strcpy(tmp, "cmpack_");
		strcat(tmp, icon_name);
		gchar *icon = get_icon_file(icon_name);
		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(icon, NULL);
		if (pixbuf) {
			int width, height;
			gdk_pixbuf_get_file_info(icon, &width, &height);
			gtk_icon_theme_add_builtin_icon(tmp, width, pixbuf);
			g_object_unref(pixbuf);
			gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(tbtn), tmp);
		}
		g_free(icon);
		g_free(tmp);
	}

	gtk_widget_set_tooltip_text(GTK_WIDGET(tbtn), tooltip);
	gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tbtn, -1);
	return tbtn;
}

static GtkToolItem *new_toggle_tool_button(GtkWidget *toolbar, const gchar *icon_name, const gchar *tooltip)
{
	GtkToolItem *tbtn = gtk_toggle_tool_button_new();
	if (icon_name && icon_name[0] != '\0') {
		gchar *tmp = (gchar*)g_malloc(strlen(icon_name) + 7);
		strcpy(tmp, "cmpack_");
		strcat(tmp, icon_name);
		gchar *icon = get_icon_file(icon_name);
		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(icon, NULL);
		if (pixbuf) {
			int width, height;
			gdk_pixbuf_get_file_info(icon, &width, &height);
			gtk_icon_theme_add_builtin_icon(tmp, width, pixbuf);
			g_object_unref(pixbuf);
			gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(tbtn), tmp);
		}
		g_free(icon);
		g_free(tmp);
	}

	gtk_widget_set_tooltip_text(GTK_WIDGET(tbtn), tooltip);
	gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tbtn, -1);
	return tbtn;
}

static GtkToolItem *new_radio_tool_button(GtkWidget *toolbar, GtkToolItem *first_in_group,
	const gchar *icon_name, const gchar *tooltip)
{
	GSList *group = NULL;
	if (first_in_group)
		group = gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(first_in_group));

	GtkToolItem *tbtn = gtk_radio_tool_button_new(group);
	if (icon_name && icon_name[0] != '\0') {
		gchar *tmp = (gchar*)g_malloc(strlen(icon_name) + 7);
		strcpy(tmp, "cmpack_");
		strcat(tmp, icon_name);
		gchar *icon = get_icon_file(icon_name);
		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(icon, NULL);
		if (pixbuf) {
			int width, height;
			gdk_pixbuf_get_file_info(icon, &width, &height);
			gtk_icon_theme_add_builtin_icon(tmp, width, pixbuf);
			g_object_unref(pixbuf);
			gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(tbtn), tmp);
		}
		g_free(icon);
		g_free(tmp);
	}

	gtk_widget_set_tooltip_text(GTK_WIDGET(tbtn), tooltip);
	gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tbtn, -1);
	return tbtn;
}

static GtkWidget *new_radio_button(GtkWidget *toolbar, GtkWidget *first_in_group,
	const gchar *label, const gchar *tooltip)
{
	GSList *group = NULL;
	if (first_in_group)
		group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(first_in_group));

	GtkWidget *tbtn = gtk_radio_button_new_with_label(group, label);
	gtk_widget_set_tooltip_text(GTK_WIDGET(tbtn), tooltip);
	GtkToolItem *item = gtk_tool_item_new();
	gtk_container_add(GTK_CONTAINER(item), tbtn);
	gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
	return tbtn;
}

static GtkWidget *new_check_button(GtkWidget *toolbar, const gchar *label, const gchar *tooltip)
{
	GtkWidget *tbtn = gtk_check_button_new_with_label(label);
	gtk_widget_set_tooltip_text(GTK_WIDGET(tbtn), tooltip);
	GtkToolItem *item = gtk_tool_item_new();
	gtk_container_add(GTK_CONTAINER(item), tbtn);
	gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
	return tbtn;
}

struct tObject { int id, index; };

static bool list_contains_id(GSList *list, int id)
{
	for (GSList *ptr = list; ptr != NULL; ptr = ptr->next) {
		if (((tObject*)ptr->data)->id == id)
			return true;
	}
	return false;
}

static GSList *list_append(GSList *list, int id, int index)
{
	tObject *item = (tObject*)g_malloc(sizeof(tObject));
	item->id = id;
	item->index = index;
	return g_slist_append(list, item);
}

static GSList *list_remove_id(GSList *list, int id)
{
	GSList *tmp = list, *prev = NULL;
	while (tmp) {
		if (((tObject*)tmp->data)->id == id) {
			if (prev)
				prev->next = tmp->next;
			else
				list = tmp->next;
			g_slist_free_1(tmp);
			break;
		}
		prev = tmp;
		tmp = prev->next;
	}
	return list;
}

static GSList *list_clear(GSList *list)
{
	g_slist_foreach(list, (GFunc)g_free, NULL);
	g_slist_free(list);
	return NULL;
}


//---------------------------   FIND VARIABLES   --------------------------------

// 
// Default constructor
//
CVarFind::CVarFind(void):m_ChartData(NULL), m_ImageData(NULL), m_MDData(NULL), m_LCData(NULL), 
	m_Image(NULL), m_Phot(NULL), m_Catalog(NULL), m_Wcs(NULL), m_ApertureIndex(-1), m_MDChannelX(-1), m_MDChannelY(-1), 
	m_LCChannelX(-1), m_LCChannelY(-1), m_Variable(-1), m_VarIndex(-1), m_VarIndex2(-1), m_CompList(NULL),
	m_MovingTarget(0), m_SelectMode(SELECT_VARIABLE), m_Updating(false), m_UpdatePos(true), 
	m_Cancelled(false), m_SelectionList(NULL), m_SelectionIndex(-1), m_ShowNewSelection(false), m_JDMin(0), 
	m_JDMax(0), m_MagScaleMode(MAG_SCALE_VAR), m_FixedMagRange(0), m_MouseWhere(MOUSE_OTHER), m_LastPosX(-1), 
	m_LastPosY(-1), m_LastPosValid(false), m_CatalogBtns(NULL), m_ImageWidth(0), m_ImageHeight(0), m_CurrentCatalog(0)
{
	GtkWidget *scrwnd;
	GtkTreeIter iter;

	m_DisplayMode = (tDisplayMode)g_Project->GetInt("VarFind", "Mode", DISPLAY_CHART, 0, DISPLAY_MIXED);
	m_DateFormat = (tDateFormat)g_Project->GetInt("Display", "DateFormat", JULIAN_DATE, 0, GREGORIAN_DATE);
	m_MagScaleMode = (tMagScaleMode)g_Project->GetInt("Display", "MagScale", MAG_SCALE_VAR, 0, MAG_SCALE_CUSTOM);
	m_Negative = CConfig::GetBool(CConfig::NEGATIVE_CHARTS);
	m_RowsUpward = CConfig::GetBool(CConfig::ROWS_UPWARD);
	m_SingleComp = g_Project->GetBool("VarFind", "SingleComp", true);
	m_VSTags = g_Project->GetBool("VarFind", "Tags", true);

	// Dialog layout
	m_Box = gtk_table_new(2, 3, FALSE);
	gtk_widget_set_size_request(m_Box, 600, 400); 

	// Varfind-curve graph
	m_MDView = cmpack_graph_view_new();
	cmpack_graph_view_set_activation_mode(CMPACK_GRAPH_VIEW(m_MDView), CMPACK_ACTIVATION_CLICK);
	cmpack_graph_view_set_mouse_control(CMPACK_GRAPH_VIEW(m_MDView), TRUE);
	cmpack_graph_view_set_selection_mode(CMPACK_GRAPH_VIEW(m_MDView), GTK_SELECTION_MULTIPLE);
	g_signal_connect(G_OBJECT(m_MDView), "mouse-moved", G_CALLBACK(mouse_moved), this);
	g_signal_connect(G_OBJECT(m_MDView), "mouse-left", G_CALLBACK(mouse_left), this);
	g_signal_connect(G_OBJECT(m_MDView), "button_press_event", G_CALLBACK(button_press_event), this);
	g_signal_connect(G_OBJECT(m_MDView), "item-activated", G_CALLBACK(graph_item_activated), this);
	cmpack_graph_view_set_scales(CMPACK_GRAPH_VIEW(m_MDView), TRUE, TRUE);
	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_MDView);
	gtk_table_attach_defaults(GTK_TABLE(m_Box), scrwnd, 0, 1, 0, 1);

	// Chart
	m_Chart = cmpack_chart_view_new();
	cmpack_chart_view_set_activation_mode(CMPACK_CHART_VIEW(m_Chart), CMPACK_ACTIVATION_CLICK);
	cmpack_chart_view_set_mouse_control(CMPACK_CHART_VIEW(m_Chart), TRUE);
	g_signal_connect(G_OBJECT(m_Chart), "mouse-moved", G_CALLBACK(mouse_moved), this);
	g_signal_connect(G_OBJECT(m_Chart), "mouse-left", G_CALLBACK(mouse_left), this);
	g_signal_connect(G_OBJECT(m_Chart), "item-activated", G_CALLBACK(chart_item_activated), this);
	g_signal_connect(G_OBJECT(m_Chart), "button-press-event", G_CALLBACK(button_press_event), 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_Chart);
	gtk_table_attach_defaults(GTK_TABLE(m_Box), scrwnd, 1, 2, 0, 1);

	// Light-curve graph
	m_LCView = cmpack_graph_view_new_with_model(NULL);
	cmpack_graph_view_set_mouse_control(CMPACK_GRAPH_VIEW(m_LCView), TRUE);
	cmpack_graph_view_set_scales(CMPACK_GRAPH_VIEW(m_LCView), TRUE, TRUE);
	cmpack_graph_view_set_selection_mode(CMPACK_GRAPH_VIEW(m_LCView), GTK_SELECTION_MULTIPLE);
	g_signal_connect(G_OBJECT(m_LCView), "mouse-moved", G_CALLBACK(mouse_moved), this);
	g_signal_connect(G_OBJECT(m_LCView), "mouse-left", G_CALLBACK(mouse_left), this);
	g_signal_connect(G_OBJECT(m_LCView), "button_press_event", G_CALLBACK(button_press_event), 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_LCView);
	gtk_table_attach_defaults(GTK_TABLE(m_Box), scrwnd, 0, 2, 1, 2);
	
	// Upper right toolbar
	GtkWidget *frame = gtk_frame_new(NULL);
	gtk_widget_set_size_request(frame, 260, -1);
	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
	gtk_table_attach(GTK_TABLE(m_Box), frame, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0);

	GtkWidget *vbox = gtk_vbox_new(FALSE, 4);
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
	gtk_container_add(GTK_CONTAINER(frame), vbox);

	// Selection mode
	GtkWidget *label = gtk_label_new("Variable star");
	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

	GtkWidget *bbox = gtk_toolbar_new();
	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
	gtk_toolbar_set_style(GTK_TOOLBAR(bbox), GTK_TOOLBAR_ICONS);
	gtk_toolbar_set_icon_size(GTK_TOOLBAR(bbox), GTK_ICON_SIZE_SMALL_TOOLBAR);

	m_SelectVar = new_radio_tool_button(bbox, NULL, "selectvar", "Switch to selection of a variable star");
	g_signal_connect(G_OBJECT(m_SelectVar), "toggled", G_CALLBACK(button_clicked), this);

	m_GoBack = new_tool_button(bbox, "selectvarleft", "Select previous object");
	g_signal_connect(G_OBJECT(m_GoBack), "clicked", G_CALLBACK(button_clicked), this);

	m_GoForward = new_tool_button(bbox, "selectvarright", "Select next object");
	g_signal_connect(G_OBJECT(m_GoForward), "clicked", G_CALLBACK(button_clicked), this);
	
	label = gtk_label_new("Comparison star(s)");
	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

	bbox = gtk_toolbar_new();
	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
	gtk_toolbar_set_style(GTK_TOOLBAR(bbox), GTK_TOOLBAR_ICONS);
	gtk_toolbar_set_icon_size(GTK_TOOLBAR(bbox), GTK_ICON_SIZE_SMALL_TOOLBAR);

	m_SelectComp = new_radio_tool_button(bbox, m_SelectVar, "selectcomp", "Switch to selection of a comparison star");
	g_signal_connect(G_OBJECT(m_SelectComp), "toggled", G_CALLBACK(button_clicked), this);

	m_AddComp = new_radio_tool_button(bbox, m_SelectVar, "selectcompplus", "Append objects to the list of comparison stars");
	g_signal_connect(G_OBJECT(m_AddComp), "toggled", G_CALLBACK(button_clicked), this);

	m_RemoveComp = new_radio_tool_button(bbox, m_SelectVar, "selectcompminus", "Remove objects from the list of comparison stars");
	g_signal_connect(G_OBJECT(m_RemoveComp), "toggled", G_CALLBACK(button_clicked), this);

	m_MultiComp = gtk_check_button_new_with_label("Ensemble photometry");
	gtk_widget_set_tooltip_text(GTK_WIDGET(m_MultiComp), "Utilize multiple comparison stars");
	gtk_box_pack_start(GTK_BOX(vbox), m_MultiComp, FALSE, FALSE, 0);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_MultiComp), !m_SingleComp);
	g_signal_connect(G_OBJECT(m_MultiComp), "toggled", G_CALLBACK(button_clicked), this);

	GtkWidget *hline = gtk_hseparator_new();
	gtk_box_pack_start(GTK_BOX(vbox), hline, FALSE, FALSE, 0);

	// Saved selections
	label = gtk_label_new("Saved selections");
	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

	m_SelectCbx = gtk_combo_box_new();
	gtk_widget_set_tooltip_text(m_SelectCbx, "Choose an item to restore recently used object selection");
	m_Selections = gtk_list_store_new(2, GTK_TYPE_INT, GTK_TYPE_STRING);
	gtk_combo_box_set_model(GTK_COMBO_BOX(m_SelectCbx), GTK_TREE_MODEL(m_Selections));
	GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(m_SelectCbx), renderer, TRUE);
	gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(m_SelectCbx), renderer, "text", 1);
	g_signal_connect(G_OBJECT(m_SelectCbx), "changed", G_CALLBACK(combo_changed), this);
	gtk_box_pack_start(GTK_BOX(vbox), m_SelectCbx, FALSE, FALSE, 0);
	
	bbox = gtk_hbox_new(TRUE, 4);
	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);

	m_SaveBtn = gtk_button_new_with_label("Save as...");
	gtk_widget_set_tooltip_text(m_SaveBtn, "Save the current object selection");
	gtk_box_pack_start_defaults(GTK_BOX(bbox), m_SaveBtn);
	g_signal_connect(G_OBJECT(m_SaveBtn), "clicked", G_CALLBACK(button_clicked), this);
	
	m_RemoveBtn = gtk_button_new_with_label("Remove");
	gtk_widget_set_tooltip_text(m_RemoveBtn, "Remove the current object selection from the list");
	gtk_box_pack_start_defaults(GTK_BOX(bbox), m_RemoveBtn);
	g_signal_connect(G_OBJECT(m_RemoveBtn), "clicked", G_CALLBACK(button_clicked), this);

	// Aperture selection
	m_AperLabel = gtk_label_new("Aperture");
	gtk_misc_set_alignment(GTK_MISC(m_AperLabel), 0, 0);
	gtk_box_pack_start(GTK_BOX(vbox), m_AperLabel, FALSE, FALSE, 0);

	m_AperCombo = gtk_combo_box_new();
	gtk_widget_set_tooltip_text(m_AperCombo, "Aperture used to get brightness of the objects");
	g_signal_connect(G_OBJECT(m_AperCombo), "changed", G_CALLBACK(button_clicked), this);
	m_Apertures = gtk_list_store_new(2, GTK_TYPE_INT, GTK_TYPE_STRING);
	GtkCellRenderer *renderer2 = gtk_cell_renderer_text_new();
	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(m_AperCombo), renderer2, TRUE);
	gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(m_AperCombo), renderer2, "text", 1);
	gtk_box_pack_start(GTK_BOX(vbox), m_AperCombo, FALSE, FALSE, 0);

	hline = gtk_hseparator_new();
	gtk_box_pack_start(GTK_BOX(vbox), hline, FALSE, FALSE, 0);

	// Aperture selection
	m_CatalogsLabel = gtk_label_new("Catalogs");
	gtk_misc_set_alignment(GTK_MISC(m_CatalogsLabel), 0, 0);
	gtk_box_pack_start(GTK_BOX(vbox), m_CatalogsLabel, FALSE, FALSE, 0);

	m_CatalogBtns = new tCatalogSet[CATALOG_COUNT];
	memset(m_CatalogBtns, 0, CATALOG_COUNT * sizeof(tCatalogSet));

	bbox = CreateCatalogsWidget();
	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
	
	// Lower right toolbar
	frame = gtk_frame_new(NULL);
	gtk_widget_set_size_request(frame, 260, -1);
	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
	gtk_table_attach(GTK_TABLE(m_Box), frame, 2, 3, 1, 2, GTK_FILL, GTK_FILL, 0, 0);

	vbox = gtk_vbox_new(FALSE, 4);
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
	gtk_container_add(GTK_CONTAINER(frame), vbox);

	// X axis
	m_XLabel = gtk_label_new("Date and time");
	gtk_misc_set_alignment(GTK_MISC(m_XLabel), 0, 0);
	gtk_box_pack_start(GTK_BOX(vbox), m_XLabel, FALSE, FALSE, 0);

	m_XCombo = gtk_combo_box_new();
	gtk_widget_set_tooltip_text(m_AperCombo, "Column shown on the horizontal axis of the graph");
	m_XChannels = gtk_list_store_new(2, GTK_TYPE_INT, GTK_TYPE_STRING);
	gtk_list_store_append(m_XChannels, &iter);
	gtk_list_store_set(m_XChannels, &iter, 0, JULIAN_DATE, 1, "JD", -1);
	gtk_list_store_append(m_XChannels, &iter);
	gtk_list_store_set(m_XChannels, &iter, 0, GREGORIAN_DATE, 1, "UTC", -1);
	gtk_combo_box_set_model(GTK_COMBO_BOX(m_XCombo), GTK_TREE_MODEL(m_XChannels));
	gtk_combo_box_set_active(GTK_COMBO_BOX(m_XCombo), m_DateFormat);
	g_signal_connect(G_OBJECT(m_XCombo), "changed", G_CALLBACK(button_clicked), this);
	renderer = gtk_cell_renderer_text_new();
	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(m_XCombo), renderer, TRUE);
	gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(m_XCombo), renderer, "text", 1);
	gtk_box_pack_start(GTK_BOX(vbox), m_XCombo, FALSE, FALSE, 0);

	// Y axis
	m_YLabel = gtk_label_new("Magnitude scale");
	gtk_misc_set_alignment(GTK_MISC(m_YLabel), 0, 0);
	gtk_box_pack_start(GTK_BOX(vbox), m_YLabel, FALSE, FALSE, 0);

	m_YCombo = gtk_combo_box_new();
	gtk_widget_set_tooltip_text(m_AperCombo, "Scaling mode of the light curves");
	m_YModes = gtk_list_store_new(2, GTK_TYPE_INT, GTK_TYPE_STRING);
	gtk_list_store_append(m_YModes, &iter);
	gtk_list_store_set(m_YModes, &iter, 0, MAG_SCALE_VAR, 1, "Adaptive", -1);
	gtk_list_store_append(m_YModes, &iter);
	gtk_list_store_set(m_YModes, &iter, 0, MAG_SCALE_FIXED, 1, "Fixed", -1);
	gtk_list_store_append(m_YModes, &iter);
	gtk_list_store_set(m_YModes, &iter, 0, MAG_SCALE_CUSTOM, 1, "Custom", -1);
	gtk_combo_box_set_model(GTK_COMBO_BOX(m_YCombo), GTK_TREE_MODEL(m_YModes));
	gtk_combo_box_set_active(GTK_COMBO_BOX(m_YCombo), m_MagScaleMode);
	g_signal_connect(G_OBJECT(m_YCombo), "changed", G_CALLBACK(button_clicked), this);
	renderer = gtk_cell_renderer_text_new();
	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(m_YCombo), renderer, TRUE);
	gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(m_YCombo), renderer, "text", 1);
	gtk_box_pack_start(GTK_BOX(vbox), m_YCombo, FALSE, FALSE, 0);
	
	m_MagScale = gtk_combo_box_entry_new_text();
	gtk_widget_set_size_request(m_MagScale, 120, -1);
	gtk_widget_set_tooltip_text(m_MagScale, "Displayed range in magnitudes");
	gtk_combo_box_append_text(GTK_COMBO_BOX(m_MagScale), "0.05");
	gtk_combo_box_append_text(GTK_COMBO_BOX(m_MagScale), "0.1");
	gtk_combo_box_append_text(GTK_COMBO_BOX(m_MagScale), "0.2");
	gtk_combo_box_append_text(GTK_COMBO_BOX(m_MagScale), "0.5");
	gtk_combo_box_append_text(GTK_COMBO_BOX(m_MagScale), "1.0");
	gtk_combo_box_append_text(GTK_COMBO_BOX(m_MagScale), "2.0");
	gtk_combo_box_append_text(GTK_COMBO_BOX(m_MagScale), "5.0");
	gtk_combo_box_append_text(GTK_COMBO_BOX(m_MagScale), "10.0");
	g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(m_MagScale))), "activate", G_CALLBACK(button_clicked), this);
	g_signal_connect(G_OBJECT(m_MagScale), "changed", G_CALLBACK(button_clicked), this);
	gtk_box_pack_start(GTK_BOX(vbox), m_MagScale, FALSE, FALSE, 0);

	// Popup menus
	m_LCGraphMenu.Create(LCGraphContextMenu);
	m_MDGraphMenu.Create(MDGraphContextMenu);
	m_ChartMenu.Create(ChartMenu);

	// Timers
	m_TimerId = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 100, GSourceFunc(timer_cb), this, NULL);
}

//
// Destructor
//
CVarFind::~CVarFind()
{
	// Disconnect graph signals
	g_signal_handlers_disconnect_by_func(G_OBJECT(m_LCView), (gpointer)button_press_event, this);

	// Release objects, free allocated memory
	g_source_remove(m_TimerId);
	if (m_MDData)
		g_object_unref(m_MDData);
	if (m_LCData)
		g_object_unref(m_LCData);
	if (m_ChartData)
		g_object_unref(m_ChartData);
	if (m_ImageData)
		g_object_unref(m_ImageData);
	g_object_unref(m_Selections);
	g_object_unref(m_Apertures);
	g_object_unref(m_XChannels);
	g_object_unref(m_YModes);
	list_clear(m_CompList);
	delete m_Wcs;
	for (int i = 0; i < CATALOG_COUNT; i++)
		g_slist_free(m_CatalogBtns[i].obj_list);
	delete[] m_CatalogBtns;
}

//
// Button click handler
//
void CVarFind::button_clicked(GtkWidget *pButton, CVarFind *pMe)
{
	pMe->OnButtonClicked(pButton);
}

void CVarFind::OnButtonClicked(GtkWidget *pBtn)
{
	if (pBtn==m_XCombo) {
		int ch = SelectedItem(GTK_COMBO_BOX(m_XCombo));
		if (ch>=0 && ch!=m_DateFormat) {
			m_DateFormat = (tDateFormat)ch;
			g_Project->SetInt("Display", "DateFormat", m_DateFormat);
			UpdateLightCurve();
			UpdateControls();
		}
	} else
	if (pBtn==m_YCombo) {
		int mode = SelectedItem(GTK_COMBO_BOX(m_YCombo));
		if (mode>=0 && mode!=m_MagScaleMode) {
			m_MagScaleMode = (tMagScaleMode)mode;
			g_Project->SetInt("Display", "MagScale", m_MagScaleMode);
			UpdateLightCurve();
			UpdateControls();
		}
	} else
	if (pBtn==m_MagScale) {
		if (m_MagScaleMode == MAG_SCALE_CUSTOM && gtk_combo_box_get_active(GTK_COMBO_BOX(m_MagScale))>=0) {
			gchar *text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(m_MagScale));
			char *endptr;
			double x = strtod(text, &endptr);
			x = MIN(MAX_MAG_RANGE, MAX(x, MIN_MAG_RANGE));
			if (*endptr=='\0' && x!=m_CustomMagRange) {
				m_CustomMagRange = x;
				UpdateLightCurve();
			}
			g_free(text);
		}
	} else
	if (pBtn == gtk_bin_get_child(GTK_BIN(m_MagScale))) {
		// Use has entered a value into a edit field in the combo box
		// This is triggered by pressing the 'Enter' key while editing a value
		if (m_MagScaleMode == MAG_SCALE_CUSTOM) {
			gchar *text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(m_MagScale));
			char *endptr;
			double x = strtod(text, &endptr);
			x = MIN(MAX_MAG_RANGE, MAX(x, MIN_MAG_RANGE));
			if (*endptr=='\0' && x!=m_CustomMagRange) {
				m_CustomMagRange = x;
				UpdateLightCurve();
			}
			g_free(text);
		}
	} else
	if (pBtn==m_AperCombo) {
		int index = SelectedItem(GTK_COMBO_BOX(m_AperCombo));
		if (index>=0 && index!=m_ApertureIndex) {
			m_ApertureIndex = index;
			if (m_Phot)
				m_Phot->SelectAperture(m_ApertureIndex);
			Callback(CB_APERTURE_CHANGED);
			UpdateChart();
			UpdateControls();
		}
	} else
	if (pBtn==GTK_WIDGET(m_SelectComp)) {
		if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(m_SelectComp))) {
			m_SelectMode = SELECT_COMPARISON;
			UpdateControls();
		}
	} else
	if (pBtn == GTK_WIDGET(m_AddComp)) {
		if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(m_AddComp))) {
			m_SelectMode = ADD_COMPARISON;
			UpdateControls();
		}
	} else
	if (pBtn == GTK_WIDGET(m_RemoveComp)) {
		if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(m_RemoveComp))) {
			m_SelectMode = REMOVE_COMPARISON;
			UpdateControls();
		}
	} else
	if (pBtn == GTK_WIDGET(m_MultiComp)) {
		if (!m_Updating) {
			m_Updating = true;
			if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_MultiComp)))
				m_SingleComp = false;
			else {
				m_SingleComp = true;
				if (m_SelectMode == ADD_COMPARISON || m_SelectMode == REMOVE_COMPARISON)
					m_SelectMode = SELECT_COMPARISON;
				if (g_slist_length(m_CompList) > 1)
					SetComparisonStar(((tObject*)m_CompList->data)->id);
			}
			m_Updating = false;
			g_Project->SetInt("VarFind", "SingleComp", m_SingleComp);
			UpdateControls();
		}
	} else
	if (pBtn==GTK_WIDGET(m_SelectVar)) {
		if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(m_SelectVar))) {
			m_SelectMode = SELECT_VARIABLE;
			UpdateControls();
		}
	} else
	if (pBtn==GTK_WIDGET(m_GoBack)) {
		if (m_MDData && m_VarIndex2>0) {
			int star_id = (int)cmpack_graph_data_get_param(m_MDData, 0, m_VarIndex2-1);
			SetVariableStar(star_id);
			Callback(CB_VARIABLE_CHANGED);
			UpdateStatus();
			UpdateControls();
		}
	} else
	if (pBtn==GTK_WIDGET(m_GoForward)) {
		if (m_MDData && m_VarIndex2<cmpack_graph_data_nrow(m_MDData)-1) {
			int star_id = (int)cmpack_graph_data_get_param(m_MDData, 0, m_VarIndex2+1);
			SetVariableStar(star_id);
			Callback(CB_VARIABLE_CHANGED);
			UpdateStatus();
			UpdateControls();
		}
	} else
	if (pBtn==GTK_WIDGET(m_SaveBtn)) {
		SaveSelection();
	} else
	if (pBtn==GTK_WIDGET(m_RemoveBtn)) {
		RemoveSelection();
	}

	for (int i = 0; i < CATALOG_COUNT; i++) {
		if (m_CatalogBtns[i].chkButton == pBtn) {
			ShowCatalog(GetTopLevel(), i, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_CatalogBtns[i].chkButton)) != 0);
			break;
		}
		else if (GTK_WIDGET(m_CatalogBtns[i].setButton) == pBtn) {
			EditCatalog(i);
			break;
		}
	}
}


//
// Get top level window
//
GtkWindow *CVarFind::GetTopLevel(void)
{
	GtkWidget *toplevel = gtk_widget_get_toplevel(m_Box);
	if (GTK_WIDGET_TOPLEVEL(toplevel) && GTK_IS_WINDOW(toplevel)) 
		return GTK_WINDOW(toplevel);
	return NULL;		
}

//
// Set selection list
//
void CVarFind::SetSelectionList(CSelectionList *list) 
{
	m_SelectionList = list;
	m_SelectionIndex = -1;
	m_ShowNewSelection = true;
	UpdateSelectionList();
	UpdateControls();
}

//
// Update list of selections
//
void CVarFind::UpdateSelectionList(void)
{
	m_Updating = true;

	gtk_combo_box_set_model(GTK_COMBO_BOX(m_SelectCbx), NULL);
	gtk_list_store_clear(m_Selections);
	if (m_SelectionList) {
		if (m_ShowNewSelection) {
			GtkTreeIter iter;
			gtk_list_store_append(m_Selections, &iter);
			gtk_list_store_set(m_Selections, &iter, 0, -1, 1, "New selection", -1);
		}
		for (int i=0; i<m_SelectionList->Count(); i++) {
			GtkTreeIter iter;
			gtk_list_store_append(m_Selections, &iter);
			gtk_list_store_set(m_Selections, &iter, 0, i, 1, m_SelectionList->Name(i), -1);
		}
	} else {
		GtkTreeIter iter;
		gtk_list_store_append(m_Selections, &iter);
		gtk_list_store_set(m_Selections, &iter, 0, -1, 1, "New selection", -1);
	}
	gtk_combo_box_set_model(GTK_COMBO_BOX(m_SelectCbx), GTK_TREE_MODEL(m_Selections));
	if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_Selections), NULL)>0) {
		SelectItem(GTK_COMBO_BOX(m_SelectCbx), m_SelectionIndex);
		if (gtk_combo_box_get_active(GTK_COMBO_BOX(m_SelectCbx))<0) {
			gtk_combo_box_set_active(GTK_COMBO_BOX(m_SelectCbx), 0);
			m_SelectionIndex = (tDateFormat)SelectedItem(GTK_COMBO_BOX(m_SelectCbx));
		}
	} else {
		gtk_combo_box_set_active(GTK_COMBO_BOX(m_SelectCbx), -1);
		m_SelectionIndex = -1;
	}

	m_Updating = false;
}

CSelection CVarFind::GetSelection(void) const
{
	CSelection objs;
	objs.Select(m_Variable, CMPACK_SELECT_VAR);
	for (GSList *ptr = m_CompList; ptr != NULL; ptr = ptr->next)
		objs.Select(((tObject*)ptr->data)->id, CMPACK_SELECT_COMP);
	return objs;
}

void CVarFind::SetSelection(int index, const CSelection &objs)
{
	bool var_changed = false, comp_changed = false;

	bool changed = false;
	for (GSList *ptr = m_CompList; ptr != NULL; ptr = ptr->next) {
		if (objs.Type(((tObject*)ptr->data)->id) != CMPACK_SELECT_COMP) {
			changed = true;
			break;
		}
	}
	int ncomp = objs.CountStars(CMPACK_SELECT_COMP);
	int *list = (int*)g_malloc(ncomp * sizeof(int));
	int count = objs.GetStarList(CMPACK_SELECT_COMP, list, ncomp);
	for (int i = 0; i < count; i++) {
		if (!list_contains_id(m_CompList, list[i])) {
			changed = true;
			break;
		}
	}
	if (changed && m_ChartData) {
		for (GSList *ptr = m_CompList; ptr != NULL; ptr = ptr->next) {
			int index = ((tObject*)ptr->data)->index;
			if (index >= 0) {
				cmpack_chart_data_set_tag(m_ChartData, index, NULL);
				cmpack_chart_data_set_color(m_ChartData, index, CMPACK_COLOR_DEFAULT);
				cmpack_chart_data_set_topmost(m_ChartData, index, FALSE);
				if (m_DisplayMode == DISPLAY_IMAGE)
					cmpack_chart_data_set_diameter(m_ChartData, index, 0);
			}
		}
		m_CompList = list_clear(m_CompList);
		for (int i = 0; i < count; i++) {
			int index = cmpack_chart_data_find_item(m_ChartData, list[i]);
			if (index >= 0) {
				cmpack_chart_data_set_tag(m_ChartData, index, "comp");
				cmpack_chart_data_set_color(m_ChartData, index, CMPACK_COLOR_GREEN);
				cmpack_chart_data_set_topmost(m_ChartData, index, TRUE);
				if (m_DisplayMode == DISPLAY_IMAGE)
					cmpack_chart_data_set_diameter(m_ChartData, index, 4.0);
			}
			m_CompList = list_append(m_CompList, list[i], index);
		}
		comp_changed = true;
		if (g_slist_length(m_CompList) > 1 && m_SingleComp) {
			m_Updating = true;
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_MultiComp), TRUE);
			m_SingleComp = false;
			if (m_SelectMode == ADD_COMPARISON || m_SelectMode == REMOVE_COMPARISON)
				m_SelectMode = SELECT_COMPARISON;
			m_Updating = false;
		}
	}
	g_free(list);

	// Variable star
	int var_id = -1;
	int ok = objs.GetStarList(CMPACK_SELECT_VAR, &var_id, 1) == 1;
	if (var_id!=m_Variable && m_ChartData && m_MDData) {
		if (m_VarIndex>=0) {
			const gchar *selection_label = getSelectionLabel((int)cmpack_chart_data_get_param(m_ChartData, m_VarIndex));
			if (selection_label) {
				// Known variable star
				cmpack_chart_data_set_tag(m_ChartData, m_VarIndex, selection_label);
				cmpack_chart_data_set_color(m_ChartData, m_VarIndex, CMPACK_COLOR_YELLOW);
				cmpack_chart_data_set_topmost(m_ChartData, m_VarIndex, TRUE);
				if (m_DisplayMode == DISPLAY_IMAGE)
					cmpack_chart_data_set_diameter(m_ChartData, m_VarIndex, 4.0);
			}
			else {
				// Unselected star
				cmpack_chart_data_set_tag(m_ChartData, m_VarIndex, NULL);
				cmpack_chart_data_set_color(m_ChartData, m_VarIndex, CMPACK_COLOR_DEFAULT);
				cmpack_chart_data_set_topmost(m_ChartData, m_VarIndex, FALSE);
				if (m_DisplayMode == DISPLAY_IMAGE)
					cmpack_chart_data_set_diameter(m_ChartData, m_VarIndex, 0);
			}
		}
		if (m_VarIndex2 >= 0) {
			const gchar *selection_label = getSelectionLabel((int)cmpack_graph_data_get_param(m_MDData, 0, m_VarIndex2));
			if (selection_label) {
				// Known variable star
				cmpack_graph_data_set_tag(m_MDData, 0, m_VarIndex2, selection_label);
				cmpack_graph_data_set_color(m_MDData, 0, m_VarIndex2, CMPACK_COLOR_YELLOW);
				cmpack_graph_data_set_topmost(m_MDData, 0, m_VarIndex2, TRUE);
			}
			else {
				// Unselected star
				cmpack_graph_data_set_tag(m_MDData, 0, m_VarIndex2, NULL);
				cmpack_graph_data_set_color(m_MDData, 0, m_VarIndex2, CMPACK_COLOR_DEFAULT);
				cmpack_graph_data_set_topmost(m_MDData, 0, m_VarIndex2, FALSE);
			}
		}
		m_Variable = var_id;
		m_VarIndex = cmpack_chart_data_find_item(m_ChartData, m_Variable);
		if (m_VarIndex>=0) {
			cmpack_chart_data_set_tag(m_ChartData, m_VarIndex, "var");
			cmpack_chart_data_set_color(m_ChartData, m_VarIndex, CMPACK_COLOR_RED);
			cmpack_chart_data_set_topmost(m_ChartData, m_VarIndex, TRUE);
			if (m_DisplayMode==DISPLAY_IMAGE)
				cmpack_chart_data_set_diameter(m_ChartData, m_VarIndex, 4.0);
		}
		int col, row;
		if (cmpack_graph_data_find_item(m_MDData, m_Variable, &col, &row)) {
			m_VarIndex2 = row;
			cmpack_graph_data_set_tag(m_MDData, 0, m_VarIndex2, "var");
			cmpack_graph_data_set_color(m_MDData, 0, m_VarIndex2, CMPACK_COLOR_RED);
			cmpack_graph_data_set_topmost(m_MDData, 0, m_VarIndex2, TRUE);
		}
		cmpack_graph_view_unselect_all(CMPACK_GRAPH_VIEW(m_MDView));
		var_changed = true;
	}
	m_SelectionIndex = index;
	if (comp_changed)
		Callback(CB_COMPARISON_CHANGED);
	else if (var_changed)
		Callback(CB_VARIABLE_CHANGED);
	if (comp_changed || var_changed)
		UpdateStatus();
	UpdateControls();
}

const gchar *CVarFind::getSelectionLabel(int star_id) const
{
	if (m_SelectionList) {
		int count = m_SelectionList->Count();
		for (int i = 0; i < count; i++) {
			CSelection sel = m_SelectionList->At(i);
			if (sel.Type(star_id) == CMPACK_SELECT_VAR)
				return m_SelectionList->Name(i);
		}
	}
	return NULL;
}

void CVarFind::DettachSelection(void)
{
	GtkTreeIter iter;

	m_NewSelection = GetSelection();
	SetSelection(-1, m_NewSelection);
	if (!m_ShowNewSelection) {
		m_ShowNewSelection = true;
		UpdateSelectionList();
	}
	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_Selections), &iter)) {
		m_Updating = true;
		gtk_combo_box_set_active_iter(GTK_COMBO_BOX(m_SelectCbx), &iter);
		m_Updating = false;
	}
}

void CVarFind::SaveSelection(void)
{
	if (!m_SelectionList)
		return;

	const gchar *defValue = (m_SelectionIndex>=0 ? m_SelectionList->Name(m_SelectionIndex) : "");
	CTextQueryDlg dlg(GetTopLevel(), "Save selection as...");
	gchar *name = dlg.Execute("Enter name for the current selection:", 255, defValue, 
		(CTextQueryDlg::tValidator*)name_validator, this);
	if (name) {
		if (m_SelectionIndex<0) 
			m_ShowNewSelection = false;
		else
			m_SelectionList->RemoveAt(m_SelectionIndex);
		m_SelectionList->Set(name, GetSelection());
		m_SelectionIndex = m_SelectionList->IndexOf(name);
		g_free(name);
		UpdateSelectionList();
		UpdateControls();
	}
}

bool CVarFind::name_validator(const gchar *name, GtkWindow *parent, CVarFind *pMe)
{
	return pMe->OnNameValidator(name, parent);
}

bool CVarFind::OnNameValidator(const gchar *name, GtkWindow *parent)
{
	if (!name || name[0]=='\0') {
		ShowError(parent, "Please, specify name of the selection.");
		return false;
	}
	if (m_SelectionList) {
		int i = m_SelectionList->IndexOf(name);
		if (i>=0 && (m_SelectionIndex<0 || i!=m_SelectionIndex))
			return ShowConfirmation(parent, "A selection with the specified name already exists.\nDo you want to overwrite it?");
	}
	return true;
}

void CVarFind::RemoveSelection(void)
{
	if (!m_SelectionList || m_SelectionIndex<0)
		return;

	m_SelectionList->RemoveAt(m_SelectionIndex);
	if (m_SelectionIndex>=m_SelectionList->Count())
		m_SelectionIndex = m_SelectionList->Count()-1;
	if (m_SelectionIndex<0) {
		m_ShowNewSelection = true;
		m_NewSelection = CSelection();
		SetSelection(-1, m_NewSelection);
	} else {
		SetSelection(m_SelectionIndex, m_SelectionList->At(m_SelectionIndex));
	}
	UpdateSelectionList();
	UpdateControls();
}

//
// Update list of apertures
//
void CVarFind::SetApertures(const CApertures &aper, int defaultApertureId)
{
	char txt[128];
	GtkTreeIter iter;

	m_Aper = aper;

	gtk_combo_box_set_model(GTK_COMBO_BOX(m_AperCombo), NULL);
	gtk_list_store_clear(m_Apertures);
	for (int i=0; i<m_Aper.Count(); i++) {
		const CAperture *aper = m_Aper.Get(i);
		if (aper->Radius()>0)
			sprintf(txt, "#%d (%.2f)", aper->Id(), aper->Radius());
		else
			sprintf(txt, "#%d", aper->Id());
		gtk_list_store_append(m_Apertures, &iter);
		gtk_list_store_set(m_Apertures, &iter, 0, i, 1, txt, -1);
	}
	gtk_combo_box_set_model(GTK_COMBO_BOX(m_AperCombo), GTK_TREE_MODEL(m_Apertures));
	if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_Apertures), NULL)>0) {
		SelectItem(GTK_COMBO_BOX(m_AperCombo), m_ApertureIndex);
		if (gtk_combo_box_get_active(GTK_COMBO_BOX(m_AperCombo))<0) {
			gtk_combo_box_set_active(GTK_COMBO_BOX(m_AperCombo), 0);
			m_ApertureIndex = SelectedItem(GTK_COMBO_BOX(m_AperCombo));
		}
	} else {
		gtk_combo_box_set_active(GTK_COMBO_BOX(m_AperCombo), -1);
		m_ApertureIndex = -1;
	}
	
	UpdateControls();
}

void CVarFind::SetImage(CImage *img)
{
	m_Image = img;
	UpdateImage();
	UpdateControls();
}

void CVarFind::UpdateImage(void)
{
	cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Chart), NULL);
	if (m_ImageData) {
		g_object_unref(m_ImageData);
		m_ImageData = NULL;
	}
	if (m_DisplayMode != DISPLAY_CHART && m_Image) {
		m_ImageData = m_Image->ToImageData(m_Negative, false, true, m_RowsUpward);
		cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Chart), m_ImageData);
	}
}

void CVarFind::SetPhotometryFile(GtkWindow *parent, CPhot *phot)
{
	m_Phot = phot;
	m_Catalog = NULL;
	m_LastFocus = tIndex();
	m_LastPosX = m_LastPosY = m_MovingTarget = -1;
	m_LastPosValid = false;
	delete m_Wcs;
	m_Wcs = NULL;
	if (phot->Wcs())
		m_Wcs = new CWcs(*phot->Wcs());
	UpdateChart();
	UpdateCatalogs(parent);
	UpdateStatus();
	UpdateControls();
}

void CVarFind::SetCatalogFile(GtkWindow *parent, CCatalog *cat)
{
	m_Catalog = cat;
	m_Phot = NULL;
	m_LastFocus = tIndex();
	m_LastPosX = m_LastPosY = m_MovingTarget = -1;
	m_LastPosValid = false;
	delete m_Wcs;
	m_Wcs = NULL;
	if (cat->Wcs())
		m_Wcs = new CWcs(*cat->Wcs());
	UpdateChart();
	UpdateCatalogs(parent);
	UpdateStatus();
	UpdateControls();
}

void CVarFind::UpdateChart(void)
{
	cmpack_chart_view_set_model(CMPACK_CHART_VIEW(m_Chart), NULL);
	cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Chart), NULL);
	if (m_ChartData) {
		g_object_unref(m_ChartData);
		m_ChartData = NULL;
	}
	if (m_Phot) {
		m_ApertureIndex = m_Phot->FindAperture(ApertureId());
		m_Phot->SelectAperture(m_ApertureIndex);
		m_ChartData = m_Phot->ToChartData(false, m_DisplayMode==DISPLAY_IMAGE);
	} else
	if (m_Catalog) {
		m_ChartData = m_Catalog->ToChartData(false, false, m_DisplayMode==DISPLAY_IMAGE);
	}
	if (g_Project->GetTargetType() == MOVING_TARGET)
		m_MovingTarget = g_Project->GetReferenceTarget();
	if (m_ChartData) {
		int count = cmpack_chart_data_count(m_ChartData);
		for (int i=0; i<count; i++) {
			CmpackChartItem *info = cmpack_chart_data_get_item(m_ChartData, i);
			if (!info->outline && !info->hidden) {
				gboolean retval = TRUE;
				Callback(CB_OBJECT_VALID_QUERY, (int)info->param, &retval);
				if (!retval)
					info->outline = TRUE;
			}
			info->disabled = info->outline;
		}
		int rowcount = cmpack_chart_data_count(m_ChartData);
		for (int row = 0; row < rowcount; row++) {
			int id = (int)cmpack_chart_data_get_param(m_ChartData, row);
			tObject *comp_ptr = NULL;
			for (GSList *ptr = m_CompList; ptr != NULL; ptr = ptr->next) {
				if (((tObject*)ptr->data)->id == id) {
					comp_ptr = (tObject*)ptr->data;
					break;
				}
			}
			if (comp_ptr) {
				// Selected (comparison) star
				cmpack_chart_data_set_tag(m_ChartData, row, "comp");
				cmpack_chart_data_set_color(m_ChartData, row, CMPACK_COLOR_GREEN);
				cmpack_chart_data_set_topmost(m_ChartData, row, TRUE);
				if (m_DisplayMode == DISPLAY_IMAGE)
					cmpack_chart_data_set_diameter(m_ChartData, row, 4.0);
				comp_ptr->index = row;
			}
			else if (id == m_Variable) {
				// Selected (variable) star
				cmpack_chart_data_set_tag(m_ChartData, row, "var");
				cmpack_chart_data_set_color(m_ChartData, row, CMPACK_COLOR_RED);
				cmpack_chart_data_set_topmost(m_ChartData, row, TRUE);
				if (m_DisplayMode == DISPLAY_IMAGE)
					cmpack_chart_data_set_diameter(m_ChartData, row, 4.0);
				m_VarIndex = row;
			}
			else {
				const gchar *selection_label = getSelectionLabel(id);
				if (selection_label) {
					// Known variable
					cmpack_chart_data_set_tag(m_ChartData, row, selection_label);
					cmpack_chart_data_set_color(m_ChartData, row, CMPACK_COLOR_YELLOW);
					cmpack_chart_data_set_topmost(m_ChartData, row, TRUE);
					if (m_DisplayMode == DISPLAY_IMAGE)
						cmpack_chart_data_set_diameter(m_ChartData, row, 4.0);
				}
				else {
					// Other stars
					cmpack_chart_data_set_tag(m_ChartData, row, NULL);
					cmpack_chart_data_set_color(m_ChartData, row, CMPACK_COLOR_DEFAULT);
					cmpack_chart_data_set_topmost(m_ChartData, row, FALSE);
					if (m_DisplayMode == DISPLAY_IMAGE)
						cmpack_chart_data_set_diameter(m_ChartData, row, 0);
				}
			}
		}
	}
	cmpack_chart_view_set_orientation(CMPACK_CHART_VIEW(m_Chart), m_RowsUpward ? CMPACK_ROWS_UPWARDS : CMPACK_ROWS_DOWNWARDS);
	cmpack_chart_view_set_negative(CMPACK_CHART_VIEW(m_Chart), m_Negative);
	if (m_DisplayMode != DISPLAY_CHART) 
		cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Chart), m_ImageData);
	cmpack_chart_view_set_model(CMPACK_CHART_VIEW(m_Chart), m_ChartData);
}

void CVarFind::SetMagDev(const CTable &tab)
{
	m_MagDev.MakeCopy(tab);
	m_MDChannelX = m_MDChannelY = -1;
	m_LastFocus = tIndex();
	m_LastPosValid = false;
	UpdateMagDevCurve();
	UpdateStatus();
	UpdateControls();
}

void CVarFind::UpdateMagDevCurve()
{
	cmpack_graph_view_set_model(CMPACK_GRAPH_VIEW(m_MDView), NULL);
	if (m_MDData) {
		g_object_unref(m_MDData);
		m_MDData = NULL;
	}
	if (m_MagDev.Valid()) {
		for (int i=0; i<m_MagDev.ChannelsX()->Count(); i++) {
			if (m_MagDev.ChannelsX()->GetInfo(i) == CChannel::DATA_VCMAG) {
				m_MDChannelX = i;
				break;
			}
		}
		for (int j=0; j<m_MagDev.ChannelsY()->Count(); j++) {
			if (m_MagDev.ChannelsY()->GetInfo(j) == CChannel::DATA_MAG_DEVIATION) {
				m_MDChannelY = j;
				break;
			}
		}
		if (m_MDChannelX>=0 && m_MDChannelY>=0) {
			m_MagDev.SetView(CMPACK_GRAPH_VIEW(m_MDView), m_MDChannelX, m_MDChannelY, false, "mag", "dev");
			m_MDData = m_MagDev.ToGraphData(m_MDChannelX, m_MDChannelY);
			if (m_MDData) {
				int rowCount = cmpack_graph_data_nrow(m_MDData);
				for (int row = 0; row < rowCount; row++) {
					int id = (int)cmpack_graph_data_get_param(m_MDData, 0, row);
					if (id == m_Variable) {
						cmpack_graph_data_set_tag(m_MDData, 0, row, "var");
						cmpack_graph_data_set_color(m_MDData, 0, row, CMPACK_COLOR_RED);
						cmpack_graph_data_set_topmost(m_MDData, 0, row, TRUE);
						m_VarIndex2 = row;
					}
					else {
						const gchar *selection_label = getSelectionLabel(id);
						if (selection_label) {
							cmpack_graph_data_set_tag(m_MDData, 0, row, selection_label);
							cmpack_graph_data_set_color(m_MDData, 0, row, CMPACK_COLOR_YELLOW);
							cmpack_graph_data_set_topmost(m_MDData, 0, row, TRUE);
						}
					}
				}
				cmpack_graph_view_set_model(CMPACK_GRAPH_VIEW(m_MDView), m_MDData);
				cmpack_graph_view_reset_zoom(CMPACK_GRAPH_VIEW(m_MDView), true, true);
			}
		}
	} 
	UpdateControls();
}

void CVarFind::SetLightCurve(const CTable &tab)
{
	m_LCurve.MakeCopy(tab);
	m_LCChannelX = m_LCChannelY = -1;
	m_LastFocus = tIndex();
	m_LastPosValid = false;
	UpdateLightCurve();
	UpdateStatus();
	UpdateControls();
}

void CVarFind::SetFixedJDRange(double jdmin, double jdmax)
{
	m_JDMin = jdmin;
	m_JDMax = jdmax;
	m_LCChannelX = m_LCChannelY = -1;
	m_LastFocus = tIndex();
	m_LastPosValid = false;
	UpdateLightCurve();
	UpdateStatus();
	UpdateControls();
}

void CVarFind::SetFixedMagRange(double magrange)
{
	m_FixedMagRange = magrange;
	m_LCChannelX = m_LCChannelY = -1;
	m_LastFocus = tIndex();
	m_LastPosValid = false;
	UpdateLightCurve();
	UpdateStatus();
	UpdateControls();
}

void CVarFind::UpdateLightCurve()
{
	double ymin, ymax;
	char buf[512];

	cmpack_graph_view_set_model(CMPACK_GRAPH_VIEW(m_LCView), NULL);
	if (m_LCData) {
		g_object_unref(m_LCData);
		m_LCData = NULL;
	}
	if (m_LCurve.Valid()) {
		for (int i=0; i<m_LCurve.ChannelsX()->Count(); i++) {
			if (m_LCurve.ChannelsX()->GetInfo(i) == CChannel::DATA_JD) {
				m_LCChannelX = i;
				break;
			}
		}
		for (int j=0; j<m_LCurve.ChannelsY()->Count(); j++) {
			if (m_LCurve.ChannelsY()->GetInfo(j) == CChannel::DATA_VCMAG) {
				m_LCChannelY = j;
				break;
			}
		}
		if (m_LCChannelX>=0 && m_LCChannelY>=0) {
			m_LCurve.GetMinMaxY(m_LCChannelY, TRUE, ymin, ymax);
			double yavg = 0.5*(ymin+ymax);
			if (m_MagScaleMode == MAG_SCALE_FIXED) 
				m_CustomMagRange = 0.01 * ceil(100 * MIN(MAX_MAG_RANGE, MAX(m_FixedMagRange, MIN_MAG_RANGE)));
			else if (m_MagScaleMode == MAG_SCALE_VAR) 
				m_CustomMagRange = 0.01 * ceil(100 * MIN(MAX_MAG_RANGE, MAX(ymax-ymin, MIN_MAG_RANGE)));
			double yrange = 0.01 * ceil(100 * m_CustomMagRange);
			sprintf(buf, "%.2lf", yrange);
			gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(m_MagScale))), buf);
			m_LCurve.SetView(CMPACK_GRAPH_VIEW(m_LCView), m_LCChannelX, m_LCChannelY, TRUE, NULL, "V-C",
				m_DateFormat, true, m_JDMin, m_JDMax, true, yavg-0.5*yrange, yavg+0.5*yrange);
			m_LCData = m_LCurve.ToGraphData(m_LCChannelX, m_LCChannelY);
			if (m_LCData) {
				cmpack_graph_view_set_model(CMPACK_GRAPH_VIEW(m_LCView), m_LCData);
				cmpack_graph_view_reset_zoom(CMPACK_GRAPH_VIEW(m_LCView), TRUE, TRUE);
			}
		}
	} else 
		gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(m_MagScale))), "");
}

void CVarFind::SetComparisonStar(int star_id)
{
	if (m_ChartData) {
		for (GSList *ptr = m_CompList; ptr != NULL; ptr = ptr->next) {
			int index = ((tObject*)ptr->data)->index;
			const gchar *selection_label = getSelectionLabel(((tObject*)ptr->data)->id);
			if (selection_label) {
				// Known variable
				cmpack_chart_data_set_tag(m_ChartData, index, selection_label);
				cmpack_chart_data_set_color(m_ChartData, index, CMPACK_COLOR_YELLOW);
				cmpack_chart_data_set_topmost(m_ChartData, index, TRUE);
				if (m_DisplayMode == DISPLAY_IMAGE)
					cmpack_chart_data_set_diameter(m_ChartData, index, 4.0);
			}
			else {
				// Other stars
				cmpack_chart_data_set_tag(m_ChartData, index, NULL);
				cmpack_chart_data_set_color(m_ChartData, index, CMPACK_COLOR_DEFAULT);
				cmpack_chart_data_set_topmost(m_ChartData, index, FALSE);
				if (m_DisplayMode == DISPLAY_IMAGE)
					cmpack_chart_data_set_diameter(m_ChartData, index, 0);
			}
		}
		DettachSelection();
		if (star_id == m_Variable)
			SetVariableStar(-1);
		m_CompList = list_clear(m_CompList);
		int index = cmpack_chart_data_find_item(m_ChartData, star_id);
		if (index >= 0) {
			// New comparison star
			m_CompList = list_append(NULL, star_id, index);
			cmpack_chart_data_set_tag(m_ChartData, index, "comp");
			cmpack_chart_data_set_color(m_ChartData, index, CMPACK_COLOR_GREEN);
			cmpack_chart_data_set_topmost(m_ChartData, index, TRUE);
			if (m_DisplayMode == DISPLAY_IMAGE)
				cmpack_chart_data_set_diameter(m_ChartData, index, 4.0);
		}
	}
	m_LastFocus = tIndex();
}

void CVarFind::SelectComparisonStar(int star_id)
{
	if (m_ChartData) {
		DettachSelection();
		if (star_id == m_Variable)
			SetVariableStar(-1);
		int index = cmpack_chart_data_find_item(m_ChartData, star_id);
		if (index >= 0) {
			m_CompList = list_append(m_CompList, star_id, index);
			cmpack_chart_data_set_tag(m_ChartData, index, "comp");
			cmpack_chart_data_set_color(m_ChartData, index, CMPACK_COLOR_GREEN);
			cmpack_chart_data_set_topmost(m_ChartData, index, TRUE);
			if (m_DisplayMode == DISPLAY_IMAGE)
				cmpack_chart_data_set_diameter(m_ChartData, index, 4.0);
		}
	}
	m_LastFocus = tIndex();
}

void CVarFind::UnselectComparisonStar(int star_id)
{
	if (m_ChartData) {
		for (GSList *ptr = m_CompList; ptr != NULL; ptr = ptr->next) {
			if (((tObject*)ptr->data)->id == star_id) {
				int index = ((tObject*)ptr->data)->index;
				const gchar *selection_label = getSelectionLabel(star_id);
				if (selection_label) {
					// Known variable
					cmpack_chart_data_set_tag(m_ChartData, index, selection_label);
					cmpack_chart_data_set_color(m_ChartData, index, CMPACK_COLOR_YELLOW);
					cmpack_chart_data_set_topmost(m_ChartData, index, TRUE);
					if (m_DisplayMode == DISPLAY_IMAGE)
						cmpack_chart_data_set_diameter(m_ChartData, index, 4.0);
				}
				else {
					// Other stars
					cmpack_chart_data_set_tag(m_ChartData, index, NULL);
					cmpack_chart_data_set_color(m_ChartData, index, CMPACK_COLOR_DEFAULT);
					cmpack_chart_data_set_topmost(m_ChartData, index, FALSE);
					if (m_DisplayMode == DISPLAY_IMAGE)
						cmpack_chart_data_set_diameter(m_ChartData, index, 0);
				}
				break;
			}
		}
		DettachSelection();
		m_CompList = list_remove_id(m_CompList, star_id);
	}
	m_LastFocus = tIndex();
}

void CVarFind::SetVariableStar(int star_id)
{
	if (star_id != m_Variable && m_MDData && m_ChartData) {
		// Unselect current variable star 
		if (m_VarIndex>=0) {
			const gchar *selection_label = getSelectionLabel((int)cmpack_chart_data_get_param(m_ChartData, m_VarIndex));
			if (selection_label) {
				// Known variable star
				cmpack_chart_data_set_tag(m_ChartData, m_VarIndex, selection_label);
				cmpack_chart_data_set_color(m_ChartData, m_VarIndex, CMPACK_COLOR_YELLOW);
				cmpack_chart_data_set_topmost(m_ChartData, m_VarIndex, TRUE);
				if (m_DisplayMode == DISPLAY_IMAGE)
					cmpack_chart_data_set_diameter(m_ChartData, m_VarIndex, 4.0);
			}
			else {
				// Unselected star
				cmpack_chart_data_set_tag(m_ChartData, m_VarIndex, NULL);
				cmpack_chart_data_set_color(m_ChartData, m_VarIndex, CMPACK_COLOR_DEFAULT);
				cmpack_chart_data_set_topmost(m_ChartData, m_VarIndex, FALSE);
				if (m_DisplayMode == DISPLAY_IMAGE)
					cmpack_chart_data_set_diameter(m_ChartData, m_VarIndex, 0);
			}
		}
		if (m_VarIndex2>=0) {
			const gchar *selection_label = getSelectionLabel((int)cmpack_graph_data_get_param(m_MDData, 0, m_VarIndex2));
			if (selection_label) {
				// Known variable star
				cmpack_graph_data_set_tag(m_MDData, 0, m_VarIndex2, selection_label);
				cmpack_graph_data_set_color(m_MDData, 0, m_VarIndex2, CMPACK_COLOR_YELLOW);
				cmpack_graph_data_set_topmost(m_MDData, 0, m_VarIndex2, TRUE);
			}
			else {
				// Unselected star
				cmpack_graph_data_set_tag(m_MDData, 0, m_VarIndex2, NULL);
				cmpack_graph_data_set_color(m_MDData, 0, m_VarIndex2, CMPACK_COLOR_DEFAULT);
				cmpack_graph_data_set_topmost(m_MDData, 0, m_VarIndex2, FALSE);
			}
		}
		// Select new variable star 
		DettachSelection();
		m_Variable = star_id;
		m_VarIndex = cmpack_chart_data_find_item(m_ChartData, star_id);
		if (m_VarIndex>=0) {
			cmpack_chart_data_set_tag(m_ChartData, m_VarIndex, "var");
			cmpack_chart_data_set_color(m_ChartData, m_VarIndex, CMPACK_COLOR_RED);
			cmpack_chart_data_set_topmost(m_ChartData, m_VarIndex, TRUE);
			if (m_DisplayMode==DISPLAY_IMAGE)
				cmpack_chart_data_set_diameter(m_ChartData, m_VarIndex, 4.0);
		}
		int col, row;
		if (cmpack_graph_data_find_item(m_MDData, m_Variable, &col, &row)) {
			m_VarIndex2 = row;
			cmpack_graph_data_set_tag(m_MDData, 0, m_VarIndex2, "var");
			cmpack_graph_data_set_color(m_MDData, 0, m_VarIndex2, CMPACK_COLOR_RED);
			cmpack_graph_data_set_topmost(m_MDData, 0, m_VarIndex2, TRUE);
		}
		cmpack_graph_view_unselect_all(CMPACK_GRAPH_VIEW(m_MDView));
	}
	m_LastFocus = tIndex();
}

void CVarFind::UpdateControls(void)
{
	bool ok;
	int var_count;

	gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(m_SelectVar), 
		(m_SelectMode == SELECT_VARIABLE));
	gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(m_SelectComp), 
		(m_SelectMode == SELECT_COMPARISON));
	gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(m_AddComp),
		(m_SelectMode == ADD_COMPARISON));
	gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(m_RemoveComp),
		(m_SelectMode == REMOVE_COMPARISON));

	if (m_SelectMode == SELECT_VARIABLE) {
		gtk_widget_set_sensitive(GTK_WIDGET(m_GoBack), m_VarIndex2>0);
		var_count = (m_MDData ? cmpack_graph_data_nrow(m_MDData) : 0);
		gtk_widget_set_sensitive(GTK_WIDGET(m_GoForward), m_VarIndex2>=0 && m_VarIndex2<var_count-1);
	} else {
		gtk_widget_set_sensitive(GTK_WIDGET(m_GoBack), FALSE);
		gtk_widget_set_sensitive(GTK_WIDGET(m_GoForward), FALSE);
	}
	gtk_widget_set_sensitive(GTK_WIDGET(m_AddComp), !m_SingleComp);
	gtk_widget_set_sensitive(GTK_WIDGET(m_RemoveComp), !m_SingleComp && g_slist_length(m_CompList)>1);
	
	ok = m_SelectionList!=NULL;
	gtk_widget_set_sensitive(m_SelectCbx, ok);
	gtk_widget_set_sensitive(GTK_WIDGET(m_SaveBtn), ok && m_VarIndex>=0 && m_CompList!=NULL);
	gtk_widget_set_sensitive(GTK_WIDGET(m_RemoveBtn), ok && m_SelectionIndex>=0);

	ok = m_LCurve.Valid();
	gtk_widget_set_sensitive(m_XLabel, ok);
	gtk_widget_set_sensitive(m_XCombo, ok);
	gtk_widget_set_sensitive(m_YLabel, ok);
	gtk_widget_set_sensitive(m_YCombo, ok);
	gtk_widget_set_sensitive(m_MagScale, ok && m_MagScaleMode == MAG_SCALE_CUSTOM);

	ok = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_Apertures), NULL)>1;
	gtk_widget_set_sensitive(m_AperCombo, ok);
	gtk_widget_set_sensitive(m_AperLabel, ok);
}

bool CVarFind::EnableCtrlQuery(tControlId ctrl)
{
	bool ok = false;
	Callback(CB_ENABLE_CTRL_QUERY, ctrl, &ok);
	return ok;
}

void CVarFind::SetDisplayMode(tDisplayMode mode)
{
	m_DisplayMode = mode;
	g_Project->SetInt("VarFind", "Mode", m_DisplayMode);
	UpdateImage();
	UpdateChart();
}


void CVarFind::SetTagsVisible(bool visible)
{
	m_VSTags = visible;
	g_Project->SetBool("VarFind", "Tags", m_VSTags);
	for (int i = 0; i < CATALOG_COUNT; i++) {
		if (m_CatalogBtns[i].layerId > 0)
			cmpack_chart_view_show_tags(CMPACK_CHART_VIEW(m_Chart), m_CatalogBtns[i].layerId, m_VSTags);
	}
}

//
// Left button click
//
void CVarFind::chart_item_activated(GtkWidget *pWidget, gint item, CVarFind *pDlg)
{
	pDlg->OnItemActivated(pWidget, item);
}
void CVarFind::graph_item_activated(GtkWidget *pWidget, gint col, gint row, CVarFind *pDlg)
{
	pDlg->OnItemActivated(pWidget, row);
}

void CVarFind::OnItemActivated(GtkWidget *pWidget, gint item)
{
	int id;

	if (pWidget==m_Chart && m_ChartData) {
		id = (int)cmpack_chart_data_get_param(m_ChartData, item);
		switch (m_SelectMode)
		{
		case SELECT_VARIABLE:
			if (id != m_Variable && !list_contains_id(m_CompList, id)) {
				SetVariableStar(id);
				Callback(CB_VARIABLE_CHANGED);
				UpdateStatus();
				UpdateControls();
			}
			break;
		case SELECT_COMPARISON:
			if (!list_contains_id(m_CompList, id)) {
				if (id == m_Variable)
					SetVariableStar(-1);
				SetComparisonStar(id);
				Callback(CB_COMPARISON_CHANGED);
				UpdateStatus();
				UpdateControls();
			}
			break;
		case ADD_COMPARISON:
			if (!list_contains_id(m_CompList, id)) {
				if (id == m_Variable)
					SetVariableStar(-1);
				SelectComparisonStar(id);
				Callback(CB_COMPARISON_CHANGED);
				UpdateStatus();
				UpdateControls();
			}
			break;

		case REMOVE_COMPARISON:
			if (list_contains_id(m_CompList, id) && g_slist_length(m_CompList)>1) {
				UnselectComparisonStar(id);
				Callback(CB_COMPARISON_CHANGED);
				UpdateStatus();
				UpdateControls();
			}
			break;
		}
	} else
	if (pWidget == m_MDView && m_MDData) {
		id = (int)cmpack_graph_data_get_param(m_MDData, 0, item);
		switch (m_SelectMode)
		{
		case SELECT_VARIABLE:
			if (id != m_Variable && !list_contains_id(m_CompList, id)) {
				SetVariableStar(id);
				Callback(CB_VARIABLE_CHANGED);
				UpdateStatus();
				UpdateControls();
			}
			break;

		case SELECT_COMPARISON:
			if (!list_contains_id(m_CompList, id)) {
				if (id == m_Variable)
					SetVariableStar(-1);
				SetComparisonStar(id);
				Callback(CB_COMPARISON_CHANGED);
				UpdateStatus();
				UpdateControls();
			}
			break;

		case ADD_COMPARISON:
			if (!list_contains_id(m_CompList, id)) {
				if (id == m_Variable)
					SetVariableStar(-1);
				SelectComparisonStar(id);
				Callback(CB_COMPARISON_CHANGED);
				UpdateStatus();
				UpdateControls();
			}
			break;

		case REMOVE_COMPARISON:
			if (list_contains_id(m_CompList, id)) {
				UnselectComparisonStar(id);
				Callback(CB_COMPARISON_CHANGED);
				UpdateStatus();
				UpdateControls();
			}
			break;
		}
	}
}

//
// Save chart to a file
//
void CVarFind::ExportChart(void)
{
	if (m_ChartData) {
		CChartExportDlg dlg(GetTopLevel());
		if (m_DisplayMode==DISPLAY_CHART) 
			dlg.Execute(m_ChartData, NULL, "chart", m_Negative, m_RowsUpward);
		else
			dlg.Execute(m_ChartData, m_ImageData, "chart", m_Negative, m_RowsUpward);
	}
}

//
// Save light curve to file
//
void CVarFind::SaveLightCurve(void)
{
	if (m_LCurve.Valid()) {
		CSaveLightCurveDlg pDlg(GetTopLevel());
		pDlg.Execute(m_LCurve, m_LCChannelX, m_LCChannelY, GetSelection(), false, NULL);
	}
}

//
// Save light curve as image
//
void CVarFind::ExportLightCurve(void)
{
	CGraphExportDlg pDlg(GetTopLevel());
	pDlg.Execute(CMPACK_GRAPH_VIEW(m_LCView), NULL);
}

//
// Save mag-dev curve to a file
//
void CVarFind::SaveMagDevCurve(void)
{
	if (m_MagDev.Valid()) {
		CExportMagDevCurveDlg dlg(GetTopLevel());
		dlg.Execute(m_MagDev);
	}
}

//
// Save mag-dev curve as image
//
void CVarFind::ExportMagDevCurve(void)
{
	CGraphExportDlg pDlg(GetTopLevel());
	pDlg.Execute(CMPACK_GRAPH_VIEW(m_MDView), NULL);
}

//
// Mouse button handler
//
gint CVarFind::button_press_event(GtkWidget *widget, GdkEventButton *event, CVarFind *pMe)
{
	if (event->type==GDK_BUTTON_PRESS && event->button == 3) {
		gtk_widget_grab_focus(widget);
		if (widget==pMe->m_LCView || widget==pMe->m_MDView) {
			int col, row;
			if (cmpack_graph_view_get_focused(CMPACK_GRAPH_VIEW(widget), &col, &row) && !cmpack_graph_view_is_selected(CMPACK_GRAPH_VIEW(widget), col, row)) {
				cmpack_graph_view_unselect_all(CMPACK_GRAPH_VIEW(widget));
				cmpack_graph_view_select(CMPACK_GRAPH_VIEW(widget), col, row);
			}
			pMe->OnContextMenu(widget, event);
			return TRUE;
		}
		else if (widget == pMe->m_Chart) {
			int focused = cmpack_chart_view_get_focused(CMPACK_CHART_VIEW(widget));
			if (focused >= 0)
				pMe->OnObjectMenu(event, focused);
			else
				pMe->OnChartMenu(event);
		}
	}
	return FALSE;
}

//
// Show context menu
//
void CVarFind::OnContextMenu(GtkWidget *widget, GdkEventButton *event)
{
	if (widget == m_LCView) {
		int selected = cmpack_graph_view_get_selected_count(CMPACK_GRAPH_VIEW(m_LCView));
		if (selected > 0) {
			m_LCGraphMenu.Enable(CMD_DELETE, EnableCtrlQuery(ID_DELETE_FROM_PROJECT));
			m_LCGraphMenu.Enable(CMD_HIDE, EnableCtrlQuery(ID_REMOVE_FROM_DATASET));
			m_LCGraphMenu.Enable(CMD_FRAMEINFO, selected == 1 && EnableCtrlQuery(ID_SHOW_FRAME_INFO));
			m_LCGraphMenu.Enable(CMD_PREVIEW, selected == 1 && EnableCtrlQuery(ID_SHOW_FRAME_PREVIEW));
			int res = m_LCGraphMenu.Execute(event);
			switch (res)
			{
			case CMD_PREVIEW:
				// Show frame preview
				Callback(CB_SHOW_FRAME_PREVIEW);
				break;
			case CMD_FRAMEINFO:
				// Show frame properties
				Callback(CB_SHOW_FRAME_INFO);
				break;
			case CMD_HIDE:
				// Remove selected frames from data set
				Callback(CB_REMOVE_FRAMES_FROM_DATASET);
				break;
			case CMD_DELETE:
				// Delete selected frames from project
				Callback(CB_DELETE_FRAMES_FROM_PROJECT);
				break;
			}
		}
	} else
	if (widget == m_MDView) {
		int selected = cmpack_graph_view_get_selected_count(CMPACK_GRAPH_VIEW(m_MDView));
		if (selected > 0) {
			GList *list = cmpack_graph_view_get_selected_rows(CMPACK_GRAPH_VIEW(m_MDView));
			int row = (list ? GPOINTER_TO_INT(list->data) : -1);
			g_list_free(list);
			m_MDGraphMenu.Enable(CMD_HIDE, EnableCtrlQuery(ID_REMOVE_FROM_DATASET));
			m_MDGraphMenu.Enable(CMD_COPY_WCS, selected == 1 && row >= 0 && m_Wcs != NULL);
			int res = m_MDGraphMenu.Execute(event);
			switch (res)
			{
			case CMD_HIDE:
				// Remove selected objects from data set
				Callback(CB_REMOVE_OBJECTS_FROM_DATASET);
				break;
			case CMD_COPY_WCS:
				// Copy WCS coordinates
				CopyWcsCoordinatesFromCurve(row);
				break;
			}
		}
	}
}

//
// Object's context menu (preview mode)
//
void CVarFind::OnObjectMenu(GdkEventButton *ev, gint row)
{
	if (m_Wcs) {
		switch (m_ChartMenu.Execute(ev))
		{
		case CMD_COPY_WCS:
			cmpack_chart_view_select(CMPACK_CHART_VIEW(m_Chart), row);
			CopyWcsCoordinatesFromChart(row);
			break;
		}
	}
}

//
// Context menu (no object focused)
//
void CVarFind::OnChartMenu(GdkEventButton *ev)
{
	double x, y, lng, lat;
	if (cmpack_chart_view_mouse_pos(CMPACK_CHART_VIEW(m_Chart), &x, &y) && m_Wcs && m_Wcs->pixelToWorld(x, y, lng, lat)) {
		switch (m_ChartMenu.Execute(ev))
		{
		case CMD_COPY_WCS:
			CopyWcsCoordinates(lng, lat);
			break;
		}
	}
}

void CVarFind::CopyWcsCoordinatesFromChart(int row)
{
	if (row >= 0 && m_ChartData && m_Wcs) {
		if (m_Phot) {
			CmpackPhtObject obj;
			double lng, lat;
			if (m_Phot->GetObjectParams(m_Phot->FindObject((int)cmpack_chart_data_get_param(m_ChartData, row)), CMPACK_PO_CENTER, &obj) && m_Wcs->pixelToWorld(obj.x, obj.y, lng, lat))
				CopyWcsCoordinates(lng, lat);
		}
		else if (m_Catalog) {
			CmpackCatObject obj;
			double lng, lat;
			if (m_Catalog->GetObjectParams(m_Catalog->FindObject((int)cmpack_chart_data_get_param(m_ChartData, row)), CMPACK_OM_CENTER, &obj) && m_Wcs->pixelToWorld(obj.center_x, obj.center_y, lng, lat))
				CopyWcsCoordinates(lng, lat);
		}
	}
}

void CVarFind::CopyWcsCoordinatesFromCurve(int row)
{
	if (row >= 0 && m_MDData && m_Wcs) {
		if (m_Phot) {
			CmpackPhtObject obj;
			double lng, lat;
			if (m_Phot->GetObjectParams(m_Phot->FindObject((int)cmpack_graph_data_get_param(m_MDData, 0, row)), CMPACK_PO_CENTER, &obj) && m_Wcs->pixelToWorld(obj.x, obj.y, lng, lat))
				CopyWcsCoordinates(lng, lat);
		}
		else if (m_Catalog) {
			CmpackCatObject obj;
			double lng, lat;
			if (m_Catalog->GetObjectParams(m_Catalog->FindObject((int)cmpack_graph_data_get_param(m_MDData, 0, row)), CMPACK_OM_CENTER, &obj) && m_Wcs->pixelToWorld(obj.center_x, obj.center_y, lng, lat))
				CopyWcsCoordinates(lng, lat);
		}
	}
}

void CVarFind::CopyWcsCoordinates(double lng, double lat)
{
	char buf[256];
	m_Wcs->print(lng, lat, buf, 256, false);
	GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
	gtk_clipboard_set_text(cb, buf, -1);
	gtk_clipboard_store(cb);
}

//
// Get aperture id
//
int CVarFind::ApertureId(void) const
{ 
	const CAperture *aper = m_Aper.Get(m_ApertureIndex);
	if (aper)
		return aper->Id(); 
	return -1;
}

//
// Get list of indices of selected rows
//
GList *CVarFind::GetSelectedFrames(void) const
{
	return cmpack_graph_view_get_selected_rows(CMPACK_GRAPH_VIEW(m_LCView));
}

//
// Get list of indices of selected rows
//
GList *CVarFind::GetSelectedObjects(void) const
{
	return cmpack_graph_view_get_selected_rows(CMPACK_GRAPH_VIEW(m_MDView));
}

//
// Get frame ID of first selected row
//
int CVarFind::SelectedFrameID(void) const
{
	int col, row;
	if (m_LCData && cmpack_graph_view_get_selected(CMPACK_GRAPH_VIEW(m_LCView), &col, &row)) 
		return (int)cmpack_graph_data_get_param(m_LCData, col, row);
	return -1;
}

void CVarFind::combo_changed(GtkComboBox *widget, CVarFind *pDlg)
{
	pDlg->OnComboChanged(widget);
}

void CVarFind::OnComboChanged(GtkComboBox *widget)
{
	if (widget == GTK_COMBO_BOX(m_SelectCbx)) {
		if (!m_Updating) {
			int index = SelectedItem(widget);
			if (index!=m_SelectionIndex) {
				if (index<0) {
					// Restore unsaved selection
					SetSelection(index, m_NewSelection);
				} else {
					if (m_SelectionIndex<0) {
						// Save current unsaved selection
						m_NewSelection = GetSelection();
					}
					// Restore selection
					SetSelection(index, m_SelectionList->At(index));
				}
			}
		}
	}
}

gboolean CVarFind::timer_cb(CVarFind *pDlg)
{
	if (pDlg->m_UpdatePos) {
		pDlg->m_UpdatePos = false;
		pDlg->UpdateStatus();
	}
	return TRUE;
}

void CVarFind::mouse_moved(GtkWidget *button, CVarFind *pDlg)
{
	pDlg->m_UpdatePos = true;
	if (button == pDlg->m_LCView) {
		pDlg->m_MouseWhere = MOUSE_ON_LCURVE;
	} else
	if (button == pDlg->m_MDView) {
		pDlg->m_MouseWhere = MOUSE_ON_MDCURVE;
	} else 
	if (button == pDlg->m_Chart) {
		pDlg->m_MouseWhere = MOUSE_ON_CHART;
	}
}

void CVarFind::mouse_left(GtkWidget *button, CVarFind *pDlg)
{
	if (pDlg->m_MouseWhere != MOUSE_OTHER) {
		pDlg->m_MouseWhere = MOUSE_OTHER;
		pDlg->UpdateStatus();
	}
}

void CVarFind::UpdateStatus(void)
{
	char buf[1024], msg[1024], buf1[512], buf2[512];

	switch (m_MouseWhere)
	{
	case CVarFind::MOUSE_ON_CHART:
		{
			int item = cmpack_chart_view_get_focused(CMPACK_CHART_VIEW(m_Chart));
			if (item>=0 && m_ChartData) {
				if (m_LastFocus!=tIndex(0, item)) {
					m_LastFocus = tIndex(0, item);
					int obj_id = (int)cmpack_chart_data_get_param(m_ChartData, item);
					if (m_Phot) {
						gdouble pos_x, pos_y;
						m_Phot->GetObjectPos(m_Phot->FindObject(obj_id), &pos_x, &pos_y);
						sprintf(buf, "Object #%d: X = %.1f, Y = %.1f", obj_id, pos_x, pos_y);
						// World coordinates
						double r, d;
						if (m_Wcs && m_Wcs->pixelToWorld(pos_x, pos_y, r, d)) {
							char cel[256];
							m_Wcs->print(r, d, cel, 256);
							strcat(buf, ", ");
							strcat(buf, cel);
						}
					} else if (m_Catalog) {
						CmpackCatObject obj;
						m_Catalog->GetObjectParams(m_Catalog->FindObject(obj_id), CMPACK_OM_CENTER, &obj);
						sprintf(buf, "Object #%d: X = %.1f, Y = %.1f", obj_id, obj.center_x, obj.center_y);
						// World coordinates
						double r, d;
						if (m_Wcs && m_Wcs->pixelToWorld(obj.center_x, obj.center_y, r, d)) {
							char cel[256];
							m_Wcs->print(r, d, cel, 256);
							strcat(buf, ", ");
							strcat(buf, cel);
						}
					}
					// Moving target
					if (obj_id == m_MovingTarget) {
						strcat(buf, ", ");
						strcat(buf, "moving target");
					}
					// Selection
					if (obj_id == m_Variable) {
						strcat(buf, ", ");
						strcat(buf, "var");
					} 
					if (list_contains_id(m_CompList, obj_id)) {
						strcat(buf, ", ");
						strcat(buf, "comp");
					}
					Callback(CB_UPDATE_STATUS, 0, buf);
				}
			} else {
				m_LastFocus = tIndex();
				double dx, dy;
				if (cmpack_chart_view_mouse_pos(CMPACK_CHART_VIEW(m_Chart), &dx, &dy)) {
					int x = (int)dx, y = (int)dy;
					if (x!=m_LastPosX || y!=m_LastPosY) {
						m_LastPosX = x;
						m_LastPosY = y;
						double r, d;
						if (m_Wcs && m_Wcs->pixelToWorld(x, y, r, d)) {
							char cel[256];
							m_Wcs->print(r, d, cel, 256);
							sprintf(buf, "Cursor: X = %d, Y = %d, %s", x, y, cel);
						} else {
							sprintf(buf, "Cursor: X = %d, Y = %d", x, y);
						}
						Callback(CB_UPDATE_STATUS, 0, buf);
					}
				} else {
					if (m_LastPosX!=-1 || m_LastPosY!=-1) {
						m_LastPosX = m_LastPosY = -1;
						Callback(CB_UPDATE_STATUS, 0, 0);
					}
				}
			}
		}
		break;

	case CVarFind::MOUSE_ON_LCURVE:
		{
			int col, row;
			if (cmpack_graph_view_get_focused(CMPACK_GRAPH_VIEW(m_LCView), &col, &row)) {
				if (m_LastFocus!=tIndex(col, row) && m_LCData) {
					m_LastFocus = tIndex(col, row);
					int frame_id = (int)cmpack_graph_data_get_param(m_LCData, col, row),
						xcol = m_LCurve.ChannelsX()->GetColumn(m_LCChannelX),
						ycol = m_LCurve.ChannelsY()->GetColumn(m_LCChannelY);
					double dx, dy;
					if (m_LCurve.Find(frame_id) && m_LCurve.GetDbl(xcol, &dx) && m_LCurve.GetDbl(ycol, &dy)) {
						PrintKeyValue(buf1, dx, m_LCurve.ChannelsX()->Get(m_LCChannelX));
						PrintKeyValue(buf2, dy, m_LCurve.ChannelsY()->Get(m_LCChannelY));
						sprintf(msg, "Frame #%d: %s, %s", frame_id, buf1, buf2);
					} else 
						sprintf(msg, "Frame #%d", frame_id);
					Callback(CB_UPDATE_STATUS, 0, msg);
				}
			} else {
				m_LastFocus = tIndex();
				double dx, dy;
				if (cmpack_graph_view_mouse_pos(CMPACK_GRAPH_VIEW(m_LCView), &dx, &dy)) {
					if (!m_LastPosValid || dx!=m_LastPosX || dy!=m_LastPosY) {
						m_LastPosValid = true;
						m_LastPosX = dx;
						m_LastPosY = dy;
						PrintKeyValue(buf1, dx, m_LCurve.ChannelsX()->Get(m_LCChannelX));
						PrintKeyValue(buf2, dy, m_LCurve.ChannelsY()->Get(m_LCChannelY));
						sprintf(msg, "Cursor: %s, %s", buf1, buf2);
						Callback(CB_UPDATE_STATUS, 0, msg);
					}
				} else {
					if (m_LastPosValid) {
						m_LastPosValid = false;
						Callback(CB_UPDATE_STATUS, 0, 0);
					}
				}
			}
		}
		break;

	case CVarFind::MOUSE_ON_MDCURVE:
		{
			int col, row;
			if (cmpack_graph_view_get_focused(CMPACK_GRAPH_VIEW(m_MDView), &col, &row)) {
				if (m_LastFocus!=tIndex(col, row) && m_MDData) {
					m_LastFocus = tIndex(col, row);
					int obj_id = (int)cmpack_graph_data_get_param(m_MDData, col, row),
						xcol = m_MagDev.ChannelsX()->GetColumn(m_MDChannelX),
						ycol = m_MagDev.ChannelsY()->GetColumn(m_MDChannelY);
					double dx, dy;
					m_MagDev.Find(obj_id);
					m_MagDev.GetDbl(xcol, &dx);
					m_MagDev.GetDbl(ycol, &dy);
					PrintKeyValue(buf1, dx, m_MagDev.ChannelsX()->Get(m_MDChannelX));
					PrintKeyValue(buf2, dy, m_MagDev.ChannelsY()->Get(m_MDChannelY));
					sprintf(msg, "Object #%d: %s, %s", obj_id, buf1, buf2);
					if (m_Phot) {
						gdouble pos_x, pos_y;
						m_Phot->GetObjectPos(m_Phot->FindObject(obj_id), &pos_x, &pos_y);
						sprintf(buf, "X = %.1f, Y = %.1f", pos_x, pos_y);
						strcat(msg, ", ");
						strcat(msg, buf);
						// World coordinates
						double r, d;
						if (m_Wcs && m_Wcs->pixelToWorld(pos_x, pos_y, r, d)) {
							char cel[256];
							m_Wcs->print(r, d, cel, 256);
							strcat(msg, ", ");
							strcat(msg, cel);
						}
					} else if (m_Catalog) {
						CmpackCatObject obj;
						m_Catalog->GetObjectParams(m_Catalog->FindObject(obj_id), CMPACK_OM_CENTER, &obj);
						sprintf(buf, "X = %.1f, Y = %.1f", obj.center_x, obj.center_y);
						strcat(msg, ", ");
						strcat(msg, buf);
						// World coordinates
						double r, d;
						if (m_Wcs && m_Wcs->pixelToWorld(obj.center_x, obj.center_y, r, d)) {
							char cel[256];
							m_Wcs->print(r, d, cel, 256);
							strcat(msg, ", ");
							strcat(msg, cel);
						}
					}
					// Moving target
					if (obj_id == m_MovingTarget) {
						strcat(buf, ", ");
						strcat(buf, "moving target");
					}
					// Selection
					if (obj_id == m_Variable) {
						strcat(msg, ", ");
						strcat(msg, "var");
					} 
					Callback(CB_UPDATE_STATUS, 0, msg);
				}
			} else {
				m_LastFocus = tIndex();
				double dx, dy;
				if (cmpack_graph_view_mouse_pos(CMPACK_GRAPH_VIEW(m_MDView), &dx, &dy)) {
					if (!m_LastPosValid || dx!=m_LastPosX || dy!=m_LastPosY) {
						m_LastPosValid = true;
						m_LastPosX = dx;
						m_LastPosY = dy;
						PrintKeyValue(buf1, dx, m_MagDev.ChannelsX()->Get(m_MDChannelX));
						PrintKeyValue(buf2, dy, m_MagDev.ChannelsY()->Get(m_MDChannelY));
						sprintf(msg, "Cursor: %s, %s", buf1, buf2);
						Callback(CB_UPDATE_STATUS, 0, msg);
					}
				} else {
					if (m_LastPosValid) {
						m_LastPosValid = false;
						Callback(CB_UPDATE_STATUS, 0, 0);
					}
				}
			}
		}
		break;

	default:
		if (m_LastPosX!=-1 || m_LastPosY!=-1) {
			m_LastPosX = m_LastPosY = -1;
			Callback(CB_UPDATE_STATUS, 0, 0);
		}
		break;
	}
}

void CVarFind::PrintKeyValue(char *buf, double value, const CChannel *channel)
{
	if (channel) {
		const gchar *name = channel->Name();
		if ((strcmp(name, "JD")==0 || strcmp(name, "JDHEL")==0) && m_DateFormat != JULIAN_DATE) {
			CmpackDateTime dt;
			cmpack_decodejd(value, &dt);
			sprintf(buf, "%s = %04d-%02d-%02d %02d:%02d:%02d", "UTC", dt.date.year, dt.date.month, dt.date.day,
				dt.time.hour, dt.time.minute, dt.time.second);
		} else {
			const gchar *unit = channel->Unit();
			if (unit)
				sprintf(buf, "%s = %.*f %s", channel->Name(), channel->Precision(), value, unit);
			else
				sprintf(buf, "%s = %.*f", channel->Name(), channel->Precision(), value);
		}
	} else {
		buf[0] = '\0';
	}
}

GtkWidget *CVarFind::CreateCatalogsWidget(void)
{
	char key[512], caption[512], tooltip[512];

	int count = 0;
	for (int i = 0; i < CATALOG_COUNT; i++) {
		m_CatalogBtns[i].enabled = CConfig::GetBool((CConfig::tParameter)Catalogs[i].par_use);
		if (m_CatalogBtns[i].enabled)
			count++;
	}
	
	GtkWidget *box = gtk_table_new(count, 2, FALSE);

	int row = 0;
	for (int i = 0; i < CATALOG_COUNT; i++) {
		if (m_CatalogBtns[i].enabled) {
			sprintf(key, "Show%s", Catalogs[i].id);
			m_CatalogBtns[i].show = CConfig::GetBool("Catalogs", key);
			sprintf(caption, "Show/hide %s", Catalogs[i].caption);
			m_CatalogBtns[i].chkButton = gtk_check_button_new_with_label(Catalogs[i].caption);
			gtk_widget_set_tooltip_text(m_CatalogBtns[i].chkButton, caption);
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_CatalogBtns[i].chkButton), m_CatalogBtns[i].show);
			g_signal_connect(G_OBJECT(m_CatalogBtns[i].chkButton), "clicked", G_CALLBACK(button_clicked), this);
			gtk_table_attach(GTK_TABLE(box), m_CatalogBtns[i].chkButton, 0, 1, row, row + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(0), 0, 0);

			m_CatalogBtns[i].icon = gtk_image_new();

			m_CatalogBtns[i].setButton = gtk_button_new();
			gtk_button_set_image(GTK_BUTTON(m_CatalogBtns[i].setButton), m_CatalogBtns[i].icon);
			sprintf(tooltip, "Select color for %s", Catalogs[i].caption);
			gtk_widget_set_tooltip_text(m_CatalogBtns[i].setButton, tooltip);
			g_signal_connect(G_OBJECT(m_CatalogBtns[i].setButton), "clicked", G_CALLBACK(button_clicked), this);
			gtk_table_attach(GTK_TABLE(box), m_CatalogBtns[i].setButton, 1, 2, row, row + 1, GtkAttachOptions(0), GtkAttachOptions(0), 0, 0);

			row++;
		}
	}
	return box;
}

void CVarFind::ShowCatalog(GtkWindow *parent, int index, bool show)
{
	char msg[512];

	m_CatalogBtns[index].show = show;
	sprintf(msg, "Show%s", Catalogs[index].id);
	CConfig::SetBool("Catalogs", msg, show);

	if (show) {
		UpdateCatalog(parent, index);
		if (m_CatalogBtns[index].layerId > 0)
			cmpack_chart_view_show_layer(CMPACK_CHART_VIEW(m_Chart), m_CatalogBtns[index].layerId, TRUE);
	}
	else {
		if (m_CatalogBtns[index].layerId > 0)
			cmpack_chart_view_show_layer(CMPACK_CHART_VIEW(m_Chart), m_CatalogBtns[index].layerId, FALSE);
	}
}

void CVarFind::UpdateCatalogs(GtkWindow *parent)
{
	char key[512];

	if (m_Wcs && (m_Phot && m_Phot->Valid() || (m_Catalog && m_Catalog->Valid()))) {
		for (int i = 0; i < CATALOG_COUNT; i++) {
			if (m_CatalogBtns[i].enabled) {
				gtk_widget_set_visible(m_CatalogBtns[i].chkButton, TRUE);
				gtk_widget_set_visible(m_CatalogBtns[i].setButton, TRUE);
				if (m_CatalogBtns[i].show) {
					UpdateCatalog(parent, i);
					if (m_CatalogBtns[i].layerId > 0) {
						cmpack_chart_view_show_layer(CMPACK_CHART_VIEW(m_Chart), m_CatalogBtns[i].layerId, TRUE);
						cmpack_chart_view_show_tags(CMPACK_CHART_VIEW(m_Chart), m_CatalogBtns[i].layerId, m_VSTags);
					}
				}
				sprintf(key, "Color%s", Catalogs[i].id);
				unsigned ucolor = CConfig::GetInt("Catalogs", key, Catalogs[i].defaultColor);
				GdkPixmap *pixmap = createPixmap(16, 16, ucolor);
				gtk_image_set_from_pixmap(GTK_IMAGE(m_CatalogBtns[i].icon), pixmap, NULL);
				gdk_pixmap_unref(pixmap);
			}
			else {
				if (m_CatalogBtns[i].chkButton)
					gtk_widget_set_visible(m_CatalogBtns[i].chkButton, FALSE);
				if (m_CatalogBtns[i].setButton)
					gtk_widget_set_visible(m_CatalogBtns[i].setButton, FALSE);
			}
		}
	}
	else {
		for (int i = 0; i < CATALOG_COUNT; i++) {
			if (m_CatalogBtns[i].chkButton)
				gtk_widget_set_visible(m_CatalogBtns[i].chkButton, FALSE);
			if (m_CatalogBtns[i].setButton)
				gtk_widget_set_visible(m_CatalogBtns[i].setButton, FALSE);
		}
	}
}

void CVarFind::UpdateCatalog(GtkWindow *parent, int index)
{
	char key[512];

	if (m_CatalogBtns[index].layerId == 0) {
		int width = 0, height = 0;
		if (m_Phot && m_Phot->Valid()) {
			CmpackPhtInfo info;
			m_Phot->GetParams(CMPACK_PI_FRAME_PARAMS, info);
			width = info.width;
			height = info.height;
		}
		else if (m_Catalog && m_Catalog->Valid()) {
			width = m_Catalog->Width();
			height = m_Catalog->Height();
		}
		if (width > 0 && height > 0 && m_Wcs) {
			m_CatalogBtns[index].layerId = cmpack_chart_view_add_layer(CMPACK_CHART_VIEW(m_Chart));
			cmpack_chart_view_show_tags(CMPACK_CHART_VIEW(m_Chart), m_CatalogBtns[index].layerId, m_VSTags);

			sprintf(key, "Color%s", Catalogs[index].id);
			unsigned ucolor = CConfig::GetInt("Catalogs", key, Catalogs[index].defaultColor);
			m_CatalogBtns[index].color.red = (double)((ucolor >> 16) & 0xFF) / 255.0;
			m_CatalogBtns[index].color.green = (double)((ucolor >> 8) & 0xFF) / 255.0;
			m_CatalogBtns[index].color.blue = (double)(ucolor & 0xFF) / 255.0;

			double r, rmax = 0, lon, lat, center_lon, center_lat;
			m_Wcs->pixelToWorld(width / 2, height / 2, center_lon, center_lat);
			center_lon = center_lon / 180.0 * M_PI;
			center_lat = center_lat / 180.0 * M_PI;
			m_Wcs->pixelToWorld(0, 0, lon, lat);
			r = angular_distance(center_lon, center_lat, lon / 180.0 * M_PI, lat / 180.0 * M_PI);
			if (r > rmax)
				rmax = r;
			m_Wcs->pixelToWorld(width - 1, 0, lon, lat);
			r = angular_distance(center_lon, center_lat, lon / 180.0 * M_PI, lat / 180.0 * M_PI);
			if (r > rmax)
				rmax = r;
			m_Wcs->pixelToWorld(0, height - 1, lon, lat);
			r = angular_distance(center_lon, center_lat, lon / 180.0 * M_PI, lat / 180.0 * M_PI);
			if (r > rmax)
				rmax = r;
			m_Wcs->pixelToWorld(width - 1, height - 1, lon, lat);
			r = angular_distance(center_lon, center_lat, lon / 180.0 * M_PI, lat / 180.0 * M_PI);
			if (r > rmax)
				rmax = r;

			VarCat::tPosFilter filter;
			filter.ra = center_lon * 180.0 / M_PI;
			filter.dec = center_lat * 180.0 / M_PI;
			filter.radius = 1.1 * rmax * 180.0 / M_PI;		// To degrees, plus 10 percent
			m_CurrentCatalog = index;
			m_ImageWidth = width;
			m_ImageHeight = height;
			VarCat::SearchEx(parent, Catalogs[index].filter, NULL, &filter, AddToLayer, this);
		}
	}
}

void CVarFind::AddToLayer(const char *objname, double ra, double dec, const char *catalog, const char *comment, void *data)
{
	CVarFind *pMe = reinterpret_cast<CVarFind*>(data);

	double x, y;
	if (pMe->m_Wcs->worldToPixel(ra, dec, x, y) && x >= 0 && y >= 0 && x < pMe->m_ImageWidth && y < pMe->m_ImageHeight) {
		int layerId = pMe->m_CatalogBtns[pMe->m_CurrentCatalog].layerId;
		int objectId = cmpack_chart_view_add_object(CMPACK_CHART_VIEW(pMe->m_Chart), layerId, x, y, CMPACK_COLOR_CUSTOM, objname);
		const tCustomColor *color = &pMe->m_CatalogBtns[pMe->m_CurrentCatalog].color;
		cmpack_chart_view_set_object_custom_color(CMPACK_CHART_VIEW(pMe->m_Chart), objectId, color->red, color->green, color->blue);
		pMe->m_CatalogBtns[pMe->m_CurrentCatalog].obj_list = g_slist_append(pMe->m_CatalogBtns[pMe->m_CurrentCatalog].obj_list, GINT_TO_POINTER(objectId));
	}
}

void CVarFind::EditCatalog(int index)
{
	char key[512];
	GdkColor gcolor;

	sprintf(key, "Color%s", Catalogs[index].id);
	unsigned old_rgb = CConfig::GetInt("Catalogs", key, Catalogs[index].defaultColor);
	gcolor.red = ((old_rgb >> 16) & 0xFF) * 257;
	gcolor.green = ((old_rgb >> 8) & 0xFF) * 257;
	gcolor.blue = (old_rgb & 0xFF) * 257;

	GtkWidget *dialog = gtk_color_selection_dialog_new("Select a color");
	GtkColorSelection *colorsel = GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel);
	gtk_color_selection_set_has_opacity_control(colorsel, FALSE);
	gtk_color_selection_set_current_color(colorsel, &gcolor);

	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
		gtk_color_selection_get_current_color(colorsel, &gcolor);
		unsigned new_rgb = (((gcolor.red >> 8) & 0xFF) << 16) | (((gcolor.green >> 8) & 0xFF) << 8) | ((gcolor.blue >> 8) & 0xFF);
		if (new_rgb != old_rgb) {
			CConfig::SetInt("Catalogs", key, new_rgb);
			GdkPixmap *pixmap = createPixmap(16, 16, new_rgb);
			gtk_image_set_from_pixmap(GTK_IMAGE(m_CatalogBtns[index].icon), pixmap, NULL);
			gdk_pixmap_unref(pixmap);
			tCustomColor *ccolor = &m_CatalogBtns[index].color;
			ccolor->red = (double)((new_rgb >> 16) & 0xFF) / 257.0;
			ccolor->green = (double)((new_rgb >> 8) & 0xFF) / 257.0;
			ccolor->blue = (double)(new_rgb & 0xFF) / 257.0;

			for (GSList *ptr = m_CatalogBtns[index].obj_list; ptr != NULL; ptr = ptr->next)
				cmpack_chart_view_set_object_custom_color(CMPACK_CHART_VIEW(m_Chart), GPOINTER_TO_INT(ptr->data), ccolor->red, ccolor->green, ccolor->blue);
		}
	}
	gtk_widget_destroy(dialog);
}

//----------------   SAVE MAG-DEV CURVE DIALOG   --------------------------

static const struct {
	const gchar *Id, *Caption, *Extension, *MimeType;
	const gchar *FilterName, *FilterPattern;
} FileFormats[] = {
	{ "TEXT",	"Text (space separated values)",	"txt",	"text/plain",	"Text files",		"*.txt" },
	{ "CSV",	"CSV (comma separated values)",		"csv",	"text/csv",		"CSV files",		"*.csv" },
	{ NULL }
};

static const gchar *FileExtension(gint type)
{
	if (type>=0)
		return FileFormats[type].Extension;
	return "";
}

static const gchar *FileMimeType(gint type)
{
	if (type>=0)
		return FileFormats[type].MimeType;
	return "";
}

static GtkFileFilter *FileFilter(gint type)
{
	if (type>=0 && FileFormats[type].FilterName && FileFormats[type].FilterPattern) {
		GtkFileFilter *filter = gtk_file_filter_new();
		gtk_file_filter_add_pattern(filter, FileFormats[type].FilterPattern);
		gtk_file_filter_set_name(filter, FileFormats[type].FilterName);
		return filter;
	}
	return NULL;
}

//
// Constructor
//
CExportMagDevCurveDlg::CExportMagDevCurveDlg(GtkWindow *pParent):m_pParent(pParent),
	m_Updating(false), m_FileType(TYPE_CSV), m_SelectedY(-1)
{
	memset(m_Options, 0, TYPE_N_ITEMS*sizeof(tOptions));
	
	// Dialog with buttons
	m_pDlg = gtk_file_chooser_dialog_new("Save mag-dev curve", pParent,
		GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 
		GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, GTK_STOCK_HELP, GTK_RESPONSE_HELP, NULL);
	gtk_file_chooser_standard_tooltips(GTK_FILE_CHOOSER(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);
	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(m_pDlg), true);

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

	// Options
	GtkWidget *frame = gtk_frame_new("Export options");
	GtkWidget *hbox = gtk_hbox_new(FALSE, 8);
	gtk_container_add(GTK_CONTAINER(frame), hbox);
	gtk_container_set_border_width(GTK_CONTAINER(hbox), 8);

	// File format
	GtkWidget *lbox = gtk_vbox_new(FALSE, 4);
	gtk_box_pack_start(GTK_BOX(hbox), lbox, TRUE, TRUE, 0);
	m_FileTypes = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
	GtkWidget *label = gtk_label_new("File type");
	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
	gtk_box_pack_start(GTK_BOX(lbox), label, FALSE, TRUE, 0);
	m_TypeCombo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(m_FileTypes));
	GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(m_TypeCombo), renderer, TRUE);
	gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(m_TypeCombo), renderer, "text", 1);
	gtk_box_pack_start(GTK_BOX(lbox), m_TypeCombo, FALSE, TRUE, 0);
	g_signal_connect(G_OBJECT(m_TypeCombo), "changed", G_CALLBACK(selection_changed), this);
	m_Header = gtk_check_button_new_with_label("Include column names");
	gtk_box_pack_start(GTK_BOX(lbox), m_Header, FALSE, TRUE, 0);
	g_signal_connect(G_OBJECT(m_Header), "toggled", G_CALLBACK(button_toggled), this);
	m_SkipInvalid = gtk_check_button_new_with_label("Discard rows with invalid values");
	gtk_box_pack_start(GTK_BOX(lbox), m_SkipInvalid, FALSE, TRUE, 0);
	g_signal_connect(G_OBJECT(m_SkipInvalid), "toggled", G_CALLBACK(button_toggled), this);
	
	// Column selection
	GtkWidget *rbox = gtk_vbox_new(FALSE, 4);
	gtk_box_pack_start(GTK_BOX(hbox), rbox, TRUE, TRUE, 0);
	m_Channels = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
	label = gtk_label_new("Columns");
	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
	gtk_box_pack_start(GTK_BOX(rbox), label, FALSE, TRUE, 0);
	m_VCCombo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(m_Channels));
	renderer = gtk_cell_renderer_text_new();
	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(m_VCCombo), renderer, TRUE);
	gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(m_VCCombo), renderer, "text", 1);
	gtk_box_pack_start(GTK_BOX(rbox), m_VCCombo, FALSE, TRUE, 0);
	g_signal_connect(G_OBJECT(m_VCCombo), "changed", G_CALLBACK(selection_changed), this);
	m_AllValues = gtk_check_button_new_with_label("Export all columns");
	gtk_box_pack_start(GTK_BOX(rbox), m_AllValues, FALSE, TRUE, 0);
	g_signal_connect(G_OBJECT(m_AllValues), "toggled", G_CALLBACK(button_toggled), this);
	
	gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(m_pDlg), frame);
	gtk_widget_show_all(frame);
}

CExportMagDevCurveDlg::~CExportMagDevCurveDlg()
{
	gtk_widget_destroy(m_pDlg);
	if (m_FileTypes)
		g_object_unref(m_FileTypes);
	if (m_Channels)
		g_object_unref(m_Channels);
}

CExportMagDevCurveDlg::tFileType CExportMagDevCurveDlg::StrToFileType(const gchar *str)
{
	if (str) {
		for (gint i=0; FileFormats[i].Id!=NULL; i++) {
			if (strcmp(FileFormats[i].Id, str)==0)
				return (tFileType)i;
		}
	}
	return TYPE_CSV;
}

const gchar *CExportMagDevCurveDlg::FileTypeToStr(tFileType type)
{
	if (type>=0 && type<TYPE_N_ITEMS)
		return FileFormats[type].Id;
	return "";
}

bool CExportMagDevCurveDlg::Execute(const CTable &table)
{
	m_Table.MakeCopy(table);
	m_SelectedY = -1;

	for (int i=0; i<m_Table.ChannelsY()->Count(); i++) {
		if (m_Table.ChannelsY()->GetInfo(i) == CChannel::DATA_MAG_DEVIATION) {
			m_SelectedY = i;
			break;
		}
	}

	gchar *aux = g_Project->GetStr("MagDevCurve", "FileType");
	m_FileType = StrToFileType(aux);
	g_free(aux);

	m_Updating = true;

	// Restore last folder and file name
	gchar *dirpath = g_Project->GetStr("Output", "Folder", NULL);
	if (!dirpath)
		dirpath = g_path_get_dirname(g_Project->Path());
	if (dirpath && g_file_test(dirpath, G_FILE_TEST_IS_DIR)) 
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(m_pDlg), dirpath);
	g_free(dirpath);
	gchar *filename = g_Project->GetStr("MagDevCurve", "File", "magdev.txt");
	const gchar *defext = FileExtension(m_FileType);
	if (defext && filename) {
		gchar *newname = SetFileExtension(filename, defext);
		g_free(filename);
		filename = newname;
	}
	gchar *basename = g_path_get_basename(filename);
	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(m_pDlg), basename);
	g_free(basename);
	g_free(filename);

	// File types
	gtk_combo_box_set_model(GTK_COMBO_BOX(m_TypeCombo), NULL);
	gtk_list_store_clear(m_FileTypes);
	for (int i=0; FileFormats[i].Caption; i++) {
		GtkTreeIter iter;
		gtk_list_store_append(m_FileTypes, &iter);
		gtk_list_store_set(m_FileTypes, &iter, 0, i, 1, FileFormats[i].Caption, -1);
	}
	gtk_combo_box_set_model(GTK_COMBO_BOX(m_TypeCombo), GTK_TREE_MODEL(m_FileTypes));
	SelectItem(GTK_COMBO_BOX(m_TypeCombo), m_FileType);
	if (gtk_combo_box_get_active(GTK_COMBO_BOX(m_TypeCombo))<0) {
		gtk_combo_box_set_active(GTK_COMBO_BOX(m_TypeCombo), 0);
		m_FileType = (tFileType)SelectedItem(GTK_COMBO_BOX(m_TypeCombo));
	}
	gtk_widget_set_sensitive(m_TypeCombo, 
		gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_FileTypes), NULL)>1);

	// Restore options (format dependent customizable options)
	m_Options[TYPE_CSV].skip_invalid = g_Project->GetBool("MagDevCurve", "CSV_SKIP_INVALID", true);
	m_Options[TYPE_CSV].header = g_Project->GetBool("MagDevCurve", "CSV_HEADER", true);
	m_Options[TYPE_CSV].all_values = g_Project->GetBool("MagDevCurve", "CSV_ALL_VALUES");
	m_Options[TYPE_TEXT].skip_invalid = g_Project->GetBool("MagDevCurve", "TEXT_SKIP_INVALID", true);
	m_Options[TYPE_TEXT].header = g_Project->GetBool("MagDevCurve", "TEXT_HEADER", true);
	m_Options[TYPE_TEXT].all_values = g_Project->GetBool("MagDevCurve", "TEXT_ALL_VALUES");

	m_Updating = false;

	OnTypeChanged();
	if (gtk_dialog_run(GTK_DIALOG(m_pDlg))!=GTK_RESPONSE_ACCEPT) {
		gtk_widget_hide(m_pDlg);
		return false;
	}
	gtk_widget_hide(m_pDlg);

	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(m_pDlg));

	// Save last folder and file name
	dirpath = g_path_get_dirname(filename);
	g_Project->SetStr("Output", "Folder", dirpath);
	g_free(dirpath);
	basename = g_path_get_basename(filename);
	g_Project->SetStr("MagDevCurve", "File", basename);
	g_free(basename);

	// Save settings
	g_Project->SetStr("MagDevCurve", "FileType", FileTypeToStr(m_FileType));
	g_Project->SetBool("MagDevCurve", "CSV_SKIP_INVALID", m_Options[TYPE_CSV].skip_invalid);
	g_Project->SetBool("MagDevCurve", "CSV_HEADER", m_Options[TYPE_CSV].header);
	g_Project->SetBool("MagDevCurve", "CSV_ALL_VALUES", m_Options[TYPE_CSV].all_values);
	g_Project->SetBool("MagDevCurve", "TEXT_SKIP_INVALID", m_Options[TYPE_TEXT].skip_invalid);
	g_Project->SetBool("MagDevCurve", "TEXT_HEADER", m_Options[TYPE_TEXT].header);
	g_Project->SetBool("MagDevCurve", "TEXT_ALL_VALUES", m_Options[TYPE_TEXT].all_values);

	const tOptions *opt = m_Options+m_FileType;

	CChannels *cx = m_Table.ChannelsX();
	for (int i=0; i<cx->Count(); i++) {
		switch (cx->GetInfo(i))
		{
		case CChannel::DATA_OBJECT_ID:
		case CChannel::DATA_VCMAG:
			cx->SetExportFlags(i, 0);
			break;
		default:
			cx->SetExportFlags(i, CChannel::EXPORT_SKIP);
		}
	}

	CChannels *cy = m_Table.ChannelsY();
	for (int i=0; i<cy->Count(); i++) {
		switch (cy->GetInfo(i))
		{
		case CChannel::DATA_MAG_DEVIATION:
		case CChannel::DATA_FREQUENCY:
			if (opt->all_values || i==m_SelectedY) 
				cy->SetExportFlags(i, 0);
			else
				cy->SetExportFlags(i, CChannel::EXPORT_SKIP);
			break;
		default:
			cy->SetExportFlags(i, CChannel::EXPORT_SKIP);
		}
	}

	int res = 0;
	unsigned flags = 0;
	if (!opt->header)
		flags |= CTable::EXPORT_NO_HEADER;
	if (opt->skip_invalid)
		flags |= CTable::EXPORT_NULVAL_SKIP_ROW;
	GError *error = NULL;
	if (!m_Table.ExportTable(filename, FileMimeType(m_FileType), flags, &error)) {
		if (error) {
			ShowError(m_pParent, error->message);
			g_error_free(error);
		}
		res = -1;
	} else
		ShowInformation(m_pParent, "The file was generated successfully.");

	g_free(filename);
	return (res==0);
}

void CExportMagDevCurveDlg::selection_changed(GtkComboBox *pWidget, CExportMagDevCurveDlg *pMe)
{
	pMe->OnSelectionChanged(pWidget);
}

void CExportMagDevCurveDlg::OnSelectionChanged(GtkComboBox *pWidget)
{
	if (!m_Updating) {
		if (GTK_WIDGET(pWidget) == m_TypeCombo) {
			int ft = SelectedItem(pWidget);
			if (ft>=0 && ft!=m_FileType) {
				m_FileType = (tFileType)ft;
				OnTypeChanged();
			}
		} else
		if (GTK_WIDGET(pWidget) == m_VCCombo) {
			int ch = SelectedItem(pWidget);
			if (ch>=0 && ch!=m_SelectedY) {
				m_SelectedY = ch;
				UpdateControls();
			}
		}
	}
}

void CExportMagDevCurveDlg::UpdateControls(void)
{
	gtk_widget_set_sensitive(m_VCCombo, 
		gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_Channels), NULL)>1 &&
		!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_AllValues)));
	gtk_widget_set_sensitive(m_AllValues, 
		gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_Channels), NULL)>1);
	gtk_widget_set_sensitive(m_Header, TRUE);
	gtk_widget_set_sensitive(m_SkipInvalid, TRUE);
}

void CExportMagDevCurveDlg::OnTypeChanged(void)
{
	// Change file filters
	GSList *list = gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(m_pDlg));
	for (GSList *ptr=list; ptr!=NULL; ptr=ptr->next) 
		gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(m_pDlg), (GtkFileFilter*)ptr->data);
	g_slist_free(list);
	GtkFileFilter *type_filter = FileFilter(m_FileType);
	if (type_filter)
		gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(m_pDlg), type_filter);
	GtkFileFilter *all_files = gtk_file_filter_new();
	gtk_file_filter_add_pattern(all_files, "*");
	gtk_file_filter_set_name(all_files, "All files");
	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(m_pDlg), all_files);
	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(m_pDlg), (type_filter ? type_filter : all_files));

	// Set file's extension
	const gchar *ext = FileExtension(m_FileType);
	if (ext) {
		gchar *oldname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(m_pDlg));
		if (oldname) {
			gchar *newname = SetFileExtension(oldname, ext);
			gchar *basename = g_path_get_basename(newname);
			gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(m_pDlg), basename);
			g_free(basename);
			g_free(newname);
			g_free(oldname);
		}
	}

	m_Updating = true;

	// Y columns
	gtk_combo_box_set_model(GTK_COMBO_BOX(m_VCCombo), NULL);
	gtk_list_store_clear(m_Channels);
	for (int i=0; i<m_Table.ChannelsY()->Count(); i++) {
		CChannel *ch = m_Table.ChannelsY()->Get(i);
		if (ch && (ch->Info() == CChannel::DATA_MAG_DEVIATION || ch->Info() == CChannel::DATA_FREQUENCY)) {
			GtkTreeIter iter;
			gtk_list_store_append(m_Channels, &iter);
			gtk_list_store_set(m_Channels, &iter, 0, i, 1, ch->Name(), -1);
		}
	}
	gtk_combo_box_set_model(GTK_COMBO_BOX(m_VCCombo), GTK_TREE_MODEL(m_Channels));
	if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_Channels), NULL)>0) {
		SelectItem(GTK_COMBO_BOX(m_VCCombo), m_SelectedY);
		if (gtk_combo_box_get_active(GTK_COMBO_BOX(m_VCCombo))<0) {
			gtk_combo_box_set_active(GTK_COMBO_BOX(m_VCCombo), 0);
			m_SelectedY = SelectedItem(GTK_COMBO_BOX(m_VCCombo));
		}
	} else {
		gtk_combo_box_set_active(GTK_COMBO_BOX(m_VCCombo), -1);
		m_SelectedY = -1;
	}
	OnSelectionChanged(GTK_COMBO_BOX(m_VCCombo));

	const tOptions *opt = m_Options+m_FileType;
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_AllValues), opt->all_values);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_Header), opt->header);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_SkipInvalid), opt->skip_invalid);

	m_Updating = false;

	UpdateControls();
}

void CExportMagDevCurveDlg::button_toggled(GtkToggleButton *pWidget, CExportMagDevCurveDlg *pMe)
{
	pMe->OnButtonToggled(pWidget);
}

void CExportMagDevCurveDlg::OnButtonToggled(GtkToggleButton *pWidget)
{
	if (GTK_WIDGET(pWidget) == m_SkipInvalid) {
		m_Options[m_FileType].skip_invalid = gtk_toggle_button_get_active(pWidget)!=0;
	} else
	if (GTK_WIDGET(pWidget) == m_Header) {
		m_Options[m_FileType].header = gtk_toggle_button_get_active(pWidget)!=0;
	} else
	if (GTK_WIDGET(pWidget) == m_AllValues) {
		m_Options[m_FileType].all_values = gtk_toggle_button_get_active(pWidget)!=0;
	}

	UpdateControls();
}

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

bool CExportMagDevCurveDlg::OnResponseDialog(gint response_id)
{
	switch (response_id)
	{
	case GTK_RESPONSE_HELP:
		// Show context help
		g_MainWnd->ShowHelp(GTK_WINDOW(m_pDlg), IDH_SAVE_TABLE);
		return false;
	}
	return true;
}
