/***************************************************************************
**
**  This file is part of QGpGuiMath.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file is distributed in the hope that it will be useful, but WITHOUT
**  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
**  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2008-10-31
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <SciFigs.h>

#include "CurveBrowser.h"
#include "CurveSelector.h"
#include "CurveProperties.h"
#include "CurveBrowserResample.h"
#include "CurveBrowserCut.h"
#include "CurveBrowserValidate.h"
#include "CurveBrowserSmooth.h"
#include "CurveBrowserSplit.h"
#include "CurvePlotProxy.h"

namespace QGpGuiMath {

  /*!
    \class CurveBrowser CurveBrowser.h
    \brief A high level widget to handle curves

  */

  /*!
    Description of constructor still missing
  */
  CurveBrowser::CurveBrowser(QWidget * parent)
      : QWidget(parent)
  {
    TRACE;
    setupUi(this);

    _proxy=nullptr;
    _plotProxy=nullptr;

    _currentLayer=nullptr;
    _editable=true;
    _legendEditor=nullptr;

    // Init the action menu
    QMenu * m=new QMenu(this);
    m->setTitle(tr("Edit"));
    m->setTearOffEnabled(true);
    editBut->setMenu(m);

    QAction * a;
    a=addCurveAction(tr("&Save"), tr("Save curve(s) in a text file"), false);
    connect(a, SIGNAL(triggered()), this, SLOT(save()));
    a=addCurveAction(tr("&Log"), tr("Show log of curve(s)"), false);
    connect(a, SIGNAL(triggered()), this, SLOT(showLog()) );
    a=addCurveAction(tr("&Remove"), tr("Remove curve(s)"), true);
    connect(a, SIGNAL(triggered()), this, SLOT(remove()));
    a=addCurveAction(tr("Resa&mple" ), tr("Resample curve(s)"), true);
    connect(a, SIGNAL(triggered()), this, SLOT(resample()));
    a=addCurveAction(tr("&Cut"), tr("Cut curve(s)"), true);
    connect(a, SIGNAL(triggered()), this, SLOT(cut()));
    a=addCurveAction(tr("Sm&ooth"), tr("Smooth curve(s)"), true);
    connect(a, SIGNAL(triggered()), this, SLOT(smooth()));
    a=addCurveAction(tr("&Average"), tr("Average curve(s)"), true);
    connect(a, SIGNAL(triggered()), this, SLOT(average()));
    a=addCurveAction(tr("&Validate samples"), tr("Change valid flag of samples for curve(s)"), true);
    connect(a, SIGNAL(triggered()), this, SLOT(validate()));
    a=addCurveAction(tr("S&plit"), tr("Split curve into continuous segments"), true);
    connect(a, SIGNAL(triggered()), this, SLOT(split()));

    curveTabs->setTabsClosable(true);
  }

  /*!
    Description of destructor still missing
  */
  CurveBrowser::~CurveBrowser()
  {
    TRACE;
    delete _proxy;
    delete _plotProxy;
  }

  void CurveBrowser::setProxy(CurveProxy * proxy)
  {
    TRACE;
    delete _proxy;
    _proxy=proxy;
  }

  void CurveBrowser::setPlotProxy(CurvePlotProxy * proxy)
  {
    TRACE;
    delete _plotProxy;
    _plotProxy=proxy;
  }

  /*!
    Called when the user clicks on close button.
  */
  void CurveBrowser::closeEvent(QCloseEvent * e)
  {
    TRACE;
    Settings::setWidget(this);
    e->accept();
  }

  /*!
    Add a new action to curve menu 'Actions' with \a title and \a toolTip. \a editCurve must be true
    if the action can modify the curve. If false, this action is not affected by setEditable().
  */
  QAction * CurveBrowser::addCurveAction(QString title, QString toolTip, bool editCurve)
  {
    TRACE;
    QAction * a;
    a=new QAction(title, this);
    a->setToolTip(toolTip);
    a->setData(editCurve);
    editBut->menu()->addAction(a);
    return a;
  }

  CurveProperties * CurveBrowser::createProperties()
  {
    return new CurveProperties;
  }

  CurveProperties * CurveBrowser::addLine(AbstractLine * line)
  {
    TRACE;
    ASSERT(_proxy);
    ASSERT(_plotProxy);
    CurveProperties * w=createProperties();
    w->setCurrentLayer(_currentLayer);
    w->setLine(line);
    connect(w, SIGNAL(curveModified()), this, SIGNAL(curveModified()));
    connect(w, SIGNAL(nameChanged(QString)), this, SLOT(setCurveName(QString)));
    CurveProxy * p=_proxy->clone();
    _plotProxy->setCurve(line, p); // Curve must be set before setting proxy to CurveProperties
    w->setProxy(p);
    QString n=p->name();
    if(n.isEmpty()) {
      n="# "+QString::number(curveTabs->count()+1);
      p->setName(n);
    }
    curveTabs->addTab(w, n);
    _currentLayer->graph()->deepUpdate();
    return w;
  }

  void CurveBrowser::takeLine(int index)
  {
    TRACE;
    CurveProperties * w=static_cast<CurveProperties *>(curveTabs->widget(index));
    curveTabs->removeTab(index);
    delete w;
  }

  void CurveBrowser::removeLine(int index)
  {
    TRACE;
    takeLine(index);
    _currentLayer->removeLine(index);
  }

  /*!
    Docks widget \a w to the right side of the browser.
    Can be called only once.
  */
  void CurveBrowser::dock(QWidget * w)
  {
    TRACE;
    w->setMinimumSize(QSize(100,100));
    layout()->removeWidget(frame);
    frame->setParent(nullptr);
    QSplitter * hSplitter=new QSplitter(this);
    hSplitter->setOrientation(Qt::Horizontal);
    hSplitter->addWidget(frame);
    QSizePolicy sp(QSizePolicy::Preferred, QSizePolicy::Preferred);
    sp.setHorizontalStretch(1);
    w->setSizePolicy(sp);
    hSplitter->addWidget(w);
    layout()->addWidget(hSplitter);
  }

  /*!
    Set the browser as editable or not.
  */
  void CurveBrowser::setEditable(bool e)
  {
    _editable=e;
    loadBut->setEnabled(e);
    clearBut->setEnabled(e && count()>0);
    editBut->setEnabled(count()>0);
    QList<QAction *> aList=editBut->menu()->actions();
    for(QList<QAction *>::iterator it=aList.begin();it!=aList.end();it++) {
      QAction& a=**it;
      if(a.data().toBool()) a.setEnabled(e);
    }
    for(int i=curveTabs->count()-1; i>=0; i--) {
      CurveProperties * p=static_cast<CurveProperties *>(curveTabs->widget(i));
      p->setEditable(e);
    }
  }

  CurveProperties * CurveBrowser::find(const QString& name) const
  {
    TRACE;
    int n=curveTabs->count();
    for(int i=0; i<n; i++) {
      CurveProperties * p=static_cast<CurveProperties *>(curveTabs->widget(i));
      if(p->proxy()->name()==name) {
        return p;
      }
    }
    return nullptr;
  }

  /*!
    Setup browser layer and axis. Don't forget to call setReferenceLine().
  */
  void CurveBrowser::initLayer(LineLayer * curveLayer)
  {
    TRACE;
    ASSERT(_plotProxy);
    AxisWindow * graph=curveLayer->graph();
    graph->xAxis()->setTitle(_proxy->xTitle());
    graph->xAxis()->setTitleInversedScale(_proxy->xInversedTitle());
    _plotProxy->setXAxisProperties(graph->xAxis());
    graph->yAxis()->setTitle(_proxy->yTitle());
    graph->yAxis()->setTitleInversedScale(_proxy->yInversedTitle());
    _plotProxy->setYAxisProperties(graph->yAxis());
    graph->updateExternalGeometry();

    curveLayer->addTrackingAction( tr("&Pick"), LineLayer::Pick,
                                   tr("Add points to the current curve."));
    curveLayer->addTrackingAction( tr("&Pick ordered"), LineLayer::PickOrdered,
                                   tr("Add points to the current curve, keeping x sample ordering."));
    curveLayer->addTrackingAction( tr("&Edit"), LineLayer::Edit,
                                   tr("Edit points of the current curve."));
    curveLayer->setEditable(false);
    connect(curveLayer, SIGNAL(pickLine(AbstractLine *)), this, SLOT(newPickedLine(AbstractLine *)));
    connect(curveLayer, SIGNAL(beginPointAdd(AbstractLine *, int)), this, SLOT(beginNewPickedPoint(AbstractLine *)));
    connect(curveLayer, SIGNAL(endPointAdd(AbstractLine *, int)), this, SLOT(endNewPickedPoint(AbstractLine *)));
  }

  /*!
    Set current graph.
  */
  void CurveBrowser::setCurrentLayer(LineLayer * layer)
  {
    TRACE;
    if(layer==_currentLayer) {
      return;
    }
    if(_currentLayer) {
      disconnect(this, SIGNAL(curveModified()), _currentLayer, SLOT(deepUpdate()));
      for(int i=_currentLayer->count()-1; i>=0; i--) {
        takeLine(i);
      }
    }
    _currentLayer=layer;
    if(_currentLayer) {
      connect(this, SIGNAL(curveModified()), _currentLayer, SLOT(deepUpdate()));
      for(int i=0; i<_currentLayer->count(); i++) {
        addLine(_currentLayer->line(i));
      }
      for(int i=curveTabs->count()-1; i>=0; i--) {
        static_cast<CurveProperties *>(curveTabs->widget(i))->setCurrentLayer(_currentLayer);
      }
    }
    emit curveModified();
  }

  void CurveBrowser::newPickedLine(AbstractLine * line)
  {
    TRACE;
    // Can be issued by a layer different from the current one
    LineLayer * layer=qobject_cast<LineLayer *>(sender());
    if(layer!=_currentLayer) {
      // TODO
    } else {
      CurveProperties * p=addLine(line);
      p->proxy()->addLog(tr("Manually picked\n"));
    }
  }

  void CurveBrowser::beginNewPickedPoint(AbstractLine * line)
  {
    TRACE;
    // Can be issued by a layer different from the current one
    LineLayer * layer=qobject_cast<LineLayer *>(sender());
    if(layer==_currentLayer) {
      CurveProperties * p=properties(line);
      p->beginCurveChange();
    }
  }

  void CurveBrowser::endNewPickedPoint(AbstractLine * line)
  {
    TRACE;
    // Can be issued by a layer different from the current one
    LineLayer * layer=qobject_cast<LineLayer *>(sender());
    if(layer==_currentLayer) {
      CurveProperties * p=properties(line);
      p->endCurveChange();
      curveTabs->setCurrentWidget(p);
      emit curveModified();
    }
  }

  /*!

  */
  int CurveBrowser::count() const
  {
    TRACE;
    if(_currentLayer)
      return _currentLayer->count();
    else
      return 0;
  }

  /*!
    Returns -1 if there is no line available.
  */
  int CurveBrowser::currentLine() const
  {
    TRACE;
    if(count()>0) {
      return curveTabs->currentIndex();
    } else {
      return -1;
    }
  }

  /*!

  */
  void CurveBrowser::setLimits()
  {
    TRACE;
    AxisWindow * w=_currentLayer->graph();
    Rect r=_currentLayer->boundingRect();
    w->xAxis()->setRange(r.x1() * 0.95, r.x2() * 1.05);
    w->yAxis()->setRange(r.y1() * 0.95, r.y2() * 1.05);
  }

  QList<CurveProperties *> CurveBrowser::curves() const
  {
    TRACE;
    QList<CurveProperties *> list;
    int n=curveTabs->count();
    for(int i=0; i<n; i++) {
      list.append(static_cast<CurveProperties *>(curveTabs->widget(i)));
    }
    return list;
  }

  CurveProperties * CurveBrowser::properties(AbstractLine * line) const
  {
    TRACE;
    int n=curveTabs->count();
    for(int i=0; i<n; i++) {
      CurveProperties * p=static_cast<CurveProperties *>(curveTabs->widget(i));
      if(line==p->line()) {
        return p;
      }
    }
    return nullptr;
  }

  /*!

  */
  void CurveBrowser::clear()
  {
    TRACE;
    if(_currentLayer) {
      for(int i=_currentLayer->count()-1; i>=0; i--) {
        removeLine(i);
      }
    }
    for(int i=curveTabs->count()-1; i>=0; i--) {
      CurveProperties * p=static_cast<CurveProperties *>(curveTabs->widget(i));
      curveTabs->removeTab(i);
      delete p;
    }
    emit curveModified();
  }

  /*!
    Can be overloaded to add support for custom file format import.
    If the number of curves was initialty zero, adjust graph limits.
  */
  void CurveBrowser::load()
  {
    TRACE;
    QStringList fileNames=Message::getOpenFileNames(tr("Load curve(s)"),tr("Mutli-column file(*)"));
    if(!fileNames.isEmpty()) {
      int curvesCount=count();
      ColumnTextParser * parser=nullptr;
      int n=fileNames.count();
      for(int i=0;i<n;i++) {
        loadMultiColumns(fileNames.at(i), i==n-1, parser);
      }
      delete parser;
      if(_currentLayer) {
        if(curvesCount==0 && count()>0) {
          setLimits();
        }
      }
    }
  }

  bool CurveBrowser::loadMultiColumns(const QString& fileName, bool lastFile, ColumnTextParser *& parser)
  {
    TRACE;
    if(fileName.isEmpty()) {
      App::log(tr("Empty curve file name\n") );
      return false;
    }
    if(parser) {
      QFile f(fileName);
      if(f.open(QIODevice::ReadOnly)) {
        parser->setText(new QTextStream(&f));
        parser->start();
        parser->wait();
        return loadMultiColumns(parser, fileName);
      } else {
        App::log(tr("Cannot read file '%1'\n").arg(fileName) );
        return false;
      }
    } else {
      bool ret=true;
      Dialog * d=new Dialog;
      d->setObjectName("CurveBrowserLoad");
      ColumnTextWidget * dg=new ColumnTextWidget;
      dg->setObjectName("CurveParser");
      dg->setStandardTypes(_proxy->columnFileTypes());
      dg->setTypes(_proxy->defaultColumnFileTypes());
      if(!dg->fileEngine()->setFile(fileName)) {
        return false;
      }
      dg->restoreParserSettings();
      QWidget * w;
      QCheckBox * sameParserForAll;
      if(lastFile) {
        w=dg;
        sameParserForAll=nullptr;
      } else {
        w=new QWidget;
        QVBoxLayout * layout=new QVBoxLayout(w);
        layout->setMargin(0);
        layout->addWidget(dg);
        sameParserForAll=new QCheckBox(w);
        sameParserForAll->setText(tr("Use the same parser for next files"));
        layout->addWidget(sameParserForAll);
      }
      d->setMainWidget(w);
      d->setWindowTitle(tr("Load curves"));
      Settings::getRect(d);
      if(d->exec()==QDialog::Accepted) {
        Settings::setRect(d);
        dg->saveParserSettings();
        ret=loadMultiColumns(dg->fileEngine(), fileName);
        if(sameParserForAll && sameParserForAll->isChecked()) {
          parser=new ColumnTextParser(*dg->fileEngine());
        }
      }
      delete dg;
      delete d;
      return ret;
    }
  }

  bool CurveBrowser::loadMultiColumns(const ColumnTextParser * parser, const QString& fileName)
  {
    TRACE;
    ColumnTextIterator it(parser);
    int nCurves=0;
    while(!it.atEnd()) {
      if(!loadMultiColumns(_currentLayer, it, fileName, nCurves)) {
        return false;
      }
    }
    return true;
  }

  bool CurveBrowser::loadMultiColumns(LineLayer * layer, ColumnTextIterator& it,
                                      const QString& fileName, int& nCurves)
  {
    TRACE;
    CurveProperties * p=addLine(layer->addLine());
    p->beginCurveChange();
    CurveProxy * px=p->proxy();
    if(px->parse(it) && px->sampleCount()>0) {
      px->setName(QString()); // Avoid '# n' automatically set by addLine()
      px->setComments(it.parser()->sectionComments(it.currentSection()-1));
      px->addLog(tr("%1 samples loaded from file %2\n").arg(px->sampleCount()).arg(fileName));
      if(px->name().isEmpty()) {
        QFileInfo fi(fileName);
        if(nCurves>0) {
          px->setName((fi.fileName()+" #%1").arg(nCurves-1));
        } else {
          px->setName(fi.fileName());
        }
      }
      nCurves++;
      p->endCurveChange();
      curveTabs->setCurrentWidget(p);
      return true;
    } else {
      // Remove line and related curve properties (including CurveProxy)
      removeLine(layer->count()-1);
      it.nextRow(); // Avoid infinite loop
      return false;
    }
  }

  /*!

  */
  void CurveBrowser::legend()
  {
    TRACE;
    Legend legend=_currentLayer->legend();
    int n=count();
    ASSERT(n==legend.count());
    for(int i=0; i<n; i++) {
      legend.setText(i, static_cast<CurveProperties *>(curveTabs->widget(i))->proxy()->name());
    }

    Dialog * d=new Dialog(this);
    ASSERT(!_legendEditor);
    _legendEditor=new LegendProperties(this);
    _legendEditor->setPropertySections(LegendTable::All);
    d->setMainWidget(_legendEditor, Dialog::OkCancel);
    _legendEditor->setLegend(legend);
    _legendEditor->setReadOnlyText(true);
    connect(_legendEditor, SIGNAL(touched()), this, SLOT(commitLegend()));
    Settings::getRect(d, "CurveBrowser::legend");
    if(d->exec()!=QDialog::Accepted) {
      _currentLayer->setLegend(legend);
      _currentLayer->deepUpdate();
    }
    Settings::setRect(d, "CurveBrowser::legend");
    delete d;
    _legendEditor=nullptr;
  }

  void CurveBrowser::commitLegend()
  {
    TRACE;
    if(_legendEditor) {
      _currentLayer->setLegend(_legendEditor->legend());
      _currentLayer->deepUpdate();
    }
  }

  void CurveBrowser::setLegend(const Legend& l)
  {
    TRACE;
    _currentLayer->setLegend(l);
    _currentLayer->deepUpdate();
  }

  QList<CurveProperties *> CurveBrowser::selectCurves()
  {
    TRACE;
    QList<CurveProperties *> list;
    CurveProperties * p;
    int n=count();
    switch(n) {
    case 0:
      return list;
    case 1:
      p=static_cast<CurveProperties *>(curveTabs->widget(0));
      if(p->proxy()->sampleCount()>0) {
        list.append(p);
      }
      return list;
    default:
      break;
    }
    CurveSelector * d=new CurveSelector(this);
    for(int i=0; i<n; i++) {
      d->addCurve(static_cast<CurveProperties *>(curveTabs->widget(i)));
    }
    d->selectAll();
    Settings::getWidget(d);
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(d);
      list=d->selectedCurves();
    }
    delete d;
    return list;
  }

  /*!

  */
  void CurveBrowser::average()
  {
    TRACE;
    QList<CurveProperties *> list=selectCurves();
    switch(list.count()) {
    case 0:
      return;
    case 1:
      Message::warning(MSG_ID, tr("Average"), tr("At least two curves are required to calculate an average curve."));
      return;
    default:
      break;
    }
    // Create new averaged curve
    CurveProperties * pa=addLine(_currentLayer->addLine());
    pa->beginCurveChange();
    CurveProxy * pxa=pa->proxy();
    pxa->addLog(tr("Average of curves:\n"));
    for(QList<CurveProperties *>::iterator it=list.begin(); it!=list.end(); it++) {
      CurveProxy * px=(*it)->proxy();
      pxa->addLog("   "+px->name()+"\n{");
      pxa->addLog(px->log()+"}\n");
      pxa->average(px);
    }
    pxa->setName(tr("average"));
    pa->endCurveChange();
  }

  /*!

  */
  void CurveBrowser::validate()
  {
    TRACE;
    QList<CurveProperties *> list=selectCurves();
    if(list.isEmpty()) {
      return;
    }
    double min=std::numeric_limits<double>::infinity(), max=-std::numeric_limits<double>::infinity();
    curveRange(list, min, max);
    CurveBrowserValidate * d=new CurveBrowserValidate(this);
    d->setCurveLimits(min, max);
    d->setAxisNames(_proxy->xName(), _proxy->xInversedName());
    Settings::getWidget(d, "CurveBrowserValidate");
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(d, "CurveBrowserValidate");
      double minimum=d->minimumEdit->text().toDouble();
      double maximum=d->maximumEdit->text().toDouble();
      SamplingOptions options=LinearScale;
      if(d->inversedBut->isChecked()) {
        options|=InversedScale;
      }
      bool value=d->validateBut->isChecked();
      for(QList<CurveProperties *>::const_iterator it=list.begin(); it!=list.end(); it++) {
        (*it)->validate(minimum, maximum, options, value);
      }
    }
    delete d;
  }

  void CurveBrowser::showLog()
  {
    TRACE;
    QList<CurveProperties *> list=selectCurves();
    if(list.isEmpty()) {
      return;
    }
    if(list.count()==1) {
      CurveProxy * p=list.first()->proxy();
      Message::information(MSG_ID, tr("Curve log: %1").arg(p->name()), p->log(), Message::close());
    } else {
      QString log;
      for(QList<CurveProperties *>::const_iterator it=list.begin(); it!=list.end(); it++) {
        CurveProxy * p=(*it)->proxy();
        log+=tr("------- %1 ------- \n").arg(p->name());
        log+=p->log();
      }
      Message::information(MSG_ID, tr("Curve logs"), log, Message::close());
    }
  }

  void CurveBrowser::save()
  {
    TRACE;
    QList<CurveProperties *> list=selectCurves();
    if(list.isEmpty()) {
      return;
    }
    QString fileName=Message::getSaveFileName(tr("Save curve"), tr("Mutli-column file(*)"));
    if(!fileName.isEmpty()) {
      QFile f(fileName);
      if(!f.open(QIODevice::WriteOnly) ) {
        Message::warning(MSG_ID, tr("Saving curve ..."),
                              tr("Impossible to write to file %1").arg(fileName), Message::cancel());
        return;
      }
      QTextStream s(&f);
      for(QList<CurveProperties *>::const_iterator it=list.begin(); it!=list.end(); it++) {
        (*it)->proxy()->save(s);
      }
    }
  }

  void CurveBrowser::curveRange(const QList<CurveProperties *>& list, double& min, double& max)
  {
    TRACE;
    double v;
    for(QList<CurveProperties *>::const_iterator it=list.begin(); it!=list.end(); it++) {
      CurveProxy * p=(*it)->proxy();
      p->sort();
      v=p->minimumX();
      if(v<min) {
        min=v;
      }
      v=p->maximumX();
      if(v>max) {
        max=v;
      }
    }
  }

  /*!

  */
  void CurveBrowser::resample()
  {
    TRACE;
    QList<CurveProperties *> list=selectCurves();
    if(list.isEmpty()) {
      return;
    }
    double min=std::numeric_limits<double>::infinity(), max=-std::numeric_limits<double>::infinity();
    curveRange(list, min, max);
    CurveBrowserResample * d=new CurveBrowserResample(this);
    d->setCurveLimits(min, max);
    d->setAxisNames(_proxy->xName(), _proxy->xInversedName());
    Settings::getWidget(d, "CurveBrowserResample");
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(d, "CurveBrowserResample");
      int n=d->sampleCount->value();
      double minimum=d->minimumEdit->text().toDouble();
      double maximum=d->maximumEdit->text().toDouble();
      SamplingOptions options=LinearScale;
      if(d->logBut->isChecked()) {
        options|=LogScale;
      } else if(d->inversedBut->isChecked()) {
        options|=InversedScale;
      }
      for(QList<CurveProperties *>::const_iterator it=list.begin(); it!=list.end(); it++) {
        (*it)->resample(n, minimum, maximum, options, d->dsBut->isChecked());
      }
    }
    delete d;
  }

  /*!

  */
  void CurveBrowser::cut()
  {
    TRACE;
    QList<CurveProperties *> list=selectCurves();
    if(list.isEmpty()) {
      return;
    }
    double min=std::numeric_limits<double>::infinity(), max=-std::numeric_limits<double>::infinity();
    curveRange(list, min, max);
    CurveBrowserCut * d=new CurveBrowserCut(this);
    d->setCurveLimits(min, max);
    d->setAxisNames(_proxy->xName(), _proxy->xInversedName());
    Settings::getWidget(d, "CurveBrowserCut" );
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(d, "CurveBrowserCut" );
      double minimum=d->minimumEdit->text().toDouble();
      double maximum=d->maximumEdit->text().toDouble();
      SamplingOptions options=LinearScale;
      if(d->inversedBut->isChecked()) {
        options|=InversedScale;
      }
      for(QList<CurveProperties *>::const_iterator it=list.begin(); it!=list.end(); it++) {
        (*it)->cut(minimum, maximum, options);
      }
    }
    delete d;
  }

  /*!

  */
  void CurveBrowser::smooth()
  {
    TRACE;
    QList<CurveProperties *> list=selectCurves();
    if(list.isEmpty()) {
      return;
    }
    double min=std::numeric_limits<double>::infinity(), max=-std::numeric_limits<double>::infinity();
    curveRange(list, min, max);
    CurveBrowserSmooth * d=new CurveBrowserSmooth(this);
    d->setCurveLimits(min, max);
    d->setAxisNames(_proxy->xName(), _proxy->xInversedName());
    Settings::getWidget(d, "CurveBrowserSmooth");
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(d, "CurveBrowserSmooth");
      double minimum=d->minimumEdit->text().toDouble();
      double maximum=d->maximumEdit->text().toDouble();
      SamplingOptions options=LinearScale;
      if(d->logBut->isChecked()) {
        options |= LogScale;
      } else if(d->inversedBut->isChecked()) {
        options |= InversedScale;
      }
      for(QList<CurveProperties *>::const_iterator it=list.begin(); it!=list.end(); it++) {
        (*it)->smooth(minimum, maximum, options);
      }
    }
    delete d;
  }

  /*!

  */
  void CurveBrowser::split()
  {
    TRACE;
    QList<CurveProperties *> list=selectCurves();
    if(list.isEmpty()) {
      return;
    }
    CurveBrowserSplit * d=new CurveBrowserSplit(this);
    Settings::getWidget(d, "CurveBrowserSplit");
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(d, "CurveBrowserSplit");
      double maxX=d->maxX->value();
      double maxErr=d->maxErr->value();
      int minCount=d->minCount->value();
      // Create new split curves
      for(QList<CurveProperties *>::const_iterator it=list.begin(); it!=list.end(); it++) {
        CurveProxy * px=(*it)->proxy();
        int n=px->splitCount(maxX, LogScale, maxErr, minCount);
        for(int i=0; i<n; i++) {
          CurveProperties * pa=addLine(_currentLayer->addLine());
          CurveProxy * pxa=pa->proxy();
          pa->beginCurveChange();
          pxa->addLog(tr("split of curve %1\n").arg((*it)->proxy()->name()));
          pxa->split(px, i, maxX, LogScale, maxErr, minCount);
          pa->endCurveChange();
        }
      }
    }
    delete d;
  }

  /*!

  */
  void CurveBrowser::remove()
  {
    TRACE;
    QList<CurveProperties *> list=selectCurves();
    if(list.isEmpty()) {
      return;
    }
    for(QList<CurveProperties *>::const_iterator it=list.begin(); it!=list.end(); it++) {
      removeLine(curveTabs->indexOf(*it));
    }
    emit curveModified();
  }

  void CurveBrowser::on_curveTabs_tabCloseRequested(int index)
  {
    TRACE;
    QString curveName=static_cast<CurveProperties *>(curveTabs->widget(index))->proxy()->name();
    if(Message::question(MSG_ID, tr("Removing curve %1").arg(curveName), tr("Are you sure?"),
                         Message::yes(), Message::no())==Message::Answer0) {
      removeLine(index);
    }
    emit curveModified();
  }

  void CurveBrowser::on_clearBut_clicked()
  {
    TRACE;
    if(Message::question(MSG_ID, tr("Clearing all curves"), tr("Are you sure?"),
                              Message::yes(), Message::no())==Message::Answer0) {
      clear();
    }
  }

  void CurveBrowser::setCurveName(QString n)
  {
    TRACE;
    CurveProperties * p=qobject_cast<CurveProperties *>(sender());
    if(p) {
      int i=curveTabs->indexOf(p);
      if(n.isEmpty()) {
        n="# "+QString::number(i+1);
      }
      curveTabs->setTabText(i, n);
    }
  }

} // namespace QGpGuiMath
