// This file is part of fityk program. Copyright (C) Marcin Wojdyr
// Licence: GNU General Public License version 2
// $Id: dialogs.cpp 326 2007-08-01 05:54:03Z wojdyr $

/// In this file:
///  small dialogs:  SumHistoryDlg, FitRunDlg, DataExportDlg, AboutDlg,
///                  MergePointsDlg

#include <wx/wxprec.h>
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include <wx/statline.h>
#include <boost/spirit/version.hpp> //SPIRIT_VERSION for AboutDlg

#include "dialogs.h"
#include "gui.h"
#include "listptxt.h"
#include "../fit.h"
#include "../ui.h"
#include "../settings.h"
#include "../datatrans.h" 
#include "../logic.h"
#include "../data.h"

#include "img/up_arrow.xpm"
#include "img/down_arrow.xpm"
#include "img/fityk.xpm"

using namespace std;


enum {
    ID_SHIST_LC             = 26100,
    ID_SHIST_UP                    ,
    ID_SHIST_DOWN                  ,
    ID_SHIST_CWSSR                 ,
    ID_SHIST_V                     , // and next 3
    ID_SHIST_V_   = ID_SHIST_V + 10,

    ID_DED_RADIO                   ,
    ID_DED_INACT_CB                ,
    ID_DED_TEXT                   
};


//=====================   data->history dialog  ==================

BEGIN_EVENT_TABLE(SumHistoryDlg, wxDialog)
    EVT_BUTTON      (ID_SHIST_UP,     SumHistoryDlg::OnUpButton)
    EVT_BUTTON      (ID_SHIST_DOWN,   SumHistoryDlg::OnDownButton)
    EVT_BUTTON      (ID_SHIST_CWSSR,  SumHistoryDlg::OnComputeWssrButton)
    EVT_LIST_ITEM_SELECTED  (ID_SHIST_LC, SumHistoryDlg::OnSelectedItem)
    EVT_LIST_ITEM_ACTIVATED (ID_SHIST_LC, SumHistoryDlg::OnActivatedItem)
    EVT_SPINCTRL    (ID_SHIST_V+0,    SumHistoryDlg::OnViewSpinCtrlUpdate)
    EVT_SPINCTRL    (ID_SHIST_V+1,    SumHistoryDlg::OnViewSpinCtrlUpdate)
    EVT_SPINCTRL    (ID_SHIST_V+2,    SumHistoryDlg::OnViewSpinCtrlUpdate)
    EVT_SPINCTRL    (ID_SHIST_V+3,    SumHistoryDlg::OnViewSpinCtrlUpdate)
    EVT_BUTTON      (wxID_CLOSE,      SumHistoryDlg::OnClose)
END_EVENT_TABLE()

SumHistoryDlg::SumHistoryDlg (wxWindow* parent, wxWindowID id)
    : wxDialog(parent, id, wxT("Parameters History"), 
               wxDefaultPosition, wxDefaultSize, 
               wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER), 
      lc(0)
{
    wxBoxSizer *top_sizer = new wxBoxSizer(wxVERTICAL);

    wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
    initialize_lc(); //wxListCtrl
    hsizer->Add (lc, 1, wxEXPAND);

    wxBoxSizer *arrows_sizer = new wxBoxSizer(wxVERTICAL);
    up_arrow = new wxBitmapButton (this, ID_SHIST_UP, wxBitmap (up_arrow_xpm));
    arrows_sizer->Add (up_arrow, 0);
    arrows_sizer->Add (10, 10, 1);
    down_arrow = new wxBitmapButton (this, ID_SHIST_DOWN, 
                                     wxBitmap (down_arrow_xpm));
    arrows_sizer->Add (down_arrow, 0);
    hsizer->Add (arrows_sizer, 0, wxALIGN_CENTER);
    top_sizer->Add (hsizer, 1, wxEXPAND);

    wxBoxSizer *buttons_sizer = new wxBoxSizer(wxHORIZONTAL);
    compute_wssr_button = new wxButton (this, ID_SHIST_CWSSR, 
                                        wxT("Compute WSSRs"));
    buttons_sizer->Add (compute_wssr_button, 0, wxALL, 5);
    buttons_sizer->Add (10, 10, 1);
    buttons_sizer->Add (new wxStaticText(this, -1, wxT("View parameters:")), 
                        0, wxALL|wxALIGN_CENTER, 5);
    for (int i = 0; i < 4; i++)
        buttons_sizer->Add (new SpinCtrl(this, ID_SHIST_V + i, view[i],
                                         0, view_max, 40),
                            0, wxALL, 5);
    buttons_sizer->Add (10, 10, 1);
    buttons_sizer->Add (new wxButton (this, wxID_CLOSE, wxT("&Close")), 
                        0, wxALL, 5);
    top_sizer->Add (buttons_sizer, 0, wxALIGN_CENTER);

    SetSizer (top_sizer);
    top_sizer->SetSizeHints (this);

    update_selection();
}

void SumHistoryDlg::initialize_lc()
{
    assert (lc == 0);
    view_max = ftk->get_parameters().size() - 1;
    assert (view_max != -1);
    for (int i = 0; i < 4; i++)
        view[i] = min (i, view_max);
    lc = new wxListCtrl (this, ID_SHIST_LC, 
                         wxDefaultPosition, wxSize(450, 250), 
                         wxLC_REPORT|wxLC_SINGLE_SEL|wxLC_HRULES|wxLC_VRULES
                             |wxSIMPLE_BORDER);
    lc->InsertColumn(0, wxT("No."));
    lc->InsertColumn(1, wxT("parameters"));
    lc->InsertColumn(2, wxT("WSSR"));
    for (int i = 0; i < 4; i++)
        lc->InsertColumn(3 + i, wxString::Format(wxT("par. %i"), view[i])); 

    FitMethodsContainer const* fmc = ftk->get_fit_container();
    for (int i = 0; i != fmc->get_param_history_size(); ++i) {
        add_item_to_lc(i, fmc->get_item(i));
    }
    for (int i = 0; i < 3+4; i++)
        lc->SetColumnWidth(i, wxLIST_AUTOSIZE);
}

void SumHistoryDlg::add_item_to_lc(int pos, vector<double> const& item)
{
    lc->InsertItem(pos, wxString::Format(wxT("  %i  "), pos));
    lc->SetItem (pos, 1, wxString::Format(wxT("%i"), item.size()));
    lc->SetItem (pos, 2, wxT("      ?      "));
    for (int j = 0; j < 4; j++) {
        int n = view[j];
        if (n < size(item))
            lc->SetItem(pos, 3 + j, wxString::Format(wxT("%g"), item[n]));
    }
}

void SumHistoryDlg::update_selection()
{
    FitMethodsContainer const* fmc = ftk->get_fit_container();
    int index = fmc->get_active_nr();
    lc->SetItemState (index, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, 
                             wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
    up_arrow->Enable (index != 0);
    down_arrow->Enable (index != fmc->get_param_history_size() - 1);
}

void SumHistoryDlg::OnUpButton (wxCommandEvent&)
{
    ftk->exec("fit undo");

    // undo can cause adding new item to the history
    FitMethodsContainer const* fmc = ftk->get_fit_container();
    int ps = fmc->get_param_history_size();
    if (lc->GetItemCount() == ps - 1)
        add_item_to_lc(ps - 1, fmc->get_item(ps - 1));
    assert(lc->GetItemCount() == ps);

    update_selection();
}

void SumHistoryDlg::OnDownButton (wxCommandEvent&)
{
    ftk->exec("fit redo");
    update_selection();
}

void SumHistoryDlg::OnComputeWssrButton (wxCommandEvent&)
{
    FitMethodsContainer const* fmc = ftk->get_fit_container();
    vector<double> const orig = ftk->get_parameters();
    vector<DataWithSum*> dsds;
    dsds.push_back(ftk->get_ds(ftk->get_active_ds_position()));
        //TODO dsds = ftk->get_dsds() if ...
    for (int i = 0; i != fmc->get_param_history_size(); ++i) {
        vector<double> const& item = fmc->get_item(i);
        if (item.size() == orig.size()) {
            double wssr = ftk->get_fit()->do_compute_wssr(item, dsds, true);
            lc->SetItem(i, 2, wxString::Format(wxT("%g"), wssr));
        }
    }
    lc->SetColumnWidth(2, wxLIST_AUTOSIZE);
}

void SumHistoryDlg::OnSelectedItem (wxListEvent&)
{
    update_selection();
}

void SumHistoryDlg::OnActivatedItem (wxListEvent& event)
{
    int n = event.GetIndex();
    ftk->exec("fit history " + S(n));
    update_selection();
}

void SumHistoryDlg::OnViewSpinCtrlUpdate (wxSpinEvent& event) 
{
    int v = event.GetId() - ID_SHIST_V;
    assert (0 <= v && v < 4);
    int n = event.GetPosition();
    assert (0 <= n && n <= view_max);
    view[v] = n;
    //update header in wxListCtrl
    wxListItem li;
    li.SetMask (wxLIST_MASK_TEXT);
    li.SetText(wxString::Format(wxT("par. %i"), n));
    lc->SetColumn(3 + v, li); 
    //update data in wxListCtrl
    FitMethodsContainer const* fmc = ftk->get_fit_container();
    for (int i = 0; i != fmc->get_param_history_size(); ++i) {
        vector<double> const& item = fmc->get_item(i);
        wxString s = n < size(item) ? wxString::Format(wxT("%g"), item[n]) 
                                    : wxString();
        lc->SetItem(i, 3 + v, s);
    }
}


//=====================    fit->run  dialog    ==================

BEGIN_EVENT_TABLE(FitRunDlg, wxDialog)
    EVT_BUTTON (wxID_OK, FitRunDlg::OnOK)
    EVT_SPINCTRL (-1, FitRunDlg::OnSpinEvent)
    EVT_CHOICE (-1, FitRunDlg::OnChangeDsOrMethod)
    EVT_RADIOBOX (-1, FitRunDlg::OnChangeDsOrMethod)
END_EVENT_TABLE()

FitRunDlg::FitRunDlg(wxWindow* parent, wxWindowID id, bool initialize)
    : wxDialog(parent, id, wxT("fit functions to data"),
               wxDefaultPosition, wxDefaultSize, 
               wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) 
{
    wxBoxSizer *top_sizer = new wxBoxSizer(wxVERTICAL);
    wxArrayString ds_choices;
    ds_choices.Add(wxT("active dataset ") + s2wx(frame->get_active_data_str()));
    ds_choices.Add(wxT("all datasets"));
    ds_rb = new wxRadioBox(this, -1, wxT("fit..."), 
                           wxDefaultPosition, wxDefaultSize,
                           ds_choices, 1, wxRA_SPECIFY_COLS);
    if (ftk->get_ds_count() == 1)
        ds_rb->Enable(1, false);
    top_sizer->Add(ds_rb, 0, wxALL|wxEXPAND, 5);
    wxBoxSizer *method_sizer = new wxBoxSizer(wxHORIZONTAL);
    method_sizer->Add(new wxStaticText(this, -1, wxT("method:")), 
                      0, wxALL|wxALIGN_CENTER_VERTICAL, 5);
    wxArrayString m_choices;
    m_choices.Add(wxT("Levenberg-Marquardt")); 
    m_choices.Add(wxT("Nelder-Mead simplex"));
    m_choices.Add(wxT("Genetic Algorithm")); 
    method_c = new wxChoice(this, -1, wxDefaultPosition, wxDefaultSize,
                            m_choices);
    int method_nr = ftk->get_settings()->get_e("fitting-method");
    method_c->SetSelection(method_nr);
    method_sizer->Add(method_c, 0, wxALL, 5);
    top_sizer->Add(method_sizer, 0);

    wxFlexGridSizer *max_sizer = new wxFlexGridSizer(2, 3, 0, 0);
    max_sizer->Add(new wxStaticText(this, -1, wxT("max. iterations")),
                   0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5);
    maxiter_sc = new SpinCtrl(this, -1, 0, 0, 999999, 70);
    max_sizer->Add(maxiter_sc, 0, wxALL, 5);
    nomaxiter_st = new wxStaticText(this, -1, wxT("(unlimited)"));
    max_sizer->Add(nomaxiter_st, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5);
    max_sizer->Add(new wxStaticText(this, -1, wxT("max. WSSR evaluations")),
                   0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5);
    int default_max_eval = ftk->get_settings()->get_i("max-wssr-evaluations"); 
    maxeval_sc = new SpinCtrl(this, -1, default_max_eval, 0, 999999, 70);
    max_sizer->Add(maxeval_sc, 0, wxALL, 5);
    nomaxeval_st = new wxStaticText(this, -1, wxT("(unlimited)"));
    max_sizer->Add(nomaxeval_st, 0, wxALIGN_CENTER_VERTICAL, 0);
    top_sizer->Add(max_sizer, 0);
    
    initialize_cb = new wxCheckBox(this, -1, wxT("initialize method"));
    initialize_cb->SetValue(initialize);
    top_sizer->Add(initialize_cb, 0, wxALL, 5);

    bool autop = (ftk->get_settings()->getp("autoplot") == "on-fit-iteration");
    autoplot_cb = new wxCheckBox(this, -1, 
                                 wxT("refresh plot after each iteration"));
    autoplot_cb->SetValue(autop); 
    top_sizer->Add(autoplot_cb, 0, wxALL, 5);


    top_sizer->Add(new wxStaticLine(this, -1), 0, wxEXPAND|wxLEFT|wxRIGHT, 10);
    top_sizer->Add(CreateButtonSizer(wxOK|wxCANCEL), 
                   0, wxALL|wxALIGN_CENTER, 5);
    SetSizerAndFit(top_sizer);
    update_allow_continue();
    update_unlimited();
}

void FitRunDlg::update_allow_continue()
{
    initialize_cb->SetValue(true);
    bool is_initialized;
    int m_sel = method_c->GetSelection();
    //use "::Fit", because Fit is also a method of wxDialog
    ::Fit const* f = ftk->get_fit_container()->get_method(m_sel);
    if (ds_rb->GetSelection() == 0) {
        DataWithSum const* ds = ftk->get_ds(ftk->get_active_ds_position());
        is_initialized = f->is_initialized(ds);
    }
    else {
        is_initialized = f->is_initialized(ftk->get_dsds());
    }
    initialize_cb->Enable(is_initialized);
}

void FitRunDlg::update_unlimited()
{
    nomaxeval_st->Show(maxeval_sc->GetValue() == 0);
    nomaxiter_st->Show(maxiter_sc->GetValue() == 0);
}

void FitRunDlg::OnOK(wxCommandEvent&)
{
    string cmd;
    int m = method_c->GetSelection();
    if (m != ftk->get_settings()->get_e("fitting-method"))
        cmd += "with fitting-method=" 
            + ftk->get_fit_container()->get_method(m)->name + " ";

    bool autop = (ftk->get_settings()->getp("autoplot") == "on-fit-iteration");
    if (autoplot_cb->GetValue() != autop) {
        cmd += string(cmd.empty() ? "with" : ",") + " autoplot=";
        cmd += (autoplot_cb->GetValue() ? "on-fit-iteration " 
                                        : "on-plot-change ");
    }

    int max_eval = maxeval_sc->GetValue();
    if (max_eval != ftk->get_settings()->get_i("max-wssr-evaluations")) 
        cmd += (cmd.empty() ? "with" : ",") 
                + string(" max-wssr-evaluations=") + S(max_eval) + " ";

    bool ini = initialize_cb->GetValue();
    cmd +=  ini ? "fit " : "fit+ ";

    int max_iter = maxiter_sc->GetValue();
    if (max_iter > 0)
        cmd += S(max_iter);

    if (ini) {
        if (ds_rb->GetSelection() == 0)
            cmd += frame->get_in_dataset();
        else
            cmd += " in @*";
    }

    Show(false);
    ftk->exec(cmd);
    close_it(this, wxID_OK);
}



/// show "Export data" dialog
bool export_data_dlg(wxWindow *parent, bool load_exported)
{
    static wxString dir = wxConfig::Get()->Read(wxT("/exportDir"));
    string columns = "";
    string ds = frame->get_active_data_str();
    if (!load_exported) {
        DataExportDlg ded(parent, -1, ds);
        if (ded.ShowModal() != wxID_OK)
            return false;
        columns = " (" + ded.get_columns() + ")";
    }
    wxFileDialog fdlg (parent, wxT("Export data to file"), dir, wxT(""),
                       wxT("x y data (*.dat, *.xy)|*.dat;*.DAT;*.xy;*.XY"),
                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
    dir = fdlg.GetDirectory();
    if (fdlg.ShowModal() == wxID_OK) {
        string path = wx2s(fdlg.GetPath());
        ftk->exec(ds + columns + " > '" + path + "'");
        if (load_exported)
            ftk->exec(ds + " < '" + path + "'");
        return true;
    }
    else
        return false;
}
//======================================================================
//                         DataExportDlg
//======================================================================
BEGIN_EVENT_TABLE(DataExportDlg, wxDialog)
    EVT_BUTTON(wxID_OK, DataExportDlg::OnOk)
    EVT_RADIOBOX(ID_DED_RADIO, DataExportDlg::OnRadioChanged)
    EVT_CHECKBOX(ID_DED_INACT_CB, DataExportDlg::OnInactiveChanged)
    EVT_TEXT(ID_DED_TEXT, DataExportDlg::OnTextChanged)
END_EVENT_TABLE()

DataExportDlg::DataExportDlg(wxWindow* parent, wxWindowID id, 
                             std::string const& ds)
    : wxDialog(parent, id, wxT("Export data/functions as points"), 
               wxDefaultPosition, wxSize(600, 500), 
               wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
{
    wxBoxSizer *top_sizer = new wxBoxSizer(wxVERTICAL);
    wxStaticText *st1 = new wxStaticText(this, -1, 
                                         wxT("       Step 1: Select columns"));
    top_sizer->Add(st1, 0, wxTOP|wxLEFT|wxRIGHT, 5);
    wxStaticText *st2 = new wxStaticText(this, -1, 
                                         wxT("       Step 2: Choose a file"));
    st2->Enable(false);
    top_sizer->Add(st2, 0, wxALL, 5);
    wxArrayString choices;
    choices.Add(wxT("x, y"));
    cv.Add(wxT("x, y"));
    choices.Add(wxT("x, y, sigma"));
    cv.Add(wxT("x, y, s"));
    choices.Add(wxT("x, sum"));
    cv.Add(wxT("x, ") + s2wx(ds) + wxT(".F(x)"));
    choices.Add(wxT("x, sum, zero shift"));
    cv.Add(wxT("x, ") + s2wx(ds) + wxT(".F(x), ") + s2wx(ds) + wxT(".Z(x)"));
    choices.Add(wxT("x, y, sigma, sum"));
    cv.Add(wxT("x, y, s, ") + s2wx(ds) + wxT(".F(x)"));
    choices.Add(wxT("x, y, sigma, sum, all functions..."));
    cv.Add(wxT("x, y, s, ") + s2wx(ds) + wxT(".F(x), *F(x)"));
    choices.Add(wxT("x, y, sigma, sum, residual"));
    cv.Add(wxT("x, y, s, ") + s2wx(ds) + wxT(".F(x), y-") + s2wx(ds) 
            + wxT(".F(x)"));
    choices.Add(wxT("x, y, sigma, weighted residual"));
    cv.Add(wxT("x, y, s, (y-") + s2wx(ds) + wxT(".F(x))/s"));
    choices.Add(wxT("custom"));
    rb = new wxRadioBox(this, ID_DED_RADIO, wxT("exported columns"),
                        wxDefaultPosition, wxDefaultSize, choices,
                        2, wxRA_SPECIFY_COLS);
    top_sizer->Add(rb, 0, wxALL|wxEXPAND, 5);
    inactive_cb = new wxCheckBox(this, ID_DED_INACT_CB, 
                                 wxT("export also inactive points"));
    top_sizer->Add(inactive_cb, 0, wxALL, 5);
    text = new wxTextCtrl(this, ID_DED_TEXT, wxT(""));
    top_sizer->Add(text, 0, wxEXPAND|wxALL, 5);
    top_sizer->Add (new wxStaticLine(this, -1), 0, wxEXPAND|wxLEFT|wxRIGHT, 5);
    top_sizer->Add(CreateButtonSizer(wxOK|wxCANCEL), 
                   0, wxALL|wxALIGN_CENTER, 5);
    SetSizerAndFit(top_sizer);

    //read settings
    wxString t = wxConfig::Get()->Read(wxT("/exportDataCols"), wxT("x, y, s"));
    for (size_t i = 0; i < cv.GetCount(); ++i) {
        if (t == cv[i]) {
            rb->SetSelection(i);
            inactive_cb->SetValue(false);
            on_widget_change();
            return;
        }
        else if (t == cv[i]+wxT(", a")) {
            rb->SetSelection(i);
            inactive_cb->SetValue(true);
            on_widget_change();
            return;
        }
    }
    rb->SetSelection(cv.GetCount());
    text->SetValue(t);
    text->MarkDirty();
}

void DataExportDlg::on_widget_change()
{
    int n = rb->GetSelection();
    bool is_custom = (n == (int) cv.GetCount());
    if (!is_custom) {
        text->SetValue(cv[n] + (inactive_cb->GetValue() ? wxT(", a"):wxT("")));
        FindWindow(wxID_OK)->Enable(true);
    }
    text->Enable(is_custom);
    inactive_cb->Enable(!is_custom);
}

void DataExportDlg::OnTextChanged(wxCommandEvent&) 
{ 
    if (!text->IsModified())
        return;
    vector<string> cols = split_string(wx2s(text->GetValue()), ",");
    bool has_a = false;
    bool parsable = true;
    for (vector<string>::const_iterator i = cols.begin(); i != cols.end(); ++i){
        string t = strip_string(*i);
        if (t == "a")
            has_a = true;
        if (!(startswith(t, "*F(") && *(t.end()-1) == ')' 
                    && validate_data_expression(string(t, 3, t.size()-4))
              || validate_data_expression(t)))
            parsable = false;
    }
    FindWindow(wxID_OK)->Enable(parsable);
    inactive_cb->SetValue(has_a);
}

void DataExportDlg::OnOk(wxCommandEvent& event) 
{
    wxConfig::Get()->Write(wxT("/exportDataCols"), text->GetValue());
    event.Skip();
}


//===============================================================
//                         AboutDlg   
//===============================================================

BEGIN_EVENT_TABLE (AboutDlg, wxDialog)
    EVT_TEXT_URL (wxID_ANY, AboutDlg::OnTextURL)
END_EVENT_TABLE()

AboutDlg::AboutDlg(wxWindow* parent)
    : wxDialog(parent, -1, wxT("About Fityk"), wxDefaultPosition, 
               wxSize(350,400), wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
{
    wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
    sizer->Add(new wxStaticBitmap(this, -1, wxBitmap(fityk_xpm)),
               0, wxALIGN_CENTER|wxALL, 5);
    wxStaticText *name = new wxStaticText(this, -1, 
                                          wxT("fityk ") + pchar2wx(VERSION));
    name->SetFont(wxFont(18, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
                         wxFONTWEIGHT_BOLD));
    sizer->Add(name, 0, wxALIGN_CENTER|wxALL, 5); 
    txt = new wxTextCtrl(this, -1, wxT(""), wxDefaultPosition, wxSize(350,250), 
                         wxTE_MULTILINE|wxTE_RICH2|wxNO_BORDER
                             |wxTE_READONLY|wxTE_AUTO_URL);
    txt->SetBackgroundColour(GetBackgroundColour());
    txt->SetDefaultStyle(wxTextAttr(wxNullColour, wxNullColour, wxNullFont,
                                    wxTEXT_ALIGNMENT_CENTRE));
    
    txt->AppendText(wxT("A curve fitting and data analysis program\n\n"));
    txt->SetDefaultStyle(wxTextAttr(wxNullColour, wxNullColour, 
                                    *wxITALIC_FONT));
    txt->AppendText(wxT("powered by ") wxVERSION_STRING wxT("\n"));
    txt->AppendText(wxString::Format(wxT("powered by Boost.Spirit %d.%d.%d\n"), 
                                       SPIRIT_VERSION / 0x1000,
                                       SPIRIT_VERSION % 0x1000 / 0x0100,
                                       SPIRIT_VERSION % 0x0100));
    txt->SetDefaultStyle(wxTextAttr(wxNullColour, wxNullColour, 
                                    *wxNORMAL_FONT));
    txt->AppendText(wxT("\nCopyright (C) 2001 - 2007 Marcin Wojdyr\n\n"));
    txt->SetDefaultStyle(wxTextAttr(*wxBLUE));
    txt->AppendText(wxT("http://www.unipress.waw.pl/fityk/\n\n"));
    txt->SetDefaultStyle(wxTextAttr(*wxBLACK));
    txt->AppendText(wxT("This program is free software; ")
      wxT("you can redistribute it ")
      wxT("and/or modify it under the terms of the GNU General Public ")
      wxT("License, version 2, as published by the Free Software Foundation"));
    sizer->Add (txt, 1, wxALL|wxEXPAND|wxFIXED_MINSIZE, 5);
    //sizer->Add (new wxStaticLine(this, -1), 0, wxEXPAND|wxLEFT|wxRIGHT, 10);
    wxButton *bu_ok = new wxButton (this, wxID_OK, wxT("OK"));
    bu_ok->SetDefault();
    sizer->Add (bu_ok, 0, wxALL|wxEXPAND, 10);
    SetSizerAndFit(sizer);
}

void AboutDlg::OnTextURL(wxTextUrlEvent& event) 
{
    if (!event.GetMouseEvent().LeftDown()) {
        event.Skip();
        return;
    }
    long start = event.GetURLStart(),
         end = event.GetURLEnd();
    wxString url = txt->GetValue().Mid(start, end - start);
    wxLaunchDefaultBrowser(url);
}


//===============================================================
//                         MergePointsDlg
//===============================================================

BEGIN_EVENT_TABLE (MergePointsDlg, wxDialog)
    EVT_LIST_ITEM_SELECTED(-1, MergePointsDlg::OnDataSelChanged)
    EVT_LIST_ITEM_DESELECTED(-1, MergePointsDlg::OnDataSelChanged)
    EVT_CHECKBOX(-1, MergePointsDlg::OnCheckBox)
END_EVENT_TABLE()

MergePointsDlg::MergePointsDlg(wxWindow* parent, wxWindowID id)
    : wxDialog(parent, id, wxT("Merge data points"),
               wxDefaultPosition, wxDefaultSize, 
               wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) 
{
    wxBoxSizer *top_sizer = new wxBoxSizer(wxVERTICAL);
    d = new DataListPlusText(this, -1, -1, 
                         vector3(pair<string,int>("No", 50),
                                 pair<string,int>("Name", 200),
                                 pair<string,int>("File", 0)));
    d->split(0.6);
    d->update_data_list(true, false);
    update_info();
    top_sizer->Add(d, 1, wxEXPAND|wxALL, 1);
    wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL); 
    dx_cb = new wxCheckBox(this, -1, wxT("merge points with |x1-x2|<"));
    dx_cb->SetValue(true);
    hsizer->Add(dx_cb, 0, wxALIGN_CENTER_VERTICAL);
    dx_val = new RealNumberCtrl(this, -1, ftk->get_settings()->getp("epsilon"));
    hsizer->Add(dx_val, 0);
    top_sizer->Add(hsizer, 0, wxALL, 5);
    y_rb = new wxRadioBox(this, -1, wxT("set y as ..."), 
                          wxDefaultPosition, wxDefaultSize, 
                          make_wxArrStr(wxT("sum"), wxT("avg")),
                          1, wxRA_SPECIFY_ROWS);
    top_sizer->Add(y_rb, 0, wxEXPAND|wxALL, 5);
    active_ds = ftk->get_active_ds_position();
    wxString this_ds = wxString::Format(wxT("dataset @%d"), active_ds);
    output_rb = new wxRadioBox(this, -1, wxT("output to ..."), 
                               wxDefaultPosition, wxDefaultSize, 
                               make_wxArrStr(this_ds, wxT("new dataset")),
                               1, wxRA_SPECIFY_ROWS);
    top_sizer->Add(output_rb, 0, wxEXPAND|wxALL, 5);
    top_sizer->Add (new wxStaticLine(this, -1), 0, wxEXPAND|wxLEFT|wxRIGHT, 5);
    top_sizer->Add(CreateButtonSizer(wxOK|wxCANCEL), 
                   0, wxALL|wxALIGN_CENTER, 5);
    SetSizerAndFit(top_sizer);
}

void MergePointsDlg::update_info()
{
    int i = d->list->GetFirstSelected();
    if (i == -1) {
        d->inf->Clear();
        //TODO disable OK button
        return;
    }
    Data const* data = ftk->get_data(i);
    fp x_min = data->get_x_min();
    fp x_max = data->get_x_max();
    int n = data->points().size();
    wxString dstr = wxString::Format(wxT("@%d"), i);
    bool only_one = true;
    while ((i = d->list->GetNextSelected(i)) != -1) {
        only_one = false;
        data = ftk->get_data(i);
        if (data->get_x_min() < x_min)
            x_min = data->get_x_min();
        if (data->get_x_max() > x_max)
            x_max = data->get_x_max();
        n += data->points().size();
        dstr += wxString::Format(wxT(" @%d"), i);
    }
    wxString s = wxString::Format(wxT("%i data points from: "), n) + dstr;
    s += wxString::Format(wxT("\nx in range (%g, %g)"), x_min, x_max);
    if (only_one && data->get_x_step() != 0.)
        s += wxString::Format(wxT("\nfixed step: %g"), data->get_x_step());
    else
        s += wxString::Format(wxT("\naverage step: %g"), (x_max-x_min) / (n-1));
    d->inf->SetValue(s);
}

string MergePointsDlg::get_command()
{
    string s;
    if (dx_cb->GetValue())
        s += "with epsilon=" + wx2s(dx_val->GetValue()) + " ";
    s += (output_rb->GetSelection() == 0 ? "@"+S(active_ds) : S("@+")) + " = ";
    if (dx_cb->GetValue())
        s += y_rb->GetSelection() == 0 ? "sum_same_x " : "avg_same_x ";
    s += join_vector(d->get_selected_data(), " + ");
    return s;
}




syntax highlighted by Code2HTML, v. 0.9.1