/*
 * Copyright (c) 2006
 *  Morten Slot Kristensen, Århus, Denmark.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include <fstream>
#include <vector>
#include <ctype.h>
#include <gtk/gtk.h>
#include "matrix.hpp"
#include "operations.hpp"
#include "gtk.hpp"
#include "defs.hpp"
using namespace std;

/* METHODS */
int id_in_use(int id);
matrix get_by_id(int id);
void do_trivial(matrix*);
int get_input(int type, string msg);
float get_input_f(int type, string msg);

/* GTK METHODS */
void interactive_dialog(int);
void define_matrix_dialog();
void build_menu();
void update_tree_view();
static void selection_changed (GtkTreeSelection*);
void set_statusbar_text(string);
void do_gtk_trivial(matrix*);

/* GLOBAL VARIABLES */
vector<matrix> matrix_vector;

int global_preview_id = -1,
	global_rows = -1,
	global_columns = -1,
	global_order = -1,
	global_from = 0,
	global_to = 0,
	m1 = -1,
	m2 = -1;

GtkWidget *window, *menubar, *menuitem, *menu, *menuMenu, *menuMatrices,
		  *menuHelp, *scrolledwindow_m1, *scrolledwindow_m2, *scrolledwindow_pre,
		  *scrolledwindow_res, *scrolledwindow_list, *vbox_1, *vbox_2, *hbox_1, 
		  *hpaned, *hpaned_2, *vpaned, *treeview, *textview_m1, *textview_m2, 
		  *textview_pre, *textview_res, *frame_list, *frame_pre, *frame_m1,
		  *frame_m2, *frame_res, *vbox_2_1, *hbox_2, *hbox_3, *btn_sel_m1,
		  *btn_sel_m2, *btn_del_m1, *btn_del_m2, *btn_del_chosen, *btn_del_all,
		  *menuOperations, *statusbar;

GtkTreeModel *list_model;
GtkListStore *store;
GtkTreeSelection *selection;
GtkTextBuffer *textbuf_m1, *textbuf_m2, *textbuf_pre, *textbuf_res;

int main(int argc, char *argv[])
{
	// If the option '-gui' is used then start up GUI
	if(argc == 2 && string(argv[1]) == "-gui")
	{
		cout << "Starting with gui.." << endl;

		gtk_init(&argc, &argv);

		window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
		gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
		gtk_signal_connect(GTK_OBJECT(window), "delete_event", 
				GTK_SIGNAL_FUNC(destroy_app), NULL);
		gtk_window_set_default_size(GTK_WINDOW(window), WINWIDTH, WINHEIGHT);
		gtk_window_set_title(GTK_WINDOW(window), WINTITLE);
		gtk_container_border_width(GTK_CONTAINER(window), WINBORDER);

		vbox_1 = gtk_vbox_new(FALSE, 0);
		vbox_2 = gtk_vbox_new(FALSE, 0);
		vbox_2_1 = gtk_vbox_new(FALSE, 0);
		
		hbox_1 = gtk_hbox_new(FALSE, 0);
		hbox_2 = gtk_hbox_new(FALSE, 0);
		hbox_3 = gtk_hbox_new(FALSE, 0);

		hpaned = gtk_hpaned_new();
		hpaned_2 = gtk_hpaned_new();

		vpaned = gtk_vpaned_new();

		frame_m1 = gtk_frame_new("[M1] Matrix 1:");
		frame_m2 = gtk_frame_new("[M2] Matrix 2:");
		frame_pre = gtk_frame_new("Preview Matrix:");
		frame_res = gtk_frame_new("Result Matrix:");
		frame_list = gtk_frame_new("Matrix list:");

		scrolledwindow_list = gtk_scrolled_window_new(NULL, NULL);
		scrolledwindow_m1 = gtk_scrolled_window_new(NULL, NULL);
		scrolledwindow_m2 = gtk_scrolled_window_new(NULL, NULL);
		scrolledwindow_pre = gtk_scrolled_window_new(NULL, NULL);
		scrolledwindow_res = gtk_scrolled_window_new(NULL, NULL);

		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow_m1),
				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow_m2),
				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow_res),
				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow_pre),
				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow_list),
				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

		textbuf_m1 = gtk_text_buffer_new(NULL);
		textbuf_m2 = gtk_text_buffer_new(NULL);
		textbuf_pre = gtk_text_buffer_new(NULL);
		textbuf_res = gtk_text_buffer_new(NULL);

		textview_m1 = gtk_text_view_new_with_buffer(textbuf_m1);
		textview_m2 = gtk_text_view_new_with_buffer(textbuf_m2);
		textview_pre = gtk_text_view_new_with_buffer(textbuf_pre);
		textview_res = gtk_text_view_new_with_buffer(textbuf_res);

		gtk_text_view_set_editable(GTK_TEXT_VIEW(textview_m1), FALSE);
		gtk_text_view_set_editable(GTK_TEXT_VIEW(textview_m2), FALSE);
		gtk_text_view_set_editable(GTK_TEXT_VIEW(textview_pre), FALSE);
		gtk_text_view_set_editable(GTK_TEXT_VIEW(textview_res), FALSE);
	
		gtk_container_add(GTK_CONTAINER(scrolledwindow_m1), textview_m1);
		gtk_container_add(GTK_CONTAINER(scrolledwindow_m2), textview_m2);
		gtk_container_add(GTK_CONTAINER(scrolledwindow_pre), textview_pre);
		gtk_container_add(GTK_CONTAINER(scrolledwindow_res), textview_res);

		gtk_container_add(GTK_CONTAINER(frame_m1), scrolledwindow_m1);
		gtk_container_add(GTK_CONTAINER(frame_m2), scrolledwindow_m2);
		gtk_container_add(GTK_CONTAINER(frame_res), scrolledwindow_res);
		gtk_container_add(GTK_CONTAINER(frame_pre), scrolledwindow_pre);
		gtk_container_add(GTK_CONTAINER(frame_list), scrolledwindow_list);

		menubar = gtk_menu_bar_new();
		gtk_box_pack_start(GTK_BOX(vbox_1), menubar, FALSE, TRUE, 0);
		gtk_widget_show(menubar);

		build_menu();
		
		// add and show it
		store = gtk_list_store_new(NUMBER_OF_COLUMNS, G_TYPE_INT, G_TYPE_STRING);
		list_model = GTK_TREE_MODEL(store);
		treeview = gtk_tree_view_new_with_model(list_model);
		gtk_container_add(GTK_CONTAINER(scrolledwindow_list), treeview);
		
		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
		gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
		g_signal_connect (selection, "changed", 
				G_CALLBACK (selection_changed),
				NULL);

		// add the columns
		add_columns_to_treeview(GTK_TREE_VIEW(treeview));

		btn_sel_m1 = gtk_button_new_with_mnemonic("Select M_1");
		btn_sel_m2 = gtk_button_new_with_mnemonic("Select M_2");
		btn_del_m1 = gtk_button_new_with_mnemonic("U_nset M1");
		btn_del_m2 = gtk_button_new_with_mnemonic("Un_set M2");
		btn_del_all = gtk_button_new_with_mnemonic("Re_move all");
		btn_del_chosen = gtk_button_new_with_mnemonic("Remove _chosen");

		gtk_signal_connect(GTK_OBJECT(btn_sel_m1), "clicked",
			GTK_SIGNAL_FUNC(btn_sel_m1_clicked), NULL);
		gtk_signal_connect(GTK_OBJECT(btn_sel_m2), "clicked",
			GTK_SIGNAL_FUNC(btn_sel_m2_clicked), NULL);
		gtk_signal_connect(GTK_OBJECT(btn_del_m1), "clicked",
			GTK_SIGNAL_FUNC(btn_del_m1_clicked), NULL);
		gtk_signal_connect(GTK_OBJECT(btn_del_m2), "clicked",
			GTK_SIGNAL_FUNC(btn_del_m2_clicked), NULL);
		gtk_signal_connect(GTK_OBJECT(btn_del_all), "clicked",
			GTK_SIGNAL_FUNC(btn_del_all_clicked), NULL);
		gtk_signal_connect(GTK_OBJECT(btn_del_chosen), "clicked",
			GTK_SIGNAL_FUNC(btn_del_chosen_clicked), NULL);

		gtk_box_pack_start(GTK_BOX(hbox_1), btn_sel_m1, TRUE, TRUE, 0);
		gtk_box_pack_start(GTK_BOX(hbox_1), btn_sel_m2, TRUE, TRUE, 0);
		
		gtk_box_pack_start(GTK_BOX(hbox_2), btn_del_m1, TRUE, TRUE, 0);
		gtk_box_pack_start(GTK_BOX(hbox_2), btn_del_m2, TRUE, TRUE, 0);
		
		gtk_box_pack_start(GTK_BOX(hbox_3), btn_del_all, TRUE, TRUE, 0);
		gtk_box_pack_start(GTK_BOX(hbox_3), btn_del_chosen, TRUE, TRUE, 0);

		gtk_box_pack_start(GTK_BOX(vbox_2_1), hbox_1, FALSE, TRUE, 0);
		gtk_box_pack_start(GTK_BOX(vbox_2_1), hbox_2, FALSE, TRUE, 0);
		gtk_box_pack_start(GTK_BOX(vbox_2_1), hbox_3, FALSE, TRUE, 0);
		
		gtk_box_pack_start(GTK_BOX(vbox_2), frame_list, TRUE, TRUE, 0);
		gtk_box_pack_start(GTK_BOX(vbox_2), vbox_2_1, FALSE, TRUE, 0);
		gtk_box_pack_start(GTK_BOX(vbox_2), frame_pre, TRUE, TRUE, 0);
		
		gtk_paned_pack1(GTK_PANED(hpaned_2), frame_m1, TRUE, TRUE);
		gtk_paned_pack2(GTK_PANED(hpaned_2), frame_m2, TRUE, TRUE);

		gtk_paned_pack1(GTK_PANED(vpaned), hpaned_2, TRUE, TRUE);
		gtk_paned_pack2(GTK_PANED(vpaned), frame_res, TRUE, TRUE);

		gtk_paned_pack1(GTK_PANED(hpaned), vbox_2, FALSE, FALSE);
		gtk_paned_pack2(GTK_PANED(hpaned), vpaned, TRUE, TRUE);

		gtk_container_add(GTK_CONTAINER(vbox_1), hpaned);

		statusbar = gtk_statusbar_new();
		gtk_box_pack_end(GTK_BOX(vbox_1), statusbar, FALSE, TRUE, 0);

		gtk_container_add(GTK_CONTAINER(window), vbox_1);

		gtk_widget_show(statusbar);
		gtk_widget_show(btn_sel_m1);
		gtk_widget_show(btn_sel_m2);
		gtk_widget_show(btn_del_m1);
		gtk_widget_show(btn_del_m2);
		gtk_widget_show(btn_del_all);
		gtk_widget_show(btn_del_chosen);
		gtk_widget_show(treeview);
		gtk_widget_show(hpaned);
		gtk_widget_show(hpaned_2);
		gtk_widget_show(vpaned);
		gtk_widget_show(scrolledwindow_list);
		gtk_widget_show(scrolledwindow_m1);
		gtk_widget_show(scrolledwindow_m2);
		gtk_widget_show(scrolledwindow_res);
		gtk_widget_show(scrolledwindow_pre);
		gtk_widget_show(frame_m1);
		gtk_widget_show(frame_m2);
		gtk_widget_show(frame_list);
		gtk_widget_show(frame_pre);
		gtk_widget_show(frame_res);
		gtk_widget_show(textview_m1);
		gtk_widget_show(textview_m2);
		gtk_widget_show(textview_res);
		gtk_widget_show(textview_pre);
		gtk_widget_show(menuitem);
		gtk_widget_show(menuMenu);
		gtk_widget_show(menuMatrices);
		gtk_widget_show(menuHelp);
		gtk_widget_show(vbox_1);
		gtk_widget_show(vbox_2);
		gtk_widget_show(vbox_2_1);
		gtk_widget_show(hbox_1);
		gtk_widget_show(hbox_2);
		gtk_widget_show(hbox_3);
		gtk_widget_show(window);

		gtk_main();

		return 0;
	}

	// If not then run as normal
	cout << APPNAME << " " << VERSION << " - Matrix Calculator\n"
		<< "Copyright (c) " << CRYEAR << " " << DEVNAME << "\n"
		<< "<" << DEVMAIL << ">\n\n"
		<< "NOTE: To run the GTK version of this program\n"
		<< "      you can execute with the following option:\n"
		<< "      % " << argv[0] << " -gui\n\n"
		<< "Type 'help' for usage information!" << endl;

	string line = "", input = "";
	while(1)
	{
		cout << "\n# ";
		getline(cin, line);
		
		if(line == "exit")
			exit(0);
		else if(line == "help")
		{
			cout << APPNAME << " is a program which can do calculations"
				<< " on several matrices.\n\nBeneath is a list of"
			  	<< " commands:\n" 
				<< "    createm     Create a matrix.\n"
				<< "    createrm    Create a random matrix.\n"
				<< "    createum    Create a unit-matrix.\n"
				<< "    delete      Remove a given matrix.\n"
				<< "    deleteall   Remove all matrices.\n\n"
			  	<< "    mul         Multiplicate two matrices.\n"
			 	<< "    muln        Multiplicate a matrix with a number.\n"
				<< "    add         Add two matrices.\n"
				<< "    sub         Subtract two matrices.\n"
				<< "    invert      Invert a matrix.\n"
				<< "    trans       Transpose a matrix.\n"
				<< "    det         Get the determinant of a matrix.\n"
				<< "    show        Print all matrices.\n\n"
				<< "    help        Shows this information.\n"
				<< "    exit        Exit the program." << endl;
		}
		else if(line == "createm" || line == "createrm")
		{
			int rows = get_input(2, "Define rows: ");
			int columns = get_input(2, "Define columns: ");
			matrix m(rows, columns);

			if(line == "createm")
			{
				cout << "\nCreating a " << rows << "x" << columns << " matrix:\n";

				for(int i=0; i<rows; i++)
				{
					for(int j=0; j<columns; j++)
					{
						float value;

						while(1)
						{
							cout << "[" << i+1 << "," << j+1 << "] = ";
							getline(cin, input);
							value = atof(input.c_str());

							if(value == NULL && !isdigit((char)input[0]))
								cout << "Error: You have to input an integer!\n";
							else 
								break;
						}

						m.set_value(i, j, value);
					}
				}
			}
			else
			{
				int from, to;

				while(1)
				{
					from = get_input(3, "Random from: ");
					to = get_input(3, "To max: ");

					if(from > to)
						cout << "Error: 'from' has to be smaller than or equal to 'max'\n";
					else
						break;
				}

				for(int i=0; i<rows; i++)
				{
					for(int j=0; j<columns; j++)
						m.set_value(i, j, (float)random_number(from, to));
				}
			}

			do_trivial(&m);
		}
		else if(line == "createum")
		{
			int order = get_input(1, "Define order: ");
			matrix m(order, order);
			
			cout << "\nCreating a " << order << "x" << order << " unit-matrix:\n";

			int checker = 0, cnt = 0;
			for(int i=0; i<order; i++)
			{
				for(int j=0; j<order; j++)
				{
					if(cnt == checker)
					{
						m.set_value(i, j, 1.0);
						checker += order+1;
					}
					else
						m.set_value(i, j, 0.0);

					cnt++;
				}
			}
			
			do_trivial(&m);
		}
		else if(line == "show")
		{
			if(matrix_vector.size() > 0)
			{
				for(vector<matrix>::iterator it = matrix_vector.begin(); 
						it != matrix_vector.end(); 
						it++)
				{
					cout << endl << "id: " << it->get_id() << endl;
					it->print();
				}
			}
			else
				cout << "There are no matrices present!\nCreate one with the 'createm' command.\n";
		}
		else if(line == "mul")
		{
			matrix m1 = get_by_id(get_input(0, "Id of the first matrix: "));
			matrix m2 = get_by_id(get_input(0, "Id of the second matrix: "));

			if(m1.get_columns() == m2.get_rows())
			{
				matrix m = multiplicate(&m1, &m2);
				do_trivial(&m);
			}
			else
				cout << "Can't multiplicate two matrices when the first's number of columns is not equal to the second's number of rows!" << endl;
		}
		else if(line == "muln")
		{
			matrix tmp = get_by_id(get_input(0, "Id of matrix: "));
			matrix m = multiplicate(&tmp, get_input_f(0, "Input the number to multiplicate with: "));
			do_trivial(&m);
		}
		else if(line == "add")
		{
			matrix m1 = get_by_id(get_input(0, "Id of the first matrix: "));
			matrix m2 = get_by_id(get_input(0, "Id of the second matrix: "));
			
			if(m1.get_rows() == m2.get_rows() && m1.get_columns() == m2.get_columns())
			{
					matrix m = add(&m1, &m2);
					do_trivial(&m);
			}
			else
				cout << "Can't add two matrices when their rows  and columns are not the same!" << endl;
		}
		else if(line == "sub")
		{
			matrix m1 = get_by_id(get_input(0, "Id of the first matrix: "));
			matrix m2 = get_by_id(get_input(0, "Id of the second matrix: "));
			
			if(m1.get_rows() == m2.get_rows() && m1.get_columns() == m2.get_columns())
			{
					matrix m = substract(&m1, &m2);
					do_trivial(&m);
			}
			else
				cout << "Can't substract two matrices when their rows  and columns are not the same!" << endl;
		}
		else if(line == "delete")
		{
			int id = get_input(0, "Id to delete: ");

			vector<matrix>::iterator loc;

			for(loc = matrix_vector.begin();
					loc != matrix_vector.end(); 
					loc++)
			{
				if(loc->get_id() == id)
					break;
			}
						
			matrix_vector.erase(loc);
		}
		else if(line == "deleteall")
		{
			matrix_vector.clear();
		}
		else if(line == "invert")
		{
			matrix tmp = get_by_id(get_input(0, "Id to invert: "));
			matrix m = invert(&tmp);
			do_trivial(&m);
		}
		else if(line == "trans")
		{
			matrix tmp = get_by_id(get_input(0, "Id to transpose: "));
			matrix m = transpose(&tmp);
			do_trivial(&m);
		}
		else if(line == "det")
		{
			int id = get_input(0, "Id to get the determinant of: ");
			matrix tmp = get_by_id(id);

			if(tmp.get_rows() == tmp.get_columns())
			{
				if(tmp.get_rows() == 1)
					cout << "Determinant is: " << tmp.get_value(0, 0) << "\n";
				else
					cout << "Determinant is: " << determinant(&tmp) << "\n";
			}
			else
				cout << "The matrix has to be square! 2x2, MxM etc.\n";
		}
		else
			cout << "Command not recognized!\nTry 'help'.\n";
	}

	return 0;
}

/*
 * Determines wether the id is in use or not
 */
int id_in_use(int id)
{
	int exists = 0;

	for(vector<matrix>::iterator it = matrix_vector.begin();
			it != matrix_vector.end(); 
			it++)
	{
			if(it->get_id() == id)
				exists = 1;
	}

	return exists;
}

/*
 * Retrieves the matrix of a given id
 */
matrix get_by_id(int id)
{
	for(vector<matrix>::iterator it = matrix_vector.begin();
			it != matrix_vector.end(); 
			it++)
	{
		if(it->get_id() == id)
			return *it;
	}
}

/*
 * Method to do trivial stuff.. Used often at the end of subrutines
 */
void do_trivial(matrix *m)
{
	m->print();
	m->set_id();
	cout << "New matrix created, id: " << m->get_id() << endl;
	matrix_vector.push_back(*m);
}

/*
 * Gtk version of the above method
 */
void do_gtk_trivial(matrix *m)
{
	m->set_id();	
	insert_matrix(textbuf_res, &(*m));
	matrix_vector.push_back(*m);
	update_tree_view();
}

/*
 * Gets the input of the user 
 * Returns an integer
 */
int get_input(int type, string msg)
{
	string input = "";
	int res; 

	while(1)
	{
		cout << msg;
		getline(cin, input);
		res = atoi(input.c_str());

		if(type == 0)
		{
			if(res == NULL || !id_in_use(res))
				cout << "Error: You have to choose a correct id!\n";
			else
				break;
		}
		else if(type == 1)
		{
			if(res == NULL || res < 1)
				cout << "Error: You have to input an integer greater than or equal to 1!\n";
			else
				break;
		}
		else if(type == 2)
		{
			if(res == NULL || res <= 0)
				cout << "Error: You need to input an integer greater than zero!\n";
			else
				break;
		}
		else if(type == 3)
		{
			if(res == NULL && !isdigit((char)res))
				cout << "Error: You have to input an integer!\n";
			else
				break;
		}
	}

	return res;
}

/*
 * Gets the input of the user 
 * Returns a float
 */
float get_input_f(int type, string msg)
{
	string input = "";
	float res; 

	while(1)
	{
		cout << msg;
		getline(cin, input);
		res = atof(input.c_str());

		if(type == 0)
		{
			if(res == NULL)
				cout << "Error: You have to input a non-zero number!\n";
			else
				break;
		}
	}

	return res;
}

/*
 * Method for building the GUI menu
 */
void build_menu()
{
	/* Exit menu */
	menuMenu = gtk_menu_item_new_with_mnemonic("M_enu");
	gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuMenu);
	gtk_widget_show(menuMenu);
	
	menu = gtk_menu_new();
	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuMenu), menu);
	
	menuitem = gtk_menu_item_new_with_mnemonic("Import matrix");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_import_clicked), NULL);
	gtk_widget_show(menuitem);
	
	menuitem = gtk_menu_item_new_with_mnemonic("Export matrix");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_export_clicked), NULL);
	gtk_widget_show(menuitem);

	menuitem = gtk_separator_menu_item_new();
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_widget_show(menuitem);
		
	menuitem = gtk_menu_item_new_with_mnemonic("E_xit");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(destroy_app), NULL);
	gtk_widget_show(menuitem);

	/* Matrices menu */
	menuMatrices = gtk_menu_item_new_with_mnemonic("M_atrices");
	gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuMatrices);
	gtk_widget_show(menuMatrices);

	menu = gtk_menu_new();
	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuMatrices), menu);

	menuitem = gtk_menu_item_new_with_mnemonic("Create _new matrix");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_new_matrix), NULL);
	gtk_widget_show(menuitem);
	
	menuitem = gtk_menu_item_new_with_mnemonic("Create new _random matrix");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_new_rnd_matrix), NULL);
	gtk_widget_show(menuitem);
	
	menuitem = gtk_menu_item_new_with_mnemonic("Create new _unit-matrix");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_new_unit_matrix), NULL);
	gtk_widget_show(menuitem);
	
	/* Operations menu */
	menuOperations = gtk_menu_item_new_with_mnemonic("O_perations");
	gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuOperations);
	gtk_widget_show(menuOperations);
	
	menu = gtk_menu_new();
	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuOperations), menu);

	menuitem = gtk_menu_item_new_with_mnemonic("M_ultiplicate (M1*M2)");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_mul_clicked), NULL);
	gtk_widget_show(menuitem);
	
	menuitem = gtk_menu_item_new_with_mnemonic("A_dd (M1+M2)");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_add_clicked), NULL);
	gtk_widget_show(menuitem);

	menuitem = gtk_menu_item_new_with_mnemonic("S_ubstract (M1-M2)");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_sub_clicked), NULL);
	gtk_widget_show(menuitem);

	menuitem = gtk_menu_item_new_with_mnemonic("I_nvert (M1)");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_invert_clicked), NULL);
	gtk_widget_show(menuitem);

	menuitem = gtk_menu_item_new_with_mnemonic("T_ranspose (M1)");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_transpose_clicked), NULL);
	gtk_widget_show(menuitem);

	menuitem = gtk_menu_item_new_with_mnemonic("G_et determinant (M1)");
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(menu_det_clicked), NULL);
	gtk_widget_show(menuitem);
	
	/* Help menu */
	menuHelp = gtk_menu_item_new_with_mnemonic("He_lp");
	gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuHelp);
	gtk_widget_show(menuHelp);
	
	menu = gtk_menu_new();
	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuHelp), menu);

	menuitem = gtk_menu_item_new_with_mnemonic(string("A_bout "+string(APPNAME)).c_str());
	gtk_menu_append(GTK_MENU(menu), menuitem);
	gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
			GTK_SIGNAL_FUNC(show_about), NULL);
	gtk_widget_show(menuitem);
}

/*
 * Executed when the list's selection changes
 */
static void selection_changed(GtkTreeSelection *selection)
{
	GtkTreeModel *model;
	GtkTreeIter iter;

	if(gtk_tree_selection_get_selected(selection, &model, &iter))
	{
		gtk_tree_model_get(model, &iter, 0, &global_preview_id, -1);

		// set the preview matrix
		matrix m = get_by_id(global_preview_id);
		insert_matrix(textbuf_pre, &m);
	}
}

gint btn_sel_m1_clicked(GtkWidget *widget, gpointer gdata)
{
  if(global_preview_id != -1)
  {
	matrix m = get_by_id(global_preview_id);
	insert_matrix(textbuf_m1, &m);
	m1 = global_preview_id;
	gtk_frame_set_label(GTK_FRAME(frame_m1), to_string("[M1] Matrix 1: ID = "+to_string(m1)).c_str());
  }
  else
  {
	m1 = -1;
	msgbox("Select a matrix first!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
  }
}

gint btn_sel_m2_clicked(GtkWidget *widget, gpointer gdata)
{
  if(global_preview_id != -1)
  {
	matrix m = get_by_id(global_preview_id);
	insert_matrix(textbuf_m2, &m);
	m2 = global_preview_id;
	gtk_frame_set_label(GTK_FRAME(frame_m2), to_string("[M2] Matrix 2: ID = "+to_string(m2)).c_str());
  }
  else
  {
	m2 = -1;
	msgbox("Select a matrix first!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
  }
}

gint btn_del_m1_clicked(GtkWidget *widget, gpointer gdata)
{
	gtk_text_buffer_set_text(textbuf_m1, "", 0);
	gtk_frame_set_label(GTK_FRAME(frame_m1), "[M1] Matrix 1:");
}

gint btn_del_m2_clicked(GtkWidget *widget, gpointer gdata)
{
	gtk_text_buffer_set_text(textbuf_m2, "", 0);
	gtk_frame_set_label(GTK_FRAME(frame_m2), "[M2] Matrix 2:");
}

gint btn_del_all_clicked(GtkWidget *widget, gpointer gdata)
{
	matrix_vector.clear();
	update_tree_view();
	global_preview_id = -1;
	gtk_text_buffer_set_text(textbuf_pre, "", 0);
	gtk_text_buffer_set_text(textbuf_m1, "", 0);
	gtk_text_buffer_set_text(textbuf_m2, "", 0);
	gtk_text_buffer_set_text(textbuf_res, "", 0);
	gtk_frame_set_label(GTK_FRAME(frame_m1), "[M1] Matrix 1:");
	gtk_frame_set_label(GTK_FRAME(frame_m2), "[M2] Matrix 2:");
	gtk_frame_set_label(GTK_FRAME(frame_res), "Result Matrix:");
}

gint btn_del_chosen_clicked(GtkWidget *widget, gpointer gdata)
{
  if(global_preview_id != -1)
  {
	vector<matrix>::iterator loc;
	for(loc = matrix_vector.begin(); loc != matrix_vector.end(); loc++)
	{
		if(loc->get_id() == global_preview_id)
			break;
	}
						
	matrix_vector.erase(loc);
	update_tree_view();
  }
  else
	msgbox("Select a matrix first!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
}

/*
 * Updates the treeview with the matrices
 * stored in the vector
 */
void update_tree_view()
{
	gtk_list_store_clear(store);
	
	vector<matrix>::iterator loc;
	for(loc = matrix_vector.begin(); loc != matrix_vector.end(); loc++)
	  add_matrix_to_treeview(GTK_TREE_VIEW(treeview), &(*loc), GTK_LIST_STORE(store));

	gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(store));
}

/*
 * Shows a dialog for defining rows, columns and
 * orders of matrices to create.
 *
 * type:
 * 		0 = rows + columns (matrix)
 * 		1 = order (unit matrix)
 * 		2 = random (matrix)
 */
void interactive_dialog(int type)
{
  	global_rows = global_columns = -1;

  	GtkWidget *dialog, *hbox, *stock, *table, *local_entry1,
			  *local_entry2, *label, *local_entry3, *local_entry4;
  	gint response;
	
  	dialog = gtk_dialog_new_with_buttons ("New matrix: Define dimensions.",
		GTK_WINDOW (window), GTK_DIALOG_MODAL,
	  	GTK_STOCK_OK, GTK_RESPONSE_OK, 
	  	"_Cancel", GTK_RESPONSE_CANCEL,
	  	NULL);

  	hbox = gtk_hbox_new (FALSE, 8);
  	gtk_container_set_border_width (GTK_CONTAINER (hbox), 8);
  	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, TRUE, TRUE, 0);

  	stock = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
  	gtk_box_pack_start (GTK_BOX (hbox), stock, FALSE, FALSE, 0);

	if(type == 0 || type == 1)
	  	table = gtk_table_new (2, 2, FALSE);
	else
	  	table = gtk_table_new (2, 4, FALSE);

  	gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  	gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  	gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);


	if(type == 0 || type == 2)
	{
  		label = gtk_label_new_with_mnemonic ("Ro_ws");
  		gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1);

		local_entry1 = gtk_entry_new ();
		gtk_entry_set_text (GTK_ENTRY (local_entry1), to_string("1").c_str());
		gtk_table_attach_defaults (GTK_TABLE (table), local_entry1, 1, 2, 0, 1);
	  	gtk_label_set_mnemonic_widget (GTK_LABEL (label), local_entry1);

	  	label = gtk_label_new_with_mnemonic ("Co_lmns");
	  	gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 1, 2);

	  	local_entry2 = gtk_entry_new ();
	  	gtk_entry_set_text (GTK_ENTRY (local_entry2), to_string("1").c_str());
	  	gtk_table_attach_defaults (GTK_TABLE (table), local_entry2, 1, 2, 1, 2);
	  	gtk_label_set_mnemonic_widget (GTK_LABEL (label), local_entry2);

		if(type == 2)
		{
	  		label = gtk_label_new_with_mnemonic ("R_andom from:");
	  		gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 2, 3);

			local_entry3 = gtk_entry_new ();
			gtk_entry_set_text (GTK_ENTRY (local_entry3), to_string("1").c_str());
			gtk_table_attach_defaults (GTK_TABLE (table), local_entry3, 1, 2, 2, 3);
		  	gtk_label_set_mnemonic_widget (GTK_LABEL (label), local_entry3);

		  	label = gtk_label_new_with_mnemonic ("T_o max:");
		  	gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 3, 4);

		  	local_entry4 = gtk_entry_new ();
		  	gtk_entry_set_text (GTK_ENTRY (local_entry4), to_string("100").c_str());
		  	gtk_table_attach_defaults (GTK_TABLE (table), local_entry4, 1, 2, 3, 4);
		  	gtk_label_set_mnemonic_widget (GTK_LABEL (label), local_entry4);
		}
	}
	else if(type == 1)
	{
		label = gtk_label_new_with_mnemonic ("O_rder");
  		gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1);

		local_entry1 = gtk_entry_new ();
		gtk_entry_set_text (GTK_ENTRY (local_entry1), to_string("1").c_str());
		gtk_table_attach_defaults (GTK_TABLE (table), local_entry1, 1, 2, 0, 1);
		gtk_label_set_mnemonic_widget (GTK_LABEL (label), local_entry1);
	}

  
	gtk_widget_show_all(hbox);
  	response = gtk_dialog_run(GTK_DIALOG (dialog));

  	if(response == GTK_RESPONSE_OK)
  	{
		if(type == 0)
		{
			string rows = gtk_entry_get_text(GTK_ENTRY (local_entry1));
			string cols = gtk_entry_get_text(GTK_ENTRY (local_entry2));
			global_rows = atoi(rows.c_str());
			global_columns = atoi(cols.c_str());
	
			if(global_rows == NULL || global_columns == NULL)
			{
				global_columns = global_rows = -1;
				msgbox("You have to input integers greater than zero!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
			}
		}
		else if(type == 1)
		{
			string order = gtk_entry_get_text(GTK_ENTRY(local_entry1));
			global_order = atoi(order.c_str());

			if(global_order == NULL)
			{
				global_order = -1;
				msgbox("You have to input an integer greater than zero!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
			}
		}
		else if(type == 2)
		{
			string rows = gtk_entry_get_text(GTK_ENTRY (local_entry1));
			string cols = gtk_entry_get_text(GTK_ENTRY (local_entry2));
			string from = gtk_entry_get_text(GTK_ENTRY (local_entry3));
			string to = gtk_entry_get_text(GTK_ENTRY (local_entry4));
			global_rows = atoi(rows.c_str());
			global_columns = atoi(cols.c_str());
			global_from = atoi(from.c_str());
			global_to = atoi(to.c_str());
	
			if(global_rows == NULL || global_columns == NULL ||
				(!isdigit((char)from.c_str()[0]) && 
				 	to_string(from.c_str()[0]) != "-") ||
				(!isdigit((char)to.c_str()[0]) && 
				 	to_string(to.c_str()[0]) != "-"))
			{
				global_rows = global_columns = -1;
				msgbox("You have to input an integer greater than zero for rows and columns. And for random 'from' to 'max' needs to be a valid integer!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
			}
		}
	}
	else
	  global_rows = global_columns = -1;

	gtk_widget_destroy(dialog);
}

gint menu_new_matrix(GtkWidget *widget, gpointer gdata)
{
	interactive_dialog(0);

	if(global_rows > 0 && global_columns > 0)
		define_matrix_dialog();
}

/*
 * Shows a dialog to fill in the contents of the matrix
 */
void define_matrix_dialog()
{
	if((global_rows * global_columns) > 1000)
	{
		msgbox(to_string("You're trying to create "+to_string(global_rows*global_columns)+" fields which will take up quite a bit of memory! The limit is set to a 1000 fields which is also very many to fill out manually; It is better to import matrices of that size from the menu.\n\nThe format of these matrices is as follows:\n\nnumber of rows<linebreak>\nnumber of columns<linebreak>\nall data chronically separated by commas and with no whitespace\n\nExample:\n2\n2\n1,2,3,4\n\nWill represent:\n\t1\t2\n\t3\t4\n\nRemember that there can only be ONE matrix in each file!"), GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
		return;
	}

  	GtkWidget *dialog, *table, *sw;
  	gint response;
	vector<GtkWidget*> entries;
	int w, h;

  	dialog = gtk_dialog_new_with_buttons ("New matrix: Fill in the context.",
		GTK_WINDOW (window), GTK_DIALOG_MODAL,
		GTK_STOCK_OK, GTK_RESPONSE_OK, 
		"_Cancel", GTK_RESPONSE_CANCEL,
		NULL);

	w = global_columns*53;
	if(w > 800)
		w = 800;

	h = global_rows*27;
	if(h > 600)
		h = 600;

	gtk_window_set_default_size(GTK_WINDOW(dialog), w, h);
	
	table = gtk_table_new(global_rows, global_columns, TRUE);

	sw = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), 
				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), table);

  	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), sw, TRUE, TRUE, 0);


	for(int i=0; i<global_rows; i++)
	{
		for(int j=0; j<global_columns; j++)
		{
			GtkWidget *entry = gtk_entry_new_with_max_length(7);
			gtk_widget_set_size_request(entry, 50, 15);
			gtk_table_attach(GTK_TABLE(table), entry, j, j+1, i, i+1, GTK_EXPAND, GTK_EXPAND, 0, 0);
			entries.push_back(entry);
		}
	}

	matrix response_matrix(global_rows, global_columns);

	gtk_widget_show_all (table);
	gtk_widget_show(sw);

	response = gtk_dialog_run (GTK_DIALOG (dialog));
	if(response == GTK_RESPONSE_OK)
	{
		int i = 0;
		for(vector<GtkWidget*>::iterator it = entries.begin();
				it != entries.end(); it++)
		{
			string input = to_string(gtk_entry_get_text(GTK_ENTRY(*it))).c_str();
			float value  = strtod(input.c_str(), 0);
			if(value == NULL && !isdigit((char)input.c_str()[0]))
			{
				msgbox("Everything you input has to be floats! (fx.: 11.2)", 
						GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
				break;
			}
			else
			{
				response_matrix.set_value_t(i, value);
				i++;			
			}	
		}

		if(i == entries.size())
		{
			response_matrix.set_id();
			matrix_vector.push_back(response_matrix);
			update_tree_view();
		}
	}

	gtk_widget_destroy(dialog);
}

gint menu_new_rnd_matrix(GtkWidget *widget, gpointer gdata)
{
	interactive_dialog(2);

	if(global_rows > 0 && global_columns > 0)
	{
		matrix m(global_rows, global_columns);

		for(int i=0; i<global_rows; i++)
		{
			for(int j=0; j<global_columns; j++)
				m.set_value(i, j, (float)random_number(global_from, global_to));
		}

		m.set_id();
		matrix_vector.push_back(m);
		update_tree_view();
	}
}

gint menu_new_unit_matrix(GtkWidget *widget, gpointer gdata)
{
	interactive_dialog(1);

	if(global_order > 0)
	{
		matrix m(global_order, global_order);

		int checker = 0, cnt = 0;
		for(int i=0; i<global_order; i++)
		{
			for(int j=0; j<global_order; j++)
			{
				if(cnt == checker)
				{
					m.set_value(i, j, 1.0);
					checker += global_order+1;
				}
				else
					m.set_value(i, j, 0.0);

				cnt++;
			}
		}

		m.set_id();
		matrix_vector.push_back(m);
		update_tree_view();
	}
}

gint menu_mul_clicked(GtkWidget *widget, gpointer gdata)
{
	if(m1 != -1 && m2 != -1)
	{
		matrix m_1 = get_by_id(m1);
		matrix m_2 = get_by_id(m2);
		matrix res;

		int set_ = 0;
		if(m_1.get_rows() == 1 && m_1.get_columns() == 1)
		{
			res = multiplicate(&m_2, m_1.get_value(0, 0));
			set_ = 1;
		}
		else if(m_2.get_rows() == 1 && m_2.get_columns() == 1)
		{
			res = multiplicate(&m_1, &m_2);
			set_ = 1;
		}
		else if(m_1.get_rows() == m_2.get_columns())
		{
			res = multiplicate(&m_1, &m_2);
			set_ = 1;
		}
		else
			msgbox("You cannot multiply two matrices when the first one's rows doesn't match the second one's columns", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);

		if(set_)
		{
			do_gtk_trivial(&res);
			set_statusbar_text("The multiplication of M1 and M2 created a new matrix, id: "+
					to_string(res.get_id()));
		}
	}
	else
		msgbox("You have to set both M1 and M2!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
}

gint menu_add_clicked(GtkWidget *widget, gpointer gdata)
{
	if(m1 != -1 && m2 != -1)
	{
		matrix m1_1 = get_by_id(m1);
		matrix m2_1 = get_by_id(m2);
			
		if(m1_1.get_rows() == m2_1.get_rows() && m1_1.get_columns() == m2_1.get_columns())
		{
			matrix m = add(&m1_1, &m2_1);

			do_gtk_trivial(&m);
			set_statusbar_text("The addition of M1 and M2 created a new matrix, id: "+
					to_string(m.get_id()));
		}
		else
		{
			msgbox("Can't add two matrices when their rows and columns are not the same!", 
					GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
		}
	}
	else
		msgbox("You have to set both M1 and M2!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
}

gint menu_sub_clicked(GtkWidget *widget, gpointer gdata)
{
	if(m1 != -1 && m2 != -1)
	{
		matrix m1_1 = get_by_id(m1);
		matrix m2_1 = get_by_id(m2);
			
		if(m1_1.get_rows() == m2_1.get_rows() && m1_1.get_columns() == m2_1.get_columns())
		{
			matrix m = substract(&m1_1, &m2_1);

			do_gtk_trivial(&m);
			set_statusbar_text("The substraction of M1 and M2 created a new matrix, id: "+
					to_string(m.get_id()));
		}
		else
		{
			msgbox("Can't substract two matrices when their rows and columns are not the same!", 
					GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
		}
	}
	else
		msgbox("You have to set both M1 and M2!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
}

gint menu_invert_clicked(GtkWidget *widget, gpointer gdata)
{
	if(m1 != -1)
	{
		matrix tmp = get_by_id(m1);
		matrix m = invert(&tmp);
		do_gtk_trivial(&m);
		set_statusbar_text("The invertion of M1 created a new matrix, id: "+
				to_string(m.get_id()));
	}
	else
		msgbox("You have to set both M1!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
}

gint menu_transpose_clicked(GtkWidget *widget, gpointer gdata)
{
	if(m1 != -1)
	{
		matrix tmp = get_by_id(m1);
		matrix m = transpose(&tmp);
		do_gtk_trivial(&m);
		set_statusbar_text("The transposition of M1 created a new matrix, id: "+
				to_string(m.get_id()));
	}
	else
		msgbox("You have to set both M1!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
}

gint menu_det_clicked(GtkWidget *widget, gpointer gdata)
{
	if(m1 != -1)
	{
		matrix tmp = get_by_id(m1);

		if(tmp.get_rows() == tmp.get_columns())
		{
			if(tmp.get_rows() == 1)
			{
				msgbox(to_string("Determinant is: "+to_string(tmp.get_value(0, 0))).c_str(),
						GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
			}
			else
			{
				msgbox(to_string("Determinant is: "+to_string(determinant(&tmp))).c_str(),
						GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
			}
		}
		else
			msgbox("The matrix has to be square! 2x2, MxM etc.", 
					GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
	}
	else
		msgbox("You have to set both M1!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
}

/*
 * Sets the text of the statusbar
 */
void set_statusbar_text(string text)
{
	gtk_statusbar_pop(GTK_STATUSBAR(statusbar), 0);
	gtk_statusbar_push(GTK_STATUSBAR(statusbar), 
			gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), "don't care"),
			text.c_str());
}

gint menu_import_clicked(GtkWidget *widget, gpointer gdata)
{
	GtkWidget *filechooser;
	
	filechooser = gtk_file_chooser_dialog_new("Choose a file containing a matrix to open",
			GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_OPEN, 
			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN,
			GTK_RESPONSE_ACCEPT, NULL);

	if(gtk_dialog_run(GTK_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
	{
		string filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
			
		// convert filename into UTF-8
		GError *err = NULL;
		gchar *utf8_filename = g_filename_to_utf8(filename.c_str(), filename.length(),
				NULL, NULL, &err);
	
		if(utf8_filename == NULL)
		{
			msgbox("Error: " + string(err->message), GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE);
			return 1;
		}

		ifstream reader(utf8_filename);
		string line, contents = "";

		if(reader.is_open())
		{
			int r, c, i = 0;
			while(!reader.eof())
			{
				getline(reader, line);

				if(i == 0)
					r = atoi(line.c_str());
				else if(i == 1)
					c = atoi(line.c_str());
				else
					contents += line;

				i++;
			}
	
			reader.close();
	
			if(contents != "")
			{
				// convert the contents to UTF-8
				gchar *utf8_data = g_convert(contents.c_str(), contents.length(), 
						"UTF-8", "", NULL, NULL, NULL);

				// create matrix
				if(r == NULL || c == NULL)
				{
					msgbox("Error while importing matrix! The contents of the file is not valid!", 
							GTK_MESSAGE_ERROR,
							GTK_BUTTONS_CLOSE);
				}
				else
				{
					string dat = to_string(utf8_data);
					matrix m = gen_matrix_from_text(r, c, dat);
					m.set_id();
					matrix_vector.push_back(m);
					update_tree_view();
					set_statusbar_text("Imported matrix, id: "+to_string(m.get_id()));
				}
			}
		}
		else
			msgbox("An error occurred while trying to read from: " + string(filename), GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE);
	}

	gtk_widget_destroy(filechooser);
}

gint menu_export_clicked(GtkWidget *widget, gpointer gdata)
{
	GtkWidget *filechooser;

	if(global_preview_id != -1)
	{
		filechooser = gtk_file_chooser_dialog_new("Save file",
			GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_SAVE,
			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE,
			GTK_RESPONSE_ACCEPT, NULL);

		// ask for confirmation when overwriting a file in the dialog
		gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(filechooser), TRUE);
	
		if(gtk_dialog_run(GTK_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
		{
			string filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
		
			GError *err = NULL;
			gchar *utf8_filename = g_filename_to_utf8(filename.c_str(), filename.length(), 
					NULL, NULL, &err);
				
			if(utf8_filename == NULL)
			{
				msgbox("Error: " + string(err->message), GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE);
				return 1;
			}

			matrix ex = get_by_id(global_preview_id);	
			ofstream writer(utf8_filename);
			if(writer.is_open())
			{
				writer << export_matrix(&ex);
				writer.close();

				set_statusbar_text("Exported matrix (id="+to_string(global_preview_id)+") to: "+filename);
			}
			else
				msgbox("An error occurred while trying to write to: " + string(utf8_filename), GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE);
		}

		gtk_widget_destroy(filechooser);
	}
	else
		msgbox("You have to select the matrix in the list to export!", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
}


syntax highlighted by Code2HTML, v. 0.9.1