for (int i = 0; i < _ui->colorsTable->rowCount(); ++i) {
      colorsVector.push_back(QVariant(_ui->colorsTable->item(i, 0)->background().color()));
    }

/**
 *
 * This file is part of Tulip (http://tulip.labri.fr)
 *
 * Authors: David Auber and the Tulip development Team
 * from LaBRI, University of Bordeaux
 *
 *
 */

#include <QFileDialog>
#include <QHeaderView>
#include <QLinearGradient>
#include <QPainter>
#include <QMessageBox>
#include <QInputDialog>

#include <algorithm>
#include <vector>

#include <tulip/TlpQtTools.h>
#include <tulip/ColorScaleConfigDialog.h>
#include <tulip/ColorScalesManager.h>
#include <tulip/TlpTools.h>
#include <tulip/TulipSettings.h>

#include "ui_ColorScaleConfigDialog.h"

using namespace std;

namespace tlp {

map<QString, vector<Color>> ColorScaleConfigDialog::tulipImageColorScales;

ColorScaleConfigDialog::ColorScaleConfigDialog(const ColorScale &colorScale, QWidget *parent)
    : QDialog(parent), _ui(new Ui::ColorScaleDialog), colorScale(colorScale) {
  _ui->setupUi(this);
  _ui->colorsTable->setColumnWidth(0, _ui->colorsTable->width());
  _ui->colorsTable->horizontalHeader()->setHidden(true);
  QPalette palette;
  palette.setColor(QPalette::Window, Qt::white);
  _ui->savedGradientPreview->setPalette(palette);
  _ui->userGradientPreview->setPalette(palette);
  _ui->savedGradientPreview->setAutoFillBackground(true);
  _ui->userGradientPreview->setAutoFillBackground(true);
  connect(_ui->savedColorScalesList,
          SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this,
          SLOT(displaySavedGradientPreview()));
  connect(_ui->savedColorScalesList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this,
          SLOT(reeditSaveColorScale(QListWidgetItem *)));
  connect(_ui->nbColors, SIGNAL(valueChanged(int)), this, SLOT(nbColorsValueChanged(int)));
  connect(_ui->colorsTable, SIGNAL(itemDoubleClicked(QTableWidgetItem *)), this,
          SLOT(colorTableItemDoubleClicked(QTableWidgetItem *)));
  connect(_ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(displaySavedGradientPreview()));
  connect(_ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(displayUserGradientPreview()));
  connect(_ui->gradientCB, SIGNAL(clicked()), this, SLOT(displayUserGradientPreview()));
  connect(_ui->saveColorScaleButton, SIGNAL(clicked()), this, SLOT(saveCurrentColorScale()));
  connect(_ui->deleteColorScaleButton, SIGNAL(clicked()), this, SLOT(deleteSavedColorScale()));
  connect(_ui->importFromImgButton, SIGNAL(clicked()), this, SLOT(importColorScaleFromImageFile()));
  connect(_ui->importFromPredefinedCSButton, SIGNAL(clicked()), this,
          SLOT(importColorScaleFromColorScaleFile()));
  connect(_ui->invertColorScaleButton, SIGNAL(clicked()), this, SLOT(invertEditedColorScale()));
  connect(_ui->globalAlphaCB, SIGNAL(toggled(bool)), _ui->globalAlphaSB, SLOT(setEnabled(bool)));
  connect(_ui->globalAlphaCB, SIGNAL(toggled(bool)), this, SLOT(applyGlobalAlphaToColorScale()));
  connect(_ui->globalAlphaSB, SIGNAL(valueChanged(int)), this,
          SLOT(applyGlobalAlphaToColorScale()));

  if (tulipImageColorScales.empty()) {
    loadTulipImageColorScales();
  }

  loadUserSavedColorScales();
  setColorScale(colorScale);
}

ColorScaleConfigDialog::~ColorScaleConfigDialog() {
  delete _ui;
}

void ColorScaleConfigDialog::accept() {
  vector<Color> colors;
  bool gradient = true;

  if (_ui->tabWidget->currentIndex() == 1) {
    if (_ui->savedColorScalesList->count() > 0 && _ui->savedColorScalesList->currentItem()) {
      QString savedColorScaleId = _ui->savedColorScalesList->currentItem()->text();

      if (tulipImageColorScales.find(savedColorScaleId) != tulipImageColorScales.end()) {
        colors = tulipImageColorScales[savedColorScaleId];
      } else {
        TulipSettings::instance().beginGroup("ColorScales");
        QList<QVariant> colorsVector = TulipSettings::instance().value(savedColorScaleId).toList();
        QString gradientScaleId = savedColorScaleId + "_gradient?";
        gradient = TulipSettings::instance().value(gradientScaleId).toBool();
        TulipSettings::instance().endGroup();

        colors.reserve(colorsVector.size());
        for (int i = 0; i < colorsVector.size(); ++i) {
          colors.emplace_back(colorsVector.at(i).value<QColor>().red(),
                              colorsVector.at(i).value<QColor>().green(),
                              colorsVector.at(i).value<QColor>().blue(),
                              colorsVector.at(i).value<QColor>().alpha());
        }

        std::reverse(colors.begin(), colors.end());
      }
    }
  } else {
    colors.reserve(_ui->colorsTable->rowCount());
    for (int i = 0; i < _ui->colorsTable->rowCount(); ++i) {
      QColor itemColor = _ui->colorsTable->item(i, 0)->background().color();
      colors.emplace_back(itemColor.red(), itemColor.green(), itemColor.blue(), itemColor.alpha());
    }

    std::reverse(colors.begin(), colors.end());
    gradient = _ui->gradientCB->isChecked();
  }

  if (!colors.empty()) {
    colorScale.setColorScale(colors, gradient);
  }

  ColorScalesManager::setLatestColorScale(colorScale);

  QDialog::accept();
}

vector<Color> ColorScaleConfigDialog::getColorScaleFromImageFile(const QString &imageFilePath) {
  QImage gradientImage(imageFilePath);
  unsigned int imageHeight = gradientImage.height();

  unsigned int step = 1;

  if (imageHeight > 50)
    step = 10;

  vector<Color> colors;
  colors.reserve(imageHeight % step ? imageHeight / step : imageHeight / step + 1);
  for (unsigned int i = 0; i < imageHeight; i += step) {
    QRgb pixelValue = gradientImage.pixel(0, i);
    colors.emplace_back(qRed(pixelValue), qGreen(pixelValue), qBlue(pixelValue),
                        qAlpha(pixelValue));
  }

  if (imageHeight % step != 0) {
    QRgb pixelValue = gradientImage.pixel(0, imageHeight - 1);
    colors.emplace_back(qRed(pixelValue), qGreen(pixelValue), qBlue(pixelValue),
                        qAlpha(pixelValue));
  }

  std::reverse(colors.begin(), colors.end());
  return colors;
}

ColorScale ColorScaleConfigDialog::getColorScaleFromImageFile(const std::string &imageFilePath,
                                                              bool gradient) {
  return ColorScale(getColorScaleFromImageFile(tlpStringToQString(imageFilePath)), gradient);
}

void ColorScaleConfigDialog::loadImageColorScalesFromDir(const QString &colorScalesDir) {
  QFileInfo colorscaleDirectory(colorScalesDir);

  if (colorscaleDirectory.exists() && colorscaleDirectory.isDir()) {
    QDir dir(colorscaleDirectory.absoluteFilePath());
    dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
    QFileInfoList list = dir.entryInfoList();

    for (int i = 0; i < list.size(); ++i) {
      QFileInfo fileInfo = list.at(i);

      if (fileInfo.isDir()) {
        loadImageColorScalesFromDir(fileInfo.absoluteFilePath());
      } else if (fileInfo.suffix() == "png") {
        tulipImageColorScales[fileInfo.fileName()] =
            getColorScaleFromImageFile(fileInfo.absoluteFilePath());
      }
    }
  }
}

void ColorScaleConfigDialog::loadTulipImageColorScales() {
  loadImageColorScalesFromDir(tlpStringToQString(TulipBitmapDir) + "colorscales");
}

void ColorScaleConfigDialog::importColorScaleFromFile(const QString &currentDir) {
  QString imageFilePath = QFileDialog::getOpenFileName(this, tr("Open Image File"), currentDir,
                                                       tr("Image Files (*.png *.jpg *.bmp)"));

  if (imageFilePath.isEmpty())
    return;

  vector<Color> &&colorsList = getColorScaleFromImageFile(imageFilePath);

  if (!colorsList.empty()) {
    ColorScale scaleTmp(colorsList, true);
    setColorScale(scaleTmp);
    displayUserGradientPreview();
  }
}

void ColorScaleConfigDialog::importColorScaleFromColorScaleFile() {
  importColorScaleFromFile(QString((tlp::TulipBitmapDir + '/' + "colorscales").c_str()));
}

void ColorScaleConfigDialog::importColorScaleFromImageFile() {
  importColorScaleFromFile(QString("./"));
}

void ColorScaleConfigDialog::pressButtonBrowse() {

  displaySavedGradientPreview();
}

void ColorScaleConfigDialog::displaySavedGradientPreview() {
  if (_ui->savedColorScalesList->count() > 0 && _ui->savedColorScalesList->currentItem()) {
    QList<QColor> colorsList;
    QString savedColorScaleId = _ui->savedColorScalesList->currentItem()->text();
    bool gradient = true;

    if (tulipImageColorScales.find(savedColorScaleId) != tulipImageColorScales.end()) {
      vector<Color> colors = tulipImageColorScales[savedColorScaleId];
      std::reverse(colors.begin(), colors.end());

      for (size_t i = 0; i < colors.size(); ++i) {
        colorsList.push_back(QColor(colors[i][0], colors[i][1], colors[i][2], colors[i][3]));
      }
    } else {
      TulipSettings::instance().beginGroup("ColorScales");
      QList<QVariant> colorsListv = TulipSettings::instance().value(savedColorScaleId).toList();
      QString gradientScaleId = savedColorScaleId + "_gradient?";
      gradient = TulipSettings::instance().value(gradientScaleId).toBool();
      TulipSettings::instance().endGroup();

      for (int i = 0; i < colorsListv.size(); ++i) {
        colorsList.push_back(colorsListv.at(i).value<QColor>());
      }
    }

    displayGradientPreview(colorsList, gradient, _ui->savedGradientPreview);
  }
}

void ColorScaleConfigDialog::displayUserGradientPreview() {

  QList<QColor> colorsVector;

  for (int i = 0; i < _ui->colorsTable->rowCount(); ++i) {
    colorsVector.push_back(_ui->colorsTable->item(i, 0)->background().color());
  }

  displayGradientPreview(colorsVector, _ui->gradientCB->isChecked(), _ui->userGradientPreview);
}

void ColorScaleConfigDialog::invertEditedColorScale() {
  QList<QTableWidgetItem *> itemsList;
  int nbItems = _ui->colorsTable->rowCount();

  for (int i = 0; i < _ui->colorsTable->rowCount(); ++i) {
    itemsList.push_front(_ui->colorsTable->takeItem(i, 0));
  }

  for (int i = 0; i < nbItems; ++i) {
    _ui->colorsTable->setItem(i, 0, itemsList.at(i));
  }

  displayUserGradientPreview();
}

static qreal clamp(qreal f, qreal minVal, qreal maxVal) {
  return min(max(f, minVal), maxVal);
}

void ColorScaleConfigDialog::displayGradientPreview(const QList<QColor> &colorsVector,
                                                    bool gradient, QLabel *displayLabel) {
  QPixmap pixmap(displayLabel->width(), displayLabel->height());
  pixmap.fill(Qt::transparent);
  QPainter painter;
  painter.begin(&pixmap);

  if (gradient) {
    QLinearGradient qLinearGradient(displayLabel->width() / 2, 0, displayLabel->width() / 2,
                                    displayLabel->height() - 1);
    qreal increment = 1.0 / (colorsVector.size() - 1);
    qreal relPos = 0;

    for (int i = 0; i < colorsVector.size(); ++i) {
      qLinearGradient.setColorAt(clamp(relPos, 0.0, 1.0), colorsVector.at(i));
      relPos += increment;
    }

    painter.fillRect(0, 0, displayLabel->width(), displayLabel->height(), qLinearGradient);
  } else {
    float rectHeight = displayLabel->height() / colorsVector.size();

    for (int i = 0; i < colorsVector.size(); ++i) {
      painter.fillRect(0, i * rectHeight, displayLabel->width(), (i + 1) * rectHeight,
                       QBrush(colorsVector.at(i)));
    }
  }

  painter.end();
  displayLabel->setPixmap(pixmap.scaled(displayLabel->width(), displayLabel->height()));
}

void ColorScaleConfigDialog::nbColorsValueChanged(int value) {
  int lastCount = _ui->colorsTable->rowCount();
  _ui->colorsTable->setRowCount(value);

  if (lastCount < value) {
    for (int j = 0; j < value - lastCount; ++j) {
      QTableWidgetItem *item = new QTableWidgetItem();
      QColor color(255, 255, 255, 255);

      if (_ui->globalAlphaCB->isChecked()) {
        color.setAlpha(_ui->globalAlphaSB->value());
      }

      item->setBackground(QBrush(color));
      item->setFlags(Qt::ItemIsEnabled);
      _ui->colorsTable->setItem(0, lastCount + j, item);
    }
  }

  displayUserGradientPreview();
}

void ColorScaleConfigDialog::colorTableItemDoubleClicked(QTableWidgetItem *item) {
  QColor itemBgColor = item->background().color();
  QColor newColor;

  if (getColorDialog(itemBgColor, this, "Select Color", newColor)) {
    if (_ui->globalAlphaCB->isChecked()) {
      newColor.setAlpha(_ui->globalAlphaSB->value());
    }

    item->setBackground(QBrush(newColor));
    displayUserGradientPreview();
  }
}

void ColorScaleConfigDialog::saveCurrentColorScale() {
  TulipSettings::instance().beginGroup("ColorScales");
  QStringList savedColorScalesList = TulipSettings::instance().childKeys();
  bool ok;
  QString text = QInputDialog::getText(this, tr("Color scale saving"),
                                       tr("Enter a name for this color scale : "),
                                       QLineEdit::Normal, "unnamed", &ok);

  if (ok && !text.isEmpty()) {
    if (savedColorScalesList.contains(text)) {
      QString question = "There is already a color scale saved under the name " + text +
                         ". Do you want to overwrite it?";

      if (QMessageBox::question(this, "Color scale saving", question,
                                QMessageBox::Yes | QMessageBox::No,
                                QMessageBox::Yes) != QMessageBox::Yes) {
        return;
      }
    }

    QList<QVariant> colorsVector;

    TulipSettings::instance().setValue(text, colorsVector);
    QString gradientId = text + "_gradient?";
    TulipSettings::instance().setValue(gradientId, _ui->gradientCB->isChecked());
  }

  TulipSettings::instance().endGroup();
  loadUserSavedColorScales();
}

void ColorScaleConfigDialog::deleteSavedColorScale() {
  if (_ui->savedColorScalesList->count() > 0 && _ui->savedColorScalesList->currentItem()) {
    QString savedColorScaleId = _ui->savedColorScalesList->currentItem()->text();

    if (QMessageBox::question(
            this, "Color scale deleting", "Delete saved color scale " + savedColorScaleId + "?",
            QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) != QMessageBox::Yes) {
      return;
    }

    TulipSettings::instance().beginGroup("ColorScales");
    TulipSettings::instance().remove(savedColorScaleId);
    TulipSettings::instance().remove(savedColorScaleId + "_gradient?");
    TulipSettings::instance().endGroup();
    loadUserSavedColorScales();
  }
}

void ColorScaleConfigDialog::loadUserSavedColorScales() {
  _ui->savedColorScalesList->clear();

  map<QString, vector<Color>>::const_iterator it = tulipImageColorScales.begin();

  for (; it != tulipImageColorScales.end(); ++it) {
    _ui->savedColorScalesList->addItem(it->first);
  }

  TulipSettings::instance().beginGroup("ColorScales");
  QStringList savedColorScalesIdList = TulipSettings::instance().childKeys();

  for (int i = 0; i < savedColorScalesIdList.size(); ++i) {
    if (!savedColorScalesIdList.at(i).contains("_gradient?"))
      _ui->savedColorScalesList->addItem(savedColorScalesIdList.at(i));
  }

  TulipSettings::instance().endGroup();
}

void ColorScaleConfigDialog::resizeEvent(QResizeEvent *) {
  displaySavedGradientPreview();
  displayUserGradientPreview();
  _ui->colorsTable->setColumnWidth(0, _ui->colorsTable->width());
}

void ColorScaleConfigDialog::showEvent(QShowEvent *) {
  displaySavedGradientPreview();
  displayUserGradientPreview();
  _ui->colorsTable->setColumnWidth(0, _ui->colorsTable->width());
}

void ColorScaleConfigDialog::reeditSaveColorScale(QListWidgetItem *savedColorScaleItem) {
  QString savedColorScaleId = savedColorScaleItem->text();
  vector<Color> colorsList;
  bool gradient = true;

  if (tulipImageColorScales.find(savedColorScaleId) != tulipImageColorScales.end()) {
    colorsList = tulipImageColorScales[savedColorScaleId];
  } else {
    TulipSettings::instance().beginGroup("ColorScales");
    QList<QVariant> colorsListv = TulipSettings::instance().value(savedColorScaleId).toList();
    QString gradientScaleId = savedColorScaleId + "_gradient?";
    gradient = TulipSettings::instance().value(gradientScaleId).toBool();
    TulipSettings::instance().endGroup();

    colorsList.reserve(colorsListv.size());
    for (int i = 0; i < colorsListv.size(); ++i) {
      QColor color = colorsListv.at(i).value<QColor>();
      colorsList.emplace_back(color.red(), color.green(), color.blue(), color.alpha());
    }

    reverse(colorsList.begin(), colorsList.end());
  }

  ColorScale scaleTmp(colorsList, gradient);
  setColorScale(scaleTmp);
}

void ColorScaleConfigDialog::setColorScale(const ColorScale &colorScale) {
  if (colorScale.colorScaleInitialized()) {
    for (int row = 0; row < _ui->savedColorScalesList->count(); ++row) {
      QListWidgetItem *item = _ui->savedColorScalesList->item(row);

      if (tulipImageColorScales.find(item->text()) != tulipImageColorScales.end() &&
          colorScale == tulipImageColorScales[item->text()]) {
        // colorScale is a predefined one
        // so select it in the list view
        _ui->savedColorScalesList->setCurrentItem(item);
      }
    }

    disconnect(_ui->nbColors, SIGNAL(valueChanged(int)), this, SLOT(nbColorsValueChanged(int)));

    _ui->colorsTable->clear();
    _ui->colorsTable->setRowCount(0);

    // init dialog with colors in the color Scale
    const auto &colorMap = colorScale.getColorMap();
    unsigned int row = 0;

    if (colorScale.isGradient()) {
      _ui->colorsTable->setRowCount(colorMap.size());
      _ui->nbColors->setValue(colorMap.size());
      _ui->gradientCB->setChecked(true);
      row = colorMap.size() - 1;
    } else {
      _ui->colorsTable->setRowCount(colorMap.size() / 2);
      _ui->nbColors->setValue(colorMap.size() / 2);
      _ui->gradientCB->setChecked(false);
      row = (colorMap.size() / 2) - 1;
    }

    for (auto it = colorMap.begin(); it != colorMap.end();) {
      QTableWidgetItem *item = new QTableWidgetItem();
      item->setFlags(Qt::ItemIsEnabled);
      item->setBackground(QBrush(
          QColor(it->second.getR(), it->second.getG(), it->second.getB(), it->second.getA())));
      _ui->colorsTable->setItem(row, 0, item);
      --row;

      if (colorScale.isGradient()) {
        ++it;
      } else {
        ++it;
        ++it;
      }
    }

    connect(_ui->nbColors, SIGNAL(valueChanged(int)), this, SLOT(nbColorsValueChanged(int)));
    _ui->tabWidget->setCurrentIndex(0);
    applyGlobalAlphaToColorScale();
  } else
    // use latest ColorScale
    setColorScale(ColorScalesManager::getLatestColorScale());
}

const ColorScale &ColorScaleConfigDialog::getColorScale() const {
  return colorScale;
}

void ColorScaleConfigDialog::applyGlobalAlphaToColorScale() {
  if (_ui->globalAlphaCB->isChecked()) {
    for (int i = 0; i < _ui->colorsTable->rowCount(); ++i) {
      QColor itemColor = _ui->colorsTable->item(i, 0)->background().color();
      itemColor.setAlpha(_ui->globalAlphaSB->value());
      _ui->colorsTable->item(i, 0)->setBackground(QBrush(itemColor));
    }

    displayUserGradientPreview();
  }
}
} // namespace tlp


/**
 *
 * This file is part of Tulip (http://tulip.labri.fr)
 *
 * Authors: David Auber and the Tulip development Team
 * from LaBRI, University of Bordeaux
 *
 *
 */

#include <QFileDialog>
#include <QHeaderView>
#include <QLinearGradient>
#include <QPainter>
#include <QMessageBox>
#include <QInputDialog>

#include <algorithm>
#include <vector>

#include <tulip/TlpQtTools.h>
#include <tulip/ColorScaleConfigDialog.h>
#include <tulip/ColorScalesManager.h>
#include <tulip/TlpTools.h>
#include <tulip/TulipSettings.h>

#include "ui_ColorScaleConfigDialog.h"

using namespace std;

namespace tlp {

map<QString, vector<Color>> ColorScaleConfigDialog::tulipImageColorScales;

ColorScaleConfigDialog::ColorScaleConfigDialog(const ColorScale &colorScale, QWidget *parent)
    : QDialog(parent), _ui(new Ui::ColorScaleDialog), colorScale(colorScale) {
  _ui->setupUi(this);
  _ui->colorsTable->setColumnWidth(0, _ui->colorsTable->width());
  _ui->colorsTable->horizontalHeader()->setHidden(true);
  QPalette palette;
  palette.setColor(QPalette::Window, Qt::white);
  _ui->savedGradientPreview->setPalette(palette);
  _ui->userGradientPreview->setPalette(palette);
  _ui->savedGradientPreview->setAutoFillBackground(true);
  _ui->userGradientPreview->setAutoFillBackground(true);
  connect(_ui->savedColorScalesList,
          SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this,
          SLOT(displaySavedGradientPreview()));
  connect(_ui->savedColorScalesList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this,
          SLOT(reeditSaveColorScale(QListWidgetItem *)));
  connect(_ui->nbColors, SIGNAL(valueChanged(int)), this, SLOT(nbColorsValueChanged(int)));
  connect(_ui->colorsTable, SIGNAL(itemDoubleClicked(QTableWidgetItem *)), this,
          SLOT(colorTableItemDoubleClicked(QTableWidgetItem *)));
  connect(_ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(displaySavedGradientPreview()));
  connect(_ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(displayUserGradientPreview()));
  connect(_ui->gradientCB, SIGNAL(clicked()), this, SLOT(displayUserGradientPreview()));
  connect(_ui->saveColorScaleButton, SIGNAL(clicked()), this, SLOT(saveCurrentColorScale()));
  connect(_ui->deleteColorScaleButton, SIGNAL(clicked()), this, SLOT(deleteSavedColorScale()));
  connect(_ui->importFromImgButton, SIGNAL(clicked()), this, SLOT(importColorScaleFromImageFile()));
  connect(_ui->importFromPredefinedCSButton, SIGNAL(clicked()), this,
          SLOT(importColorScaleFromColorScaleFile()));
  connect(_ui->invertColorScaleButton, SIGNAL(clicked()), this, SLOT(invertEditedColorScale()));
  connect(_ui->globalAlphaCB, SIGNAL(toggled(bool)), _ui->globalAlphaSB, SLOT(setEnabled(bool)));
  connect(_ui->globalAlphaCB, SIGNAL(toggled(bool)), this, SLOT(applyGlobalAlphaToColorScale()));
  connect(_ui->globalAlphaSB, SIGNAL(valueChanged(int)), this,
          SLOT(applyGlobalAlphaToColorScale()));

  if (tulipImageColorScales.empty()) {
    loadTulipImageColorScales();
  }

  loadUserSavedColorScales();
  setColorScale(colorScale);
}

ColorScaleConfigDialog::~ColorScaleConfigDialog() {
  delete _ui;
}

void ColorScaleConfigDialog::accept() {
  vector<Color> colors;
  bool gradient = true;

  if (_ui->tabWidget->currentIndex() == 1) {
    if (_ui->savedColorScalesList->count() > 0 && _ui->savedColorScalesList->currentItem()) {
      QString savedColorScaleId = _ui->savedColorScalesList->currentItem()->text();

      if (tulipImageColorScales.find(savedColorScaleId) != tulipImageColorScales.end()) {
        colors = tulipImageColorScales[savedColorScaleId];
      } else {
        TulipSettings::instance().beginGroup("ColorScales");
        QList<QVariant> colorsVector = TulipSettings::instance().value(savedColorScaleId).toList();
        QString gradientScaleId = savedColorScaleId + "_gradient?";
        gradient = TulipSettings::instance().value(gradientScaleId).toBool();
        TulipSettings::instance().endGroup();

        colors.reserve(colorsVector.size());
        for (int i = 0; i < colorsVector.size(); ++i) {
          colors.emplace_back(colorsVector.at(i).value<QColor>().red(),
                              colorsVector.at(i).value<QColor>().green(),
                              colorsVector.at(i).value<QColor>().blue(),
                              colorsVector.at(i).value<QColor>().alpha());
        }

        std::reverse(colors.begin(), colors.end());
      }
    }
  } else {
    colors.reserve(_ui->colorsTable->rowCount());
    for (int i = 0; i < _ui->colorsTable->rowCount(); ++i) {
      QColor itemColor = _ui->colorsTable->item(i, 0)->background().color();
      colors.emplace_back(itemColor.red(), itemColor.green(), itemColor.blue(), itemColor.alpha());
    }

    std::reverse(colors.begin(), colors.end());
    gradient = _ui->gradientCB->isChecked();
  }

  if (!colors.empty()) {
    colorScale.setColorScale(colors, gradient);
  }

  ColorScalesManager::setLatestColorScale(colorScale);

  QDialog::accept();
}

vector<Color> ColorScaleConfigDialog::getColorScaleFromImageFile(const QString &imageFilePath) {
  QImage gradientImage(imageFilePath);
  unsigned int imageHeight = gradientImage.height();

  unsigned int step = 1;

  if (imageHeight > 50)
    step = 10;

  vector<Color> colors;
  colors.reserve(imageHeight % step ? imageHeight / step : imageHeight / step + 1);
  for (unsigned int i = 0; i < imageHeight; i += step) {
    QRgb pixelValue = gradientImage.pixel(0, i);
    colors.emplace_back(qRed(pixelValue), qGreen(pixelValue), qBlue(pixelValue),
                        qAlpha(pixelValue));
  }

  if (imageHeight % step != 0) {
    QRgb pixelValue = gradientImage.pixel(0, imageHeight - 1);
    colors.emplace_back(qRed(pixelValue), qGreen(pixelValue), qBlue(pixelValue),
                        qAlpha(pixelValue));
  }

  std::reverse(colors.begin(), colors.end());
  return colors;
}

ColorScale ColorScaleConfigDialog::getColorScaleFromImageFile(const std::string &imageFilePath,
                                                              bool gradient) {
  return ColorScale(getColorScaleFromImageFile(tlpStringToQString(imageFilePath)), gradient);
}

void ColorScaleConfigDialog::loadImageColorScalesFromDir(const QString &colorScalesDir) {
  QFileInfo colorscaleDirectory(colorScalesDir);

  if (colorscaleDirectory.exists() && colorscaleDirectory.isDir()) {
    QDir dir(colorscaleDirectory.absoluteFilePath());
    dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
    QFileInfoList list = dir.entryInfoList();

    for (int i = 0; i < list.size(); ++i) {
      QFileInfo fileInfo = list.at(i);

      if (fileInfo.isDir()) {
        loadImageColorScalesFromDir(fileInfo.absoluteFilePath());
      } else if (fileInfo.suffix() == "png") {
        tulipImageColorScales[fileInfo.fileName()] =
            getColorScaleFromImageFile(fileInfo.absoluteFilePath());
      }
    }
  }
}

void ColorScaleConfigDialog::loadTulipImageColorScales() {
  loadImageColorScalesFromDir(tlpStringToQString(TulipBitmapDir) + "colorscales");
}

void ColorScaleConfigDialog::importColorScaleFromFile(const QString &currentDir) {
  QString imageFilePath = QFileDialog::getOpenFileName(this, tr("Open Image File"), currentDir,
                                                       tr("Image Files (*.png *.jpg *.bmp)"));

  if (imageFilePath.isEmpty())
    return;

  vector<Color> &&colorsList = getColorScaleFromImageFile(imageFilePath);

  if (!colorsList.empty()) {
    ColorScale scaleTmp(colorsList, true);
    setColorScale(scaleTmp);
    displayUserGradientPreview();
  }
}

void ColorScaleConfigDialog::importColorScaleFromColorScaleFile() {
  importColorScaleFromFile(QString((tlp::TulipBitmapDir + '/' + "colorscales").c_str()));
}

void ColorScaleConfigDialog::importColorScaleFromImageFile() {
  importColorScaleFromFile(QString("./"));
}

void ColorScaleConfigDialog::pressButtonBrowse() {

  displaySavedGradientPreview();
}

void ColorScaleConfigDialog::displaySavedGradientPreview() {
  if (_ui->savedColorScalesList->count() > 0 && _ui->savedColorScalesList->currentItem()) {
    QList<QColor> colorsList;
    QString savedColorScaleId = _ui->savedColorScalesList->currentItem()->text();
    bool gradient = true;

    if (tulipImageColorScales.find(savedColorScaleId) != tulipImageColorScales.end()) {
      vector<Color> colors = tulipImageColorScales[savedColorScaleId];
      std::reverse(colors.begin(), colors.end());

      for (size_t i = 0; i < colors.size(); ++i) {
        colorsList.push_back(QColor(colors[i][0], colors[i][1], colors[i][2], colors[i][3]));
      }
    } else {
      TulipSettings::instance().beginGroup("ColorScales");
      QList<QVariant> colorsListv = TulipSettings::instance().value(savedColorScaleId).toList();
      QString gradientScaleId = savedColorScaleId + "_gradient?";
      gradient = TulipSettings::instance().value(gradientScaleId).toBool();
      TulipSettings::instance().endGroup();

      for (int i = 0; i < colorsListv.size(); ++i) {
        colorsList.push_back(colorsListv.at(i).value<QColor>());
      }
    }

    displayGradientPreview(colorsList, gradient, _ui->savedGradientPreview);
  }
}

void ColorScaleConfigDialog::displayUserGradientPreview() {

  QList<QColor> colorsVector;

  for (int i = 0; i < _ui->colorsTable->rowCount(); ++i) {
    colorsVector.push_back(_ui->colorsTable->item(i, 0)->background().color());
  }

  displayGradientPreview(colorsVector, _ui->gradientCB->isChecked(), _ui->userGradientPreview);
}

void ColorScaleConfigDialog::invertEditedColorScale() {
  QList<QTableWidgetItem *> itemsList;
  int nbItems = _ui->colorsTable->rowCount();

  for (int i = 0; i < _ui->colorsTable->rowCount(); ++i) {
    itemsList.push_front(_ui->colorsTable->takeItem(i, 0));
  }

  for (int i = 0; i < nbItems; ++i) {
    _ui->colorsTable->setItem(i, 0, itemsList.at(i));
  }

  displayUserGradientPreview();
}

static qreal clamp(qreal f, qreal minVal, qreal maxVal) {
  return min(max(f, minVal), maxVal);
}

void ColorScaleConfigDialog::displayGradientPreview(const QList<QColor> &colorsVector,
                                                    bool gradient, QLabel *displayLabel) {
  QPixmap pixmap(displayLabel->width(), displayLabel->height());
  pixmap.fill(Qt::transparent);
  QPainter painter;
  painter.begin(&pixmap);

  if (gradient) {
    QLinearGradient qLinearGradient(displayLabel->width() / 2, 0, displayLabel->width() / 2,
                                    displayLabel->height() - 1);
    qreal increment = 1.0 / (colorsVector.size() - 1);
    qreal relPos = 0;

    for (int i = 0; i < colorsVector.size(); ++i) {
      qLinearGradient.setColorAt(clamp(relPos, 0.0, 1.0), colorsVector.at(i));
      relPos += increment;
    }

    painter.fillRect(0, 0, displayLabel->width(), displayLabel->height(), qLinearGradient);
  } else {
    float rectHeight = displayLabel->height() / colorsVector.size();

    for (int i = 0; i < colorsVector.size(); ++i) {

      painter.fillRect(0, i * rectHeight, displayLabel->width(), (i + 1) * rectHeight,
                       QBrush(colorsVector.at(i)));
    }
  }

  painter.end();
  displayLabel->setPixmap(pixmap.scaled(displayLabel->width(), displayLabel->height()));
}

void ColorScaleConfigDialog::nbColorsValueChanged(int value) {
  int lastCount = _ui->colorsTable->rowCount();
  _ui->colorsTable->setRowCount(value);

  if (lastCount < value) {
    for (int j = 0; j < value - lastCount; ++j) {
      QTableWidgetItem *item = new QTableWidgetItem();
      QColor color(255, 255, 255, 255);

      if (_ui->globalAlphaCB->isChecked()) {
        color.setAlpha(_ui->globalAlphaSB->value());
      }

      item->setBackground(QBrush(color));
      item->setFlags(Qt::ItemIsEnabled);
      _ui->colorsTable->setItem(0, lastCount + j, item);
    }
  }

  displayUserGradientPreview();
}

void ColorScaleConfigDialog::colorTableItemDoubleClicked(QTableWidgetItem *item) {
  QColor itemBgColor = item->background().color();
  QColor newColor;

  if (getColorDialog(itemBgColor, this, "Select Color", newColor)) {
    if (_ui->globalAlphaCB->isChecked()) {
      newColor.setAlpha(_ui->globalAlphaSB->value());
    }

    item->setBackground(QBrush(newColor));
    displayUserGradientPreview();
  }
}

void ColorScaleConfigDialog::saveCurrentColorScale() {
  TulipSettings::instance().beginGroup("ColorScales");
  QStringList savedColorScalesList = TulipSettings::instance().childKeys();
  bool ok;
  QString text = QInputDialog::getText(this, tr("Color scale saving"),
                                       tr("Enter a name for this color scale : "),
                                       QLineEdit::Normal, "unnamed", &ok);

  if (ok && !text.isEmpty()) {
    if (savedColorScalesList.contains(text)) {
      QString question = "There is already a color scale saved under the name " + text +
                         ". Do you want to overwrite it?";

      if (QMessageBox::question(this, "Color scale saving", question,
                                QMessageBox::Yes | QMessageBox::No,
                                QMessageBox::Yes) != QMessageBox::Yes) {
        return;
      }
    }

    QList<QVariant> colorsVector;

    for (int i = 0; i < _ui->colorsTable->rowCount(); ++i) {
      colorsVector.push_back(QVariant(_ui->colorsTable->item(i, 0)->background().color()));
    }

    TulipSettings::instance().setValue(text, colorsVector);
    QString gradientId = text + "_gradient?";
    TulipSettings::instance().setValue(gradientId, _ui->gradientCB->isChecked());
  }

  TulipSettings::instance().endGroup();
  loadUserSavedColorScales();
}

void ColorScaleConfigDialog::deleteSavedColorScale() {
  if (_ui->savedColorScalesList->count() > 0 && _ui->savedColorScalesList->currentItem()) {
    QString savedColorScaleId = _ui->savedColorScalesList->currentItem()->text();

    if (QMessageBox::question(
            this, "Color scale deleting", "Delete saved color scale " + savedColorScaleId + "?",
            QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) != QMessageBox::Yes) {
      return;
    }

    TulipSettings::instance().beginGroup("ColorScales");
    TulipSettings::instance().remove(savedColorScaleId);
    TulipSettings::instance().remove(savedColorScaleId + "_gradient?");
    TulipSettings::instance().endGroup();
    loadUserSavedColorScales();
  }
}

void ColorScaleConfigDialog::loadUserSavedColorScales() {
  _ui->savedColorScalesList->clear();

  map<QString, vector<Color>>::const_iterator it = tulipImageColorScales.begin();

  for (; it != tulipImageColorScales.end(); ++it) {
    _ui->savedColorScalesList->addItem(it->first);
  }

  TulipSettings::instance().beginGroup("ColorScales");
  QStringList savedColorScalesIdList = TulipSettings::instance().childKeys();

  for (int i = 0; i < savedColorScalesIdList.size(); ++i) {
    if (!savedColorScalesIdList.at(i).contains("_gradient?"))
      _ui->savedColorScalesList->addItem(savedColorScalesIdList.at(i));
  }

  TulipSettings::instance().endGroup();
}

void ColorScaleConfigDialog::resizeEvent(QResizeEvent *) {
  displaySavedGradientPreview();
  displayUserGradientPreview();
  _ui->colorsTable->setColumnWidth(0, _ui->colorsTable->width());
}

void ColorScaleConfigDialog::showEvent(QShowEvent *) {
  displaySavedGradientPreview();
  displayUserGradientPreview();
  _ui->colorsTable->setColumnWidth(0, _ui->colorsTable->width());
}

void ColorScaleConfigDialog::reeditSaveColorScale(QListWidgetItem *savedColorScaleItem) {
  QString savedColorScaleId = savedColorScaleItem->text();
  vector<Color> colorsList;
  bool gradient = true;

  if (tulipImageColorScales.find(savedColorScaleId) != tulipImageColorScales.end()) {
    colorsList = tulipImageColorScales[savedColorScaleId];
  } else {
    TulipSettings::instance().beginGroup("ColorScales");
    QList<QVariant> colorsListv = TulipSettings::instance().value(savedColorScaleId).toList();
    QString gradientScaleId = savedColorScaleId + "_gradient?";
    gradient = TulipSettings::instance().value(gradientScaleId).toBool();
    TulipSettings::instance().endGroup();

    colorsList.reserve(colorsListv.size());
    for (int i = 0; i < colorsListv.size(); ++i) {
      QColor color = colorsListv.at(i).value<QColor>();
      colorsList.emplace_back(color.red(), color.green(), color.blue(), color.alpha());
    }

    reverse(colorsList.begin(), colorsList.end());
  }

  ColorScale scaleTmp(colorsList, gradient);
  setColorScale(scaleTmp);
}

void ColorScaleConfigDialog::setColorScale(const ColorScale &colorScale) {
  if (colorScale.colorScaleInitialized()) {
    for (int row = 0; row < _ui->savedColorScalesList->count(); ++row) {
      QListWidgetItem *item = _ui->savedColorScalesList->item(row);

      if (tulipImageColorScales.find(item->text()) != tulipImageColorScales.end() &&
          colorScale == tulipImageColorScales[item->text()]) {
        // colorScale is a predefined one
        // so select it in the list view
        _ui->savedColorScalesList->setCurrentItem(item);
      }
    }

    disconnect(_ui->nbColors, SIGNAL(valueChanged(int)), this, SLOT(nbColorsValueChanged(int)));

    _ui->colorsTable->clear();
    _ui->colorsTable->setRowCount(0);

    // init dialog with colors in the color Scale
    const auto &colorMap = colorScale.getColorMap();
    unsigned int row = 0;

    if (colorScale.isGradient()) {
      _ui->colorsTable->setRowCount(colorMap.size());
      _ui->nbColors->setValue(colorMap.size());
      _ui->gradientCB->setChecked(true);
      row = colorMap.size() - 1;
    } else {
      _ui->colorsTable->setRowCount(colorMap.size() / 2);
      _ui->nbColors->setValue(colorMap.size() / 2);
      _ui->gradientCB->setChecked(false);
      row = (colorMap.size() / 2) - 1;
    }

    for (auto it = colorMap.begin(); it != colorMap.end();) {
      QTableWidgetItem *item = new QTableWidgetItem();
      item->setFlags(Qt::ItemIsEnabled);
      item->setBackground(QBrush(
          QColor(it->second.getR(), it->second.getG(), it->second.getB(), it->second.getA())));
      _ui->colorsTable->setItem(row, 0, item);
      --row;

      if (colorScale.isGradient()) {
        ++it;
      } else {
        ++it;
        ++it;
      }
    }

    connect(_ui->nbColors, SIGNAL(valueChanged(int)), this, SLOT(nbColorsValueChanged(int)));
    _ui->tabWidget->setCurrentIndex(0);
    applyGlobalAlphaToColorScale();
  } else
    // use latest ColorScale
    setColorScale(ColorScalesManager::getLatestColorScale());
}

const ColorScale &ColorScaleConfigDialog::getColorScale() const {
  return colorScale;
}

void ColorScaleConfigDialog::applyGlobalAlphaToColorScale() {
  if (_ui->globalAlphaCB->isChecked()) {
    for (int i = 0; i < _ui->colorsTable->rowCount(); ++i) {
      QColor itemColor = _ui->colorsTable->item(i, 0)->background().color();
      itemColor.setAlpha(_ui->globalAlphaSB->value());
      _ui->colorsTable->item(i, 0)->setBackground(QBrush(itemColor));
    }

    displayUserGradientPreview();
  }
}
} // namespace tlp


/**

   Author: Leonardo de Moura

   C++11 does not support std::shared_mutex and std::shared_lock yet.
   This piece of code is based on the proposal submitted to the C++
   standardization committee. The proposal was written by Howard
   Hinnant. The proposal is also part of the Boost library which is
*/
#include "util/debug.h"
#include "util/shared_mutex.h"

namespace lean {
#if defined(LEAN_MULTI_THREAD)
shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {}
shared_mutex::~shared_mutex() {
    lock_guard<mutex> lock(m_mutex);
}

void shared_mutex::lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return; // already has the lock
    }
    while (m_state & write_entered)
        m_gate1.wait(lock);
    m_state |= write_entered;
    while (m_state & readers)
        m_gate2.wait(lock);
    lean_assert(m_rw_counter == 0);
    m_rw_owner   = this_thread::get_id();
    m_rw_counter = 1;
}

{
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return true; // already has the lock
    }
    if (m_state == 0) {
        m_state      = write_entered;
        lean_assert(m_rw_counter == 0);
        m_rw_owner   = this_thread::get_id();
        m_rw_counter = 1;
        return true;
    }
    return false;
}

void shared_mutex::unlock() {
    lock_guard<mutex> lock(m_mutex);
    lean_assert(m_rw_owner == this_thread::get_id());
    lean_assert(m_rw_counter > 0);
    m_rw_counter--;
    if (m_rw_counter > 0)
        return; // keep the lock
    m_rw_owner = thread::id(); // reset owner
    lean_assert(m_rw_counter == 0);
    lean_assert(m_rw_owner != this_thread::get_id());
    m_state = 0;
    m_gate1.notify_all();
}

void shared_mutex::lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return; // already has the lock
    }
    while ((m_state & write_entered) || (m_state & readers) == readers)
        m_gate1.wait(lock);
    unsigned num_readers = (m_state & readers) + 1;
    m_state &= ~readers;
    m_state |= num_readers;
}

bool shared_mutex::try_lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return true; // already has the lock
    }
    unsigned num_readers = m_state & readers;
    if (!(m_state & write_entered) && num_readers != readers) {
        ++num_readers;
        m_state &= ~readers;
        m_state |= num_readers;
        return true;
    }
    return false;
}

void shared_mutex::unlock_shared() {
    lock_guard<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        // if we have a rw (aka unshared) lock, then
        // the shared lock must be nested.
        lean_assert(m_rw_counter > 1);
        m_rw_counter--;
        return;
    }
    lean_assert(m_rw_counter == 0);
    unsigned num_readers = (m_state & readers) - 1;
    m_state &= ~readers;
    m_state |= num_readers;
    if (m_state & write_entered) {
        if (num_readers == 0)
            m_gate2.notify_one();
    } else {
        if (num_readers == readers - 1)
            m_gate1.notify_one();
    }
}
#endif
}


{
    lock_guard<mutex> lock(m_mutex);
}

/**

   Author: Leonardo de Moura

   C++11 does not support std::shared_mutex and std::shared_lock yet.
   This piece of code is based on the proposal submitted to the C++
   standardization committee. The proposal was written by Howard
   Hinnant. The proposal is also part of the Boost library which is
*/
#include "util/debug.h"
#include "util/shared_mutex.h"

namespace lean {
#if defined(LEAN_MULTI_THREAD)
shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {}
shared_mutex::~shared_mutex()

void shared_mutex::lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return; // already has the lock
    }
    while (m_state & write_entered)
        m_gate1.wait(lock);
    m_state |= write_entered;
    while (m_state & readers)
        m_gate2.wait(lock);
    lean_assert(m_rw_counter == 0);
    m_rw_owner   = this_thread::get_id();
    m_rw_counter = 1;
}

bool shared_mutex::try_lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return true; // already has the lock
    }
    if (m_state == 0) {
        m_state      = write_entered;
        lean_assert(m_rw_counter == 0);
        m_rw_owner   = this_thread::get_id();
        m_rw_counter = 1;
        return true;
    }
    return false;
}

void shared_mutex::unlock() {
    lock_guard<mutex> lock(m_mutex);
    lean_assert(m_rw_owner == this_thread::get_id());
    lean_assert(m_rw_counter > 0);
    m_rw_counter--;
    if (m_rw_counter > 0)
        return; // keep the lock
    m_rw_owner = thread::id(); // reset owner
    lean_assert(m_rw_counter == 0);
    lean_assert(m_rw_owner != this_thread::get_id());
    m_state = 0;
    m_gate1.notify_all();
}

void shared_mutex::lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return; // already has the lock
    }
    while ((m_state & write_entered) || (m_state & readers) == readers)
        m_gate1.wait(lock);
    unsigned num_readers = (m_state & readers) + 1;
    m_state &= ~readers;
    m_state |= num_readers;
}

bool shared_mutex::try_lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return true; // already has the lock
    }
    unsigned num_readers = m_state & readers;
    if (!(m_state & write_entered) && num_readers != readers) {
        ++num_readers;
        m_state &= ~readers;
        m_state |= num_readers;
        return true;
    }
    return false;
}

void shared_mutex::unlock_shared() {
    lock_guard<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        // if we have a rw (aka unshared) lock, then
        // the shared lock must be nested.
        lean_assert(m_rw_counter > 1);
        m_rw_counter--;
        return;
    }
    lean_assert(m_rw_counter == 0);
    unsigned num_readers = (m_state & readers) - 1;
    m_state &= ~readers;
    m_state |= num_readers;
    if (m_state & write_entered) {
        if (num_readers == 0)
            m_gate2.notify_one();
    } else {
        if (num_readers == readers - 1)
            m_gate1.notify_one();
    }
}
#endif
}


/**

   Author: Leonardo de Moura

   C++11 does not support std::shared_mutex and std::shared_lock yet.
   This piece of code is based on the proposal submitted to the C++
   standardization committee. The proposal was written by Howard
   Hinnant. The proposal is also part of the Boost library which is
*/
#include "util/debug.h"
#include "util/shared_mutex.h"

namespace lean {
#if defined(LEAN_MULTI_THREAD)
shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {}
shared_mutex::~shared_mutex() {
    lock_guard<mutex> lock(m_mutex);
}

void shared_mutex::lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return; // already has the lock
    }
    while (m_state & write_entered)
        m_gate1.wait(lock);
    m_state |= write_entered;
    while (m_state & readers)
        m_gate2.wait(lock);
    lean_assert(m_rw_counter == 0);
    m_rw_owner   = this_thread::get_id();
    m_rw_counter = 1;
}

bool shared_mutex::try_lock()

    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return true; // already has the lock
    }
    if (m_state == 0) {
        m_state      = write_entered;
        lean_assert(m_rw_counter == 0);
        m_rw_owner   = this_thread::get_id();
        m_rw_counter = 1;
        return true;
    }
    return false;
}

void shared_mutex::unlock() {
    lock_guard<mutex> lock(m_mutex);
    lean_assert(m_rw_owner == this_thread::get_id());
    lean_assert(m_rw_counter > 0);
    m_rw_counter--;
    if (m_rw_counter > 0)
        return; // keep the lock
    m_rw_owner = thread::id(); // reset owner
    lean_assert(m_rw_counter == 0);
    lean_assert(m_rw_owner != this_thread::get_id());
    m_state = 0;
    m_gate1.notify_all();
}

void shared_mutex::lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return; // already has the lock
    }
    while ((m_state & write_entered) || (m_state & readers) == readers)
        m_gate1.wait(lock);
    unsigned num_readers = (m_state & readers) + 1;
    m_state &= ~readers;
    m_state |= num_readers;
}

bool shared_mutex::try_lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return true; // already has the lock
    }
    unsigned num_readers = m_state & readers;
    if (!(m_state & write_entered) && num_readers != readers) {
        ++num_readers;
        m_state &= ~readers;
        m_state |= num_readers;
        return true;
    }
    return false;
}

void shared_mutex::unlock_shared() {
    lock_guard<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        // if we have a rw (aka unshared) lock, then
        // the shared lock must be nested.
        lean_assert(m_rw_counter > 1);
        m_rw_counter--;
        return;
    }
    lean_assert(m_rw_counter == 0);
    unsigned num_readers = (m_state & readers) - 1;
    m_state &= ~readers;
    m_state |= num_readers;
    if (m_state & write_entered) {
        if (num_readers == 0)
            m_gate2.notify_one();
    } else {
        if (num_readers == readers - 1)
            m_gate1.notify_one();
    }
}
#endif
}


if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return true; // already has the lock
    }
    if (m_state == 0) {
        m_state      = write_entered;
        lean_assert(m_rw_counter == 0);
        m_rw_owner   = this_thread::get_id();
        m_rw_counter = 1;
        return true;
    }

/**

   Author: Leonardo de Moura

   C++11 does not support std::shared_mutex and std::shared_lock yet.
   This piece of code is based on the proposal submitted to the C++
   standardization committee. The proposal was written by Howard
   Hinnant. The proposal is also part of the Boost library which is
*/
#include "util/debug.h"
#include "util/shared_mutex.h"

namespace lean {
#if defined(LEAN_MULTI_THREAD)
shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {}
shared_mutex::~shared_mutex() {
    lock_guard<mutex> lock(m_mutex);
}

void shared_mutex::lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return; // already has the lock
    }
    while (m_state & write_entered)
        m_gate1.wait(lock);
    m_state |= write_entered;
    while (m_state & readers)
        m_gate2.wait(lock);
    lean_assert(m_rw_counter == 0);
    m_rw_owner   = this_thread::get_id();
    m_rw_counter = 1;
}

bool shared_mutex::try_lock() {
    unique_lock<mutex> lock(m_mutex);

    return false;
}

void shared_mutex::unlock() {
    lock_guard<mutex> lock(m_mutex);
    lean_assert(m_rw_owner == this_thread::get_id());
    lean_assert(m_rw_counter > 0);
    m_rw_counter--;
    if (m_rw_counter > 0)
        return; // keep the lock
    m_rw_owner = thread::id(); // reset owner
    lean_assert(m_rw_counter == 0);
    lean_assert(m_rw_owner != this_thread::get_id());
    m_state = 0;
    m_gate1.notify_all();
}

void shared_mutex::lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return; // already has the lock
    }
    while ((m_state & write_entered) || (m_state & readers) == readers)
        m_gate1.wait(lock);
    unsigned num_readers = (m_state & readers) + 1;
    m_state &= ~readers;
    m_state |= num_readers;
}

bool shared_mutex::try_lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return true; // already has the lock
    }
    unsigned num_readers = m_state & readers;
    if (!(m_state & write_entered) && num_readers != readers) {
        ++num_readers;
        m_state &= ~readers;
        m_state |= num_readers;
        return true;
    }
    return false;
}

void shared_mutex::unlock_shared() {
    lock_guard<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        // if we have a rw (aka unshared) lock, then
        // the shared lock must be nested.
        lean_assert(m_rw_counter > 1);
        m_rw_counter--;
        return;
    }
    lean_assert(m_rw_counter == 0);
    unsigned num_readers = (m_state & readers) - 1;
    m_state &= ~readers;
    m_state |= num_readers;
    if (m_state & write_entered) {
        if (num_readers == 0)
            m_gate2.notify_one();
    } else {
        if (num_readers == readers - 1)
            m_gate1.notify_one();
    }
}
#endif
}


/**

   Author: Leonardo de Moura

   C++11 does not support std::shared_mutex and std::shared_lock yet.
   This piece of code is based on the proposal submitted to the C++
   standardization committee. The proposal was written by Howard
   Hinnant. The proposal is also part of the Boost library which is
*/
#include "util/debug.h"
#include "util/shared_mutex.h"

namespace lean {
#if defined(LEAN_MULTI_THREAD)
shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {}
shared_mutex::~shared_mutex() {
    lock_guard<mutex> lock(m_mutex);
}

void shared_mutex::lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return; // already has the lock
    }
    while (m_state & write_entered)
        m_gate1.wait(lock);
    m_state |= write_entered;
    while (m_state & readers)
        m_gate2.wait(lock);
    lean_assert(m_rw_counter == 0);
    m_rw_owner   = this_thread::get_id();
    m_rw_counter = 1;
}

bool shared_mutex::try_lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return true; // already has the lock
    }
    if (m_state == 0) {
        m_state      = write_entered;
        lean_assert(m_rw_counter == 0);
        m_rw_owner   = this_thread::get_id();
        m_rw_counter = 1;
        return true;
    }

void shared_mutex::unlock() {
    lock_guard<mutex> lock(m_mutex);
    lean_assert(m_rw_owner == this_thread::get_id());
    lean_assert(m_rw_counter > 0);
    m_rw_counter--;
    if (m_rw_counter > 0)
        return; // keep the lock
    m_rw_owner = thread::id(); // reset owner
    lean_assert(m_rw_counter == 0);
    lean_assert(m_rw_owner != this_thread::get_id());
    m_state = 0;
    m_gate1.notify_all();
}

void shared_mutex::lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return; // already has the lock
    }
    while ((m_state & write_entered) || (m_state & readers) == readers)
        m_gate1.wait(lock);
    unsigned num_readers = (m_state & readers) + 1;
    m_state &= ~readers;
    m_state |= num_readers;
}

bool shared_mutex::try_lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return true; // already has the lock
    }
    unsigned num_readers = m_state & readers;
    if (!(m_state & write_entered) && num_readers != readers) {
        ++num_readers;
        m_state &= ~readers;
        m_state |= num_readers;
        return true;
    }
    return false;
}

void shared_mutex::unlock_shared() {
    lock_guard<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        // if we have a rw (aka unshared) lock, then
        // the shared lock must be nested.
        lean_assert(m_rw_counter > 1);
        m_rw_counter--;
        return;
    }
    lean_assert(m_rw_counter == 0);
    unsigned num_readers = (m_state & readers) - 1;
    m_state &= ~readers;
    m_state |= num_readers;
    if (m_state & write_entered) {
        if (num_readers == 0)
            m_gate2.notify_one();
    } else {
        if (num_readers == readers - 1)
            m_gate1.notify_one();
    }
}
#endif
}


/**

   Author: Leonardo de Moura

   C++11 does not support std::shared_mutex and std::shared_lock yet.
   This piece of code is based on the proposal submitted to the C++
   standardization committee. The proposal was written by Howard
   Hinnant. The proposal is also part of the Boost library which is
*/
#include "util/debug.h"
#include "util/shared_mutex.h"

namespace lean {
#if defined(LEAN_MULTI_THREAD)
shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {}
shared_mutex::~shared_mutex() {
    lock_guard<mutex> lock(m_mutex);
}

void shared_mutex::lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return; // already has the lock
    }
    while (m_state & write_entered)
        m_gate1.wait(lock);
    m_state |= write_entered;
    while (m_state & readers)
        m_gate2.wait(lock);
    lean_assert(m_rw_counter == 0);
    m_rw_owner   = this_thread::get_id();
    m_rw_counter = 1;
}

bool shared_mutex::try_lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return true; // already has the lock
    }
    if (m_state == 0) {
        m_state      = write_entered;
        lean_assert(m_rw_counter == 0);
        m_rw_owner   = this_thread::get_id();
        m_rw_counter = 1;
        return true;
    }
    return false;
}

void shared_mutex::unlock() {
    lock_guard<mutex> lock(m_mutex);
    lean_assert(m_rw_owner == this_thread::get_id());
    lean_assert(m_rw_counter > 0);
    m_rw_counter--;
    if (m_rw_counter > 0)
        return; // keep the lock
    m_rw_owner = thread::id(); // reset owner
    lean_assert(m_rw_counter == 0);
    lean_assert(m_rw_owner != this_thread::get_id());
    m_state = 0;
    m_gate1.notify_all();
}

void shared_mutex::lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return; // already has the lock
    }
    while ((m_state & write_entered) || (m_state & readers) == readers)
        m_gate1.wait(lock);
    unsigned num_readers = (m_state & readers) + 1;
    m_state &

    m_state |= num_readers;
}

bool shared_mutex::try_lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return true; // already has the lock
    }
    unsigned num_readers = m_state & readers;
    if (!(m_state & write_entered) && num_readers != readers) {
        ++num_readers;
        m_state &= ~readers;
        m_state |= num_readers;
        return true;
    }
    return false;
}

void shared_mutex::unlock_shared() {
    lock_guard<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        // if we have a rw (aka unshared) lock, then
        // the shared lock must be nested.
        lean_assert(m_rw_counter > 1);
        m_rw_counter--;
        return;
    }
    lean_assert(m_rw_counter == 0);
    unsigned num_readers = (m_state & readers) - 1;
    m_state &= ~readers;
    m_state |= num_readers;
    if (m_state & write_entered) {
        if (num_readers == 0)
            m_gate2.notify_one();
    } else {
        if (num_readers == readers - 1)
            m_gate1.notify_one();
    }
}
#endif
}


/**

   Author: Leonardo de Moura

   C++11 does not support std::shared_mutex and std::shared_lock yet.
   This piece of code is based on the proposal submitted to the C++
   standardization committee. The proposal was written by Howard
   Hinnant. The proposal is also part of the Boost library which is
*/
#include "util/debug.h"
#include "util/shared_mutex.h"

namespace lean {
#if defined(LEAN_MULTI_THREAD)
shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {}
shared_mutex::~shared_mutex() {
    lock_guard<mutex> lock(m_mutex);
}

void shared_mutex::lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return; // already has the lock
    }
    while (m_state & write_entered)
        m_gate1.wait(lock);
    m_state |= write_entered;
    while (m_state & readers)
        m_gate2.wait(lock);
    lean_assert(m_rw_counter == 0);
    m_rw_owner   = this_thread::get_id();
    m_rw_counter = 1;
}

bool shared_mutex::try_lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return true; // already has the lock
    }
    if (m_state == 0) {
        m_state      = write_entered;
        lean_assert(m_rw_counter == 0);
        m_rw_owner   = this_thread::get_id();
        m_rw_counter = 1;
        return true;
    }
    return false;
}

void shared_mutex::unlock() {
    lock_guard<mutex> lock(m_mutex);
    lean_assert(m_rw_owner == this_thread::get_id());
    lean_assert(m_rw_counter > 0);
    m_rw_counter--;

 // keep the lock
    m_rw_owner = thread::id(); // reset owner
    lean_assert(m_rw_counter == 0);
    lean_assert(m_rw_owner != this_thread::get_id());
    m_state = 0;
    m_gate1.notify_all();
}

void shared_mutex::lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return; // already has the lock
    }
    while ((m_state & write_entered) || (m_state & readers) == readers)
        m_gate1.wait(lock);
    unsigned num_readers = (m_state & readers) + 1;
    m_state &= ~readers;
    m_state |= num_readers;
}

bool shared_mutex::try_lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return true; // already has the lock
    }
    unsigned num_readers = m_state & readers;
    if (!(m_state & write_entered) && num_readers != readers) {
        ++num_readers;
        m_state &= ~readers;
        m_state |= num_readers;
        return true;
    }
    return false;
}

void shared_mutex::unlock_shared() {
    lock_guard<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        // if we have a rw (aka unshared) lock, then
        // the shared lock must be nested.
        lean_assert(m_rw_counter > 1);
        m_rw_counter--;
        return;
    }
    lean_assert(m_rw_counter == 0);
    unsigned num_readers = (m_state & readers) - 1;
    m_state &= ~readers;
    m_state |= num_readers;
    if (m_state & write_entered) {
        if (num_readers == 0)
            m_gate2.notify_one();
    } else {
        if (num_readers == readers - 1)
            m_gate1.notify_one();
    }
}
#endif
}


while (m_state & readers)
        m_gate2.wait(lock);

/**

   Author: Leonardo de Moura

   C++11 does not support std::shared_mutex and std::shared_lock yet.
   This piece of code is based on the proposal submitted to the C++
   standardization committee. The proposal was written by Howard
   Hinnant. The proposal is also part of the Boost library which is
*/
#include "util/debug.h"
#include "util/shared_mutex.h"

namespace lean {
#if defined(LEAN_MULTI_THREAD)
shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {}
shared_mutex::~shared_mutex() {
    lock_guard<mutex> lock(m_mutex);
}

void shared_mutex::lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return; // already has the lock
    }
    while (m_state & write_entered)
        m_gate1.wait(lock);
    m_state |= write_entered;

    lean_assert(m_rw_counter == 0);
    m_rw_owner   = this_thread::get_id();
    m_rw_counter = 1;
}

bool shared_mutex::try_lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return true; // already has the lock
    }
    if (m_state == 0) {
        m_state      = write_entered;
        lean_assert(m_rw_counter == 0);
        m_rw_owner   = this_thread::get_id();
        m_rw_counter = 1;
        return true;
    }
    return false;
}

void shared_mutex::unlock() {
    lock_guard<mutex> lock(m_mutex);
    lean_assert(m_rw_owner == this_thread::get_id());
    lean_assert(m_rw_counter > 0);
    m_rw_counter--;
    if (m_rw_counter > 0)
        return; // keep the lock
    m_rw_owner = thread::id(); // reset owner
    lean_assert(m_rw_counter == 0);
    lean_assert(m_rw_owner != this_thread::get_id());
    m_state = 0;
    m_gate1.notify_all();
}

void shared_mutex::lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return; // already has the lock
    }
    while ((m_state & write_entered) || (m_state & readers) == readers)
        m_gate1.wait(lock);
    unsigned num_readers = (m_state & readers) + 1;
    m_state &= ~readers;
    m_state |= num_readers;
}

bool shared_mutex::try_lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return true; // already has the lock
    }
    unsigned num_readers = m_state & readers;
    if (!(m_state & write_entered) && num_readers != readers) {
        ++num_readers;
        m_state &= ~readers;
        m_state |= num_readers;
        return true;
    }
    return false;
}

void shared_mutex::unlock_shared() {
    lock_guard<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        // if we have a rw (aka unshared) lock, then
        // the shared lock must be nested.
        lean_assert(m_rw_counter > 1);
        m_rw_counter--;
        return;
    }
    lean_assert(m_rw_counter == 0);
    unsigned num_readers = (m_state & readers) - 1;
    m_state &= ~readers;
    m_state |= num_readers;
    if (m_state & write_entered) {
        if (num_readers == 0)
            m_gate2.notify_one();
    } else {
        if (num_readers == readers - 1)
            m_gate1.notify_one();
    }
}
#endif
}


/**

   Author: Leonardo de Moura

   C++11 does not support std::shared_mutex and std::shared_lock yet.
   This piece of code is based on the proposal submitted to the C++
   standardization committee. The proposal was written by Howard
   Hinnant. The proposal is also part of the Boost library which is
*/
#include "util/debug.h"
#include "util/shared_mutex.h"

namespace lean {
#if defined(LEAN_MULTI_THREAD)
shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {}
shared_mutex::~shared_mutex() {
    lock_guard<mutex> lock(m_mutex);
}

void shared_mutex::lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return; // already has the lock
    }
    while (m_state & write_entered)
        m_gate1.wait(lock);

m_state |= write_entered;
    while (m_state & readers)
        m_gate2.wait(lock);
    lean_assert(m_rw_counter == 0);
    m_rw_owner   = this_thread::get_id();
    m_rw_counter = 1;
}

bool shared_mutex::try_lock() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        m_rw_counter++;
        return true; // already has the lock
    }
    if (m_state == 0) {
        m_state      = write_entered;
        lean_assert(m_rw_counter == 0);
        m_rw_owner   = this_thread::get_id();
        m_rw_counter = 1;
        return true;
    }
    return false;
}

void shared_mutex::unlock() {
    lock_guard<mutex> lock(m_mutex);
    lean_assert(m_rw_owner == this_thread::get_id());
    lean_assert(m_rw_counter > 0);
    m_rw_counter--;
    if (m_rw_counter > 0)
        return; // keep the lock
    m_rw_owner = thread::id(); // reset owner
    lean_assert(m_rw_counter == 0);
    lean_assert(m_rw_owner != this_thread::get_id());
    m_state = 0;
    m_gate1.notify_all();
}

void shared_mutex::lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return; // already has the lock
    }
    while ((m_state & write_entered) || (m_state & readers) == readers)
        m_gate1.wait(lock);
    unsigned num_readers = (m_state & readers) + 1;
    m_state &= ~readers;
    m_state |= num_readers;
}

bool shared_mutex::try_lock_shared() {
    unique_lock<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        lean_assert(m_rw_counter > 0);
        m_rw_counter++;
        return true; // already has the lock
    }
    unsigned num_readers = m_state & readers;
    if (!(m_state & write_entered) && num_readers != readers) {
        ++num_readers;
        m_state &= ~readers;
        m_state |= num_readers;
        return true;
    }
    return false;
}

void shared_mutex::unlock_shared() {
    lock_guard<mutex> lock(m_mutex);
    if (m_rw_owner == this_thread::get_id()) {
        // if we have a rw (aka unshared) lock, then
        // the shared lock must be nested.
        lean_assert(m_rw_counter > 1);
        m_rw_counter--;
        return;
    }
    lean_assert(m_rw_counter == 0);
    unsigned num_readers = (m_state & readers) - 1;
    m_state &= ~readers;
    m_state |= num_readers;
    if (m_state & write_entered) {
        if (num_readers == 0)
            m_gate2.notify_one();
    } else {
        if (num_readers == readers - 1)
            m_gate1.notify_one();
    }
}
#endif
}


/* === S Y N F I G ========================================================= */
/*!	\file dock_canvases.cpp
**	\brief Template File
**
**	\legal
**
**	This file is part of Synfig.
**
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "docks/dock_canvases.h"

#include <gui/app.h>
#include <gui/canvasview.h>
#include <gui/localization.h>

#endif

/* === U S I N G =========================================================== */

using namespace synfig;
using namespace studio;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

Dock_Canvases::Dock_Canvases():
	Dockable("canvases",_("Canvas Browser"),Gtk::StockID("synfig-canvas"))
{

	App::signal_instance_created().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::new_instance));
	App::signal_instance_deleted().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::delete_instance));
	App::signal_instance_selected().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::set_selected_instance_signal));

	add(*create_canvas_tree());

/*  // \todo Implement canvas management in canvas browser
	add_button(
		Gtk::StockID("synfig-canvas_new"),
		_("Insert a new canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_new_canvas
		)
	);

	add_button(
		Gtk::StockID("gtk-delete"),
		_("Remove selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_delete
		)
	);

	add_button(
		Gtk::StockID("synfig-rename"),
		_("Rename selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_rename
		)
	);
*/
}

Dock_Canvases::~Dock_Canvases()
{
}

Gtk::Widget*
Dock_Canvases::create_canvas_tree()
{
	studio::Instance::CanvasTreeModel canvas_tree_model;
	canvas_tree=manage(new class Gtk::TreeView());
	{
		Gtk::TreeView::Column* column = Gtk::manage( new Gtk::TreeView::Column(_("ID")) );

		column->pack_start(canvas_tree_model.icon, false); //false = don't expand.
		column->pack_start(canvas_tree_model.label);

		canvas_tree->append_column(*column);
	}
	canvas_tree->set_rules_hint();
	canvas_tree->signal_row_activated().connect(sigc::mem_fun(*this,&Dock_Canvases::on_row_activate));
	canvas_tree->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
	canvas_tree->add_events(Gdk::BUTTON1_MOTION_MASK);
	canvas_tree->show();
	canvas_tree->set_headers_visible(false);

	Gtk::ScrolledWindow *scrolledwindow = manage(new class Gtk::ScrolledWindow());
	scrolledwindow->set_can_focus(true);
	scrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	scrolledwindow->add(*canvas_tree);
	scrolledwindow->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
	scrolledwindow->show_all();

	return scrolledwindow;
}

etl::loose_handle<studio::CanvasView>
Dock_Canvases::get_selected_canvas_view()
{
	return get_selected_instance()->find_canvas_view(get_selected_canvas());
}

etl::loose_handle<synfig::Canvas>
Dock_Canvases::get_selected_canvas()
{
	Glib::RefPtr<Gtk::TreeSelection> selection=canvas_tree->get_selection();

	if(!selection || !selection->get_selected())
		return 0;

	studio::Instance::CanvasTreeModel canvas_tree_model;

	return static_cast<etl::handle<synfig::Canvas> >((*selection->get_selected())[canvas_tree_model.canvas]);
}

void
Dock_Canvases::set_selected_instance_signal(etl::handle<studio::Instance> x)
{
	set_selected_instance(x);
}

void
Dock_Canvases::set_selected_instance_(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	selected_instance=instance;
	if(instance)
	{
		canvas_tree->set_model(instance->canvas_tree_store());
		canvas_tree->show();
	}
	else
	{
		canvas_tree->set_model(Glib::RefPtr< Gtk::TreeModel >());
		canvas_tree->hide();
	}
}

void
Dock_Canvases::set_selected_instance(etl::loose_handle<studio::Instance> x)
{
	if(studio::App::shutdown_in_progress)
		return;

	// if it's already selected, don't select it again
	if (x==selected_instance)
		return;

	set_selected_instance_(x);
}

{
	if(studio::App::shutdown_in_progress)
		return;

	assert(instance);

	etl::loose_handle<studio::Instance> loose_instance(instance);

	instance->synfigapp::Instance::signal_filename_changed().connect(sigc::mem_fun(*this,&Dock_Canvases::refresh_instances));
	instance->synfigapp::Instance::signal_filename_changed().connect(
		sigc::bind<etl::loose_handle<studio::Instance> >(
			sigc::mem_fun(*this,&Dock_Canvases::set_selected_instance),
			loose_instance
		)
	);
}

void
Dock_Canvases::delete_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	refresh_instances();

	if(selected_instance==instance)
	{
		set_selected_instance(0);
	}
}

void
Dock_Canvases::refresh_instances()
{
	if(studio::App::shutdown_in_progress)
		return;
}

void
Dock_Canvases::on_row_activate(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *)
{
	assert(get_selected_instance());
	studio::Instance::CanvasTreeModel canvas_tree_model;
	const Gtk::TreeRow row = *(get_selected_instance()->canvas_tree_store()->get_iter(path));
	if(row[canvas_tree_model.is_canvas])
		get_selected_instance()->focus(row[canvas_tree_model.canvas]);
	else
		studio::App::dialog_not_implemented();
}


{
	return get_selected_instance()->find_canvas_view(get_selected_canvas());
}

/* === S Y N F I G ========================================================= */
/*!	\file dock_canvases.cpp
**	\brief Template File
**
**	\legal
**
**	This file is part of Synfig.
**
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "docks/dock_canvases.h"

#include <gui/app.h>
#include <gui/canvasview.h>
#include <gui/localization.h>

#endif

/* === U S I N G =========================================================== */

using namespace synfig;
using namespace studio;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

Dock_Canvases::Dock_Canvases():
	Dockable("canvases",_("Canvas Browser"),Gtk::StockID("synfig-canvas"))
{

	App::signal_instance_created().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::new_instance));
	App::signal_instance_deleted().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::delete_instance));
	App::signal_instance_selected().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::set_selected_instance_signal));

	add(*create_canvas_tree());

/*  // \todo Implement canvas management in canvas browser
	add_button(
		Gtk::StockID("synfig-canvas_new"),
		_("Insert a new canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_new_canvas
		)
	);

	add_button(
		Gtk::StockID("gtk-delete"),
		_("Remove selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_delete
		)
	);

	add_button(
		Gtk::StockID("synfig-rename"),
		_("Rename selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_rename
		)
	);
*/
}

Dock_Canvases::~Dock_Canvases()
{
}

Gtk::Widget*
Dock_Canvases::create_canvas_tree()
{
	studio::Instance::CanvasTreeModel canvas_tree_model;
	canvas_tree=manage(new class Gtk::TreeView());
	{
		Gtk::TreeView::Column* column = Gtk::manage( new Gtk::TreeView::Column(_("ID")) );

		column->pack_start(canvas_tree_model.icon, false); //false = don't expand.
		column->pack_start(canvas_tree_model.label);

		canvas_tree->append_column(*column);
	}
	canvas_tree->set_rules_hint();
	canvas_tree->signal_row_activated().connect(sigc::mem_fun(*this,&Dock_Canvases::on_row_activate));
	canvas_tree->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
	canvas_tree->add_events(Gdk::BUTTON1_MOTION_MASK);
	canvas_tree->show();
	canvas_tree->set_headers_visible(false);

	Gtk::ScrolledWindow *scrolledwindow = manage(new class Gtk::ScrolledWindow());
	scrolledwindow->set_can_focus(true);
	scrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	scrolledwindow->add(*canvas_tree);
	scrolledwindow->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
	scrolledwindow->show_all();

	return scrolledwindow;
}

etl::loose_handle<studio::CanvasView>
Dock_Canvases::get_selected_canvas_view()

etl::loose_handle<synfig::Canvas>
Dock_Canvases::get_selected_canvas()
{
	Glib::RefPtr<Gtk::TreeSelection> selection=canvas_tree->get_selection();

	if(!selection || !selection->get_selected())
		return 0;

	studio::Instance::CanvasTreeModel canvas_tree_model;

	return static_cast<etl::handle<synfig::Canvas> >((*selection->get_selected())[canvas_tree_model.canvas]);
}

void
Dock_Canvases::set_selected_instance_signal(etl::handle<studio::Instance> x)
{
	set_selected_instance(x);
}

void
Dock_Canvases::set_selected_instance_(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	selected_instance=instance;
	if(instance)
	{
		canvas_tree->set_model(instance->canvas_tree_store());
		canvas_tree->show();
	}
	else
	{
		canvas_tree->set_model(Glib::RefPtr< Gtk::TreeModel >());
		canvas_tree->hide();
	}
}

void
Dock_Canvases::set_selected_instance(etl::loose_handle<studio::Instance> x)
{
	if(studio::App::shutdown_in_progress)
		return;

	// if it's already selected, don't select it again
	if (x==selected_instance)
		return;

	set_selected_instance_(x);
}

void
Dock_Canvases::new_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	assert(instance);

	etl::loose_handle<studio::Instance> loose_instance(instance);

	instance->synfigapp::Instance::signal_filename_changed().connect(sigc::mem_fun(*this,&Dock_Canvases::refresh_instances));
	instance->synfigapp::Instance::signal_filename_changed().connect(
		sigc::bind<etl::loose_handle<studio::Instance> >(
			sigc::mem_fun(*this,&Dock_Canvases::set_selected_instance),
			loose_instance
		)
	);
}

void
Dock_Canvases::delete_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	refresh_instances();

	if(selected_instance==instance)
	{
		set_selected_instance(0);
	}
}

void
Dock_Canvases::refresh_instances()
{
	if(studio::App::shutdown_in_progress)
		return;
}

void
Dock_Canvases::on_row_activate(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *)
{
	assert(get_selected_instance());
	studio::Instance::CanvasTreeModel canvas_tree_model;
	const Gtk::TreeRow row = *(get_selected_instance()->canvas_tree_store()->get_iter(path));
	if(row[canvas_tree_model.is_canvas])
		get_selected_instance()->focus(row[canvas_tree_model.canvas]);
	else
		studio::App::dialog_not_implemented();
}


{
	if(studio::App::shutdown_in_progress)
		return;

	assert(instance);

/* === S Y N F I G ========================================================= */
/*!	\file dock_canvases.cpp
**	\brief Template File
**
**	\legal
**
**	This file is part of Synfig.
**
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "docks/dock_canvases.h"

#include <gui/app.h>
#include <gui/canvasview.h>
#include <gui/localization.h>

#endif

/* === U S I N G =========================================================== */

using namespace synfig;
using namespace studio;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

Dock_Canvases::Dock_Canvases():
	Dockable("canvases",_("Canvas Browser"),Gtk::StockID("synfig-canvas"))
{

	App::signal_instance_created().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::new_instance));
	App::signal_instance_deleted().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::delete_instance));
	App::signal_instance_selected().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::set_selected_instance_signal));

	add(*create_canvas_tree());

/*  // \todo Implement canvas management in canvas browser
	add_button(
		Gtk::StockID("synfig-canvas_new"),
		_("Insert a new canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_new_canvas
		)
	);

	add_button(
		Gtk::StockID("gtk-delete"),
		_("Remove selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_delete
		)
	);

	add_button(
		Gtk::StockID("synfig-rename"),
		_("Rename selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_rename
		)
	);
*/
}

Dock_Canvases::~Dock_Canvases()
{
}

Gtk::Widget*
Dock_Canvases::create_canvas_tree()
{
	studio::Instance::CanvasTreeModel canvas_tree_model;
	canvas_tree=manage(new class Gtk::TreeView());
	{
		Gtk::TreeView::Column* column = Gtk::manage( new Gtk::TreeView::Column(_("ID")) );

		column->pack_start(canvas_tree_model.icon, false); //false = don't expand.
		column->pack_start(canvas_tree_model.label);

		canvas_tree->append_column(*column);
	}
	canvas_tree->set_rules_hint();
	canvas_tree->signal_row_activated().connect(sigc::mem_fun(*this,&Dock_Canvases::on_row_activate));
	canvas_tree->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
	canvas_tree->add_events(Gdk::BUTTON1_MOTION_MASK);
	canvas_tree->show();
	canvas_tree->set_headers_visible(false);

	Gtk::ScrolledWindow *scrolledwindow = manage(new class Gtk::ScrolledWindow());
	scrolledwindow->set_can_focus(true);
	scrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	scrolledwindow->add(*canvas_tree);
	scrolledwindow->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
	scrolledwindow->show_all();

	return scrolledwindow;
}

etl::loose_handle<studio::CanvasView>
Dock_Canvases::get_selected_canvas_view()
{
	return get_selected_instance()->find_canvas_view(get_selected_canvas());
}

etl::loose_handle<synfig::Canvas>
Dock_Canvases::get_selected_canvas()
{
	Glib::RefPtr<Gtk::TreeSelection> selection=canvas_tree->get_selection();

	if(!selection || !selection->get_selected())
		return 0;

	studio::Instance::CanvasTreeModel canvas_tree_model;

	return static_cast<etl::handle<synfig::Canvas> >((*selection->get_selected())[canvas_tree_model.canvas]);
}

void
Dock_Canvases::set_selected_instance_signal(etl::handle<studio::Instance> x)
{
	set_selected_instance(x);
}

void
Dock_Canvases::set_selected_instance_(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	selected_instance=instance;
	if(instance)
	{
		canvas_tree->set_model(instance->canvas_tree_store());
		canvas_tree->show();
	}
	else
	{
		canvas_tree->set_model(Glib::RefPtr< Gtk::TreeModel >());
		canvas_tree->hide();
	}
}

void
Dock_Canvases::set_selected_instance(etl::loose_handle<studio::Instance> x)
{
	if(studio::App::shutdown_in_progress)
		return;

	// if it's already selected, don't select it again
	if (x==selected_instance)
		return;

	set_selected_instance_(x);
}

void
Dock_Canvases::new_instance(etl::handle<studio::Instance> instance)

	etl::loose_handle<studio::Instance> loose_instance(instance);

	instance->synfigapp::Instance::signal_filename_changed().connect(sigc::mem_fun(*this,&Dock_Canvases::refresh_instances));
	instance->synfigapp::Instance::signal_filename_changed().connect(
		sigc::bind<etl::loose_handle<studio::Instance> >(
			sigc::mem_fun(*this,&Dock_Canvases::set_selected_instance),
			loose_instance
		)
	);
}

void
Dock_Canvases::delete_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	refresh_instances();

	if(selected_instance==instance)
	{
		set_selected_instance(0);
	}
}

void
Dock_Canvases::refresh_instances()
{
	if(studio::App::shutdown_in_progress)
		return;
}

void
Dock_Canvases::on_row_activate(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *)
{
	assert(get_selected_instance());
	studio::Instance::CanvasTreeModel canvas_tree_model;
	const Gtk::TreeRow row = *(get_selected_instance()->canvas_tree_store()->get_iter(path));
	if(row[canvas_tree_model.is_canvas])
		get_selected_instance()->focus(row[canvas_tree_model.canvas]);
	else
		studio::App::dialog_not_implemented();
}


/* === S Y N F I G ========================================================= */
/*!	\file dock_canvases.cpp
**	\brief Template File
**
**	\legal
**
**	This file is part of Synfig.
**
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "docks/dock_canvases.h"

#include <gui/app.h>
#include <gui/canvasview.h>
#include <gui/localization.h>

#endif

/* === U S I N G =========================================================== */

using namespace synfig;
using namespace studio;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

Dock_Canvases::Dock_Canvases():
	Dockable("canvases",_("Canvas Browser"),Gtk::StockID("synfig-canvas"))
{

	App::signal_instance_created().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::new_instance));
	App::signal_instance_deleted().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::delete_instance));
	App::signal_instance_selected().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::set_selected_instance_signal));

	add(*create_canvas_tree());

/*  // \todo Implement canvas management in canvas browser
	add_button(
		Gtk::StockID("synfig-canvas_new"),
		_("Insert a new canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_new_canvas
		)
	);

	add_button(
		Gtk::StockID("gtk-delete"),
		_("Remove selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_delete
		)
	);

	add_button(
		Gtk::StockID("synfig-rename"),
		_("Rename selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_rename
		)
	);
*/
}

Dock_Canvases::~Dock_Canvases()
{
}

Gtk::Widget*
Dock_Canvases::create_canvas_tree()
{
	studio::Instance::CanvasTreeModel canvas_tree_model;
	canvas_tree=manage(new class Gtk::TreeView());
	{
		Gtk::TreeView::Column* column = Gtk::manage( new Gtk::TreeView::Column(_("ID")) );

		column->pack_start(canvas_tree_model.icon, false); //false = don't expand.
		column->pack_start(canvas_tree_model.label);

		canvas_tree->append_column(*column);
	}
	canvas_tree->set_rules_hint();
	canvas_tree->signal_row_activated().connect(sigc::mem_fun(*this,&Dock_Canvases::on_row_activate));
	canvas_tree->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
	canvas_tree->add_events(Gdk::BUTTON1_MOTION_MASK);
	canvas_tree->show();
	canvas_tree->set_headers_visible(false);

	Gtk::ScrolledWindow *scrolledwindow = manage(new class Gtk::ScrolledWindow());
	scrolledwindow->set_can_focus(true);
	scrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	scrolledwindow->add(*canvas_tree);
	scrolledwindow->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
	scrolledwindow->show_all();

	return scrolledwindow;
}

etl::loose_handle<studio::CanvasView>
Dock_Canvases::get_selected_canvas_view()
{
	return get_selected_instance()->find_canvas_view(get_selected_canvas());
}

etl::loose_handle<synfig::Canvas>
Dock_Canvases::get_selected_canvas()
{
	Glib::RefPtr<Gtk::TreeSelection> selection=canvas_tree->get_selection();

	if(!selection || !selection->get_selected())
		return 0;

	studio::Instance::CanvasTreeModel canvas_tree_model;

	return static_cast<etl::handle<synfig::Canvas> >((*selection->get_selected())[canvas_tree_model.canvas]);
}

void
Dock_Canvases::set_selected_instance_signal(etl::handle<studio::Instance> x)
{
	set_selected_instance(x);
}

void
Dock_Canvases::set_selected_instance_(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	selected_instance=instance;
	if(instance)
	{
		canvas_tree->set_model(instance->canvas_tree_store());
		canvas_tree->show();
	}
	else
	{
		canvas_tree->set_model(Glib::RefPtr< Gtk::TreeModel >());
		canvas_tree->hide();
	}
}

void
Dock_Canvases::set_selected_instance(etl::loose_handle<studio::Instance> x)
{
	if(studio::App::shutdown_in_progress)
		return;

	// if it's already selected, don't select it again
	if (x==selected_instance)
		return;

	set_selected_instance_(x);
}

void
Dock_Canvases::new_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	assert(instance);

	instance->synfigapp::Instance::signal_filename_changed().connect(
		sigc::bind<etl::loose_handle<studio::Instance> >(
			sigc::mem_fun(*this,&Dock_Canvases::set_selected_instance),
			loose_instance
		)
	);
}

void
Dock_Canvases::delete_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	refresh_instances();

	if(selected_instance==instance)
	{
		set_selected_instance(0);
	}
}

void
Dock_Canvases::refresh_instances()
{
	if(studio::App::shutdown_in_progress)
		return;
}

void
Dock_Canvases::on_row_activate(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *)
{
	assert(get_selected_instance());
	studio::Instance::CanvasTreeModel canvas_tree_model;
	const Gtk::TreeRow row = *(get_selected_instance()->canvas_tree_store()->get_iter(path));
	if(row[canvas_tree_model.is_canvas])
		get_selected_instance()->focus(row[canvas_tree_model.canvas]);
	else
		studio::App::dialog_not_implemented();
}


instance->synfigapp::Instance::signal_filename_changed().connect(
		sigc::bind<etl::loose_handle<studio::Instance> >(
			sigc::mem_fun(*this,&Dock_Canvases::set_selected_instance),
			loose_instance
		)
	);
}

/* === S Y N F I G ========================================================= */
/*!	\file dock_canvases.cpp
**	\brief Template File
**
**	\legal
**
**	This file is part of Synfig.
**
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "docks/dock_canvases.h"

#include <gui/app.h>
#include <gui/canvasview.h>
#include <gui/localization.h>

#endif

/* === U S I N G =========================================================== */

using namespace synfig;
using namespace studio;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

Dock_Canvases::Dock_Canvases():
	Dockable("canvases",_("Canvas Browser"),Gtk::StockID("synfig-canvas"))
{

	App::signal_instance_created().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::new_instance));
	App::signal_instance_deleted().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::delete_instance));
	App::signal_instance_selected().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::set_selected_instance_signal));

	add(*create_canvas_tree());

/*  // \todo Implement canvas management in canvas browser
	add_button(
		Gtk::StockID("synfig-canvas_new"),
		_("Insert a new canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_new_canvas
		)
	);

	add_button(
		Gtk::StockID("gtk-delete"),
		_("Remove selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_delete
		)
	);

	add_button(
		Gtk::StockID("synfig-rename"),
		_("Rename selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_rename
		)
	);
*/
}

Dock_Canvases::~Dock_Canvases()
{
}

Gtk::Widget*
Dock_Canvases::create_canvas_tree()
{
	studio::Instance::CanvasTreeModel canvas_tree_model;
	canvas_tree=manage(new class Gtk::TreeView());
	{
		Gtk::TreeView::Column* column = Gtk::manage( new Gtk::TreeView::Column(_("ID")) );

		column->pack_start(canvas_tree_model.icon, false); //false = don't expand.
		column->pack_start(canvas_tree_model.label);

		canvas_tree->append_column(*column);
	}
	canvas_tree->set_rules_hint();
	canvas_tree->signal_row_activated().connect(sigc::mem_fun(*this,&Dock_Canvases::on_row_activate));
	canvas_tree->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
	canvas_tree->add_events(Gdk::BUTTON1_MOTION_MASK);
	canvas_tree->show();
	canvas_tree->set_headers_visible(false);

	Gtk::ScrolledWindow *scrolledwindow = manage(new class Gtk::ScrolledWindow());
	scrolledwindow->set_can_focus(true);
	scrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	scrolledwindow->add(*canvas_tree);
	scrolledwindow->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
	scrolledwindow->show_all();

	return scrolledwindow;
}

etl::loose_handle<studio::CanvasView>
Dock_Canvases::get_selected_canvas_view()
{
	return get_selected_instance()->find_canvas_view(get_selected_canvas());
}

etl::loose_handle<synfig::Canvas>
Dock_Canvases::get_selected_canvas()
{
	Glib::RefPtr<Gtk::TreeSelection> selection=canvas_tree->get_selection();

	if(!selection || !selection->get_selected())
		return 0;

	studio::Instance::CanvasTreeModel canvas_tree_model;

	return static_cast<etl::handle<synfig::Canvas> >((*selection->get_selected())[canvas_tree_model.canvas]);
}

void
Dock_Canvases::set_selected_instance_signal(etl::handle<studio::Instance> x)
{
	set_selected_instance(x);
}

void
Dock_Canvases::set_selected_instance_(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	selected_instance=instance;
	if(instance)
	{
		canvas_tree->set_model(instance->canvas_tree_store());
		canvas_tree->show();
	}
	else
	{
		canvas_tree->set_model(Glib::RefPtr< Gtk::TreeModel >());
		canvas_tree->hide();
	}
}

void
Dock_Canvases::set_selected_instance(etl::loose_handle<studio::Instance> x)
{
	if(studio::App::shutdown_in_progress)
		return;

	// if it's already selected, don't select it again
	if (x==selected_instance)
		return;

	set_selected_instance_(x);
}

void
Dock_Canvases::new_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	assert(instance);

	etl::loose_handle<studio::Instance> loose_instance(instance);

	instance->synfigapp::Instance::signal_filename_changed().connect(sigc::mem_fun(*this,&Dock_Canvases::refresh_instances));

void
Dock_Canvases::delete_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	refresh_instances();

	if(selected_instance==instance)
	{
		set_selected_instance(0);
	}
}

void
Dock_Canvases::refresh_instances()
{
	if(studio::App::shutdown_in_progress)
		return;
}

void
Dock_Canvases::on_row_activate(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *)
{
	assert(get_selected_instance());
	studio::Instance::CanvasTreeModel canvas_tree_model;
	const Gtk::TreeRow row = *(get_selected_instance()->canvas_tree_store()->get_iter(path));
	if(row[canvas_tree_model.is_canvas])
		get_selected_instance()->focus(row[canvas_tree_model.canvas]);
	else
		studio::App::dialog_not_implemented();
}