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

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

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

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

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

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

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

#include "project.h"
#include "chart_dlg.h"
#include "main.h"
#include "utils.h"
#include "configuration.h"
#include "configuration.h"
#include "export_dlgs.h"
#include "objects_dlg.h"
#include "ctxhelp.h"
#include "profile.h"

enum tCommandId
{
	CMD_COPY_WCS = 100
};

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

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

class tObjectsPrintData
{
public:
	tObjectsPrintData() {}
	virtual ~ tObjectsPrintData() {}
};

static void DestroyPrintData(tObjectsPrintData *data)
{
	delete data;
}

class tObjectsPrintIntData:public tObjectsPrintData
{
public:
	CObjectsDlg::tColumnId m_col;
	int m_min, m_max;
	tObjectsPrintIntData(CObjectsDlg::tColumnId col, int min, int max):m_col(col), 
		m_min(min), m_max(max) {}
};

class tObjectsPrintDblData:public tObjectsPrintData
{
public:
	CObjectsDlg::tColumnId m_col;
	double	m_min, m_max;
	int		m_prec;
	tObjectsPrintDblData(CObjectsDlg::tColumnId col, double min, double max, int prec):m_col(col), 
		m_min(min), m_max(max), m_prec(prec) {}
};

class tObjectsPrintStrData:public tObjectsPrintData
{
public:
	CObjectsDlg::tColumnId m_col;
	tObjectsPrintStrData(CObjectsDlg::tColumnId col):m_col(col) {}
};

class tObjectsPrintWcsData:public tObjectsPrintData
{
public:
	CWcs *m_wcs;
	tObjectsPrintWcsData(CWcs *wcs):m_wcs(wcs) {}
};


static void SetCellRenderer(GtkCellRenderer *cell, const gchar *text, CmpackColor fg_color)
{
	switch (fg_color) 
	{
	case CMPACK_COLOR_RED:
		g_object_set(cell, "text", text, "foreground", "red", NULL);
		break;
	case CMPACK_COLOR_BLUE:
		g_object_set(cell, "text", text, "foreground", "blue", NULL);
		break;
	case CMPACK_COLOR_GREEN:
		g_object_set(cell, "text", text, "foreground", "green", NULL);
		break;
	case CMPACK_COLOR_YELLOW:
		g_object_set(cell, "text", text, "foreground", "yellow", NULL);
		break;
	case CMPACK_COLOR_GRAY:
		g_object_set(cell, "text", text, "foreground", "gray", NULL);
		break;
	default:
		g_object_set(cell, "text", text, "foreground", NULL, NULL);
	}
}

static void PrintStr(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
	GtkTreeModel *tree_model, GtkTreeIter *iter, tObjectsPrintStrData *data)
{
	int fg_color;
	gchar *text;

	gtk_tree_model_get(tree_model, iter, data->m_col, &text, CObjectsDlg::COL_FG_COLOR, &fg_color, -1);
	SetCellRenderer(cell, text, (CmpackColor)fg_color);
	g_free(text);
}

static void PrintInt(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
	GtkTreeModel *tree_model, GtkTreeIter *iter, tObjectsPrintIntData *data)
{
	int value, fg_color;

	gtk_tree_model_get(tree_model, iter, data->m_col, &value, CObjectsDlg::COL_FG_COLOR, &fg_color, -1);
	if (value>=data->m_min && value<=data->m_max) {
		gchar buf[256];
		sprintf(buf, "%d", value);
		SetCellRenderer(cell, buf, (CmpackColor)fg_color);
	} else 
		SetCellRenderer(cell, "--", (CmpackColor)fg_color);
}

static void PrintDouble(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
	GtkTreeModel *tree_model, GtkTreeIter *iter, tObjectsPrintDblData *data)
{
	int fg_color;
	double value;

	gtk_tree_model_get(tree_model, iter, data->m_col, &value, CObjectsDlg::COL_FG_COLOR, &fg_color, -1);
	if (value>=data->m_min && value<=data->m_max) {
		gchar buf[256];
		sprintf(buf, "%.*f", data->m_prec, value);
		SetCellRenderer(cell, buf, (CmpackColor)fg_color);
	} else 
		SetCellRenderer(cell, "------", (CmpackColor)fg_color);
}

static void PrintBrightness(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
	GtkTreeModel *tree_model, GtkTreeIter *iter, tObjectsPrintData *data)
{
	int mag_valid, fg_color;
	double mag;

	gtk_tree_model_get(tree_model, iter, CObjectsDlg::COL_MAG_VALID, &mag_valid, CObjectsDlg::COL_MAG, &mag, 
		CObjectsDlg::COL_FG_COLOR, &fg_color, -1);
	if (mag_valid) {
		gchar buf[256];
		sprintf(buf, "%.4f", mag);
		SetCellRenderer(cell, buf, (CmpackColor)fg_color);
	} else 
		SetCellRenderer(cell, "------", (CmpackColor)fg_color);
}

static gint CompareBrightness(GtkTreeModel *tree_model, GtkTreeIter *a, GtkTreeIter *b, 
	tObjectsPrintData *data)
{
	int mag_valid_a, mag_valid_b;
	double mag_a, mag_b;

	gtk_tree_model_get(tree_model, a, CObjectsDlg::COL_MAG_VALID, &mag_valid_a, CObjectsDlg::COL_MAG, &mag_a, -1);
	gtk_tree_model_get(tree_model, b, CObjectsDlg::COL_MAG_VALID, &mag_valid_b, CObjectsDlg::COL_MAG, &mag_b, -1);
	if (!mag_valid_a && !mag_valid_b)
		return 0;
	else if (!mag_valid_a)
		return 1;
	else if (!mag_valid_b)
		return -1;
	else if (mag_a < mag_b)
		return -1;
	else if (mag_a == mag_b)
		return 0;
	else
		return 1;
}

static void AddViewCol(GtkTreeView *view, GtkTreeCellDataFunc print_fn, tObjectsPrintData *print_data, 
	const gchar *name, double xalign, int sort_column_id)
{
	GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
	g_object_set(renderer, "xalign", xalign, NULL);
	int index = gtk_tree_view_insert_column_with_data_func(view, -1, name, renderer, print_fn, print_data, 
		(GDestroyNotify)DestroyPrintData)-1;
	GtkTreeViewColumn *col = gtk_tree_view_get_column(view, index);
	g_object_set_data(G_OBJECT(col), "sort-column-id", GINT_TO_POINTER(sort_column_id));
}

static void AddViewCol_Text(GtkTreeView *view, CObjectsDlg::tColumnId column, const gchar *name, 
	int sort_column_id)
{
	AddViewCol(view, (GtkTreeCellDataFunc)PrintStr, new tObjectsPrintStrData(column), name, 
		0.0, sort_column_id);
}

static void AddViewCol_Int(GtkTreeView *view, CObjectsDlg::tColumnId column, int min, int max, 
	const gchar *name, int sort_column_id)
{
	AddViewCol(view, (GtkTreeCellDataFunc)PrintInt, new tObjectsPrintIntData(column, min, max), name, 
		1.0, sort_column_id);
}

static void AddViewCol_Dbl(GtkTreeView *view, CObjectsDlg::tColumnId column, double min, double max, int prec, 
	const gchar *name, int sort_column_id)
{
	AddViewCol(view, (GtkTreeCellDataFunc)PrintDouble, new tObjectsPrintDblData(column, min, max, prec), name, 
		1.0, sort_column_id);
}

static void PrintLongitude(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
	GtkTreeModel *tree_model, GtkTreeIter *iter, tObjectsPrintWcsData *data)
{
	int wcs_valid;
	double lng;
	char buf[256];

	gtk_tree_model_get(tree_model, iter, CObjectsDlg::COL_WCS_VALID, &wcs_valid, CObjectsDlg::COL_WCS_LNG, &lng, -1);
	if (wcs_valid) {
		data->m_wcs->printLongitude(lng, buf, 256, false);
		g_object_set(cell, "text", buf, NULL);
	} else 
		g_object_set(cell, "text", "------", NULL);
}

static void PrintLatitude(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
	GtkTreeModel *tree_model, GtkTreeIter *iter, tObjectsPrintWcsData *data)
{
	int wcs_valid;
	double lat;
	char buf[256];

	gtk_tree_model_get(tree_model, iter, CObjectsDlg::COL_WCS_VALID, &wcs_valid, CObjectsDlg::COL_WCS_LAT, &lat, -1);
	if (wcs_valid) {
		data->m_wcs->printLatitude(lat, buf, 256, false);
		g_object_set(cell, "text", buf, NULL);
	} else 
		g_object_set(cell, "text", "------", NULL);
}

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

CObjectsDlg::CObjectsDlg(GtkWindow *pParent):m_pParent(pParent), m_Wcs(NULL), m_TableData(NULL), 
	m_SortColumnId(COL_SEL_TYPIDX), m_ApertureIndex(-1), m_SortType(GTK_SORT_ASCENDING), 
	m_SortCol(NULL), m_SelectedPath(NULL), m_DefaultFileName(NULL)
{
	GtkWidget *tbox;
	GdkRectangle rc;

	m_SortColumnId = g_Project->GetInt("ObjectsDlg", "SortColumn", COL_SEL_TYPIDX);
	m_SortType     = g_Project->GetInt("ObjectsDlg", "SortType")==0 ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;

	// Dialog with buttons
	m_pDlg = gtk_dialog_new_with_buttons("Table of objects", pParent, 
		(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR),
		GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, 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 = gtk_window_get_screen(pParent);
	int mon = gdk_screen_get_monitor_at_window(scr, GTK_WIDGET(pParent)->window);
	gdk_screen_get_monitor_geometry(scr, mon, &rc);
	if (rc.width>0 && rc.height>0)
		gtk_window_set_default_size(GTK_WINDOW(m_pDlg), RoundToInt(0.7*rc.width), RoundToInt(0.7*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);

	// File section
	toolbar_new_label(tbox, "File");
	m_SaveBtn = toolbar_new_button(tbox, "Export", "Export the table to a file");
	g_signal_connect(G_OBJECT(m_SaveBtn), "clicked", G_CALLBACK(button_clicked), this);

	toolbar_new_separator(tbox);

	// Table
	m_TableView = gtk_tree_view_new_with_model(NULL);
	m_TableScrWnd = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(m_TableScrWnd), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(m_TableScrWnd), GTK_SHADOW_ETCHED_IN);
	g_signal_connect(G_OBJECT(m_TableView), "button_press_event", G_CALLBACK(button_press_event), this);
	gtk_container_add(GTK_CONTAINER(m_TableScrWnd), m_TableView);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(m_pDlg)->vbox), m_TableScrWnd, TRUE, TRUE, 0);
	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_TableView));
	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
	gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(m_TableView), TRUE);
	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(m_TableView), TRUE);
	g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(selection_changed), this);

	// Make popup menus
	m_ContextMenu.Create(ContextMenu);

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

CObjectsDlg::~CObjectsDlg()
{
	if (m_TableData)
		g_object_unref(m_TableData);
	if (m_SelectedPath)
		gtk_tree_path_free(m_SelectedPath);
	gtk_widget_destroy(m_pDlg);
	delete m_Wcs;
}

void CObjectsDlg::Execute(const CSelection &sel, int aperIndex, const CLightCurveDlg::tParamsRec &params, const gchar *default_file_name)
{
	int	res = 0;
	const gchar *tmp_file;
	GtkTreePath *refpath;
	GError *error = NULL;

	m_Selection = sel;
	m_Tags = *g_Project->Tags();
	m_Params = params;
	m_DefaultFileName = default_file_name;
	
	gtk_tree_view_set_model(GTK_TREE_VIEW(m_TableView), NULL);
	if (m_TableData) {
		g_object_unref(m_TableData);
		m_TableData = NULL;
	}
	if (m_SelectedPath) {
		gtk_tree_path_free(m_SelectedPath);
		m_SelectedPath = NULL;
	}
	m_Phot.Clear();
	m_Catalog.Clear();
	m_ApertureIndex = -1;
	delete m_Wcs;
	m_Wcs = NULL;

	switch (g_Project->GetReferenceType())
	{
	case REF_FRAME:
		// Load reference 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(aperIndex);
					m_ApertureIndex = m_Phot.SelectedAperture();
					if (m_Phot.Wcs())
						m_Wcs = new CWcs(*m_Phot.Wcs());
					SetView();
					UpdateTable();
				} 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) {
			if (m_Catalog.Load(tmp_file, &error)) {
				if (m_Catalog.Wcs())
					m_Wcs = new CWcs(*m_Catalog.Wcs());
				SetView();
				UpdateTable();
			} 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) {
		gtk_dialog_run(GTK_DIALOG(m_pDlg));
		gtk_widget_hide(m_pDlg);
	}
}

void CObjectsDlg::SetView(void)
{
	GtkTreeView *view = GTK_TREE_VIEW(m_TableView);

	// Delete old columns
	GList *list = gtk_tree_view_get_columns(view);
	for (GList *ptr=list; ptr!=NULL; ptr=ptr->next) 
		gtk_tree_view_remove_column(view, (GtkTreeViewColumn*)(ptr->data));
	g_list_free(list);

	AddViewCol_Text(view, COL_NAME, "Name", COL_SEL_TYPIDX);
	AddViewCol_Int(view, COL_ID, 0, INT_MAX, "Obj. #", COL_ID);
	AddViewCol_Dbl(view, COL_POS_X, INT32_MIN, INT32_MAX, 2, "X", COL_POS_X);
	AddViewCol_Dbl(view, COL_POS_Y, INT32_MIN, INT32_MAX, 2, "Y", COL_POS_Y);
	if (m_Wcs) {
		AddViewCol(view, (GtkTreeCellDataFunc)PrintLongitude, new tObjectsPrintWcsData(m_Wcs), m_Wcs->AxisName(CWcs::LNG), 1.0, COL_WCS_LNG);
		AddViewCol(view, (GtkTreeCellDataFunc)PrintLatitude, new tObjectsPrintWcsData(m_Wcs), m_Wcs->AxisName(CWcs::LAT), 1.0, COL_WCS_LAT);
	} 
	AddViewCol(view, (GtkTreeCellDataFunc)PrintBrightness, NULL, "Brightness [mag]", 1.0, COL_MAG);
	AddViewCol_Text(view, COL_TAGS, "Tag", COL_TAGS);
	gtk_tree_view_insert_column_with_data_func(view, -1, NULL, gtk_cell_renderer_text_new(), NULL, NULL, NULL);

	// Enable sorting
	list = gtk_tree_view_get_columns(view);
	for (GList *ptr=list; ptr!=NULL; ptr=ptr->next) {
		if (ptr->next) {		// Except the last empty column
			GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN(ptr->data);
			gtk_tree_view_column_set_clickable(col, TRUE);
			g_signal_connect(G_OBJECT(col), "clicked", G_CALLBACK(table_column_clicked), this);
			if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col), "sort-column-id")) == m_SortColumnId) {
				m_SortCol = col;
				gtk_tree_view_column_set_sort_order(col, m_SortType);
				gtk_tree_view_column_set_sort_indicator(col, TRUE);
			}
		}
	}
	g_list_free(list);
}

void CObjectsDlg::UpdateTable(void)
{
	gtk_tree_view_set_model(GTK_TREE_VIEW(m_TableView), NULL);
	if (m_TableData) {
		g_object_unref(m_TableData);
		m_TableData = NULL;
	}

	GtkListStore *data = gtk_list_store_new(COL_COUNT, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, 
		G_TYPE_DOUBLE, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_INT, G_TYPE_DOUBLE, 
		G_TYPE_ULONG, G_TYPE_STRING, G_TYPE_INT);

	if (m_Phot.Valid()) {
		static const int mask = CMPACK_PO_ID | CMPACK_PO_REF_ID | CMPACK_PO_CENTER | 
			CMPACK_PO_FWHM | CMPACK_PO_SKY;

		int count = m_Phot.ObjectCount();
		for (int i=0; i<count; i++) {
			CmpackPhtObject obj;
			if (m_Phot.GetObjectParams(i, mask, &obj) && obj.ref_id>=0) {
				GtkTreeIter iter;
				gtk_list_store_append(data, &iter);
				gtk_list_store_set(data, &iter, COL_ID, obj.ref_id, COL_POS_X, obj.x, COL_POS_Y, obj.y, -1);
				double r, d;
				if (m_Wcs && m_Wcs->pixelToWorld(obj.x, obj.y, r, d)) 
					gtk_list_store_set(data, &iter, COL_WCS_VALID, 1, COL_WCS_LNG, r, COL_WCS_LAT, d, -1);
				CmpackPhtData mag;
				CmpackError code;
				if (m_Phot.GetMagnitudeAndCode(i, mag, code) && mag.mag_valid) 
					gtk_list_store_set(data, &iter, COL_MAG_VALID, mag.mag_valid, COL_MAG, mag.magnitude, -1);
				gtk_list_store_set(data, &iter, COL_NAME, m_Selection.Label(obj.ref_id), 
					COL_SEL_TYPIDX, m_Selection.SortKey(obj.ref_id),
					COL_FG_COLOR, (int)m_Selection.Color(obj.ref_id), 
					COL_TAGS, m_Tags.caption(obj.id), -1);
			}
		}
	} else 
	if (m_Catalog.Valid()) {
		static const int mask = CMPACK_OM_ID | CMPACK_OM_MAGNITUDE | CMPACK_OM_CENTER;

		int count = m_Catalog.ObjectCount();
		for (int i=0; i<count; i++) {
			CmpackCatObject obj;
			if (m_Catalog.GetObjectParams(i, mask, &obj)) {
				GtkTreeIter iter;
				gtk_list_store_append(data, &iter);
				gtk_list_store_set(data, &iter, COL_ID, obj.id, COL_POS_X, obj.center_x, 
					COL_POS_Y, obj.center_y, COL_MAG_VALID, obj.refmag_valid, COL_MAG, obj.refmagnitude, -1);
				double r, d;
				if (m_Wcs && m_Wcs->pixelToWorld(obj.center_x, obj.center_y, r, d)) 
					gtk_list_store_set(data, &iter, COL_WCS_VALID, 1, COL_WCS_LNG, r, COL_WCS_LAT, d, -1);
				gtk_list_store_set(data, &iter, COL_NAME, m_Selection.Label(obj.id),
					COL_SEL_TYPIDX, m_Selection.SortKey(obj.id), 
					COL_FG_COLOR, (int)m_Selection.Color(obj.id), 
					COL_TAGS, m_Tags.caption(obj.id), -1);
			}
		}
	}
	GtkTreeSortable *sortable = GTK_TREE_SORTABLE(data);
	gtk_tree_sortable_set_sort_func(sortable, COL_MAG, (GtkTreeIterCompareFunc)CompareBrightness, 0, 0);
	gtk_tree_sortable_set_sort_column_id(sortable, m_SortColumnId, m_SortType);

	m_TableData = GTK_TREE_MODEL(data);

	gtk_tree_view_set_model(GTK_TREE_VIEW(m_TableView), m_TableData);
}

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

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

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

void CObjectsDlg::OnButtonClicked(GtkWidget *pBtn)
{
	if (pBtn==GTK_WIDGET(m_SaveBtn)) 
		SaveTable();
}

void CObjectsDlg::table_column_clicked(GtkTreeViewColumn *pCol, CObjectsDlg *pMe)
{
	pMe->OnTableColumnClicked(pCol);
}

void CObjectsDlg::OnTableColumnClicked(GtkTreeViewColumn *pCol)
{
	if (m_SelectedPath) {
		gtk_tree_path_free(m_SelectedPath);
		m_SelectedPath = NULL;
	}

	if (m_TableData) {
		if (pCol == m_SortCol) {
			// Swap the sort type (order)
			m_SortType = (GtkSortType)(1-m_SortType);
			g_Project->SetInt("ObjectsDlg", "SortType", (m_SortType==GTK_SORT_ASCENDING ? 0 : 1));
		} else {
			// Change the sort column, keep sort type
			if (m_SortCol)
				gtk_tree_view_column_set_sort_indicator(m_SortCol, FALSE);
			m_SortCol = pCol;
			m_SortColumnId = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(pCol), "sort-column-id"));
			g_Project->SetInt("ObjectsDlg", "SortColumn", m_SortColumnId);
			gtk_tree_view_column_set_sort_indicator(pCol, TRUE);
		}
		gtk_tree_view_column_set_sort_order(pCol, m_SortType);
		gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_TableData), m_SortColumnId, m_SortType);
		OnSelectionChanged();
		gtk_widget_grab_focus(m_TableView);
	}
}

void CObjectsDlg::selection_changed(GtkWidget *pChart, CObjectsDlg *pDlg)
{
	pDlg->OnSelectionChanged();
}

//
// Left button click
//
void CObjectsDlg::OnSelectionChanged(void)
{
	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_TableView));
	GList *list = gtk_tree_selection_get_selected_rows(selection, NULL);
	if (list && m_TableData) {
		GtkTreePath *path = (GtkTreePath*)(list->data);
		if (!m_SelectedPath || gtk_tree_path_compare(path, m_SelectedPath)) {
			if (m_SelectedPath)
				gtk_tree_path_free(m_SelectedPath);
			m_SelectedPath = gtk_tree_path_copy(path);
			gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(m_TableView), m_SelectedPath, 0, 0, 0, 0);
		}
	} else {
		if (m_SelectedPath) {
			gtk_tree_path_free(m_SelectedPath);
			m_SelectedPath = NULL;
		}
	}
	g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL);
	g_list_free (list);
}

void CObjectsDlg::SaveTable()
{
	if (!m_TableData)
		return;

	CObjectsExportDlg::tSortColumn sort;
	switch (m_SortColumnId)
	{
	case COL_POS_X:		sort = CObjectsExportDlg::SORT_POS_X; break;
	case COL_POS_Y:		sort = CObjectsExportDlg::SORT_POS_Y; break;
	case COL_WCS_LNG:	sort = CObjectsExportDlg::SORT_WCS_LNG; break;
	case COL_WCS_LAT:	sort = CObjectsExportDlg::SORT_WCS_LAT; break;
	case COL_ID:		sort = CObjectsExportDlg::SORT_ID; break;
	case COL_MAG:		sort = CObjectsExportDlg::SORT_MAG; break;
	case COL_TAGS:		sort = CObjectsExportDlg::SORT_TAG; break;
	default:			sort = CObjectsExportDlg::SORT_NAME; break;
	}
	
	gchar *filename = AddFileExtension((m_DefaultFileName != NULL ? m_DefaultFileName : "objects"), "csv");
	gchar *basename = g_path_get_basename(filename);
	gchar *newname = StripFileExtension(basename);
	if (m_Phot.Valid()) {
		CPhotObjectsExportDlg dlg(GTK_WINDOW(m_pDlg));
		dlg.Execute(m_Phot, m_Selection, m_Tags, m_ApertureIndex, newname, sort, m_SortType);
	} else 
	if (m_Catalog.Valid()) {
		CCatalogObjectsExportDlg dlg(GTK_WINDOW(m_pDlg));
		dlg.Execute(m_Catalog, m_Selection, m_Tags, newname, sort, m_SortType);
	}
	g_free(basename);
	g_free(newname);
	g_free(filename);
}

//
// Right mouse click
//
gint CObjectsDlg::button_press_event(GtkWidget *widget, GdkEventButton *event, CObjectsDlg *pMe)
{
	if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
		gtk_widget_grab_focus(widget);
		if (widget == pMe->m_TableView) {
			int x = (int)event->x, y = (int)event->y;
			GtkTreePath *path;
			if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(pMe->m_TableView), x, y, &path, NULL, NULL, NULL)) {
				GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pMe->m_TableView));
				if (gtk_tree_selection_count_selected_rows(sel) <= 1)
					gtk_tree_view_set_cursor(GTK_TREE_VIEW(pMe->m_TableView), path, NULL, FALSE);
					pMe->OnContextMenu(event, path);
				gtk_tree_path_free(path);
			}
		}
		return TRUE;
	}
	return FALSE;
}

//
// Object's context menu (preview mode)
//
void CObjectsDlg::OnContextMenu(GdkEventButton *ev, GtkTreePath *path)
{
	if (m_TableData && m_Wcs) {
		switch (m_ContextMenu.Execute(ev))
		{
		case CMD_COPY_WCS:
			gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(m_TableView)), path);
			CopyWcsCoordinatesFromTable(path);
			break;
		}
	}
}

void CObjectsDlg::CopyWcsCoordinatesFromTable(GtkTreePath *path)
{
	GtkTreeIter iter;
	if (m_TableData && m_Wcs && gtk_tree_model_get_iter(m_TableData, &iter, path)) {
		int star_id;
		gtk_tree_model_get(m_TableData, &iter, COL_ID, &star_id, -1);
		if (m_Phot.Valid()) {
			CmpackPhtObject obj;
			double lng, lat;
			if (m_Phot.GetObjectParams(m_Phot.FindObject(star_id), CMPACK_PO_CENTER, &obj) && m_Wcs->pixelToWorld(obj.x, obj.y, lng, lat))
				CopyWcsCoordinates(m_Wcs, lng, lat);
		}
		else if (m_Catalog.Valid()) {
			CmpackCatObject obj;
			double lng, lat;
			if (m_Catalog.GetObjectParams(m_Catalog.FindObject(star_id), CMPACK_OM_CENTER, &obj) && m_Wcs->pixelToWorld(obj.center_x, obj.center_y, lng, lat))
				CopyWcsCoordinates(m_Wcs, lng, lat);
		}
	}
}

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