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

stars_dlg.cpp (C-Munipack project)
The 'Choose stars' dialog
Copyright (C) 2008 David Motl, dmotl@volny.cz

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

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

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

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

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

#include "project.h"
#include "editselections_dlg.h"
#include "main.h"
#include "utils.h"
#include "configuration.h"
#include "configuration.h"
#include "profile.h"
#include "ctxhelp.h"
#include "varcat.h"

//-------------------------   POPUP MENU   ---------------------------

enum tPopupCommand
{
	CMD_SET_VARIABLE,
	CMD_SET_TARGET,
	CMD_SET_COMPARISON,
	CMD_SET_CHECK,
	CMD_UNSET,
	CMD_NEW_SELECTION,
	CMD_SAVE_SELECTION,
	CMD_EDIT_TAG,
	CMD_REMOVE_TAG,
	CMD_CLEAR_TAGS,
	CMD_COPY_WCS
};

static const CPopupMenu::tPopupMenuItem SelectMenuST[] = {
	{ CPopupMenu::MB_ITEM, CMD_SET_VARIABLE,	"_Variable" },
	{ CPopupMenu::MB_ITEM, CMD_SET_COMPARISON,	"_Comparison" },
	{ CPopupMenu::MB_ITEM, CMD_SET_CHECK,		"Chec_k" },
	{ CPopupMenu::MB_SEPARATOR },
	{ CPopupMenu::MB_ITEM, CMD_UNSET,			"_Unselect" },
	{ CPopupMenu::MB_ITEM, CMD_NEW_SELECTION,	"_New selection" },
	{ CPopupMenu::MB_ITEM, CMD_SAVE_SELECTION,	"_Save selection as..." },
	{ CPopupMenu::MB_SEPARATOR },
	{ CPopupMenu::MB_ITEM, CMD_EDIT_TAG,		"_Edit tag" },
	{ CPopupMenu::MB_ITEM, CMD_REMOVE_TAG,		"_Remove tag" },
	{ CPopupMenu::MB_ITEM, CMD_CLEAR_TAGS,		"Clear _all tags" },
	{ CPopupMenu::MB_SEPARATOR },
	{ CPopupMenu::MB_ITEM, CMD_COPY_WCS,		"Copy _WCS coordinates" },
	{ CPopupMenu::MB_END }
};

static const CPopupMenu::tPopupMenuItem SelectMenuMT[] = {
	{ CPopupMenu::MB_ITEM, CMD_SET_VARIABLE,	"_Variable" },
	{ CPopupMenu::MB_ITEM, CMD_SET_TARGET,		"Set moving _target as a variable" },
	{ CPopupMenu::MB_ITEM, CMD_SET_COMPARISON,	"_Comparison" },
	{ CPopupMenu::MB_ITEM, CMD_SET_CHECK,		"Chec_k" },
	{ CPopupMenu::MB_SEPARATOR },
	{ CPopupMenu::MB_ITEM, CMD_UNSET,			"_Unselect" },
	{ CPopupMenu::MB_ITEM, CMD_NEW_SELECTION,	"_New selection" },
	{ CPopupMenu::MB_ITEM, CMD_SAVE_SELECTION,	"_Save selection as..." },
	{ CPopupMenu::MB_SEPARATOR },
	{ CPopupMenu::MB_ITEM, CMD_EDIT_TAG,		"_Edit tag" },
	{ CPopupMenu::MB_ITEM, CMD_REMOVE_TAG,		"_Remove tag" },
	{ CPopupMenu::MB_ITEM, CMD_CLEAR_TAGS,		"Clear _all tags" },
	{ CPopupMenu::MB_SEPARATOR },
	{ CPopupMenu::MB_ITEM, CMD_COPY_WCS,		"Copy _WCS coordinates" },
	{ CPopupMenu::MB_END }
};

static const CPopupMenu::tPopupMenuItem ContextMenuST[] = {
	{ CPopupMenu::MB_ITEM, CMD_NEW_SELECTION,	"_New selection" },
	{ CPopupMenu::MB_ITEM, CMD_SAVE_SELECTION,	"_Save selection as..." },
	{ CPopupMenu::MB_SEPARATOR },
	{ CPopupMenu::MB_ITEM, CMD_CLEAR_TAGS,		"Clear _all tags" },
	{ CPopupMenu::MB_END }
};

static const CPopupMenu::tPopupMenuItem ContextMenuMT[] = {
	{ CPopupMenu::MB_ITEM, CMD_SET_TARGET,		"Set moving _target as a variable" },
	{ CPopupMenu::MB_SEPARATOR },
	{ CPopupMenu::MB_ITEM, CMD_NEW_SELECTION,	"_New selection" },
	{ CPopupMenu::MB_ITEM, CMD_SAVE_SELECTION,	"_Save selection as..." },
	{ CPopupMenu::MB_SEPARATOR },
	{ CPopupMenu::MB_ITEM, CMD_CLEAR_TAGS,		"Clear _all tags" },
	{ 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, 0xFFFF00, VarCat::GCVS },
	{ "NSV", "NSV", CConfig::SEARCH_NSV,  CConfig::NSV_PATH, 0xFFFF00, VarCat::NSV },
	{ "NSVS", "NSVS", CConfig::SEARCH_NSVS, CConfig::NSVS_PATH, 0xFFFF00, VarCat::NSVS },
	{ "VSX", "VSX", CConfig::SEARCH_VSX, CConfig::VSX_PATH, 0x00FFFF, VarCat::VSX }
};

//-------------------------   CHOOSE STARS DIALOG   --------------------------------

//
// Constructor
//
CEditSelectionsDlg::CEditSelectionsDlg(GtkWindow *pParent):m_pParent(pParent), m_SelectionList(NULL), 
	m_Image(NULL), m_Wcs(NULL), m_ImageData(NULL), m_ChartData(NULL), m_SelectMenu(NULL), 
	m_ContextMenu(NULL), m_SelectionIndex(-1), m_MovingTarget(0), m_StatusCtx(-1), m_StatusMsg(-1), 
	m_LastFocus(-1), m_LastPosX(-1), m_LastPosY(-1), m_Ensemble(true), m_AllStars(false), 
	m_InstMag(false), m_UpdatePos(true), m_ShowNewSelection(false), m_Updating(false), m_CurrentCatalog(0)
{
	GtkWidget *tbox, *scrwnd;
	GdkRectangle rc;
	char key[512], caption[512], tooltip[512];

	m_DisplayMode = (tDisplayMode)g_Project->GetInt("ChooseStarsDlg", "Mode", DISPLAY_IMAGE, 0, DISPLAY_FULL);
	m_Negative = CConfig::GetBool(CConfig::NEGATIVE_CHARTS);
	m_RowsUpward = CConfig::GetBool(CConfig::ROWS_UPWARD);
	m_VSTags = g_Project->GetBool("ChooseStarsDlg", "Tags", TRUE);

	// Dialog with buttons
	m_pDlg = gtk_dialog_new_with_buttons("Choose stars", pParent, 
		(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR),
		GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 
		GTK_STOCK_HELP, GTK_RESPONSE_HELP, NULL);
	gtk_dialog_widget_standard_tooltips(GTK_DIALOG(m_pDlg));
	g_signal_connect(G_OBJECT(m_pDlg), "response", G_CALLBACK(response_dialog), this);

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

	// Dialog size
	GdkScreen *scr = gdk_screen_get_default();
	gdk_screen_get_monitor_geometry(scr, 0, &rc);
	if (rc.width>0 && rc.height>0)
		gtk_window_set_default_size(GTK_WINDOW(m_pDlg), RoundToInt(0.9*rc.width), RoundToInt(0.8*rc.height));
	gtk_window_set_position(GTK_WINDOW(m_pDlg), GTK_WIN_POS_CENTER);

	// Toolbar
	tbox = gtk_toolbar_new();
	gtk_toolbar_set_style(GTK_TOOLBAR(tbox), GTK_TOOLBAR_ICONS);
	gtk_toolbar_set_orientation(GTK_TOOLBAR(tbox), GTK_ORIENTATION_HORIZONTAL);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(m_pDlg)->vbox), tbox, FALSE, FALSE, 0);

	// View mode
	toolbar_new_label(tbox, "View");
	m_ShowImage = toolbar_new_radio_button(tbox, NULL, "Image", "Display an image only");
	g_signal_connect(G_OBJECT(m_ShowImage), "toggled", G_CALLBACK(button_clicked), this);
	m_ShowChart = toolbar_new_radio_button(tbox, m_ShowImage, "Chart", "Display objects on a flat background");
	g_signal_connect(G_OBJECT(m_ShowChart), "toggled", G_CALLBACK(button_clicked), this);
	m_ShowMixed = toolbar_new_radio_button(tbox, m_ShowImage, "Mixed", "Display objects over an image");
	g_signal_connect(G_OBJECT(m_ShowMixed), "toggled", G_CALLBACK(button_clicked), this);
	toolbar_new_separator(tbox);

	// Object selection
	toolbar_new_label(tbox, "Selection");
	m_SelectCbx = toolbar_new_combo(tbox, "Choose an item to restore recently used object selection", 240);
	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);
	m_ClearBtn = toolbar_new_button(tbox, "New", "Start a new object selection");
	g_signal_connect(G_OBJECT(m_ClearBtn), "clicked", G_CALLBACK(button_clicked), this);
	m_SaveBtn = toolbar_new_button(tbox, "Save as...", "Save the current object selection");
	g_signal_connect(G_OBJECT(m_SaveBtn), "clicked", G_CALLBACK(button_clicked), this);
	m_RemoveBtn = toolbar_new_button(tbox, "Remove", "Remove the current object selection from the list");
	g_signal_connect(G_OBJECT(m_RemoveBtn), "clicked", G_CALLBACK(button_clicked), this);

	toolbar_new_separator(tbox);

	// Zoom
	toolbar_new_label(tbox, "Zoom");
	m_ZoomFit = toolbar_new_button_from_stock(tbox, GTK_STOCK_ZOOM_FIT, "Fit the frame to the window");
	g_signal_connect(G_OBJECT(m_ZoomFit), "clicked", G_CALLBACK(button_clicked), this);
	m_ZoomOut = toolbar_new_button_from_stock(tbox, GTK_STOCK_ZOOM_OUT, "Zoom out");
	g_signal_connect(G_OBJECT(m_ZoomOut), "clicked", G_CALLBACK(button_clicked), this);
	m_ZoomIn = toolbar_new_button_from_stock(tbox, GTK_STOCK_ZOOM_IN, "Zoom in");
	g_signal_connect(G_OBJECT(m_ZoomIn), "clicked", G_CALLBACK(button_clicked), this);

	toolbar_new_separator(tbox);

	// Catalogs
	m_CatalogBtns = new tCatalogSet[CATALOG_COUNT];
	memset(m_CatalogBtns, 0, CATALOG_COUNT * sizeof(tCatalogSet));
	toolbar_new_label(tbox, "Catalogs");
	for (int i = 0; i < CATALOG_COUNT; i++) {
		m_CatalogBtns[i].enabled = CConfig::GetBool((CConfig::tParameter)Catalogs[i].par_use);
		sprintf(key, "Show%s", Catalogs[i].id);
		m_CatalogBtns[i].show = CConfig::GetBool("Catalogs", key);
		if (m_CatalogBtns[i].enabled) {
			sprintf(caption, "Show/hide %s", Catalogs[i].caption);
			m_CatalogBtns[i].chkButton = toolbar_new_check_button(tbox, Catalogs[i].caption, 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);
			m_CatalogBtns[i].icon = gtk_image_new();
			sprintf(tooltip, "Select color for %s", Catalogs[i].caption);
			m_CatalogBtns[i].setButton = toolbar_new_button(tbox, m_CatalogBtns[i].icon, NULL, tooltip);
			g_signal_connect(G_OBJECT(m_CatalogBtns[i].setButton), "clicked", G_CALLBACK(button_clicked), this);
		}
	}
	m_ShowTags = toolbar_new_toggle_button(tbox, "Tags", "Display tags for known variables");
	g_signal_connect(G_OBJECT(m_ShowTags), "toggled", G_CALLBACK(button_clicked), this);

	// Chart
	m_Chart = cmpack_chart_view_new();
	cmpack_chart_view_set_mouse_control(CMPACK_CHART_VIEW(m_Chart), TRUE);
	cmpack_chart_view_set_activation_mode(CMPACK_CHART_VIEW(m_Chart), CMPACK_ACTIVATION_CLICK);
	g_signal_connect(G_OBJECT(m_Chart), "item-activated", G_CALLBACK(chart_item_activated), this);
	g_signal_connect(G_OBJECT(m_Chart), "mouse-moved", G_CALLBACK(chart_mouse_moved), this);
	g_signal_connect(G_OBJECT(m_Chart), "mouse-left", G_CALLBACK(chart_mouse_left), this);
	g_signal_connect(G_OBJECT(m_Chart), "button_press_event", G_CALLBACK(button_press_event), this);
	gtk_widget_set_size_request(m_Chart, 320, 200);
	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_box_pack_start(GTK_BOX(GTK_DIALOG(m_pDlg)->vbox), scrwnd, TRUE, TRUE, 0);

	// Status bar
	m_Status = gtk_statusbar_new();
	gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(m_Status), FALSE);
	gtk_box_pack_end(GTK_BOX(GTK_DIALOG(m_pDlg)->vbox), m_Status, FALSE, FALSE, 0);
	m_StatusCtx = gtk_statusbar_get_context_id(GTK_STATUSBAR(m_Status), "Main");

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

	// Show widgets
	gtk_widget_show_all(GTK_DIALOG(m_pDlg)->vbox);
}


//
// Destructor
//
CEditSelectionsDlg::~CEditSelectionsDlg()
{
	g_source_remove(m_TimerId);
	g_signal_handlers_disconnect_by_func(G_OBJECT(m_Chart), (gpointer)chart_mouse_moved, this);
	g_signal_handlers_disconnect_by_func(G_OBJECT(m_Chart), (gpointer)chart_mouse_left, this);
	if (m_ChartData)
		g_object_unref(m_ChartData);
	if (m_ImageData)
		g_object_unref(m_ImageData);
	gtk_widget_destroy(m_pDlg);
	g_object_unref(m_Selections);
	delete m_SelectMenu;
	delete m_ContextMenu;
	delete m_Image;
	delete m_Wcs;
	for (int i = 0; i < CATALOG_COUNT; i++)
		g_slist_free(m_CatalogBtns[i].obj_list);
	delete[] m_CatalogBtns;
}


//
// Execute the dialog
//
bool CEditSelectionsDlg::Execute()
{
	int	res = 0;
	gchar *fts_file;
	GtkTreePath *refpath;
	const gchar *tmp_file;

	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_ImageData) {
		g_object_unref(m_ImageData);
		m_ImageData = NULL;
	}
	delete m_Image;
	m_Image = NULL;
	delete m_Wcs;
	m_Wcs = NULL;
	m_LastFocus = -1;
	m_LastPosX = m_LastPosY = -1;
	m_UpdatePos = true;

	switch (g_Project->GetReferenceType())
	{
	case REF_FRAME:
		refpath = g_Project->GetReferencePath();
		if (refpath) {
			gchar *pht_file = g_Project->GetPhotFile(refpath);
			if (pht_file) {
				GError *error = NULL;
				if (m_Phot.Load(pht_file, &error)) {
					m_Phot.SelectAperture(0);
					UpdateChart();
					fts_file = g_Project->GetImageFile(refpath);
					if (fts_file) {
						m_Image = CImage::fromFile(fts_file, CMPACK_BITPIX_AUTO, &error);
						UpdateImage();
						g_free(fts_file);
					}
					if (m_Phot.Wcs())
						m_Wcs = new CWcs(*m_Phot.Wcs());
				}
				else {
					if (error) {
						ShowError(m_pParent, error->message);
						g_error_free(error);
					}
					res = -1;
				}
				g_free(pht_file);
			}
			gtk_tree_path_free(refpath);
		}
		break;

	case REF_CATALOG_FILE:
		// Load catalog file
		tmp_file = g_Project->GetTempCatFile()->FullPath();
		if (tmp_file) {
			GError *error = NULL;
			if (m_Catalog.Load(tmp_file, &error)) {
				UpdateChart();
				fts_file = SetFileExtension(tmp_file, FILE_EXTENSION_FITS);
				if (fts_file) {
					m_Image = CImage::fromFile(fts_file, CMPACK_BITPIX_AUTO, &error);
					UpdateImage();
					g_free(fts_file);
				}
				if (m_Catalog.Wcs())
					m_Wcs = new CWcs(*m_Catalog.Wcs());
			}
			else {
				if (error) {
					ShowError(m_pParent, error->message);
					g_error_free(error);
				}
				res = -1;
			}
		}
		break;

	default:
		ShowError(m_pParent, "No reference file");
		res = -1;
	}
	if (res != 0)
		return false;

	gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(m_ShowChart),
		m_DisplayMode == DISPLAY_CHART);
	gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(m_ShowImage),
		m_DisplayMode == DISPLAY_IMAGE);
	gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(m_ShowMixed),
		m_DisplayMode == DISPLAY_FULL);
	gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(m_ShowTags),
		m_VSTags);
	gtk_widget_set_sensitive(GTK_WIDGET(m_ShowImage),
		m_Image && m_Image->Width() > 0 && m_Image->Height() > 0);
	gtk_widget_set_sensitive(GTK_WIDGET(m_ShowMixed),
		m_Image && m_Image->Width() > 0 && m_Image->Height() > 0);

	UpdateSelectionList();
	UpdateCatalogs(m_pParent);
	UpdateStatus();
	UpdateControls();

	bool retval = gtk_dialog_run(GTK_DIALOG(m_pDlg)) == GTK_RESPONSE_ACCEPT;
	gtk_widget_hide(m_pDlg);
	return retval;
}


//
// Left button click
//
void CEditSelectionsDlg::chart_item_activated(GtkWidget *widget, gint item, CEditSelectionsDlg *pMe)
{
	GdkEventButton ev;
	ev.button = 1;
	ev.time = gtk_get_current_event_time();
	pMe->OnSelectMenu(&ev, item);
}

//
// Right mouse click
//
gint CEditSelectionsDlg::button_press_event(GtkWidget *widget, GdkEventButton *event, CEditSelectionsDlg *pMe)
{
	int focused;

	if (event->type==GDK_BUTTON_PRESS && event->button==3) {
		gtk_widget_grab_focus(widget);
		if (widget==pMe->m_Chart) {
			focused = cmpack_chart_view_get_focused(CMPACK_CHART_VIEW(widget));
			if (focused>=0) 
				pMe->OnSelectMenu(event, focused);
			else
				pMe->OnContextMenu(event);
		}
		return TRUE;
	}
	return FALSE;
}

//
// Set status text
//
void CEditSelectionsDlg::SetStatus(const char *text)
{
	if (m_StatusMsg>=0) {
		gtk_statusbar_pop(GTK_STATUSBAR(m_Status), m_StatusCtx);
		m_StatusMsg = -1;
	}
	if (text && strlen(text)>0) 
		m_StatusMsg = gtk_statusbar_push(GTK_STATUSBAR(m_Status), m_StatusCtx, text);
}

void CEditSelectionsDlg::chart_mouse_moved(GtkWidget *button, CEditSelectionsDlg *pDlg)
{
	pDlg->m_UpdatePos = true;
}

void CEditSelectionsDlg::chart_mouse_left(GtkWidget *button, CEditSelectionsDlg *pDlg)
{
	pDlg->UpdateStatus();
}

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

//
// Object's context menu
//
void CEditSelectionsDlg::OnSelectMenu(GdkEventButton *event, gint row)
{
	if (!m_ChartData)
		return;

	if (!m_SelectMenu)
		m_SelectMenu = new CPopupMenu(m_MovingTarget>0 ? SelectMenuMT : SelectMenuST);

	int star_id = (int)cmpack_chart_data_get_param(m_ChartData, row);
	CmpackSelectionType type = m_Current.Type(star_id);
	m_SelectMenu->Enable(CMD_SET_VARIABLE, type!=CMPACK_SELECT_VAR && !m_AllStars);
	m_SelectMenu->Enable(CMD_SET_TARGET, m_MovingTarget>0 && !m_AllStars);
	m_SelectMenu->Enable(CMD_SET_COMPARISON, type!=CMPACK_SELECT_COMP);
	m_SelectMenu->Enable(CMD_SET_CHECK, type!=CMPACK_SELECT_CHECK && !m_AllStars);
	m_SelectMenu->Enable(CMD_UNSET, type!=CMPACK_SELECT_NONE);
	m_SelectMenu->Enable(CMD_NEW_SELECTION, m_SelectionIndex>=0 || m_Current.Count()>0);
	m_SelectMenu->Enable(CMD_SAVE_SELECTION, m_Current.Count()>0);
	m_SelectMenu->Enable(CMD_REMOVE_TAG, m_Tags && m_Tags->caption(star_id) != 0);
	m_SelectMenu->Enable(CMD_CLEAR_TAGS, m_Tags && m_Tags->count() > 0);
	m_SelectMenu->Enable(CMD_COPY_WCS, m_ChartData && m_Wcs);
	switch (m_SelectMenu->Execute(event))
	{
	case CMD_SET_VARIABLE:
		Select(star_id, CMPACK_SELECT_VAR);
		break;
	case CMD_SET_TARGET:
		Select(m_MovingTarget, CMPACK_SELECT_VAR);
		break;
	case CMD_SET_COMPARISON:
		Select(star_id, CMPACK_SELECT_COMP);
		break;
	case CMD_SET_CHECK:
		Select(star_id, CMPACK_SELECT_CHECK);
		break;
	case CMD_UNSET:
		Unselect(star_id);
		break;
	case CMD_NEW_SELECTION:
		NewSelection();
		break;
	case CMD_SAVE_SELECTION:
		SaveSelection();
		break;
	case CMD_EDIT_TAG:
		EditTag(row, star_id);
		break;
	case CMD_REMOVE_TAG:
		RemoveTag(row, star_id);
		break;
	case CMD_CLEAR_TAGS:
		ClearTags();
		break;
	case CMD_COPY_WCS:
		CopyWcsCoordinatesFromChart(star_id);
		break;
	}
	UpdateControls();
}

//
// Context menu (no object focused)
//
void CEditSelectionsDlg::OnContextMenu(GdkEventButton *event)
{
	if (!m_ContextMenu)
		m_ContextMenu = new CPopupMenu(m_MovingTarget>0 ? ContextMenuMT : ContextMenuST);

	m_ContextMenu->Enable(CMD_NEW_SELECTION, m_SelectionIndex>=0 || m_Current.Count()>0);
	m_ContextMenu->Enable(CMD_SAVE_SELECTION, m_Current.Count()>0);
	m_ContextMenu->Enable(CMD_CLEAR_TAGS, m_Tags && m_Tags->count() > 0);
	switch (m_ContextMenu->Execute(event))
	{
	case CMD_SET_TARGET:
		Select(m_MovingTarget, CMPACK_SELECT_VAR);
		break;
	case CMD_NEW_SELECTION:
		NewSelection();
		break;
	case CMD_SAVE_SELECTION:
		SaveSelection();
		break;
	case CMD_CLEAR_TAGS:
		ClearTags();
		break;
	}
	UpdateControls();
}

void CEditSelectionsDlg::Select(int star_id, CmpackSelectionType type)
{
	if (star_id>0) {
		DettachSelection();
		m_Current.Select(star_id, type);
		UpdateStatus();
		UpdateAll();
	}
}

void CEditSelectionsDlg::Unselect(int star_id)
{
	if (star_id>0) {
		DettachSelection();
		m_Current.Select(star_id, CMPACK_SELECT_NONE);
		UpdateStatus();
		UpdateAll();
	}
}

void CEditSelectionsDlg::UnselectType(CmpackSelectionType type)
{
	int count = m_Current.CountStars(type);
	if (count>0 && m_ChartData) {
		DettachSelection();
		int *stars = (int*)g_malloc(count*sizeof(int));
		m_Current.GetStarList(type, stars, count);
		for (int i=0; i<count; i++) {
			int row = cmpack_chart_data_find_item(m_ChartData, stars[i]);
			if (row>=0)
				m_Current.Select(stars[i], CMPACK_SELECT_NONE);
		}
		g_free(stars);
		UpdateStatus();
		UpdateAll();
	}
}

void CEditSelectionsDlg::NewSelection(void)
{
	DettachSelection();
	SetNewSelection(CSelection());
	SetCurrentSelection(CSelection());
	m_SelectionIndex = -1;
	UpdateStatus();
	UpdateAll();
	UpdateControls();
}

void CEditSelectionsDlg::SaveSelection(void)
{
	const gchar *defValue = (m_SelectionIndex>=0 ? m_SelectionList->Name(m_SelectionIndex) : "");
	CTextQueryDlg dlg(GTK_WINDOW(m_pDlg), "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, m_Current);
		m_SelectionIndex = m_SelectionList->IndexOf(name);
		g_free(name);
		UpdateSelectionList();
		UpdateControls();
	}
}

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

bool CEditSelectionsDlg::OnNameValidator(const gchar *name, GtkWindow *parent)
{
	if (!name || name[0]=='\0') {
		ShowError(parent, "Please, specify name of the selection.");
		return false;
	}
	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 CEditSelectionsDlg::RemoveSelection(void)
{
	if (m_SelectionIndex<0) {
		if (m_SelectionList->Count()>0) {
			m_ShowNewSelection = false;
			m_SelectionIndex = 0;
			SetCurrentSelection(m_SelectionList->At(m_SelectionIndex));
		} else {
			m_ShowNewSelection = true;
			SetNewSelection(CSelection());
			SetCurrentSelection(CSelection());
			m_SelectionIndex = -1;
		}
	} else {
		m_SelectionList->RemoveAt(m_SelectionIndex);
		if (m_SelectionList->Count()>0) {
			if (m_SelectionIndex>=m_SelectionList->Count())
				m_SelectionIndex = m_SelectionList->Count()-1;
			SetCurrentSelection(m_SelectionList->At(m_SelectionIndex));
		} else {
			m_ShowNewSelection = true;
			SetNewSelection(CSelection());
			SetCurrentSelection(CSelection());
			m_SelectionIndex = -1;
		}
	}
	UpdateStatus();
	UpdateAll();
	UpdateSelectionList();
	UpdateControls();
}

void CEditSelectionsDlg::EditTag(int row, int star_id)
{
	if (row>=0 && star_id>=0 && m_Tags) {
		char buf[256];
		const gchar *cap = m_Current.Description(star_id);
		if (cap) 
			sprintf(buf, "Enter tag caption for %s:", cap);
		else 
			sprintf(buf, "Enter tag caption for object #%d:", star_id);
		CTextQueryDlg dlg(GTK_WINDOW(m_pDlg), "Edit tag");
		gchar *value = dlg.Execute(buf, MAX_TAG_SIZE, m_Tags->caption(star_id),
			(CTextQueryDlg::tValidator*)tag_validator, this);
		if (value) {
			SetTag(row, star_id, value);
			g_free(value);
		}
	}
}

bool CEditSelectionsDlg::tag_validator(const gchar *name, GtkWindow *parent, CEditSelectionsDlg *pMe)
{
	return pMe->OnTagValidator(name, parent);
}

bool CEditSelectionsDlg::OnTagValidator(const gchar *name, GtkWindow *parent)
{
	if (!name || name[0]=='\0') {
		ShowError(parent, "Please, specify caption for the new tag.");
		return false;
	}
	return true;
}

void CEditSelectionsDlg::SetTag(int row, int star_id, const gchar *value)
{
	if (row>=0 && star_id>=0 && m_Tags) {
		if (value && value[0]!='\0') 
			m_Tags->insert(star_id, value);
		else 
			m_Tags->remove(star_id);
		UpdateObject(row, star_id);
		UpdateStatus();
	}
}

void CEditSelectionsDlg::RemoveTag(int row, int star_id)
{
	if (row>=0 && m_Tags && m_Tags->contains(star_id)) {
		m_Tags->remove(star_id);
		UpdateObject(row, star_id);
		UpdateStatus();
	}
}

void CEditSelectionsDlg::ClearTags(void)
{
	if (m_Tags) {
		m_Tags->clear();
		UpdateStatus();
		UpdateAll();
		UpdateControls();
	}
}

void CEditSelectionsDlg::UpdateSelectionList(void)
{
	m_Updating = true;

	gtk_combo_box_set_model(GTK_COMBO_BOX(m_SelectCbx), NULL);
	gtk_list_store_clear(m_Selections);
	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);
	}
	int defIndex = m_SelectionList->IndexOf("");
	if (defIndex>=0) {
		GtkTreeIter iter;
		gtk_list_store_append(m_Selections, &iter);
		gtk_list_store_set(m_Selections, &iter, 0, defIndex, 1, "Default selection", -1);
	}
	for (int i=0; i<m_SelectionList->Count(); i++) {
		if (i!=defIndex) {
			GtkTreeIter iter;
			gtk_list_store_append(m_Selections, &iter);
			gtk_list_store_set(m_Selections, &iter, 0, i, 1, m_SelectionList->Name(i), -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;
}

void CEditSelectionsDlg::DettachSelection(void)
{
	SetNewSelection(m_Current);
	SetCurrentSelection(m_NewSelection);
	m_SelectionIndex = -1;
	UpdateStatus();
	UpdateAll();
	UpdateControls();
	if (!m_ShowNewSelection) {
		m_ShowNewSelection = true;
		UpdateSelectionList();
	}
	GtkTreeIter iter;
	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 CEditSelectionsDlg::button_clicked(GtkWidget *pButton, CEditSelectionsDlg *pMe)
{
	pMe->OnButtonClicked(pButton);
}

void CEditSelectionsDlg::OnButtonClicked(GtkWidget *pBtn)
{
	double zoom;

	if (pBtn==GTK_WIDGET(m_ShowChart)) {
		m_DisplayMode = DISPLAY_CHART;
		g_Project->SetInt("ChooseStarsDlg", "Mode", m_DisplayMode);
		cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Chart), NULL);
		UpdateImage();
		UpdateChart();
		UpdateStatus();
	} else
	if (pBtn==GTK_WIDGET(m_ShowImage)) {
		m_DisplayMode = DISPLAY_IMAGE;
		g_Project->SetInt("ChooseStarsDlg", "Mode", m_DisplayMode);
		cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Chart), m_ImageData);
		UpdateImage();
		UpdateChart();
		UpdateStatus();
	} else
	if (pBtn==GTK_WIDGET(m_ShowMixed)) {
		m_DisplayMode = DISPLAY_FULL;
		g_Project->SetInt("ChooseStarsDlg", "Mode", m_DisplayMode);
		cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Chart), m_ImageData);
		UpdateImage();
		UpdateChart();
		UpdateStatus();
	} else
	if (pBtn == GTK_WIDGET(m_ShowTags)) {
		m_VSTags = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(m_ShowTags)) != FALSE;
		g_Project->SetBool("ChooseStarsDlg", "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);
		}
	} else
	if (pBtn==GTK_WIDGET(m_ZoomIn)) {
		zoom = cmpack_chart_view_get_zoom(CMPACK_CHART_VIEW(m_Chart));
		cmpack_chart_view_set_zoom(CMPACK_CHART_VIEW(m_Chart), zoom + 5.0);
	} else 
	if (pBtn==GTK_WIDGET(m_ZoomOut)) {
		zoom = cmpack_chart_view_get_zoom(CMPACK_CHART_VIEW(m_Chart));
		cmpack_chart_view_set_zoom(CMPACK_CHART_VIEW(m_Chart), zoom - 5.0);
	} else 
	if (pBtn==GTK_WIDGET(m_ZoomFit)) {
		cmpack_chart_view_set_auto_zoom(CMPACK_CHART_VIEW(m_Chart), TRUE);
	} else
	if (pBtn==GTK_WIDGET(m_ClearBtn)) {
		NewSelection();
	} 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(GTK_WINDOW(m_pDlg), 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;
		}
	}
}

void CEditSelectionsDlg::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, false, m_RowsUpward);
		cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Chart), m_ImageData);
	}
}

//
// Update chart
//
void CEditSelectionsDlg::UpdateChart(void)
{
	cmpack_chart_view_set_model(CMPACK_CHART_VIEW(m_Chart), NULL);
	if (m_ChartData) {
		g_object_unref(m_ChartData);
		m_ChartData = NULL;
	}

	if (m_Phot.Valid())
		m_ChartData = m_Phot.ToChartData(false, m_DisplayMode==DISPLAY_IMAGE);
	else if (m_Catalog.Valid())
		m_ChartData = m_Catalog.ToChartData(false, false, m_DisplayMode==DISPLAY_IMAGE);
	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);
	cmpack_chart_view_set_model(CMPACK_CHART_VIEW(m_Chart), m_ChartData);
	UpdateStatus();
	UpdateAll();
}


//
// Enable/disable controls
//
void CEditSelectionsDlg::UpdateControls(void)
{
	gtk_widget_set_sensitive(m_SelectCbx, TRUE);
	gtk_widget_set_sensitive(GTK_WIDGET(m_ClearBtn), m_SelectionIndex>=0 || m_Current.Count()>0);
	gtk_widget_set_sensitive(GTK_WIDGET(m_SaveBtn), m_Current.Count()>0);
	gtk_widget_set_sensitive(GTK_WIDGET(m_RemoveBtn), TRUE);
}

//
// Update displayed object
//
void CEditSelectionsDlg::UpdateObject(int row, int star_id) 
{
	if (m_ChartData) {
		const gchar *tag = (m_Tags ? m_Tags->caption(star_id) : NULL);
		const gchar *cap = m_Current.Caption(star_id);
		if (cap) {
			if (tag) {
				// Selected object, with tag
				gchar *buf = (gchar*)g_malloc((strlen(cap)+strlen(tag)+2)*sizeof(gchar));
				sprintf(buf, "%s\n%s", cap, tag);
				cmpack_chart_data_set_tag(m_ChartData, row, buf);
				g_free(buf);
			} else {
				// Selected object, no tag
				cmpack_chart_data_set_tag(m_ChartData, row, cap);
			}
			cmpack_chart_data_set_color(m_ChartData, row, m_Current.Color(star_id));
			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 {
			if (tag) {
				// Not selected, with tag
				cmpack_chart_data_set_tag(m_ChartData, row, tag);
				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 {
				// Not selected, no tag
				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.0);
			}
		}
	}
}

// 
// Update selection and tags for all object
//
void CEditSelectionsDlg::UpdateAll(void)
{
	if (m_ChartData) {
		int count = cmpack_chart_data_count(m_ChartData);
		for (int row=0; row<count; row++) 
			UpdateObject(row, (int)cmpack_chart_data_get_param(m_ChartData, row));
	}
}

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

bool CEditSelectionsDlg::OnResponseDialog(gint response_id)
{
	switch (response_id)
	{
	case GTK_RESPONSE_ACCEPT:
		// Check input
		if (!OnCloseQuery())
			return false;
		break;

	case GTK_RESPONSE_HELP:
		// Show context help
		g_MainWnd->ShowHelp(GTK_WINDOW(m_pDlg), IDH_CHOOSE_STARS);
		return false;
	}
	return true;
}

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

void CEditSelectionsDlg::OnComboChanged(GtkComboBox *widget)
{
	if (widget == GTK_COMBO_BOX(m_SelectCbx)) {
		if (!m_Updating) {
			int index = SelectedItem(widget);
			if (index!=m_SelectionIndex) {
				if (index<0) {
					// New selection
					SetCurrentSelection(m_NewSelection);
				} else {
					// Stored selection
					if (m_SelectionIndex<0)
						SetNewSelection(m_Current);
					SetCurrentSelection(m_SelectionList->At(index));
				}
				m_SelectionIndex = index;
				UpdateStatus();
				UpdateAll();
				UpdateControls();
			}
		}
	}
}

void CEditSelectionsDlg::ShowSelection(int index) 
{
	if (index<0) {
		// New selection
		if (!m_ShowNewSelection) {
			m_ShowNewSelection = true;
			SetNewSelection(CSelection());
		}
		SetCurrentSelection(m_NewSelection);
		m_SelectionIndex = -1;
	} else {
		// Stored selection
		if (m_SelectionIndex<0)
			SetNewSelection(m_Current);
		SetCurrentSelection(m_SelectionList->At(index));
		m_SelectionIndex = index;
	}
	UpdateSelectionList();
	UpdateStatus();
	UpdateAll();
	UpdateControls();
}

void CEditSelectionsDlg::SetCurrentSelection(const CSelection &sel)
{
	m_Current = sel;
	UpdateSelection(m_Current);
	UpdateStatus();
}

void CEditSelectionsDlg::SetNewSelection(const CSelection &sel)
{
	m_NewSelection = sel;
	UpdateSelection(m_NewSelection);
	UpdateStatus();
}

void CEditSelectionsDlg::UpdateSelection(CSelection &sel)
{
	if (m_AllStars)
		sel.setSelectionMode(CMPACK_SELECT_VAR, CSelection::NONE);
	else if (m_InstMag)
		sel.setSelectionMode(CMPACK_SELECT_VAR, CSelection::ANY);
	else
		sel.setSelectionMode(CMPACK_SELECT_VAR, CSelection::SINGLE);
	if (m_AllStars && m_InstMag)
		sel.setSelectionMode(CMPACK_SELECT_COMP, CSelection::NONE);
	else if (m_Ensemble)
		sel.setSelectionMode(CMPACK_SELECT_COMP, CSelection::ANY);
	else
		sel.setSelectionMode(CMPACK_SELECT_COMP, CSelection::SINGLE);
	if (m_AllStars)
		sel.setSelectionMode(CMPACK_SELECT_CHECK, CSelection::NONE);
	else
		sel.setSelectionMode(CMPACK_SELECT_CHECK, CSelection::ANY);
}

void CEditSelectionsDlg::UpdateStatus(void)
{
	gchar		buf[1024];
	gint		obj_id, ref_id, index;

	int item = cmpack_chart_view_get_focused(CMPACK_CHART_VIEW(m_Chart));
	if (item>=0 && m_ChartData) {
		if (m_LastFocus!=item) {
			m_LastFocus = item;
			obj_id = (int)cmpack_chart_data_get_param(m_ChartData, item);
			index = m_Phot.FindObject(obj_id);
			ref_id = (index>=0 ? m_Phot.GetObjectRefID(index) : -1);
			gdouble pos_x, pos_y;
			m_Phot.GetObjectPos(index, &pos_x, &pos_y);
			sprintf(buf, "Object #%d: X = %.1f, Y = %.1f", ref_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, true);
				strcat(buf, ", ");
				strcat(buf, cel);
			}
			// Moving target
			if (obj_id == m_MovingTarget) {
				strcat(buf, ", ");
				strcat(buf, "moving target");
			}
			// Selection
			const gchar *cap = m_Current.Description(obj_id);
			if (cap) {
				strcat(buf, ", ");
				strcat(buf, cap);
			}
			// Tag
			const gchar *tag = (m_Tags ? m_Tags->caption(obj_id) : NULL);
			if (tag) {
				strcat(buf, ", ");
				strcat(buf, tag);
			}
			SetStatus(buf);
		}
	} else {
		m_LastFocus = -1;
		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, true);
					sprintf(buf, "Cursor: X = %d, Y = %d, %s", x, y, cel);
				} else {
					sprintf(buf, "Cursor: X = %d, Y = %d", x, y);
				}
				SetStatus(buf);
			}
		} else {
			if (m_LastPosX!=-1 || m_LastPosY!=-1) {
				m_LastPosX = m_LastPosY = -1;
				SetStatus(NULL);
			}
		}
	}
}

void CEditSelectionsDlg::CopyWcsCoordinatesFromChart(int star_id)
{
	if (m_ChartData) {
		if (m_Phot.Valid() && m_Phot.Wcs()) {
			CmpackPhtObject obj;
			double lng, lat;
			if (m_Phot.GetObjectParams(m_Phot.FindObject(star_id), CMPACK_PO_CENTER, &obj) && m_Phot.Wcs()->pixelToWorld(obj.x, obj.y, lng, lat))
				CopyWcsCoordinates(m_Phot.Wcs(), lng, lat);
		}
		else if (m_Catalog.Valid() && m_Catalog.Wcs()) {
			CmpackCatObject obj;
			double lng, lat;
			if (m_Catalog.GetObjectParams(m_Catalog.FindObject(star_id), CMPACK_OM_CENTER, &obj) && m_Catalog.Wcs()->pixelToWorld(obj.center_x, obj.center_y, lng, lat))
				CopyWcsCoordinates(m_Catalog.Wcs(), lng, lat);
		}
	}
}

void CEditSelectionsDlg::CopyWcsCoordinates(CWcs *wcs, double lng, double lat)
{
	char buf[256];
	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);
}

void CEditSelectionsDlg::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 CEditSelectionsDlg::UpdateCatalogs(GtkWindow *parent)
{
	char key[512];

	if (m_Wcs && (m_Phot.Valid() || 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_tool_item_set_visible_horizontal(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_tool_item_set_visible_horizontal(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_tool_item_set_visible_horizontal(m_CatalogBtns[i].setButton, FALSE);
		}
	}
}

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

	if (m_CatalogBtns[index].layerId == 0) {
		int width = 0, height = 0;
		if (m_Phot.Valid()) {
			CmpackPhtInfo info;
			m_Phot.GetParams(CMPACK_PI_FRAME_PARAMS, info);
			width = info.width;
			height = info.height;
		}
		else if (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 CEditSelectionsDlg::AddToLayer(const char *objname, double ra, double dec, const char *catalog, const char *comment, void *data)
{
	CEditSelectionsDlg *pMe = reinterpret_cast<CEditSelectionsDlg*>(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 CEditSelectionsDlg::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);
}

//-------------------------   TEXT QUERY DIALOG   ----------------------------

CTextQueryDlg::CTextQueryDlg(GtkWindow *pParent, const gchar *caption):m_ValidatorProc(NULL),
	m_ValidatorData(0)
{
	// Dialog with buttons
	m_pDlg = gtk_dialog_new_with_buttons(caption, pParent, 
		(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR),
		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
	gtk_dialog_widget_standard_tooltips(GTK_DIALOG(m_pDlg));
	gtk_window_set_position(GTK_WINDOW(m_pDlg), GTK_WIN_POS_CENTER);
	g_signal_connect(G_OBJECT(m_pDlg), "response", G_CALLBACK(response_dialog), this);

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

	GtkWidget *vbox = gtk_vbox_new(FALSE, 4);
	gtk_widget_set_size_request(vbox, 360, -1);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(m_pDlg)->vbox), vbox, TRUE, TRUE, 0);
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);

	m_Query = gtk_label_new(NULL);
	gtk_misc_set_alignment(GTK_MISC(m_Query), 0, 0.5);
	gtk_box_pack_start(GTK_BOX(vbox), m_Query, TRUE, TRUE, 0);
	m_Entry = gtk_entry_new();
	gtk_box_pack_start(GTK_BOX(vbox), m_Entry, TRUE, TRUE, 0);

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

CTextQueryDlg::~CTextQueryDlg()
{
	gtk_widget_destroy(m_pDlg);
}

gchar *CTextQueryDlg::Execute(const gchar *query, int maxsize, const gchar *defval,
	CTextQueryDlg::tValidator validator_proc, gpointer validator_data)
{
	m_ValidatorProc = validator_proc;
	m_ValidatorData = validator_data;
	gtk_label_set_text(GTK_LABEL(m_Query), query);
	gtk_entry_set_text(GTK_ENTRY(m_Entry), (defval ? defval : ""));
	gtk_entry_set_max_length(GTK_ENTRY(m_Entry), maxsize);
	bool retval = gtk_dialog_run(GTK_DIALOG(m_pDlg)) == GTK_RESPONSE_ACCEPT;
	gtk_widget_hide(m_pDlg);
	return (retval ? g_strdup(gtk_entry_get_text(GTK_ENTRY(m_Entry))) : NULL);
}

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

bool CTextQueryDlg::OnResponseDialog(gint response_id)
{
	if (response_id == GTK_RESPONSE_ACCEPT) 
		return OnCloseQuery();
	return true;
}

bool CTextQueryDlg::OnCloseQuery()
{
	if (m_ValidatorProc) {
		return m_ValidatorProc(gtk_entry_get_text(GTK_ENTRY(m_Entry)), GTK_WINDOW(m_pDlg),
			m_ValidatorData);
	}
	return true;
}
