static const char* szModule = "FEditSets.cpp";

//----------------------------------------------------------------------------//
//    module FEditSets.cpp                                                    //
//                                                                            //
//    Form to edit the parameter sets for the parameter tuning                //
//                                                                            //
//    copyright (c) 2003 by Lars Haendel                                      //
//    home: www.newty.de                                                      //
//                                                                            //
//    This program is free software and can be used under the terms of the    //
//    GNU licence. See header file for further information and disclaimer.    //
//                                                                            //
//----------------------------------------------------------------------------//


#include <vcl.h>
#pragma hdrstop

#pragma package(smart_init)
#pragma resource "*.dfm"

#include <htmlhelp>        // due to   HTML help


//----------------------------------------------------------------------------------------------------------------------
#include "FEditSets.h"
#include "para.h"          // due to   default parameters
#include "fileutil.h"      //          flag to string
#include "FMain.h"         //          TFMain

#define MAX_TASKS 20       // maximal # tasks

static char text[256];

int ParseParameters(const char*const& szPar, float*& para, const float& defVal, const char*const& szName
                     , const float& min, const bool f_Integer/*=false*/, const float& max/*=MAXFLOAT*/);



//----------------------------------------------------------------------------------------------------------------------
// check if parameter string is valid, else ask the user to restore defaults or to re-edit string
bool CheckParameterString(const bool& f_OnlyOne, AnsiString& szPara, const AnsiString& szName, const float& defVal,
      const float& min, const bool& f_Integer, const float& max)
{
   AnsiString szErrText = "Error in parameter '" + szName + "'";     // compose message box caption

   bool f_restore=false;   // flag: restore default value
   float* para;            // dummy parameter array, needed for ParseParameters(), will be deleted afterwards
   try{
      ParseParameters(szPara.c_str(), para, -1, szName.c_str(), min, f_Integer, max);}          // parse
   catch(TExceptionU excp)                                                                      // error handling
   {
      sprintf(text, "%s Use default value?", excp.GetErrorText());                              // compose error text

      // ask user to use defaults or to try again
      if(Application->MessageBox(text, szErrText.c_str(), MB_YESNO | MB_ICONERROR)== ID_YES)
         f_restore = true;                                                                      // set restore flag
      else
      {
         delete[] para;                                                                         // try again ...
         return true;                                                                           // keep focus
      }
   }


   // if the string is empty, i.e. does not contain any parameter ... display error message and set restore flag
   if(szPara.IsEmpty()){
      // message: no parameter specified: using defaults
      Application->MessageBox("No parameter(s) specified! Using default value!", szErrText.c_str(), MB_OK|MB_ICONERROR);
      f_restore = true;}


   // if flag is set ... restore default value
   const bool f_boolean = (f_Integer && min==0 && max==1);
   if(f_restore)
      if(f_boolean && !f_OnlyOne)               // default value is boolean ...
         szPara = FlagToString(defVal);         // ... initialize string with 'true' ore 'false'
      else
         szPara = ValueToText1(defVal, 0, PREC_PARA);   // else: set default value 


   // if parameter is 'true' or 'false' -> change to '0/1' if 'f_OnlyOne' is set
   if(f_OnlyOne)
      if(strcmp("true", szPara.c_str())==0)
         szPara = 1;
      else
         if(strcmp("false", szPara.c_str())==0)
            szPara = 0;


   delete[] para;       // release dummy parameter array
   return f_restore;    //
}



TFEditSets *FEditSets;

//----------------------------------------------------------------------------------------------------------------------
// initialize controls
void __fastcall TFEditSets::FormShow(TObject *Sender)
{
   // pre-check
   IfTrueThrowTypeA(!data, "No learn data object associated!", "TFEditSets::FormShow", szModule);

   RbNormal->Checked = true;        // initialize task wizard radio buttons
   if(BtWizard->Enabled)            // set focus ...
      ActiveControl = BtWizard;     // if possible to wizard button ...
   else
      ActiveControl = BtOk;         // ... and else to Button 'Ok'



   // a) disable/enable parameter edit fields and descriptions depending on task type (regression or classification)
   ESigma->Visible    = prj->Regression();
   LaSigma->Visible   = prj->Regression();
   ESpecial->Visible  = !prj->Regression();
   LaSpecial->Visible = !prj->Regression();
   EnableTaskEditing(CbEnableEditing->Checked);       // enable/disable editing fields and buttons


   // b) set column names in list view
   LvTasks->Columns->Clear();                         // clear all columns

   LvTasks->Columns->Add()->Caption = "Task";
   LvTasks->Columns->Add()->Caption = "Sets";
   LvTasks->Columns->Add()->Caption = "Learn";
   LvTasks->Columns->Add()->Caption = "Intervals";
   LvTasks->Columns->Add()->Caption = "Anti COD";
   LvTasks->Columns->Add()->Caption = "Eta";
   LvTasks->Columns->Add()->Caption = "Min. rule mass";

   if(prj->Regression())
      LvTasks->Columns->Add()->Caption = "Sigma";
   else
      LvTasks->Columns->Add()->Caption = "Kernel width";

   LvTasks->Columns->Add()->Caption = "Weights";
   LvTasks->Columns->Add()->Caption = "Prune";
   LvTasks->Columns->Add()->Caption = "Euclidean";


   // set column width and enable auto sizing
   int w = LvTasks->Width/LvTasks->Columns->Count;              // width of each column
   for(int i=0;i<LvTasks->Columns->Count;i++)
   {
      LvTasks->Columns->Items[i]->AutoSize = true;
      LvTasks->Columns->Items[i]->Width = w;
   }

   // c) transfer tasks to task list view
   LvTasks->Items->Clear();                       // delete all
   for(int i=0;i<prj->nTasks();i++)                // for all tasks
      AddTaskToListView(&(prj->GetTask(i)));


   DisplaySetCount();   // ... and display it
   SetDefaults();       // initialize parameters (edit task group box) with default values

   f_SomethingChanged = false;
}


//----------------------------------------------------------------------------------------------------------------------
// response to button 'Ok': close window
void __fastcall TFEditSets::BtOkClick(TObject *Sender)
{
   Close();
}


//----------------------------------------------------------------------------------------------------------------------
// check box 'Enable task editing' clicked:
void __fastcall TFEditSets::CbEnableEditingClick(TObject *Sender)
{
   EnableTaskEditing(CbEnableEditing->Checked);    // enable/disable editing fields and buttons
}


//----------------------------------------------------------------------------------------------------------------------
// enable/disable task editing
void TFEditSets::EnableTaskEditing(const bool& f_Enable)
{
   // labels
   LaIntervals  ->Enabled = f_Enable && prj->Regression();     // enable only for regression tasks
   LaWmin       ->Enabled = f_Enable;
   LaEta        ->Enabled = f_Enable;
   LaSpecial    ->Enabled = f_Enable;
   LaSigma      ->Enabled = f_Enable;
   LaCardinality->Enabled = f_Enable;
   LaPrune      ->Enabled = f_Enable;
   LaWeights    ->Enabled = f_Enable;
   LaEuclidean  ->Enabled = f_Enable;

   // edit fields
   EIntervals  ->Enabled = f_Enable && prj->Regression();      // enable only for regression tasks
   EWmin       ->Enabled = f_Enable;
   EEta        ->Enabled = f_Enable;
   ESpecial    ->Enabled = f_Enable;
   ESigma      ->Enabled = f_Enable;
   ECardinality->Enabled = f_Enable;
   EPrune      ->Enabled = f_Enable;
   EWeights    ->Enabled = f_Enable;
   EEuclidean  ->Enabled = f_Enable;

   // buttons
   BtDefaults->Enabled = f_Enable;
   BtAccept  ->Enabled = f_Enable;
   BtEdit    ->Enabled = f_Enable && LvTasks->Selected;          // enable only if list item (task) is selected
   BtDelete  ->Enabled = f_Enable && LvTasks->Selected;
   BtClearAll->Enabled = f_Enable && (LvTasks->Items->Count!=0); // enable only if list contains tasks

   // wizard (disable if other controls get enabled)
   RbFast      ->Enabled = !f_Enable;
   RbNormal    ->Enabled = !f_Enable;
   RbExhaustive->Enabled = !f_Enable;
   BtWizard    ->Enabled = !f_Enable;
}


//----------------------------------------------------------------------------------------------------------------------
// add given task to list view
void TFEditSets::AddTaskToListView(const TTask*const& task)
{
   TListItem* item = LvTasks->Items->Add();    // add new list item

   // copy parameter strings to list view
   item->Caption = LvTasks->Items->Count;                        // task id
   item->SubItems->Add(AnsiString(task->nSets));                  // # runs (all)
   item->SubItems->Add(AnsiString(task->nSetsLearn));             // # runs (learn)

   item->SubItems->Add(task->szN_Int);                            // 'Intervals'
   item->SubItems->Add(task->szW_COD);                            // 'w_COD'
   item->SubItems->Add(task->szEta);                              // 'Eta'

   item->SubItems->Add(task->szP_Min);                            // 'min. cuboid mass'

   if(prj->Regression())                                          // type specific parameters
      item->SubItems->Add(task->szSigma);                         // 'Sigma'
   else
      item->SubItems->Add(task->szW_Kernel);                      // kernel width

   item->SubItems->Add(task->szWeights);                          // 'Use Weights'
   item->SubItems->Add(task->szPrune);                            // 'Prune Cuboids'
   item->SubItems->Add(task->szMetric);                           // 'Euclidean Distance'

   f_SomethingChanged = true;
}


//----------------------------------------------------------------------------------------------------------------------
// initialize parameters (edit task group box) with default values
void TFEditSets::SetDefaults()
{
   // other parameters
   if(prj->Regression())
      EIntervals->Text = ValueToText1(para->N_Int);                              // # output intervals
   else
      EIntervals->Text = ValueToText1(prj->nClasses());                          // # classes

   EWmin        ->Text = ValueToText1(para->w_COD, 0, PREC_PARA);                // w_COD
   EEta         ->Text = ValueToText1(para->Eta);                                // eta
   ECardinality ->Text = ValueToText1(para->p_min);                              // min. cuboid mass
   ESigma       ->Text = ValueToText1(para->Sigma, 0, PREC_PARA);                // sigma
   ESpecial     ->Text = ValueToText1(para->W_Kernel);                           // special (kernel width)


   // boolean parameters: initialize check boxes
   EWeights  ->Text = FlagToString(para->Weights);                               // use weights
   EPrune    ->Text = FlagToString(para->Prune);                                 // prune cuboids
   EEuclidean->Text = FlagToString(para->Metric==2);                             // euclidean distance
}


//----------------------------------------------------------------------------------------------------------------------
// display total number of parameter sets
void TFEditSets::DisplaySetCount()
{
   // display # runs
   LaSetCount1->Caption = "You've defined " + AnsiString(prj->nParaSets()) + " parameter set(s).";
   LaSetCount2->Caption = AnsiString(prj->nParaSetsLearn()) + " times must be learned!";
}


//----------------------------------------------------------------------------------------------------------------------
// call SetDefaults() which will initialize all parameters (edit task group box) with default values
void __fastcall TFEditSets::BtDefaultsClick(TObject *Sender){
   SetDefaults();}


//----------------------------------------------------------------------------------------------------------------------
// enable buttons 'Edit' and 'Delete' (accepted tasks group box) if an list item (task) is selected
void __fastcall TFEditSets::LvTasksSelectItem(TObject*, TListItem*, bool Selected){
   BtEdit  ->Enabled = Selected && CbEnableEditing->Checked;
   BtDelete->Enabled = Selected && CbEnableEditing->Checked;}



//----------------------------------------------------------------------------------------------------------------------
// initialize tasks using a heuristic approach
void __fastcall TFEditSets::BtWizardClick(TObject *Sender)
{
   // a) initialize
   DeleteAllTasks();                                                       // delete all existing tasks

   TTask task;
   task.szSigma[0] = task.szW_Kernel[0] = '\0';    // terminate (eventually) unused parameter strings


   // b) initialize task strings

   //-------------------------------------------------------------------------------------------------------------------
   // b1) fast
   if(RbFast->Checked)
   {
      strcpy(task.szEta, "0.2");                                        // 'Eta'


      float w1, w2, w3;                                                 // 'w_COD'
      w2 = para->w_COD;
      w1 = floor(0.5 + 10*w2*0.66)/10;                                  // round to one digit precision
      w3 = floor(0.5 + 10*w2*1.33)/10;

      if(w2-w1 >= 0.6)      // if values differ by at least 0.6 ...
      {
         strcpy(task.szW_COD, ValueToText1(w1, 0, PREC_PARA));          // ... copy first value and ...
         strcat(task.szW_COD, " ");
         strcat(task.szW_COD, ValueToText1(w2, 0, PREC_PARA));          // ... add 2nd value and ...

         if(w3 <= Max_w_COD(data))
         {
            strcat(task.szW_COD, " ");
            strcat(task.szW_COD, ValueToText1(w3, 0, PREC_PARA));       // ... add third value if it's not to high
         }
      }
      else
         strcpy(task.szW_COD, ValueToText1(w2, 0, PREC_PARA));          // else: use only 2nd value



      if(prj->Regression())
      {
         strcpy(task.szN_Int, "10 15");                                 // 'Intervals' (regression)
         strcpy(task.szSigma, "0.001");                                 // 'Sigma'
      }
      else
      {
         sprintf(task.szN_Int, "%d", prj->nClasses());                  // 'Intervals' (classification)
         strcpy(task.szW_Kernel, "1 3");                                // kernel width
      }

      strcpy(task.szP_Min,    "2 4");                                   // 'min. cuboid mass'
      strcpy(task.szPrune,    "true");                                  // 'Prune Cuboids'
      strcpy(task.szWeights,  "true");                                  // 'Use Weights'
      strcpy(task.szMetric,   "1");                                     // 'Euclidean Distance'
   }

   //-------------------------------------------------------------------------------------------------------------------
   // b2) normal
   if(RbNormal->Checked)
   {
      strcpy(task.szEta, "0.2 0.5");                                    // 'Eta'


      float w1, w2, w3;                                                 // 'w_COD'
      w2 = para->w_COD;
      w1 = floor(0.5 + 10*w2*0.66)/10;                                  // round to one digit precision
      w3 = floor(0.5 + 10*w2*1.33)/10;

      if(w2-w1 >= 0.4)      // if values differ by at least 0.4 ...
      {
         strcpy(task.szW_COD, ValueToText1(w1, 0, PREC_PARA));          // ... copy first value and ...
         strcat(task.szW_COD, " ");
         strcat(task.szW_COD, ValueToText1(w2, 0, PREC_PARA));          // ... add 2nd value and ...

         if(w3 <= Max_w_COD(data))
         {
            strcat(task.szW_COD, " ");
            strcat(task.szW_COD, ValueToText1(w3, 0, PREC_PARA));       // ... add third value if it's not to high
         }
      }
      else
         strcpy(task.szW_COD, ValueToText1(w2, 0, PREC_PARA));          // else: use only 2nd value


      if(prj->Regression())
      {
         strcpy(task.szN_Int, "10 15");                                 // 'Intervals' (regression)
         strcpy(task.szSigma, "0.001 0.01");                            // 'Sigma'
      }
      else
      {
         sprintf(task.szN_Int, "%d", prj->nClasses());                  // 'Intervals' (classification)
         strcpy(task.szW_Kernel, "1 2 3");                              // kernel width
      }

      strcpy(task.szP_Min,    "2 4 7");                                 // 'min. cuboid mass'
      strcpy(task.szPrune,    "true false");                            // 'Prune Cuboids'
      strcpy(task.szWeights,  "true");                                  // 'Use Weights'
      strcpy(task.szMetric,   "1");                                     // 'Euclidean Distance'
   }


   //-------------------------------------------------------------------------------------------------------------------
   // b3) exhaustive
   if(RbExhaustive->Checked)
   {
      strcpy(task.szEta, "0.2 0.5 0.7");                                // 'Eta'

      float w1, w2, w3, w4;                                             // 'w_COD'
      w3 = para->w_COD;
      w1 = floor(0.5 + 10*w3*0.33)/10;                                  // round to one digit precision
      w2 = floor(0.5 + 10*w3*0.66)/10;
      w4 = floor(0.5 + 10*w3*1.33)/10;


      if(w3-w1 >= 0.4)      // if values differ by at least 0.4 ...
      {
         strcpy(task.szW_COD, ValueToText1(w1, 0, PREC_PARA));          // ... copy first value and ...
         strcat(task.szW_COD, " ");
         strcat(task.szW_COD, ValueToText1(w2, 0, PREC_PARA));          // ... add 2nd value and ...
         strcat(task.szW_COD, " ");
         strcat(task.szW_COD, ValueToText1(w3, 0, PREC_PARA));          // ... add third value and ...


         if(w3 <= Max_w_COD(data))
         {
            strcat(task.szW_COD, " ");
            strcat(task.szW_COD, ValueToText1(w4, 0, PREC_PARA));       // ... add fourth value if it's not to high
         }
      }
      else
         strcpy(task.szW_COD, ValueToText1(w3, 0, PREC_PARA));          // else: use only third value



      if(prj->Regression())
      {
         strcpy(task.szN_Int, "10 15 20");                              // 'Intervals' (regression)
         strcpy(task.szSigma, "0.001 0.01");                            // 'Sigma'
      }
      else
      {
         sprintf(task.szN_Int, "%d", prj->nClasses());                  // 'Intervals' (classification)
         strcpy(task.szW_Kernel, "1 2 3");                              // kernel width
      }

      strcpy(task.szP_Min,    "2 4 7");                                 // 'min. cuboid mass'
      strcpy(task.szPrune,    "true false");                            // 'Prune Cuboids'
      strcpy(task.szWeights,  "true false");                            // 'Use Weights'
      strcpy(task.szMetric,   "1");                                     // 'Euclidean Distance'
   }


   // c) add new task to project
   try
   {
      prj->AddTask(&task);
   }
   catch(TExceptionU excp)
   {
      Application->MessageBox(excp.GetErrorText(), "Error", MB_OK | MB_ICONERROR);
   }


   // d) add new task to list view
   AddTaskToListView(&prj->GetTask(prj->nTasks()-1));


   // e) reset parameter strings and display actualized # parameter sets
   SetDefaults();
   DisplaySetCount();

   ActiveControl = BtOk;      // set focus to button 'Ok'
}


//----------------------------------------------------------------------------------------------------------------------
// button 'Clear All' clicked: ask user and delete all tasks from list
void __fastcall TFEditSets::BtClearAllClick(TObject *Sender)
{
   // ask
   if(!f_DisableQuestions)
      if(Application->MessageBox("Clear all tasks ??", "Question", MB_YESNO | MB_ICONQUESTION)==ID_NO)
         return;

   DeleteAllTasks();

   // disable buttons
   BtClearAll->Enabled = false;
   BtDelete  ->Enabled = false;        // 'Delete'
   BtEdit    ->Enabled = false;        // 'Edit'
}


//----------------------------------------------------------------------------------------------------------------------
// functions gets called if the one of the parameter edit boxes (TEdit) exits focus: check if actual parameter string
// is valid using function CheckParameterString()


// note: the steps are always the same: a) buffer text, b) check text, c) eventually give focud back and d) write back
void __fastcall TFEditSets::EIntervalsExit(TObject *Sender){                                     // 'Intervals'
   AnsiString tmp = EIntervals->Text;
   if(CheckParameterString(false, tmp, LaIntervals->Caption, para->N_Int, MIN_N_INT, INT_N_INT, MAX_N_INT))
      ActiveControl = EIntervals;
   EIntervals->Text = tmp;}


void __fastcall TFEditSets::EWminExit(TObject *Sender){                                          // 'w_min'
   AnsiString tmp = EWmin->Text;
   if(CheckParameterString(false, tmp, LaWmin->Caption, para->w_COD, MIN_W_COD, INT_W_COD, Max_w_COD(data)))
      ActiveControl = EWmin;
   EWmin->Text = tmp;}

void __fastcall TFEditSets::EEtaExit(TObject *Sender){                                           // 'Eta'
   AnsiString tmp = EEta->Text;
   if(CheckParameterString(false, tmp, LaEta->Caption, para->Eta, MIN_ETA, INT_ETA, MAX_ETA))
      ActiveControl = EEta;
   EEta->Text = tmp;}

void __fastcall TFEditSets::ECardinalityExit(TObject *Sender){                                   // 'min. cuboid mass'
   AnsiString tmp = ECardinality->Text;
   if(CheckParameterString(false, tmp, LaCardinality->Caption, para->p_min, MIN_P_MIN, INT_P_MIN, Max_P_Min(data)))
      ActiveControl = ECardinality;
   ECardinality->Text = tmp;}

void __fastcall TFEditSets::ESigmaExit(TObject *Sender){                                         // 'Sigma'
   AnsiString tmp = ESigma->Text;
   if(CheckParameterString(false, tmp, LaSigma->Caption, para->Sigma, MIN_SIGMA, INT_SIGMA, MAX_SIGMA))
      ActiveControl = ESigma;
   ESigma->Text = tmp;}

void __fastcall TFEditSets::ESpecialExit(TObject *Sender){                                       // 'Special'
   AnsiString tmp = ESpecial->Text;
   if(CheckParameterString(false, tmp, LaSpecial->Caption, para->W_Kernel, MIN_W_KERNEL, INT_W_KERNEL, MAX_W_KERNEL))
      ActiveControl = ESpecial;
   ESpecial->Text = tmp;}

void __fastcall TFEditSets::EWeightsExit(TObject *Sender){                                       // 'Use Weights'
   AnsiString tmp = EWeights->Text;
   if(CheckParameterString(false, tmp, LaWeights->Caption, para->Weights, MIN_WEIGHTS, INT_WEIGHTS, MAX_WEIGHTS))
      ActiveControl = EWeights;
   EWeights->Text = tmp;}

void __fastcall TFEditSets::EPruneExit(TObject *Sender){                                         // 'Prune Cuboids'
   AnsiString tmp = EPrune->Text;
   if(CheckParameterString(false, tmp, LaPrune->Caption, para->Prune, MIN_PRUNE, INT_PRUNE, MAX_PRUNE))
      ActiveControl = EPrune;
   EPrune->Text = tmp;}

void __fastcall TFEditSets::EEuclideanExit(TObject *Sender){                                     // 'Euclidean Distance'
   AnsiString tmp = EEuclidean->Text;
   bool euclid = (para->Metric==2);
   if(CheckParameterString(false, tmp, LaEuclidean->Caption, euclid, 0, 1, 1))
      ActiveControl = EEuclidean;
   EEuclidean->Text = tmp;}



//----------------------------------------------------------------------------------------------------------------------
// button 'Add Task' clicked: add current task to list and reset parameters to default
void __fastcall TFEditSets::BtAcceptClick(TObject *Sender)
{
   // a) check if maximum n-  umber of tasks is exceeded
   if(LvTasks->Items->Count>=MAX_TASKS)
   {
      sprintf(text, "Too many task! The maximum number of tasks is %d. Cannot add any more!", MAX_TASKS);
      Application->MessageBox(text, "Error! Action aborted!", MB_OK | MB_ICONERROR);
      return;
   }


   // copy parameter strings to list
   TTask task;

   strcpy(task.szN_Int, EIntervals->Text.c_str());                               // 'Intervals'
   strcpy(task.szEta, EEta->Text.c_str());                                       // 'Eta'
   strcpy(task.szW_COD, EWmin->Text.c_str());                                    // 'w_COD'

   strcpy(task.szSigma, ESigma->Text.c_str());                                   // 'Sigma'
   strcpy(task.szW_Kernel, ESpecial->Text.c_str());                              // kernel width
   strcpy(task.szP_Min, ECardinality->Text.c_str());                             // 'min. cuboid mass'

   strcpy(task.szPrune, EPrune->Text.c_str());                                   // 'Prune Cuboids'
   strcpy(task.szWeights, EWeights->Text.c_str());                               // 'Use Weights'

   // 'Euclidean Distance'
   // note: map, as parameter is entered as 'true' (1) or 'false' (0) and it is needed as '2' and '1' in TTask
   float* par = new float[12];
   int nPar = ParseParameters(EEuclidean->Text.c_str(), par, 1, "Metric (intern)", 0);
   if(nPar==1)
      sprintf(task.szMetric, "%.0f", ++par[0]);
   else
      sprintf(task.szMetric, "%.0f;%.0f", ++par[0], ++par[1]);


   // add new task to project
   try
   {
      prj->AddTask(&task);
   }
   catch(TExceptionU excp)
   {
      Application->MessageBox(excp.GetErrorText(), "Error", MB_OK | MB_ICONERROR);
   }


   // and add task to list view
   AddTaskToListView(&prj->GetTask(prj->nTasks()-1));


   // e) reset parameter strings and display actualized # parameter sets
   SetDefaults();
   DisplaySetCount();

   BtClearAll->Enabled = true;
}


//----------------------------------------------------------------------------------------------------------------------
// delete key: forward to BtDeleteClick()
void __fastcall TFEditSets::LvTasksKeyUp(TObject *Sender, WORD &Key, TShiftState Shift){
   if(Key==VK_DELETE)
      BtDeleteClick(this);}


//----------------------------------------------------------------------------------------------------------------------
// button 'Delete' clicked: ask user and delete task from list
void __fastcall TFEditSets::BtDeleteClick(TObject *Sender)
{
   // a) get selected item and it's index, return if none is selected
   TListItem* item = LvTasks->Selected;
   if(!item)
      return;
   const int index = LvTasks->Items->IndexOf(item);


   // b) ask user if he really wants to delete task
   if(!f_DisableQuestions)
   {
      AnsiString text = "Delete task " + AnsiString(index+1) + " from list?? The other tasks will be re-numbered.";
      if(Application->MessageBox(text.c_str(), "Question", MB_YESNO | MB_ICONQUESTION)!=ID_YES)
         return;
   }

   // c) delete
   DeleteTask(index);
}


//----------------------------------------------------------------------------------------------------------------------
// delete all tasks from list
void TFEditSets::DeleteAllTasks()
{
   LvTasks->Items->Clear();         // delete list view
   prj->DeleteAllTasks();            // delete in project (TProjectG) object
   DisplaySetCount();                // display actualized # sets
}


//----------------------------------------------------------------------------------------------------------------------
// delete task from list
void TFEditSets::DeleteTask(const int& index)
{
   // a) delete from listview and disable buttons 'Delete' and 'Edit'
   LvTasks->Items->Delete(index);                        // delete
   BtDelete->Enabled = false;                            // disable buttons 'Delete' and 'Edit'
   BtEdit  ->Enabled = false;
   if(LvTasks->Items->Count==0)                          // disable 'Clear All' button if task list is empty
      BtClearAll->Enabled = false;


   // correct task Id's
   for(int i=0;i<LvTasks->Items->Count;i++)
      LvTasks->Items->Item[i]->Caption = i+1;

   // delete from project
   prj->DeleteTask(index);


   // display actualized # sets
   DisplaySetCount();

   f_SomethingChanged = true;
}


//----------------------------------------------------------------------------------------------------------------------
// buttin 'Edit' clicked: delete selected task from list and copy it back to the parameter strings (edit task group box)
void __fastcall TFEditSets::BtEditClick(TObject *Sender)
{
   // a) get selected file and it's index, return if none is selected
   TListItem* item = LvTasks->Selected;
   if(!item)
      return;
   const int index = LvTasks->Items->IndexOf(item);


   // b) ask user
   if(!f_DisableQuestions)
   {
      AnsiString text = "Move task " + AnsiString(index+1) + " back to the editing enviroment ??\n\
The other tasks will be re-numbered.";
      if(Application->MessageBox(text.c_str(), "Question", MB_YESNO | MB_ICONQUESTION)!=ID_YES)
         return;
   }


   // c) copy to parameter strings
   const TTask& task = prj->GetTask(index);
   EIntervals  ->Text = task.szN_Int;                               // # output intervals
   EWmin       ->Text = task.szW_COD;                               // w_COD
   EEta        ->Text = task.szEta;                                 // eta

   ESigma      ->Text = task.szSigma;                               // sigma
   ESpecial    ->Text = task.szW_Kernel;                            // special (kernel width)
   ECardinality->Text = task.szP_Min;                               // min. cuboid mass


   // boolean parameters
   EPrune  ->Text = task.szPrune;                                   // prune cuboids
   EWeights->Text = task.szWeights;                                 // use weights

   int i=0, nPara=0;
   char szText[256];
   szText[0]='\0';
   while(task.szMetric[i]!='\0')
   {
      if(task.szMetric[i]=='1')
      {
         strcat(szText, "false");
         nPara++;
      }
      else
         if(task.szMetric[i]=='2')
         {
            if(nPara>0)
               strcat(szText, ";");
            strcat(szText, "true");
            nPara++;
         }
      i++;
   }

   EEuclidean->Text = szText;                                // euclidean distance


   // d) delete from list view and project
   DeleteTask(index);
}