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

matching_mt.cpp (C-Munipack project)
The 'Match stars' tab for tracking moving targets
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 "configuration.h"
#include "configuration.h"
#include "matching_mt.h"
#include "progress_dlg.h"
#include "catfile_dlg.h"
#include "phtfile_dlg.h"
#include "main.h"
#include "utils.h"
#include "proc_classes.h"
#include "ctxhelp.h"
#include "project_dlg.h"

//-------------------------   TABLES   ------------------------------------

enum tFrameColumnId
{
	FCOL_ID,				// Frame ID
	FCOL_JULDAT,			// Julian date
	FCOL_STARS,				// Number of stars found
	FCOL_KEY,				// Key frame (0 = No, 1 = Key frame)
	FCOL_OBJ,				// Target (object ID)
	FNCOLS
};

struct tTreeViewColumn {
	const char *caption;		// Column name
	int column;					// Model column index
	GtkTreeCellDataFunc datafn;	// Data function
	gfloat align;				// Text alignment
	const char *maxtext;		// Maximum text (for width estimation)
};

struct tColData {
	GtkTreeViewColumn *col;
	const tTreeViewColumn *data;
};

struct tGetFileInfo
{
	int			id;
	GtkTreePath *path;
	char		*file;
};

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

static gboolean find_frame(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
	int fileid;
	tGetFileInfo *pData = (tGetFileInfo*)data;

	gtk_tree_model_get(model, iter, FCOL_ID, &fileid, -1);
	if (fileid == pData->id) {
		pData->path = gtk_tree_path_copy(path);
		return TRUE;
	}
	return FALSE;
}

static int text_width(GtkWidget *widget, const gchar *buf)
{
	PangoRectangle logical_rect;

	if (buf) {
		PangoLayout *layout = gtk_widget_create_pango_layout(widget, buf);
		pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
		g_object_unref (layout);
		return logical_rect.width;
	}
	return 0;
}

//-------------------------   LIST COLUMNS   --------------------------------

static void GetFrameID(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
	GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
{
	int id;
	char buf[64];

	gtk_tree_model_get(tree_model, iter, FCOL_ID, &id, -1);

	g_snprintf(buf, sizeof(buf), "%d", id); 
	g_object_set(cell, "text", buf, NULL);
}

static void GetDateTime(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
	GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
{
	double jd;
	CmpackDateTime dt;
	char buf[256];

	gtk_tree_model_get(tree_model, iter, FCOL_JULDAT, &jd, -1);

	if (cmpack_decodejd(jd, &dt)==0) {
		sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", dt.date.year, dt.date.month, dt.date.day,
			dt.time.hour, dt.time.minute, dt.time.second);
		g_object_set(cell, "text", buf, NULL);
	} else {
		g_object_set(cell, "text", "", NULL);
	}
}

static void GetStars(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
	GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
{
	int value;
	char buf[64];

	gtk_tree_model_get(tree_model, iter, FCOL_STARS, &value, -1);

	if (value>0) {
		g_snprintf(buf, sizeof(buf), "%d", value);
		g_object_set(cell, "text", buf, NULL);
	} else {
		g_object_set(cell, "text", "", NULL);
	}
}

const static tTreeViewColumn FrameColumns[] = {
	{ "Frame #",				FCOL_ID,		GetFrameID,		1.0 },
	{ "Date and time (UTC)",	FCOL_JULDAT,	GetDateTime,	0.0, "9999-19-99 99:99:99" },
	{ "Stars",					FCOL_STARS,		GetStars,		1.0 },
	{ NULL }
};

//-------------------------   MAIN WINDOW   --------------------------------

CMatchingMTTab::CMatchingMTTab(CMatchingDlg *pDlg):CMatchingTab(pDlg), m_SelectedFrame(-1), 
	m_ObjectID(-1), m_ReferenceFrame(-1), m_KeyCount(0), m_FrameCols(NULL), 
	m_ChartData(NULL), m_ImageData(NULL), m_SelectionName(NULL), m_Updating(false)
{
	int i, w, width;
	GtkWidget *tbox, *tbar, *frame, *hbox;

	m_Icons[ICO_KEY_FIRST] = gdk_pixbuf_new_from_file(get_icon_file("key_r16"), NULL);
	m_Icons[ICO_KEY_OTHER] = gdk_pixbuf_new_from_file(get_icon_file("key_w16"), NULL);

	m_Negative = CConfig::GetBool(CConfig::NEGATIVE_CHARTS);
	m_RowsUpward = CConfig::GetBool(CConfig::ROWS_UPWARD);

	// Initial state
	m_DisplayMode = (tDisplayMode)g_Project->GetInt("MatchingMT", "Display", DISPLAY_CHART);

	// List of frames
	m_Frames = gtk_list_store_new(FNCOLS, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);

	// Hints
	m_Label1 = gtk_label_new(NULL);
	gtk_misc_set_alignment(GTK_MISC(m_Label1), 0.0, 0.5);
	gtk_box_pack_start(GTK_BOX(m_Box), m_Label1, FALSE, TRUE, 0);

	// Buttons
	hbox = gtk_hbox_new(FALSE, 8);
	gtk_box_pack_start(GTK_BOX(m_Box), hbox, FALSE, TRUE, 0);
	m_AddBtn = gtk_button_new_with_label("Add key frame");
	gtk_widget_set_tooltip_text(m_AddBtn, "Set selected frame as a key frame");
	g_signal_connect(G_OBJECT(m_AddBtn), "clicked", G_CALLBACK(button_clicked), this);
	gtk_box_pack_start(GTK_BOX(hbox), m_AddBtn, FALSE, TRUE, 0);
	m_SetRefBtn = gtk_button_new_with_label("Set as a reference frame");
	gtk_widget_set_tooltip_text(m_SetRefBtn, "Use the selected frame as a reference frame");
	g_signal_connect(G_OBJECT(m_SetRefBtn), "clicked", G_CALLBACK(button_clicked), this);
	gtk_box_pack_start(GTK_BOX(hbox), m_SetRefBtn, FALSE, TRUE, 0);
	m_RemoveBtn = gtk_button_new_with_label("Remove key frame");
	gtk_widget_set_tooltip_text(m_RemoveBtn, "Remove selected frame from the list a key frames");
	g_signal_connect(G_OBJECT(m_RemoveBtn), "clicked", G_CALLBACK(button_clicked), this);
	gtk_box_pack_start(GTK_BOX(hbox), m_RemoveBtn, FALSE, TRUE, 0);
	m_ObjectBtn = gtk_button_new_with_label("Change target object");
	gtk_widget_set_tooltip_text(m_ObjectBtn, "Change target object for selected key frame");
	g_signal_connect(G_OBJECT(m_ObjectBtn), "clicked", G_CALLBACK(button_clicked), this);
	gtk_box_pack_start(GTK_BOX(hbox), m_ObjectBtn, FALSE, TRUE, 0);
	m_ResetBtn = gtk_button_new_with_label("Clear key frames");
	gtk_widget_set_tooltip_text(m_ResetBtn, "Clear all key frames and start again");
	g_signal_connect(G_OBJECT(m_ResetBtn), "clicked", G_CALLBACK(button_clicked), this);
	gtk_box_pack_end(GTK_BOX(hbox), m_ResetBtn, FALSE, TRUE, 0);

	tbox = gtk_table_new(2, 3, FALSE);
	gtk_table_set_col_spacings(GTK_TABLE(tbox), 0);
	gtk_table_set_col_spacing(GTK_TABLE(tbox), 1, 8);
	gtk_table_set_row_spacings(GTK_TABLE(tbox), 0);
	gtk_table_set_row_spacing(GTK_TABLE(tbox), 1, 8);
	gtk_box_pack_start(GTK_BOX(m_Box), tbox, TRUE, TRUE, 0);

	// Table of reference frames
	m_FrameView = gtk_tree_view_new();
	width = 8;
	for (i=0; FrameColumns[i].caption!=NULL; i++) {
		GtkTreeViewColumn *tvcol = gtk_tree_view_column_new();
		// Set column name and alignment
		gtk_tree_view_column_set_title(tvcol, FrameColumns[i].caption);
		gtk_tree_view_append_column(GTK_TREE_VIEW(m_FrameView), tvcol);
		// Add icon renderer to the first column
		if (FrameColumns[i].column == FCOL_ID) {
			GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
			gtk_tree_view_column_pack_start(tvcol, renderer, FALSE);
			gtk_tree_view_column_set_cell_data_func(tvcol, renderer, GetIcon, this, NULL);
		}
		// Add text renderer
		GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
		gtk_tree_view_column_pack_start(tvcol, renderer, TRUE);
		g_object_set(renderer, "xalign", FrameColumns[i].align, NULL);
		if (FrameColumns[i].datafn) 
			gtk_tree_view_column_set_cell_data_func(tvcol, renderer, FrameColumns[i].datafn, NULL, NULL);
		else
			gtk_tree_view_column_add_attribute(tvcol, renderer, "text", FrameColumns[i].column);
		// Add to column descriptor list
		tColData *data = (tColData*)g_malloc(sizeof(tColData));
		data->col = tvcol;
		data->data = &FrameColumns[i];
		m_FrameCols = g_slist_append(m_FrameCols, data);
		// Update required with
		if (FrameColumns[i].maxtext)
			w = text_width(m_FrameView, FrameColumns[i].maxtext);
		else
			w = text_width(m_FrameView, FrameColumns[i].caption);
		width += w + 24;
	}
	gtk_tree_view_set_model(GTK_TREE_VIEW(m_FrameView), GTK_TREE_MODEL(m_Frames));
	gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(m_FrameView), true);
	gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(m_FrameView)), GTK_SELECTION_SINGLE);
	m_FrameBox = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(m_FrameBox),
		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(m_FrameBox), 
		GTK_SHADOW_ETCHED_IN);
	gtk_container_add(GTK_CONTAINER(m_FrameBox), m_FrameView);
	gtk_widget_set_size_request(m_FrameView, width, -1);
	gtk_table_attach(GTK_TABLE(tbox), m_FrameBox, 0, 1, 0, 2, GTK_FILL, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), 0, 0);

	// Register callback for selection change
	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_FrameView));
	gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
	g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(selection_changed), this);

	// Toolbox
	tbar = gtk_toolbar_new();
	gtk_toolbar_set_style(GTK_TOOLBAR(tbar), GTK_TOOLBAR_TEXT);
	m_ShowChart = toolbar_new_radio_button(tbar, NULL, "Chart", "Display objects on a flat background");
	g_signal_connect(G_OBJECT(m_ShowChart), "toggled", G_CALLBACK(button_clicked), this);
	m_ShowImage = toolbar_new_radio_button(tbar, m_ShowChart, "Image", "Display an image only");
	g_signal_connect(G_OBJECT(m_ShowImage), "toggled", G_CALLBACK(button_clicked), this);
	gtk_table_attach(GTK_TABLE(tbox), tbar, 2, 3, 0, 1, 
		(GtkAttachOptions)(GTK_EXPAND | GTK_FILL), GTK_FILL, 0, 0);

	// Frame preview
	frame = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
	m_Preview = cmpack_chart_view_new();
	gtk_container_add(GTK_CONTAINER(frame), m_Preview);
	gtk_widget_set_size_request(frame, 160, 120);
	gtk_table_attach_defaults(GTK_TABLE(tbox), frame, 2, 3, 1, 2);

	m_LegendLabel = gtk_label_new("Legend:");
	gtk_misc_set_alignment(GTK_MISC(m_LegendLabel), 0.0, 0.5);
	gtk_box_pack_start(optionsBox(), m_LegendLabel, FALSE, TRUE, 0);
	m_FirstKeyIcon = gtk_image_new_from_pixbuf(m_Icons[ICO_KEY_FIRST]);
	gtk_box_pack_start(optionsBox(), m_FirstKeyIcon, FALSE, TRUE, 0);
	m_FirstKeyLabel = gtk_label_new("= key frame used as a reference frame");
	gtk_misc_set_alignment(GTK_MISC(m_FirstKeyLabel), 0.0, 0.5);
	gtk_box_pack_start(optionsBox(), m_FirstKeyLabel, FALSE, TRUE, 0);
	m_OtherKeyIcon = gtk_image_new_from_pixbuf(m_Icons[ICO_KEY_OTHER]);
	gtk_box_pack_start(optionsBox(), m_OtherKeyIcon, FALSE, TRUE, 0);
	m_OtherKeyLabel = gtk_label_new("= other key frames");
	gtk_misc_set_alignment(GTK_MISC(m_OtherKeyLabel), 0.0, 0.5);
	gtk_box_pack_start(optionsBox(), m_OtherKeyLabel, FALSE, TRUE, 0);
	GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(get_icon_file("target16"), NULL);
	m_TargetIcon = gtk_image_new_from_pixbuf(pixbuf);
	gtk_box_pack_start(optionsBox(), m_TargetIcon, FALSE, TRUE, 0);
	m_TargetLabel = gtk_label_new("= target object");
	gtk_misc_set_alignment(GTK_MISC(m_TargetLabel), 0.0, 0.5);
	gtk_box_pack_start(optionsBox(), m_TargetLabel, FALSE, TRUE, 0);

	gtk_widget_show_all(m_Box);
}

CMatchingMTTab::~CMatchingMTTab()
{
	for (GSList *ptr=m_FrameCols; ptr!=NULL; ptr=ptr->next)
		g_free(ptr->data);
	g_slist_free(m_FrameCols);

	if (m_ChartData)
		g_object_unref(m_ChartData);
	if (m_ImageData)
		g_object_unref(m_ImageData);
	g_object_unref(m_Frames);
	g_free(m_SelectionName);
}

bool CMatchingMTTab::OnInitDialog(tInit init, int *init_frame_id, gchar **init_fpath, GError **error)
{
	m_SelectedFrame = m_ObjectID = m_ReferenceFrame = -1;
	g_free(m_SelectionName);
	m_SelectionName = NULL;

	if (init != INIT_MATCH)
		return true;

	// Update list of frames
	m_SelectedFrame = g_Project->GetInt("MatchingMT", "Frame", 0);
	ReadFrames();
	UpdateControls();

	// Check inputs
	if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_Frames), NULL)==0) {
		set_error(error, "There are no frames usable as a reference frame.");
		return false;
	}

	// Select reference frame or the first frame
	return true;
}

bool CMatchingMTTab::OnResponseDialog(gint response_id, GError **error)
{
	if (m_KeyCount<3) {
		set_error(error, "Please, choose at least three key frames.");
		return false;
	}
	if (m_ReferenceFrame<0) {
		set_error(error, "Please, choose a reference key frame.");
		return false;
	}
	return true;
}

void CMatchingMTTab::OnTabShow(void)
{
	gtk_widget_show(m_LegendLabel);
	gtk_widget_show(m_FirstKeyIcon);
	gtk_widget_show(m_FirstKeyLabel);
	gtk_widget_show(m_OtherKeyIcon);
	gtk_widget_show(m_OtherKeyLabel);
	gtk_widget_show(m_TargetLabel);
	gtk_widget_show(m_TargetIcon);
	UpdateControls();
}

void CMatchingMTTab::OnTabHide(void)
{
	gtk_widget_hide(m_LegendLabel);
	gtk_widget_hide(m_FirstKeyIcon);
	gtk_widget_hide(m_FirstKeyLabel);
	gtk_widget_hide(m_OtherKeyIcon);
	gtk_widget_hide(m_OtherKeyLabel);
	gtk_widget_hide(m_TargetLabel);
	gtk_widget_hide(m_TargetIcon);
}

int CMatchingMTTab::ProcessFiles(GList *files, int &in_files, int &out_files, CProgressDlg *sender)
{
	int frameid;
	gchar *phot, msg[128];
	GtkTreePath *path;
	CTrackingProc match;
	GError *error = NULL;

	g_Project->SetInt("MatchingMT", "Frame", m_ReferenceFrame);
	g_Project->ClearReference();

	in_files = out_files = 0;
	sender->Print("------ Matching ------");

	GtkTreePath *refpath = g_Project->GetPath(m_ReferenceFrame);
	bool retval = match.InitWithReferenceFrame(sender, refpath, &error);
	gtk_tree_path_free(refpath);
	if (!retval) {
		sender->SetError(error);
		return false;
	}

	// Process key frames first
	sender->Print("Processing key frames");
	for (GList *node = files; node != NULL && !sender->Cancelled(); node = node->next) {
		path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)node->data);
		if (path) {
			if (g_Project->GetMBKey(path) > 0) {
				frameid = g_Project->GetFrameID(path);
				phot = g_Project->GetPhotFileName(path);
				sender->SetFileName(phot);
				g_free(phot);
				sender->SetProgress(in_files++);
				sprintf(msg, "Frame #%d:", frameid);
				sender->Print(msg);
				GError *error = NULL;
				if (match.Execute(path, &error)) {
					out_files++;
				} else {
					sender->Print(error->message);
					g_error_free(error);
					gtk_tree_path_free(path);
					return false;
				}
			}
			gtk_tree_path_free(path);
		}
	}
	if (sender->Cancelled()) {
		sender->Print("Cancelled at the user's request");
		return false;
	}

	// Interpolate position of the minor body
	sender->Print("Fitting polynomial function");
	if (!match.MBTrack(&error)) {
		sender->Print(error->message);
		g_error_free(error);
		return false;
	}

	// Now match non-key frames
	sender->Print("Processing other frames");
	for (GList *node = files; node != NULL && !sender->Cancelled(); node = node->next) {
		path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)node->data);
		if (path) {
			if (g_Project->GetMBKey(path) <= 0) {
				frameid = g_Project->GetFrameID(path);
				phot = g_Project->GetPhotFileName(path);
				sender->SetFileName(phot);
				g_free(phot);
				sender->SetProgress(in_files++);
				sprintf(msg, "Frame #%d:", frameid);
				sender->Print(msg);
				GError *error = NULL;
				if (match.Execute(path, &error)) {
					out_files++;
				} else {
					sender->Print(error->message);
					g_error_free(error);
				}
			}
			gtk_tree_path_free(path);
		}
	}
	if (sender->Cancelled()) {
		sender->Print("Cancelled at the user's request");
		return false;
	}

	sprintf(msg, "====== %d succeeded, %d failed ======", out_files, in_files-out_files);
	sender->Print(msg);
	return true;
}

void CMatchingMTTab::selection_changed(GtkTreeSelection *widget, CMatchingMTTab *pMe)
{
	if (!pMe->m_Updating) {
		pMe->m_Updating = true;
		pMe->UpdatePreview();
		pMe->UpdateControls();
		pMe->m_Updating = false;
	}
}

void CMatchingMTTab::UpdatePreview(bool force_update)
{
	int frame_id;
	GtkTreeIter iter;
	GtkTreeModel *model;

	// Reference frame
	GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_FrameView));
	if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
		gtk_tree_model_get(model, &iter, FCOL_ID, &frame_id, -1);
		if (force_update || m_SelectedFrame!=frame_id) {
			cmpack_chart_view_set_model(CMPACK_CHART_VIEW(m_Preview), NULL);
			cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Preview), NULL);
			if (m_ChartData) {
				g_object_unref(m_ChartData);
				m_ChartData = NULL;
			}
			if (m_ImageData) {
				g_object_unref(m_ImageData);
				m_ImageData = NULL;
			}
			m_SelectedFrame = frame_id;
			GtkTreePath *path = g_Project->GetPath(m_SelectedFrame);
			if (path) {
				m_ObjectID = (g_Project->GetMBKey(path) > 0 ? g_Project->GetMBObject(path) : -1);
				gchar *pht_file = g_Project->GetPhotFile(path);
				CPhot pht;
				if (pht_file && pht.Load(pht_file)) 
					UpdateChart(pht);
				g_free(pht_file);
				gchar *fts_file = g_Project->GetImageFile(path);
				CImage *img = CImage::fromFile(fts_file, g_Project->Profile());
				if (img) {
					UpdateImage(img);
					delete img;
				}
				g_free(fts_file);
				gtk_tree_path_free(path);
			}
		}
	} else {
		if (force_update || m_SelectedFrame>=0) {
			m_SelectedFrame = -1;
			cmpack_chart_view_set_model(CMPACK_CHART_VIEW(m_Preview), NULL);
			cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Preview), NULL);
			if (m_ChartData) {
				g_object_unref(m_ChartData);
				m_ChartData = NULL;
			}
			if (m_ImageData) {
				g_object_unref(m_ImageData);
				m_ImageData = NULL;
			}
		}
	}
}

void CMatchingMTTab::button_clicked(GtkWidget *button, CMatchingMTTab *pDlg)
{
	pDlg->OnButtonClicked(button);
}

void CMatchingMTTab::OnButtonClicked(GtkWidget *pBtn)
{
	if (!m_Updating) {
		m_Updating = true;
		if (pBtn==GTK_WIDGET(m_ShowChart)) {
			if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(m_ShowChart))) {
				m_DisplayMode = DISPLAY_CHART;
				g_Project->SetInt("MatchingMT", "Display", m_DisplayMode);
				UpdatePreview(true);
				UpdateControls();
			}
		} else if (pBtn==GTK_WIDGET(m_ShowImage)) {
			if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(m_ShowImage))) {
				m_DisplayMode = DISPLAY_IMAGE;
				g_Project->SetInt("MatchingMT", "Display", m_DisplayMode);
				UpdatePreview(true);
				UpdateControls();
			}
		} else if (pBtn==GTK_WIDGET(m_ResetBtn)) {
			ResetAll();
			UpdateControls();
		} else if (pBtn==GTK_WIDGET(m_AddBtn)) {
			SetKeyFrame();
			UpdateControls();
		} else if (pBtn==GTK_WIDGET(m_RemoveBtn)) {
			ResetKeyFrame();
			UpdateControls();
		} else if (pBtn==GTK_WIDGET(m_SetRefBtn)) {
			SetRefFrame();
			UpdateControls();
		} else if (pBtn==GTK_WIDGET(m_ObjectBtn)) {
			ChangeTarget();
			UpdateControls();
		}
		m_Updating = false;
	}
}

void CMatchingMTTab::ReadFrames()
{
	gboolean ok;
	GtkTreeModel *pList = g_Project->FileList();
	GtkTreeIter iter, iter2;

	m_KeyCount = 0;
	m_ReferenceFrame = m_ObjectID = -1;
	m_Updating = true;

	gtk_tree_view_set_model(GTK_TREE_VIEW(m_FrameView), NULL);
	gtk_list_store_clear(m_Frames);
	ok = gtk_tree_model_get_iter_first(pList, &iter);
	while (ok) {
		GtkTreePath *pPath = gtk_tree_model_get_path(GTK_TREE_MODEL(pList), &iter);
		if (pPath) {
			int nstars = g_Project->GetStars(pPath);
			if ((g_Project->GetState(pPath) & CFILE_PHOTOMETRY) && (nstars > 0)) {
				int frameId = g_Project->GetFrameID(pPath), mb_key = g_Project->GetMBKey(pPath), mb_obj = g_Project->GetMBObject(pPath);
				double jd = g_Project->GetJulDate(pPath);
				gtk_list_store_append(m_Frames, &iter2);
				gtk_list_store_set(m_Frames, &iter2, FCOL_ID, frameId, FCOL_STARS, nstars, FCOL_JULDAT, jd, FCOL_KEY, mb_key, FCOL_OBJ, mb_obj, -1);
				if (mb_key > 0) 
					m_KeyCount++;
				if (mb_key == 1)
					m_ReferenceFrame = frameId;
			}
			gtk_tree_path_free(pPath);
		}
		ok = gtk_tree_model_iter_next(pList, &iter);
	}
	gtk_tree_view_set_model(GTK_TREE_VIEW(m_FrameView), GTK_TREE_MODEL(m_Frames));

	tGetFileInfo info;
	info.path = NULL;
	if (m_SelectedFrame>0) {
		info.id = m_SelectedFrame;
		gtk_tree_model_foreach(GTK_TREE_MODEL(m_Frames), GtkTreeModelForeachFunc(find_frame), &info);
	}
	if (!info.path && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_Frames), &iter)) {
		info.path = gtk_tree_model_get_path(GTK_TREE_MODEL(m_Frames), &iter);
		gtk_tree_model_get(GTK_TREE_MODEL(m_Frames), &iter, FCOL_ID, &m_SelectedFrame, -1);
	}
	if (info.path) {
		m_ObjectID = g_Project->GetMBObject(info.path);
		GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_FrameView));
		gtk_tree_selection_select_path(sel, info.path);
		gtk_tree_path_free(info.path);
	}

	m_Updating = false;

	UpdatePreview(true);
}

void CMatchingMTTab::UpdateControls(void)
{
	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_ShowChart),
		(m_DisplayMode == DISPLAY_CHART));

	if (m_KeyCount==0) {
		// No key frame defined
		gtk_widget_set_sensitive(GTK_WIDGET(m_AddBtn), m_SelectedFrame>=0);
		gtk_widget_set_sensitive(GTK_WIDGET(m_RemoveBtn), FALSE);
		gtk_widget_set_sensitive(GTK_WIDGET(m_SetRefBtn), FALSE);
		gtk_widget_set_sensitive(GTK_WIDGET(m_ResetBtn), FALSE);
		gtk_widget_set_sensitive(GTK_WIDGET(m_ObjectBtn), FALSE);
	} else {
		if (m_ObjectID>0) {
			gtk_widget_set_sensitive(GTK_WIDGET(m_AddBtn), FALSE);
			gtk_widget_set_sensitive(GTK_WIDGET(m_RemoveBtn), TRUE);
			gtk_widget_set_sensitive(GTK_WIDGET(m_SetRefBtn), m_SelectedFrame != m_ReferenceFrame);
			gtk_widget_set_sensitive(GTK_WIDGET(m_ObjectBtn), TRUE);
		} else {
			gtk_widget_set_sensitive(GTK_WIDGET(m_AddBtn), TRUE);
			gtk_widget_set_sensitive(GTK_WIDGET(m_RemoveBtn), FALSE);
			gtk_widget_set_sensitive(GTK_WIDGET(m_SetRefBtn), FALSE);
			gtk_widget_set_sensitive(GTK_WIDGET(m_ObjectBtn), FALSE);
		}
		gtk_widget_set_sensitive(GTK_WIDGET(m_ResetBtn), TRUE);
	}

	if (m_ReferenceFrame<0)
		gtk_label_set_markup(GTK_LABEL(m_Label1), "<b>Select a frame and click the 'Add' button to define a reference key frame.</b>");
	else
		gtk_label_set_markup(GTK_LABEL(m_Label1), "<b>Select a frame and click the 'Add' button to define another key frame.</b>");
}

void CMatchingMTTab::UpdateImage(CImage *img)
{
	cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Preview), NULL);
	if (m_ImageData) {
		g_object_unref(m_ImageData);
		m_ImageData = NULL;
	}
	if (m_DisplayMode != DISPLAY_CHART) {
		m_ImageData = img->ToImageData(m_Negative, false, true, m_RowsUpward);
		cmpack_chart_view_set_image(CMPACK_CHART_VIEW(m_Preview), m_ImageData);
		cmpack_chart_view_set_auto_zoom(CMPACK_CHART_VIEW(m_Preview), TRUE);
	}
}

void CMatchingMTTab::UpdateChart(CPhot &pht)
{
	cmpack_chart_view_set_model(CMPACK_CHART_VIEW(m_Preview), NULL);
	if (m_ChartData)
		g_object_unref(m_ChartData);
	pht.SelectAperture(0);
	m_ChartData = pht.ToChartData(false, m_DisplayMode == DISPLAY_IMAGE);
	if (m_ChartData && m_SelectedFrame>0 && m_ObjectID>0) {
		int row = cmpack_chart_data_find_item(m_ChartData, m_ObjectID);
		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);
	}
	cmpack_chart_view_set_negative(CMPACK_CHART_VIEW(m_Preview), m_Negative);
	cmpack_chart_view_set_orientation(CMPACK_CHART_VIEW(m_Preview), (m_RowsUpward ? CMPACK_ROWS_UPWARDS : CMPACK_ROWS_DOWNWARDS));
	cmpack_chart_view_set_model(CMPACK_CHART_VIEW(m_Preview), m_ChartData);
	cmpack_chart_view_set_auto_zoom(CMPACK_CHART_VIEW(m_Preview), TRUE);
}

void CMatchingMTTab::SetKeyFrame(void)
{
	bool changed = false;
	GtkTreePath *path = g_Project->GetPath(m_SelectedFrame);
	if (path) {
		int obj_id = g_Project->GetMBObject(path);
		CChooseTargetDlg dlg(parentWindow());
		GError *error = NULL;
		if (dlg.Execute(path, obj_id, &error)) {
			g_Project->setField(path, FRAME_MB_OBJ, obj_id, FRAME_MB_KEY, (m_ReferenceFrame >= 0 ? 2 : 1), -1);
			if (m_ReferenceFrame < 0)
				m_ReferenceFrame = m_SelectedFrame;
			m_ObjectID = obj_id;
			changed = true;
		}
		else {
			if (error) {
				ShowError(parentWindow(), error->message);
				g_error_free(error);
			}
		}
		gtk_tree_path_free(path);
	}
	if (changed)
		ReadFrames();
}

void CMatchingMTTab::ChangeTarget(void)
{
	GtkTreePath *path = g_Project->GetPath(m_SelectedFrame);
	if (path) {
		bool changed = false;
		if (g_Project->GetMBKey(path) > 0) {
			int old_obj = g_Project->GetMBObject(path);
			CChooseTargetDlg dlg(parentWindow());
			GError *error = NULL;
			if (dlg.Execute(path, m_ObjectID, &error)) {
				if (old_obj != m_ObjectID) {
					g_Project->setField(path, FRAME_MB_OBJ, m_ObjectID, -1);
					changed = true;
				}
			}
			else {
				if (error) {
					ShowError(parentWindow(), error->message);
					g_error_free(error);
				}
			}
		}
		gtk_tree_path_free(path);
		if (changed)
			ReadFrames();
	}
}

void CMatchingMTTab::ResetKeyFrame(void)
{
	GtkTreePath *path = g_Project->GetPath(m_SelectedFrame);
	if (path) {
		bool changed = false;
		int old_key = g_Project->GetMBKey(path);
		if (old_key != 0) {
			g_Project->setField(path, FRAME_MB_KEY, 0, -1);
			if (old_key == 1) {
				// We have to choose another reference frame
				int max_stars = 0;
				GtkTreePath *ref_path = NULL;
				GtkTreeIter tmp_iter;
				GtkTreeModel *pList = g_Project->FileList();
				gboolean ok = gtk_tree_model_get_iter_first(pList, &tmp_iter);
				while (ok) {
					GtkTreePath *tmpPath = gtk_tree_model_get_path(pList, &tmp_iter);
					if (tmpPath) {
						int nstars = g_Project->GetStars(tmpPath);
						if ((g_Project->GetState(tmpPath) & CFILE_PHOTOMETRY) != 0 && (nstars > max_stars) && g_Project->GetMBKey(tmpPath) == 2) {
							gtk_tree_path_free(ref_path);
							ref_path = gtk_tree_path_copy(tmpPath);
							max_stars = nstars;
						}
						gtk_tree_path_free(tmpPath);
					}
					ok = gtk_tree_model_iter_next(pList, &tmp_iter);
				}
				if (ref_path) {
					g_Project->setField(ref_path, FRAME_MB_KEY, 1, -1);
					m_ReferenceFrame = g_Project->GetFrameID(ref_path);
					gtk_tree_path_free(ref_path);
				}
				else
					m_ReferenceFrame = -1;
			}
			m_ObjectID = -1;
			changed = true;
		}
		gtk_tree_path_free(path);
		if (changed)
			ReadFrames();
	}
}

void CMatchingMTTab::SetRefFrame(void)
{
	GtkTreePath *path = g_Project->GetPath(m_SelectedFrame);
	if (path) {
		bool changed = false;
		GtkTreeIter iter;
		GtkTreeModel *pList = g_Project->FileList();
		gboolean ok = gtk_tree_model_get_iter_first(pList, &iter);
		while (ok) {
			GtkTreePath *pPath = gtk_tree_model_get_path(pList, &iter);
			if (pPath) {
				if ((g_Project->GetState(pPath) & CFILE_PHOTOMETRY) && g_Project->GetMBKey(pPath) > 0) {
					g_Project->setField(pPath, FRAME_MB_KEY, (g_Project->GetFrameID(pPath) == m_SelectedFrame ? 1 : 2), -1);
					changed = true;
				}
				gtk_tree_path_free(pPath);
			}
			ok = gtk_tree_model_iter_next(pList, &iter);
		}
		gtk_tree_path_free(path);
		if (changed)
			ReadFrames();
	}
}

void CMatchingMTTab::ResetAll(void)
{
	if (ShowConfirmation(parentWindow(), "Do you want to clear all key frames?")) {
		g_Project->ClearKeyFrames();
		m_KeyCount = 0;
		m_ReferenceFrame = m_ObjectID = -1;
		ReadFrames();
	}
}

void CMatchingMTTab::GetIcon(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
	GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
{
	int key_frame;
	CMatchingMTTab *pMe = (CMatchingMTTab*)data;

	gtk_tree_model_get(tree_model, iter, FCOL_KEY, &key_frame, -1);
	if (key_frame==1)
		g_object_set(cell, "pixbuf", pMe->m_Icons[ICO_KEY_FIRST], NULL);
	else if (key_frame==2)
		g_object_set(cell, "pixbuf", pMe->m_Icons[ICO_KEY_OTHER], NULL);
	else
		g_object_set(cell, "pixbuf", NULL, NULL);
}

//----------------------   TARGET CHOOSE DIALOG   --------------------------------

CChooseTargetDlg::CChooseTargetDlg(GtkWindow *pParent):m_ObjectID(-1), m_UpdatePos(false), 
	m_StatusCtx(-1), m_StatusMsg(-1), m_LastFocus(-1), m_LastPosX(-1), m_LastPosY(-1), 
	m_Image(NULL), m_Wcs(NULL), m_ImageData(NULL), m_ChartData(NULL)
{
	GdkRectangle rc;

	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);

	// Dialog with buttons
	m_pDlg = gtk_dialog_new_with_buttons("Choose target object", pParent, 
		(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
		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));
	gtk_window_set_position(GTK_WINDOW(m_pDlg), GTK_WIN_POS_CENTER);
	g_signal_connect(G_OBJECT(m_pDlg), "response", G_CALLBACK(response_dialog), this);

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

	// Dialog 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
	GtkWidget *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);

	// 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);

	// 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);
	gtk_widget_set_size_request(m_Chart, 320, 200);
	GtkWidget *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);

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

CChooseTargetDlg::~CChooseTargetDlg()
{
	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);
	delete m_Image;
}

bool CChooseTargetDlg::Execute(GtkTreePath *path, int &objectID, GError **error)
{
	m_ObjectID = objectID;

	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;
	}
	m_Phot.Clear();
	m_Catalog.Clear();
	delete m_Image;
	m_Image = NULL;
	delete m_Wcs;
	m_Wcs = NULL;
	m_LastFocus = -1;
	m_LastPosX = m_LastPosY = -1;
	m_UpdatePos = true;

	gchar *pht_file = g_Project->GetPhotFile(path);
	if (pht_file) {
		if (!m_Phot.Load(pht_file, error)) {
			g_free(pht_file);
			return false;
		}
		g_free(pht_file);
		m_Phot.SelectAperture(0);
		UpdateChart();
		gchar *fts_file = g_Project->GetImageFile(path);
		if (fts_file) {
			m_Image = CImage::fromFile(fts_file, g_Project->Profile(), CMPACK_BITPIX_AUTO);
			UpdateImage();
			g_free(fts_file);
		}
		if (m_Phot.Wcs())
			m_Wcs = new CWcs(*m_Phot.Wcs());
	}

	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_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);

	UpdateStatus();
	UpdateControls();

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

	gtk_widget_hide(m_pDlg);
	objectID = m_ObjectID;
	return true;
}

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

bool CChooseTargetDlg::OnResponseDialog(gint response_id)
{
	switch (response_id)
	{
	case GTK_RESPONSE_ACCEPT:
		if (m_ObjectID<=0) {
			ShowError(GTK_WINDOW(m_pDlg), "Please, choose a target object", true);
			return false;
		}
		break;

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

//
// Left button click
//
void CChooseTargetDlg::chart_item_activated(GtkWidget *widget, gint row, CChooseTargetDlg *pMe)
{
	pMe->OnChartItemActivated(row);
}
void CChooseTargetDlg::OnChartItemActivated(gint row)
{
	int star_id = (int)cmpack_chart_data_get_param(m_ChartData, row);
	if (star_id != m_ObjectID) {
		m_ObjectID = star_id;
		UpdateStatus();
		UpdateAll();
		UpdateControls();
	}
}

//
// Set status text
//
void CChooseTargetDlg::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 CChooseTargetDlg::chart_mouse_moved(GtkWidget *button, CChooseTargetDlg *pDlg)
{
	pDlg->m_UpdatePos = true;
}

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

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

void CChooseTargetDlg::button_clicked(GtkWidget *pButton, CChooseTargetDlg *pMe)
{
	pMe->OnButtonClicked(pButton);
}

void CChooseTargetDlg::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_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);
	}
}

void CChooseTargetDlg::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 CChooseTargetDlg::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 CChooseTargetDlg::UpdateControls(void)
{
}

//
// Update displayed object
//
void CChooseTargetDlg::UpdateObject(int row, int star_id) 
{
	if (m_ChartData) {
		if (star_id == m_ObjectID) {
			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);
		} else {
			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 CChooseTargetDlg::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 CChooseTargetDlg::UpdateStatus(void)
{
	gchar		buf[1024];

	int item = cmpack_chart_view_get_focused(CMPACK_CHART_VIEW(m_Chart));
	if (item>=0 && m_ChartData) {
		if (m_LastFocus!=item) {
			m_LastFocus = item;
			int obj_id = (int)cmpack_chart_data_get_param(m_ChartData, item);
			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);
			}
			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);
					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);
			}
		}
	}
}
