<commit_before>//
// RemotePhotoTool - remote camera control software
// Copyright (C) 2008-2014 Michael Fink
//
/// \file CdsdkSourceDeviceImpl.hpp CDSDK - SourceDevice impl
//
#pragma once

// includes
#include "SourceDevice.hpp"
#include "CdsdkCommon.hpp"
#include "CdsdkRemoteReleaseControlImpl.hpp"
#include "CdsdkViewfinderImpl.hpp"
#include "CdsdkDevicePropertyAccess.hpp"

namespace CDSDK
{

/// source device impl for CDSDK
class SourceDeviceImpl:
   public SourceDevice,
   public std::enable_shared_from_this<SourceDeviceImpl>
{
public:
   /// ctor
   SourceDeviceImpl(RefSp spRef, cdHSource hSource)
      :m_spRef(spRef),
       m_hSource(hSource)
   {
   }

   /// dtor
   virtual ~SourceDeviceImpl() throw()
   {
      cdError err = CDCloseSource(m_hSource);
      LOG_TRACE(_T("CDCloseSource(%08x) returned %08x\n"), m_hSource, err);
   }

   virtual bool GetDeviceCapability(SourceDevice::T_enDeviceCapability enDeviceCapability) const override
   {
      try
      {
         switch (enDeviceCapability)
         {
         case SourceDevice::capRemoteReleaseControl:
            {
               DeviceProperty p = GetDeviceProperty(cdDEVICE_PROP_RELEASE_CONTROL_CAP);
               unsigned int uiValue = p.Value().Get<unsigned int>();

               return (uiValue & 0x40000000) != 0;
            }
            break;

         case SourceDevice::capRemoteViewfinder:
            // TODO implement
            ATLASSERT(false);
            break;

         default:
            ATLASSERT(false);
         }
      }
      catch (Exception& ex)
      {
         LOG_TRACE(_T("Exception occured in GetDeviceCapability(): %s\n"),
            ex.Message().GetString());
      }
      catch (boost::bad_any_cast&)
      {
         LOG_TRACE(_T("boost::bad_any_cast exception occured in GetDeviceCapability()\n"));
      }
      catch (std::exception& ex)
      {
         LOG_TRACE(_T("std::exception occured in GetDeviceCapability(): %Hs\n"),
            ex.what());
      }
      return false;
   }

   virtual CString ModelName() const override
   {
      try
      {
         DevicePropertyAccess p(const_cast<cdHSource&>(m_hSource));
         Variant v = p.Get(cdDEVICE_PROP_MODEL_NAME);
         return v.Get<CString>();
      }
      catch (Exception& ex)
      {
         LOG_TRACE(_T("exception in ModelName(): %s\n"), ex.Message().GetString());
      }

      return CString();
   }

   virtual CString SerialNumber() const override
   {
      return CString();
   }

   virtual std::vector<unsigned int> EnumDeviceProperties() const override
   {
      // start enumerating
      cdHEnum hEnum = 0;

      // may return cdINVALID_PARAMETER, cdINVALID_HANDLE
      cdError err = CDEnumDevicePropertyReset(m_hSource, 0, &hEnum);
      LOG_TRACE(_T("CDEnumDevicePropertyReset(%08x) returned %08x\n"), m_hSource, err);
      CheckError(_T("CDEnumDeviceReset"), err, __FILE__, __LINE__);

      // get count of properties
      cdUInt32 count = 0;

      // returns cdINVALID_HANDLE, cdINVALID_PARAMETER
      err = CDGetDevicePropertyCount(hEnum, &count);
      LOG_TRACE(_T("CDGetDevicePropertyCount(%08x, &count = %u) returned %08x\n"), hEnum, count, err);
      CheckError(_T("CDGetDevicePropertyCount"), err, __FILE__, __LINE__);

      std::vector<unsigned int> vecData;
      for (cdUInt32 i=0; i<count+1; i++)
      {
         cdDevicePropertyStruct deviceProperty;

         // may return cdINVALID_PARAMETER, cdINVALID_HANDLE, cdENUM_NA
         err = CDEnumDevicePropertyNext(hEnum, &deviceProperty);
         LOG_TRACE(_T("CDEnumDevicePropertyNext(%08x, &propId = %08x) returned %08x\n"), hEnum, deviceProperty.DevPropID, err);
         if ((err & cdERROR_ERRORID_MASK) == cdENUM_NA)
            break;

         CheckError(_T("CDEnumDevicePropertyNext"), err, __FILE__, __LINE__);

         vecData.push_back(deviceProperty.DevPropID);
      }

      // end enumerating
      // may return cdINVALID_HANDLE, cdINVALID_FN_CALL
      err = CDEnumDevicePropertyRelease(hEnum);
      LOG_TRACE(_T("CDEnumDevicePropertyRelease(%08x) returned %08x\n"), hEnum, err);
      CheckError(_T("CDEnumDevicePropertyRelease"), err, __FILE__, __LINE__);

      return vecData;
   }

   virtual DeviceProperty GetDeviceProperty(unsigned int uiPropertyId) const override
   {
      DevicePropertyAccess access(GetSource());
      Variant value = access.Get(uiPropertyId);

      return DeviceProperty(variantCdsdk, uiPropertyId, value, true);
   }

   virtual std::shared_ptr<RemoteReleaseControl> EnterReleaseControl() override
   {
      if (!GetDeviceCapability(capRemoteReleaseControl))
      {
         throw CameraException(_T("EnterReleaseControl"), _T("Not supported"),
            cdERROR_CDSDK_COMPONENTID | cdNOT_SUPPORTED, __FILE__, __LINE__);
      }

      std::shared_ptr<SourceDeviceImpl> spSourceDevice = shared_from_this();

      return std::shared_ptr<RemoteReleaseControl>(new RemoteReleaseControlImpl(spSourceDevice));
   }

   /// returns source
   cdHSource GetSource() const throw() { return m_hSource; }

private:
   /// source device handle
   cdHSource m_hSource;

   /// SDK ref
   RefSp m_spRef;
};

inline cdHSource RemoteReleaseControlImpl::GetSource() const throw()
{
   return m_spSourceDevice->GetSource();
}

inline cdHSource ViewfinderImpl::GetSource() const throw()
{
   return m_spSourceDevice->GetSource();
}

} // namespace CDSDK
<commit_msg>added outputting device property name when enumerating device properties<commit_after>//
// RemotePhotoTool - remote camera control software
// Copyright (C) 2008-2014 Michael Fink
//
/// \file CdsdkSourceDeviceImpl.hpp CDSDK - SourceDevice impl
//
#pragma once

// includes
#include "SourceDevice.hpp"
#include "CdsdkCommon.hpp"
#include "CdsdkRemoteReleaseControlImpl.hpp"
#include "CdsdkViewfinderImpl.hpp"
#include "CdsdkDevicePropertyAccess.hpp"

namespace CDSDK
{

/// source device impl for CDSDK
class SourceDeviceImpl:
   public SourceDevice,
   public std::enable_shared_from_this<SourceDeviceImpl>
{
public:
   /// ctor
   SourceDeviceImpl(RefSp spRef, cdHSource hSource)
      :m_spRef(spRef),
       m_hSource(hSource)
   {
   }

   /// dtor
   virtual ~SourceDeviceImpl() throw()
   {
      cdError err = CDCloseSource(m_hSource);
      LOG_TRACE(_T("CDCloseSource(%08x) returned %08x\n"), m_hSource, err);
   }

   virtual bool GetDeviceCapability(SourceDevice::T_enDeviceCapability enDeviceCapability) const override
   {
      try
      {
         switch (enDeviceCapability)
         {
         case SourceDevice::capRemoteReleaseControl:
            {
               DeviceProperty p = GetDeviceProperty(cdDEVICE_PROP_RELEASE_CONTROL_CAP);
               unsigned int uiValue = p.Value().Get<unsigned int>();

               return (uiValue & 0x40000000) != 0;
            }
            break;

         case SourceDevice::capRemoteViewfinder:
            // TODO implement
            ATLASSERT(false);
            break;

         default:
            ATLASSERT(false);
         }
      }
      catch (Exception& ex)
      {
         LOG_TRACE(_T("Exception occured in GetDeviceCapability(): %s\n"),
            ex.Message().GetString());
      }
      catch (boost::bad_any_cast&)
      {
         LOG_TRACE(_T("boost::bad_any_cast exception occured in GetDeviceCapability()\n"));
      }
      catch (std::exception& ex)
      {
         LOG_TRACE(_T("std::exception occured in GetDeviceCapability(): %Hs\n"),
            ex.what());
      }
      return false;
   }

   virtual CString ModelName() const override
   {
      try
      {
         DevicePropertyAccess p(const_cast<cdHSource&>(m_hSource));
         Variant v = p.Get(cdDEVICE_PROP_MODEL_NAME);
         return v.Get<CString>();
      }
      catch (Exception& ex)
      {
         LOG_TRACE(_T("exception in ModelName(): %s\n"), ex.Message().GetString());
      }

      return CString();
   }

   virtual CString SerialNumber() const override
   {
      return CString();
   }

   virtual std::vector<unsigned int> EnumDeviceProperties() const override
   {
      // start enumerating
      cdHEnum hEnum = 0;

      // may return cdINVALID_PARAMETER, cdINVALID_HANDLE
      cdError err = CDEnumDevicePropertyReset(m_hSource, 0, &hEnum);
      LOG_TRACE(_T("CDEnumDevicePropertyReset(%08x) returned %08x\n"), m_hSource, err);
      CheckError(_T("CDEnumDeviceReset"), err, __FILE__, __LINE__);

      // get count of properties
      cdUInt32 count = 0;

      // returns cdINVALID_HANDLE, cdINVALID_PARAMETER
      err = CDGetDevicePropertyCount(hEnum, &count);
      LOG_TRACE(_T("CDGetDevicePropertyCount(%08x, &count = %u) returned %08x\n"), hEnum, count, err);
      CheckError(_T("CDGetDevicePropertyCount"), err, __FILE__, __LINE__);

      std::vector<unsigned int> vecData;
      for (cdUInt32 i=0; i<count+1; i++)
      {
         cdDevicePropertyStruct deviceProperty;

         // may return cdINVALID_PARAMETER, cdINVALID_HANDLE, cdENUM_NA
         err = CDEnumDevicePropertyNext(hEnum, &deviceProperty);
         LOG_TRACE(_T("CDEnumDevicePropertyNext(%08x, &propId = %08x \"%s\") returned %08x\n"),
            hEnum, deviceProperty.DevPropID,
            DevicePropertyAccess::NameFromId(deviceProperty.DevPropID).GetString(),
            err);
         if ((err & cdERROR_ERRORID_MASK) == cdENUM_NA)
            break;

         CheckError(_T("CDEnumDevicePropertyNext"), err, __FILE__, __LINE__);

         vecData.push_back(deviceProperty.DevPropID);
      }

      // end enumerating
      // may return cdINVALID_HANDLE, cdINVALID_FN_CALL
      err = CDEnumDevicePropertyRelease(hEnum);
      LOG_TRACE(_T("CDEnumDevicePropertyRelease(%08x) returned %08x\n"), hEnum, err);
      CheckError(_T("CDEnumDevicePropertyRelease"), err, __FILE__, __LINE__);

      return vecData;
   }

   virtual DeviceProperty GetDeviceProperty(unsigned int uiPropertyId) const override
   {
      DevicePropertyAccess access(GetSource());
      Variant value = access.Get(uiPropertyId);

      return DeviceProperty(variantCdsdk, uiPropertyId, value, true);
   }

   virtual std::shared_ptr<RemoteReleaseControl> EnterReleaseControl() override
   {
      if (!GetDeviceCapability(capRemoteReleaseControl))
      {
         throw CameraException(_T("EnterReleaseControl"), _T("Not supported"),
            cdERROR_CDSDK_COMPONENTID | cdNOT_SUPPORTED, __FILE__, __LINE__);
      }

      std::shared_ptr<SourceDeviceImpl> spSourceDevice = shared_from_this();

      return std::shared_ptr<RemoteReleaseControl>(new RemoteReleaseControlImpl(spSourceDevice));
   }

   /// returns source
   cdHSource GetSource() const throw() { return m_hSource; }

private:
   /// source device handle
   cdHSource m_hSource;

   /// SDK ref
   RefSp m_spRef;
};

inline cdHSource RemoteReleaseControlImpl::GetSource() const throw()
{
   return m_spSourceDevice->GetSource();
}

inline cdHSource ViewfinderImpl::GetSource() const throw()
{
   return m_spSourceDevice->GetSource();
}

} // namespace CDSDK
<|endoftext|>


<commit_before>/*
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 */

#include "resultview.h"

#include "../iconmanager.h"

#include <QModelIndex>

bool ResultView::alternateRows = false;
QList<ResultView*> ResultView::instances = QList<ResultView*>();

ResultView::ResultView(QWidget *parent)
  : QWidget(parent) {
  setupUi(this);
  // Sincèrement, un jour, il faudra se poser la question de l'utilité de la
  // ligne suivante :
  table->setModel(0);

  // Par défaut, on visualise
  currentAction = Browse;
  // Aucun tri
  currentSorting = QPair<int, Qt::SortOrder>(-1, Qt::AscendingOrder);
  // Nécessaire pour la synchronisation sur l'option d'alternance des lignes
  instances << this;
  // On commence à la première ligne
  offset = 0;
  // Purement arbitraire, j'avoue
  m_mode = QueryMode;
  shortModel = new QStandardItemModel(this);
  table->setModel(shortModel);
  table->setAlternatingRowColors(alternateRows);

//  setupMenus();
  setupConnections();

  // loading icons from theme
  firstPageButton->setIcon(IconManager::get("go-first"));
  lastPageButton->setIcon(IconManager::get("go-last"));
  nextPageButton->setIcon(IconManager::get("go-next"));
  prevPageButton->setIcon(IconManager::get("go-previous"));
  reloadButton->setIcon(IconManager::get("view-refresh"));
  insertButton->setIcon(IconManager::get("list-add"));
  deleteButton->setIcon(IconManager::get("list-remove"));

  insertButton->setVisible(false);
  deleteButton->setVisible(false);
}

ResultView::~ResultView() {
  instances.removeAll(this);
}

void ResultView::apply() {
  if (modifiedRecords.size() == 0) {
    return;
  }

  insertButton->setIcon(IconManager::get("list-add"));
  deleteButton->setIcon(IconManager::get("list-remove"));

  QSqlTableModel *tmodel = (QSqlTableModel*) model;

  foreach (int row, modifiedRecords.keys()) {
    tmodel->setRecord(row, modifiedRecords[row]);
  }

  modifiedRecords.clear();

  if (!tmodel->submitAll()) {
    QMessageBox::critical(this, "Error", tmodel->lastError().text());
  }

  currentAction = Browse;
  tmodel->select();
  updateView();
}

void ResultView::exportContent()
{
  if (model == 0)
    return;

  if (model->columnCount() == 0 || model->rowCount() == 0)
    return;

//  exportWizard = new ExportWizard(model, this);
  if (m_mode == TableMode) {
    exportWizard = new ExportWizard(model, this);
  } else {
    exportWizard = new ExportWizard(m_token, this);
  }

  exportWizard->exec();
}

/**
 * Clic sur le bouton supprimer : peut annuler l'opération en cours ou supprimer
 * la ligne en cours.
 */
void ResultView::on_deleteButton_clicked()
{
  if(m_mode != TableMode)
    return;

  QSqlTableModel *tmodel = (QSqlTableModel*) model;

  if (currentAction != Browse) {
    // Annulation de l'opération en cours
    insertButton->setIcon(IconManager::get("list-add"));
    deleteButton->setIcon(IconManager::get("list-remove"));

    currentAction = Browse;
    tmodel->revertAll();
  } else {
    // suppression des lignes sélectionnées
    QSet<int> rows;
    foreach(QModelIndex i, table->selectionModel()->selectedIndexes())
      rows << (i.row() + offset);

    foreach(int r, rows)
      model->removeRow(r);

    tmodel->submitAll();
  }

  updateView();
}

/**
 * Clic sur le bouton d'insertion : peut passer en mode insertion ou valider un
 * ajout/modif.
 */
void ResultView::on_insertButton_clicked()
{
  if(m_mode != TableMode)
    return;

  if (currentAction == Insert || currentAction == Update) {
    apply();
  } else {
    insertButton->setIcon(QIcon());
    insertButton->setText(tr("Apply"));
    deleteButton->setIcon(QIcon());
    deleteButton->setText(tr("Cancel"));

    model->insertRow(model->rowCount());
    currentAction = Insert;
    updateView();
    table->scrollToBottom();
//    table->hold();
  }
}

void ResultView::on_reloadButton_clicked()
{
  switch (m_mode) {
  case QueryMode:
    emit reloadRequested();
    break;

  case TableMode:
    ((QSqlTableModel*) model)->select();
    updateView();
    break;
  }
}

void ResultView::resizeColumnsToContents()
{
  table->resizeColumnsToContents();
}

void ResultView::resizeRowsToContents()
{
  table->resizeRowsToContents();
}

void ResultView::scrollBegin()
{
  offset = 0;
  updateView();
}

void ResultView::scrollDown() {
  offset += resultSpinBox->value();
  updateView();
}

void ResultView::scrollEnd() {
  double last;
  modf(model->rowCount() / resultSpinBox->value(), &last);

  offset = last * resultSpinBox->value();
  updateView();
}

void ResultView::scrollUp() {
  offset -= resultSpinBox->value();
  updateView();
}

void ResultView::setAlternatingRowColors(bool enable, bool loop) {
  alternateRows = enable;
  table->setAlternatingRowColors(enable);

  if (loop) {
    foreach (ResultView *v, instances) {
      if (dynamic_cast<ResultView*>(v) && v != this) {
        v->setAlternatingRowColors(enable, false);
      }
    }
  }
}

void ResultView::setMode(Mode m) {
  m_mode = m;
  reloadButton->setEnabled(true);
  resultSpinBox->setEnabled(true);

  switch(m_mode) {
  case QueryMode:
    insertButton->setVisible(false);
    deleteButton->setVisible(false);
    table->setSortingEnabled(false);
    break;

  case TableMode:
    insertButton->setVisible(true);
    deleteButton->setVisible(true);
    table->setSortingEnabled(true);
    break;
  }
}

void ResultView::setModel(QSqlQueryModel *model) {
  this->model = model;
  updateView();
}

bool ResultView::setTable(QString table, QSqlDatabase *db) {
  setMode(TableMode);
  QSqlTableModel *m = new QSqlTableModel(this, *db);
  m->setTable(table);
  m->setEditStrategy(QSqlTableModel::OnManualSubmit);
  m->select();
  if(m->lastError().type() == QSqlError::NoError)   {
    setModel(m);
    return true;
  } else {
    QMessageBox::critical(this,
                          tr("Error"),
                          tr("Unable to open the table. Returned error :\n%1")
                          .arg(m->lastError().text()),
                          QMessageBox::Ok);
    return false;
  }
}

void ResultView::setToken(QueryToken *token) {
  setMode(QueryMode);
  m_token = token;
  offset = 0;

  setModel(m_token->model());
}

void ResultView::setupConnections() {
  connect(firstPageButton, SIGNAL(clicked()), this, SLOT(scrollBegin()));
  connect(prevPageButton, SIGNAL(clicked()), this, SLOT(scrollUp()));
  connect(nextPageButton, SIGNAL(clicked()), this, SLOT(scrollDown()));
  connect(lastPageButton, SIGNAL(clicked()), this, SLOT(scrollEnd()));

  connect(resultSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateView()));

  connect(shortModel, SIGNAL(itemChanged(QStandardItem*)),
          this, SLOT(updateItem(QStandardItem*)));

  connect(table, SIGNAL(rowLeaved(int)), this, SLOT(apply()));
  connect(table->horizontalHeader(), SIGNAL(sectionClicked(int)),
          this, SLOT(sort(int)));
  connect(table, SIGNAL(alternateRowsRequested(bool)),
          this, SLOT(setAlternatingRowColors(bool)));
  connect(table, SIGNAL(exportRequested()), this, SLOT(exportContent()));
}

void ResultView::sort(int col) {
  if (m_mode == QueryMode) {
    return;
  }

  if (currentSorting.first == col) {
    if (currentSorting.second == Qt::AscendingOrder) {
      currentSorting.second = Qt::DescendingOrder;
    } else {
      currentSorting.second = Qt::AscendingOrder;
    }
  } else {
    currentSorting = QPair<int, Qt::SortOrder>(col, Qt::AscendingOrder);
  }
  model->sort(currentSorting.first, currentSorting.second);
  updateView();
}

/**
 * Called by the shortmodel's signal dataChanged in order to forward it to the
 * real one.
 */
void ResultView::updateItem(QStandardItem *item) {
  if (currentAction == Browse) {
    currentAction = Update;
    insertButton->setIcon(QIcon());
    insertButton->setText(tr("Apply"));
    deleteButton->setIcon(QIcon());
    deleteButton->setText(tr("Cancel"));
  }

  QSqlRecord record;
  int row = item->row() + offset;
  if (modifiedRecords.contains(row)) {
    record = modifiedRecords[row];
  } else {
    record = model->record(row);
  }
  record.setValue(item->column(), item->data(Qt::DisplayRole));
  modifiedRecords[row] = record;
}

/**
 * Mise à jour pagination
 */
void ResultView::updateView() {
  int hpos = table->horizontalScrollBar()->value();

  shortModel->clear();

  if (!model) {
    return;
  }

  for(int i=0; i<model->columnCount(); i++)
    shortModel->setHorizontalHeaderItem(i, new QStandardItem(
        model->headerData(i, Qt::Horizontal).toString()));

  if(model->rowCount() == 0)
    return;

  // 1ère ligne à afficher
  int startIndex;
  startIndex = offset;
  if (startIndex > model->rowCount())
    startIndex = model->rowCount() - resultSpinBox->value();
  if (startIndex < 0)
    startIndex = 0;

  // Page en cours, nb de page
  double page, maxpage;
  modf(startIndex / resultSpinBox->value(), &page);
  modf(model->rowCount() / resultSpinBox->value(), &maxpage);
  pageLabel->setText(tr("Page %1/%2")
                     .arg(page+1)
                     .arg(maxpage+1));

  // dernière ligne à afficher
  int endIndex;
  endIndex = startIndex + resultSpinBox->value();
  if (endIndex > model->rowCount())
    endIndex = model->rowCount();

  firstPageButton->setEnabled(startIndex > 0);
  prevPageButton->setEnabled(startIndex > 0);
  nextPageButton->setEnabled(endIndex < model->rowCount());
  lastPageButton->setEnabled(endIndex < model->rowCount());

  QStandardItem *item;
  QStringList vlabels;
  QSqlRecord r;
  QList<QStandardItem*> row;
  for(int i=startIndex; i<endIndex; i++) {
    row.clear();
    r = model->record(i);
    for(int j=0; j<model->columnCount(); j++) {
      item = new QStandardItem();
//      if (r.value(j).canConvert(QVariant::String)
//          && r.value(j).toString().length() > 50) {
//        QString val = r.value(j).toString();
//        // BLOB
//        item->setData(val.left(47).append("..."), Qt::DisplayRole);
//        item->setData(val, Qt::ToolTipRole);
//        item->setEditable(false);
//        item->setData(true, Qt::UserRole);
//      } else {
        item->setData(r.value(j), Qt::DisplayRole);
        item->setData(false, Qt::UserRole);
//      }
      item->setEditable(m_mode == TableMode);
      row << item;
    }
    shortModel->appendRow(row);
    vlabels << QString::number(i+1);
  }
  if(currentAction == Insert) {
    vlabels.removeLast();
    vlabels << "*";
  }
  shortModel->setVerticalHeaderLabels(vlabels);

  resizeColumnsToContents();
  resizeRowsToContents();

  if (m_mode == TableMode) {
    table->horizontalHeader()->setSortIndicatorShown(true);
    table->horizontalHeader()->setSortIndicator(currentSorting.first,
                                                currentSorting.second);
  }

  table->horizontalScrollBar()->setValue(hpos);
}
<commit_msg>Défilement vertical auto<commit_after>/*
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 */

#include "resultview.h"

#include "../iconmanager.h"

#include <QModelIndex>

bool ResultView::alternateRows = false;
QList<ResultView*> ResultView::instances = QList<ResultView*>();

ResultView::ResultView(QWidget *parent)
  : QWidget(parent) {
  setupUi(this);
  // Sincèrement, un jour, il faudra se poser la question de l'utilité de la
  // ligne suivante :
  table->setModel(0);

  // Par défaut, on visualise
  currentAction = Browse;
  // Aucun tri
  currentSorting = QPair<int, Qt::SortOrder>(-1, Qt::AscendingOrder);
  // Nécessaire pour la synchronisation sur l'option d'alternance des lignes
  instances << this;
  // On commence à la première ligne
  offset = 0;
  // Purement arbitraire, j'avoue
  m_mode = QueryMode;
  shortModel = new QStandardItemModel(this);
  table->setModel(shortModel);
  table->setAlternatingRowColors(alternateRows);

//  setupMenus();
  setupConnections();

  // loading icons from theme
  firstPageButton->setIcon(IconManager::get("go-first"));
  lastPageButton->setIcon(IconManager::get("go-last"));
  nextPageButton->setIcon(IconManager::get("go-next"));
  prevPageButton->setIcon(IconManager::get("go-previous"));
  reloadButton->setIcon(IconManager::get("view-refresh"));
  insertButton->setIcon(IconManager::get("list-add"));
  deleteButton->setIcon(IconManager::get("list-remove"));

  insertButton->setVisible(false);
  deleteButton->setVisible(false);
}

ResultView::~ResultView() {
  instances.removeAll(this);
}

void ResultView::apply() {
  if (modifiedRecords.size() == 0) {
    return;
  }

  insertButton->setIcon(IconManager::get("list-add"));
  deleteButton->setIcon(IconManager::get("list-remove"));

  QSqlTableModel *tmodel = (QSqlTableModel*) model;

  foreach (int row, modifiedRecords.keys()) {
    tmodel->setRecord(row, modifiedRecords[row]);
  }

  modifiedRecords.clear();

  if (!tmodel->submitAll()) {
    QMessageBox::critical(this, "Error", tmodel->lastError().text());
  }

  currentAction = Browse;
  tmodel->select();
  updateView();
}

void ResultView::exportContent()
{
  if (model == 0)
    return;

  if (model->columnCount() == 0 || model->rowCount() == 0)
    return;

//  exportWizard = new ExportWizard(model, this);
  if (m_mode == TableMode) {
    exportWizard = new ExportWizard(model, this);
  } else {
    exportWizard = new ExportWizard(m_token, this);
  }

  exportWizard->exec();
}

/**
 * Clic sur le bouton supprimer : peut annuler l'opération en cours ou supprimer
 * la ligne en cours.
 */
void ResultView::on_deleteButton_clicked()
{
  if(m_mode != TableMode)
    return;

  QSqlTableModel *tmodel = (QSqlTableModel*) model;

  if (currentAction != Browse) {
    // Annulation de l'opération en cours
    insertButton->setIcon(IconManager::get("list-add"));
    deleteButton->setIcon(IconManager::get("list-remove"));

    currentAction = Browse;
    tmodel->revertAll();
  } else {
    // suppression des lignes sélectionnées
    QSet<int> rows;
    foreach(QModelIndex i, table->selectionModel()->selectedIndexes())
      rows << (i.row() + offset);

    foreach(int r, rows)
      model->removeRow(r);

    tmodel->submitAll();
  }

  updateView();
}

/**
 * Clic sur le bouton d'insertion : peut passer en mode insertion ou valider un
 * ajout/modif.
 */
void ResultView::on_insertButton_clicked()
{
  if(m_mode != TableMode)
    return;

  if (currentAction == Insert || currentAction == Update) {
    apply();
  } else {
    insertButton->setIcon(QIcon());
    insertButton->setText(tr("Apply"));
    deleteButton->setIcon(QIcon());
    deleteButton->setText(tr("Cancel"));

    model->insertRow(model->rowCount());
    currentAction = Insert;
    updateView();
    table->scrollToBottom();
//    table->hold();
  }
}

void ResultView::on_reloadButton_clicked()
{
  switch (m_mode) {
  case QueryMode:
    emit reloadRequested();
    break;

  case TableMode:
    ((QSqlTableModel*) model)->select();
    updateView();
    break;
  }
}

void ResultView::resizeColumnsToContents()
{
  table->resizeColumnsToContents();
}

void ResultView::resizeRowsToContents()
{
  table->resizeRowsToContents();
}

void ResultView::scrollBegin()
{
  offset = 0;
  updateView();
}

void ResultView::scrollDown() {
  offset += resultSpinBox->value();
  updateView();
}

void ResultView::scrollEnd() {
  double last;
  modf(model->rowCount() / resultSpinBox->value(), &last);

  offset = last * resultSpinBox->value();
  updateView();
}

void ResultView::scrollUp() {
  offset -= resultSpinBox->value();
  updateView();
}

void ResultView::setAlternatingRowColors(bool enable, bool loop) {
  alternateRows = enable;
  table->setAlternatingRowColors(enable);

  if (loop) {
    foreach (ResultView *v, instances) {
      if (dynamic_cast<ResultView*>(v) && v != this) {
        v->setAlternatingRowColors(enable, false);
      }
    }
  }
}

void ResultView::setMode(Mode m) {
  m_mode = m;
  reloadButton->setEnabled(true);
  resultSpinBox->setEnabled(true);

  switch(m_mode) {
  case QueryMode:
    insertButton->setVisible(false);
    deleteButton->setVisible(false);
    table->setSortingEnabled(false);
    break;

  case TableMode:
    insertButton->setVisible(true);
    deleteButton->setVisible(true);
    table->setSortingEnabled(true);
    break;
  }
}

void ResultView::setModel(QSqlQueryModel *model) {
  this->model = model;
  updateView();
}

bool ResultView::setTable(QString table, QSqlDatabase *db) {
  setMode(TableMode);
  QSqlTableModel *m = new QSqlTableModel(this, *db);
  m->setTable(table);
  m->setEditStrategy(QSqlTableModel::OnManualSubmit);
  m->select();
  if(m->lastError().type() == QSqlError::NoError)   {
    setModel(m);
    return true;
  } else {
    QMessageBox::critical(this,
                          tr("Error"),
                          tr("Unable to open the table. Returned error :\n%1")
                          .arg(m->lastError().text()),
                          QMessageBox::Ok);
    return false;
  }
}

void ResultView::setToken(QueryToken *token) {
  setMode(QueryMode);
  m_token = token;
  offset = 0;

  setModel(m_token->model());
}

void ResultView::setupConnections() {
  connect(firstPageButton, SIGNAL(clicked()), this, SLOT(scrollBegin()));
  connect(prevPageButton, SIGNAL(clicked()), this, SLOT(scrollUp()));
  connect(nextPageButton, SIGNAL(clicked()), this, SLOT(scrollDown()));
  connect(lastPageButton, SIGNAL(clicked()), this, SLOT(scrollEnd()));

  connect(resultSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateView()));

  connect(shortModel, SIGNAL(itemChanged(QStandardItem*)),
          this, SLOT(updateItem(QStandardItem*)));

  connect(table, SIGNAL(rowLeaved(int)), this, SLOT(apply()));
  connect(table->horizontalHeader(), SIGNAL(sectionClicked(int)),
          this, SLOT(sort(int)));
  connect(table, SIGNAL(alternateRowsRequested(bool)),
          this, SLOT(setAlternatingRowColors(bool)));
  connect(table, SIGNAL(exportRequested()), this, SLOT(exportContent()));
}

void ResultView::sort(int col) {
  if (m_mode == QueryMode) {
    return;
  }

  if (currentSorting.first == col) {
    if (currentSorting.second == Qt::AscendingOrder) {
      currentSorting.second = Qt::DescendingOrder;
    } else {
      currentSorting.second = Qt::AscendingOrder;
    }
  } else {
    currentSorting = QPair<int, Qt::SortOrder>(col, Qt::AscendingOrder);
  }
  model->sort(currentSorting.first, currentSorting.second);
  updateView();
}

/**
 * Called by the shortmodel's signal dataChanged in order to forward it to the
 * real one.
 */
void ResultView::updateItem(QStandardItem *item) {
  if (currentAction == Browse) {
    currentAction = Update;
    insertButton->setIcon(QIcon());
    insertButton->setText(tr("Apply"));
    deleteButton->setIcon(QIcon());
    deleteButton->setText(tr("Cancel"));
  }

  QSqlRecord record;
  int row = item->row() + offset;
  if (modifiedRecords.contains(row)) {
    record = modifiedRecords[row];
  } else {
    record = model->record(row);
  }
  record.setValue(item->column(), item->data(Qt::DisplayRole));
  modifiedRecords[row] = record;
}

/**
 * Mise à jour pagination
 */
void ResultView::updateView() {
  int hpos = table->horizontalScrollBar()->value();
  int vpos = table->verticalScrollBar()->value();

  shortModel->clear();

  if (!model) {
    return;
  }

  for(int i=0; i<model->columnCount(); i++)
    shortModel->setHorizontalHeaderItem(i, new QStandardItem(
        model->headerData(i, Qt::Horizontal).toString()));

  if(model->rowCount() == 0)
    return;

  // 1ère ligne à afficher
  int startIndex;
  startIndex = offset;
  if (startIndex > model->rowCount())
    startIndex = model->rowCount() - resultSpinBox->value();
  if (startIndex < 0)
    startIndex = 0;

  // Page en cours, nb de page
  double page, maxpage;
  modf(startIndex / resultSpinBox->value(), &page);
  modf(model->rowCount() / resultSpinBox->value(), &maxpage);
  pageLabel->setText(tr("Page %1/%2")
                     .arg(page+1)
                     .arg(maxpage+1));

  // dernière ligne à afficher
  int endIndex;
  endIndex = startIndex + resultSpinBox->value();
  if (endIndex > model->rowCount())
    endIndex = model->rowCount();

  firstPageButton->setEnabled(startIndex > 0);
  prevPageButton->setEnabled(startIndex > 0);
  nextPageButton->setEnabled(endIndex < model->rowCount());
  lastPageButton->setEnabled(endIndex < model->rowCount());

  QStandardItem *item;
  QStringList vlabels;
  QSqlRecord r;
  QList<QStandardItem*> row;
  for(int i=startIndex; i<endIndex; i++) {
    row.clear();
    r = model->record(i);
    for(int j=0; j<model->columnCount(); j++) {
      item = new QStandardItem();
//      if (r.value(j).canConvert(QVariant::String)
//          && r.value(j).toString().length() > 50) {
//        QString val = r.value(j).toString();
//        // BLOB
//        item->setData(val.left(47).append("..."), Qt::DisplayRole);
//        item->setData(val, Qt::ToolTipRole);
//        item->setEditable(false);
//        item->setData(true, Qt::UserRole);
//      } else {
        item->setData(r.value(j), Qt::DisplayRole);
        item->setData(false, Qt::UserRole);
//      }
      item->setEditable(m_mode == TableMode);
      row << item;
    }
    shortModel->appendRow(row);
    vlabels << QString::number(i+1);
  }
  if(currentAction == Insert) {
    vlabels.removeLast();
    vlabels << "*";
  }
  shortModel->setVerticalHeaderLabels(vlabels);

  resizeColumnsToContents();
  resizeRowsToContents();

  if (m_mode == TableMode) {
    table->horizontalHeader()->setSortIndicatorShown(true);
    table->horizontalHeader()->setSortIndicator(currentSorting.first,
                                                currentSorting.second);
  }

  table->horizontalScrollBar()->setValue(hpos);
  table->verticalScrollBar()->setValue(vpos);
}
<|endoftext|>


<commit_before>// For conditions of distribution and use, see copyright notice in LICENSE

#include "StableHeaders.h"
#include "DebugOperatorNew.h"

#include "EC_PhysicsMotor.h"
#include "EC_RigidBody.h"
#include "EC_Placeable.h"
#include "Entity.h"
#include "Scene/Scene.h"
#include "PhysicsModule.h"
#include "PhysicsWorld.h"
#include "PhysicsUtils.h"
#include "LoggingFunctions.h"
#include "Profiler.h"

#include "MemoryLeakCheck.h"

using namespace Physics;

EC_PhysicsMotor::EC_PhysicsMotor(Scene* scene) :
    IComponent(scene),
    INIT_ATTRIBUTE_VALUE(absoluteMoveForce, "Absolute Move Force", float3::zero),
    INIT_ATTRIBUTE_VALUE(relativeMoveForce, "Relative Move Force", float3::zero),
    INIT_ATTRIBUTE_VALUE(dampingForce, "Damping Force", float3::zero)
{
    connect(this, SIGNAL(ParentEntitySet()), this, SLOT(UpdateSignals()), Qt::UniqueConnection);
}

EC_PhysicsMotor::~EC_PhysicsMotor()
{
}

void EC_PhysicsMotor::UpdateSignals()
{
    Entity* parent = ParentEntity();
    if (!parent)
        return;
    Scene* scene = parent->ParentScene();
    PhysicsWorld* world = scene->GetWorld<PhysicsWorld>().get();
    if (world)
        connect(world, SIGNAL(Updated(float)), this, SLOT(OnPhysicsUpdate()), Qt::UniqueConnection);

    connect(parent, SIGNAL(ComponentAdded(IComponent*, AttributeChange::Type)), this, SLOT(OnComponentStructureChanged()), Qt::UniqueConnection);
    connect(parent, SIGNAL(ComponentRemoved(IComponent*, AttributeChange::Type)), this, SLOT(OnComponentStructureChanged()), Qt::UniqueConnection);
    OnComponentStructureChanged(); // Check for Rigidbody & Placeable immediately
}

void EC_PhysicsMotor::OnComponentStructureChanged()
{
    Entity* parent = ParentEntity();
    if (!parent)
        return;
    rigidBody_ = parent->GetComponent<EC_RigidBody>();
    placeable_ = parent->GetComponent<EC_Placeable>();
}

void EC_PhysicsMotor::OnPhysicsUpdate()
{
    PROFILE(EC_PhysicsMotor_OnPhysicsUpdate);

    EC_RigidBody* body = rigidBody_.lock().get();
    EC_Placeable* placeable = placeable_.lock().get();

    if (body && placeable)
    {
        float3 absolute = absoluteMoveForce.Get();
        if (!absolute.Equals(float3::zero))
            body->ApplyImpulse(absolute);

        float3 relative = relativeMoveForce.Get();
        if (!relative.Equals(float3::zero))
        {
            float3 translate, scale;
            Quat rot;

            placeable->LocalToWorld().Decompose(translate, rot, scale);
            relative = rot * relative;
            body->ApplyImpulse(relative);
        }

        float3 damping = dampingForce.Get();
        if (!damping.Equals(float3::zero))
        {
            damping = -body->GetLinearVelocity() * damping;
            body->ApplyImpulse(damping);
        }
    }
}
<commit_msg>Use float3.Mul() instead of operator * to avoid ambiguous function call.<commit_after>// For conditions of distribution and use, see copyright notice in LICENSE

#include "StableHeaders.h"
#include "DebugOperatorNew.h"

#include "EC_PhysicsMotor.h"
#include "EC_RigidBody.h"
#include "EC_Placeable.h"
#include "Entity.h"
#include "Scene/Scene.h"
#include "PhysicsModule.h"
#include "PhysicsWorld.h"
#include "PhysicsUtils.h"
#include "LoggingFunctions.h"
#include "Profiler.h"

#include "MemoryLeakCheck.h"

using namespace Physics;

EC_PhysicsMotor::EC_PhysicsMotor(Scene* scene) :
    IComponent(scene),
    INIT_ATTRIBUTE_VALUE(absoluteMoveForce, "Absolute Move Force", float3::zero),
    INIT_ATTRIBUTE_VALUE(relativeMoveForce, "Relative Move Force", float3::zero),
    INIT_ATTRIBUTE_VALUE(dampingForce, "Damping Force", float3::zero)
{
    connect(this, SIGNAL(ParentEntitySet()), this, SLOT(UpdateSignals()), Qt::UniqueConnection);
}

EC_PhysicsMotor::~EC_PhysicsMotor()
{
}

void EC_PhysicsMotor::UpdateSignals()
{
    Entity* parent = ParentEntity();
    if (!parent)
        return;
    Scene* scene = parent->ParentScene();
    PhysicsWorld* world = scene->GetWorld<PhysicsWorld>().get();
    if (world)
        connect(world, SIGNAL(Updated(float)), this, SLOT(OnPhysicsUpdate()), Qt::UniqueConnection);

    connect(parent, SIGNAL(ComponentAdded(IComponent*, AttributeChange::Type)), this, SLOT(OnComponentStructureChanged()), Qt::UniqueConnection);
    connect(parent, SIGNAL(ComponentRemoved(IComponent*, AttributeChange::Type)), this, SLOT(OnComponentStructureChanged()), Qt::UniqueConnection);
    OnComponentStructureChanged(); // Check for Rigidbody & Placeable immediately
}

void EC_PhysicsMotor::OnComponentStructureChanged()
{
    Entity* parent = ParentEntity();
    if (!parent)
        return;
    rigidBody_ = parent->GetComponent<EC_RigidBody>();
    placeable_ = parent->GetComponent<EC_Placeable>();
}

void EC_PhysicsMotor::OnPhysicsUpdate()
{
    PROFILE(EC_PhysicsMotor_OnPhysicsUpdate);

    EC_RigidBody* body = rigidBody_.lock().get();
    EC_Placeable* placeable = placeable_.lock().get();

    if (body && placeable)
    {
        float3 absolute = absoluteMoveForce.Get();
        if (!absolute.Equals(float3::zero))
            body->ApplyImpulse(absolute);

        float3 relative = relativeMoveForce.Get();
        if (!relative.Equals(float3::zero))
        {
            float3 translate, scale;
            Quat rot;

            placeable->LocalToWorld().Decompose(translate, rot, scale);
            relative = rot * relative;
            body->ApplyImpulse(relative);
        }

        float3 damping = dampingForce.Get();
        if (!damping.Equals(float3::zero))
        {
            damping = -body->GetLinearVelocity().Mul(damping);
            body->ApplyImpulse(damping);
        }
    }
}
<|endoftext|>


<commit_before>class MicroStrings {
public:
	string makeMicroString(int, int);
}
<commit_msg>untested solution<commit_after>#include <string>
#include <vector>
#include <sstream>
using namespace std;

class MicroStrings {
public:
	string makeMicroString(int, int);
}

string MicroStrings::makeMicroString(int A, int D) {
	vector<int> nums;
	int n = A / D;
	for (int i = 0; i <= n; i++) nums.push_back(A - i * D);

	stringstream ss;
	for (int i = 0; i < nums.size(); i++) ss << nums[i];

	return ss.str();
}
<|endoftext|>


<commit_before>// LanguageDlg.cpp : implementation file
//

#include "LanguageDlg.h"
#include "TolonSpellCheckInternals.h"
#include "tscSession.h"
#include <iostream>
#include <string>
#include <sstream>
#include <windows.h>
#include <commctrl.h>
#include <assert.h>

extern HINSTANCE g_hInstDll;

using namespace TolonSpellCheck;
using namespace std;

static enum COLUMNS {
    COL_CODE,
    COL_NAME,

    COLUMN_COUNT
};

static struct COLUMN_DATA {
    int Id;
    int Width;
    const wchar_t* Name;
} s_columns[COLUMN_COUNT] = {
    { COL_CODE, 80,                       L"Code" },
    { COL_NAME, LVSCW_AUTOSIZE_USEHEADER, L"Language" }
};

//static const int s_nColDisplayName;
//static const int s_nColCodeName;

static const bool s_bDefaultSortAsc = true;

// CLanguageDlg dialog

static CLanguageDlg* s_pThis;

bool CLanguageDlg::LangEnumCallback(LANGUAGE_DESC_WIDEDATA* pData, void* pUserData)
{
    bool bResult = false;

    CLanguageDlg* pThis = reinterpret_cast<CLanguageDlg*>(pUserData);

    if (pData && pThis)
    {
        LVITEM lvi = {0};

        lvi.mask = LVIF_TEXT;
        lvi.iItem = 0;

        lvi.iSubItem = COL_CODE;
        lvi.pszText = pData->wszCodeName;
        ListView_InsertItem(pThis->GetLangListHwnd(), &lvi);

        ListView_SetItemText(pThis->GetLangListHwnd(), lvi.iItem, COL_NAME, pData->wszDisplayName);

        bResult = true;
    }

    return bResult;
}

CLanguageDlg::CLanguageDlg(TolonSpellCheck::CSession* pSession, HWND hwndParent /*=NULL*/) :
    m_pSession(pSession),
    m_hwnd(NULL),
    m_hwndParent(hwndParent),
    m_hwndLangList(NULL)
{
    m_SortOptions.bAsc = s_bDefaultSortAsc;
    m_SortOptions.nCol = COL_NAME;
}

CLanguageDlg::~CLanguageDlg()
{
}

int CLanguageDlg::DoModal()
{
    s_pThis = this;
    DialogBox(g_hInstDll, MAKEINTRESOURCE(CLanguageDlg::IDD), m_hwndParent, CLanguageDlg::WndProc);
    return 0;
}

// CLanguageDlg message handlers
BOOL CLanguageDlg::OnInitDialog()
{
    // Sort out controls
    InitLangList();

    // Populate language list
    m_pSession->EnumLanguages(static_cast<LanguageEnumFn>(&CLanguageDlg::LangEnumCallback), this);

    // Fix up language list now it's populated with data
    ListView_SetColumnWidth(GetLangListHwnd(), 0, LVSCW_AUTOSIZE);
    ListView_SortItemsEx(GetLangListHwnd(), &CompareFunc,  reinterpret_cast<LPARAM>(this));

    // Get default language
    UpdateLanguageDisplay();

    return TRUE;
}

void CLanguageDlg::InitLangList()
{
    assert(m_hwnd);

    if (!m_hwnd)
        return;

    // Obtain HWND
    m_hwndLangList = GetDlgItem(IDC_DIC_LIST);

    // Set styles
    ListView_SetExtendedListViewStyleEx(GetLangListHwnd(), LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

    // Set up columns
    LVCOLUMN lvc = {0};
    lvc.mask = LVCF_TEXT;
    int nItem = 0;

    for ( int i = 0; i < COLUMN_COUNT; ++i )
    {
        lvc.pszText = const_cast<wchar_t*>(s_columns[i].Name);
        nItem = ListView_InsertColumn(GetLangListHwnd(), s_columns[i].Id, &lvc);
        ListView_SetColumnWidth(GetLangListHwnd(), nItem, s_columns[i].Width);
    }
}

int CALLBACK CLanguageDlg::WndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    BOOL bHandled = FALSE;

    if (!s_pThis)
        return 0;

    if (!s_pThis->m_hwnd)
        s_pThis->m_hwnd = hDlg;

    switch (message)
    {
    case WM_COMMAND:
        {
            const WORD nCmd = LOWORD(wParam);

            if (nCmd == IDOK)
            {
                s_pThis->OnCmdOk();
                EndDialog(hDlg, nCmd);
            }
            else if (nCmd == IDCANCEL)
            {
                s_pThis->OnCmdCancel();
                EndDialog(hDlg, nCmd);
            }
            else if (nCmd == IDC_MAKEDEFAULT_BTN)
            { s_pThis->OnCmdMakeDefault(); }

            bHandled = TRUE;

            break;
        }

    case WM_INITDIALOG:
        return s_pThis->OnInitDialog();

    case WM_NOTIFY:
        switch (((LPNMHDR)lParam)->code)
        {
        case LVN_COLUMNCLICK:
            if (((LPNMHDR)lParam)->idFrom == IDC_DIC_LIST)
            {
                s_pThis->OnDicListColClick(lParam);
                return TRUE;
            }
            break;
        break;
        }
    }

    return bHandled;
}

void CLanguageDlg::OnCmdOk()
{
    TolonSpellCheck::CSession* pSession = GetSession();

    if (!pSession)
    {
        ::MessageBox(GetHwnd(), L"Internal Error. Could not set the chosen dictionary.", L"TolonSpellCheck", MB_OK | MB_ICONEXCLAMATION);
        return;
    }
    else
    {
        string sChosenLang;

        // Get chosen language
        GetChosenLanguage(sChosenLang);

        // Set it on the session
        tsc_result r = TSC_E_FAIL;
        r = pSession->SetLanguage(sChosenLang.c_str());

        if (TSC_FAILED(r))
        {
            ::MessageBox(GetHwnd(), L"Failed to set new language. :(", L"TolonSpellCheckLib", MB_OK | MB_ICONEXCLAMATION);
        }
    }
}

void CLanguageDlg::OnCmdCancel()
{
}

void CLanguageDlg::OnCmdMakeDefault()
{
}

void CLanguageDlg::GetChosenLanguage(string& sLang)
{
    int nItem = -1;
    nItem = ListView_GetNextItem(GetLangListHwnd(), -1, LVNI_SELECTED);

    if (nItem != -1)
    {
        const int nBufLen = 13;
        wchar_t wszBuf[nBufLen] = L"\0";

        ListView_GetItemText( GetLangListHwnd(),
                              nItem,
                              COL_CODE,
                              &wszBuf[0],
                              nBufLen );

        char szBuffer[nBufLen] = "\0";
        ::WideCharToMultiByte(CP_UTF8, 0, wszBuf, -1, szBuffer, nBufLen, NULL, NULL);

        sLang.assign(szBuffer);
    }
}

void CLanguageDlg::UpdateLanguageDisplay()
{
    TolonSpellCheck::CSession* pSession = GetSession();

    if (pSession)
    {
        tsc_result result = TSC_S_OK;

        LANGUAGE_DESC_WIDEDATA ldwd;
        wchar_t wszLang[13];
        memset(&ldwd, 0, sizeof(LANGUAGE_DESC_WIDEDATA));
        memset(wszLang, 0, sizeof(wszLang));

        ldwd.cbSize = sizeof(LANGUAGE_DESC_WIDEDATA);
        result = pSession->GetCurrentLanguage(wszLang);

        if (TSC_SUCCEEDED(result))
        {
            result = m_pSession->DescribeLanguage(wszLang, &ldwd);

            if (TSC_SUCCEEDED(result))
            {
                HWND hwnd = GetDlgItem(IDC_DEFAULTLANG_STATIC);
                if (hwnd)
                {
                    wstring ws(L"Default: ");
                    ws.append(ldwd.wszDisplayName);
                    SetWindowText(hwnd, ws.c_str());
                }
            }
        }
    }
}

HWND CLanguageDlg::GetDlgItem(int nDlgItem) const
{
    HWND hMain = GetHwnd();
    HWND hItem = NULL;

    if (hMain)
    {
        hItem = ::GetDlgItem(hMain, nDlgItem);
    }

    assert(hItem != NULL);
    return hItem;
}

HWND CLanguageDlg::GetHwnd() const
{
    assert(m_hwnd != NULL);
    return m_hwnd;
}

HWND CLanguageDlg::GetLangListHwnd() const
{
    assert(m_hwndLangList != NULL);
    return m_hwndLangList;
}

TolonSpellCheck::CSession* CLanguageDlg::GetSession() const
{
    assert(m_pSession != NULL);
    return m_pSession;
}

//! @param lParam1 The list view index of the first item to compare.
//! @param lParam2 The list view index of the second item to compare.
//! @param lParamSort Indicates the order of the sort; ascending or descending.
int CALLBACK CLanguageDlg::CompareFunc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
{
    CLanguageDlg* pThis = reinterpret_cast<CLanguageDlg*>(lParamSort);
    const size_t nTextBufferLength = 64;
    static wchar_t sText1[nTextBufferLength], sText2[nTextBufferLength];

    const int nSortColumn = pThis->m_SortOptions.nCol;
    const bool bAsc = pThis->m_SortOptions.bAsc;

    sText1[0] = L'\0';
    ListView_GetItemText( pThis->GetLangListHwnd(), lParam1, nSortColumn, sText1, nTextBufferLength );
    sText2[0] = L'\0';
    ListView_GetItemText( pThis->GetLangListHwnd(), lParam2, nSortColumn, sText2, nTextBufferLength );

    // We're dealing with a windows user interface, so use the Win32 string comparison.
    int nComp = CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE, sText1, wcslen(sText1), sText2, wcslen(sText2) );

    // CompareString documentation says substract 2 to return standard <0, ==0, and >0 values.
    nComp = nComp - 2;
    if (bAsc)
        return -nComp;
    else
        return nComp;
}

void CLanguageDlg::OnDicListColClick(LPARAM lParam)
{
    const NMLISTVIEW* pnmlv = reinterpret_cast<const NMLISTVIEW*>(lParam);

    if (pnmlv->iSubItem == m_SortOptions.nCol)
    {
        m_SortOptions.bAsc = !m_SortOptions.bAsc;
    }
    else
    {
        m_SortOptions.nCol = pnmlv->iSubItem;
    }

    ListView_SortItemsEx(GetLangListHwnd(), &CompareFunc,  reinterpret_cast<LPARAM>(this));
}
<commit_msg>Completed CompareProc logic<commit_after>// LanguageDlg.cpp : implementation file
//

#include "LanguageDlg.h"
#include "TolonSpellCheckInternals.h"
#include "tscSession.h"
#include <iostream>
#include <string>
#include <sstream>
#include <windows.h>
#include <commctrl.h>
#include <assert.h>

extern HINSTANCE g_hInstDll;

using namespace TolonSpellCheck;
using namespace std;

static enum COLUMNS {
    COL_CODE,
    COL_NAME,

    COLUMN_COUNT
};

static struct COLUMN_DATA {
    int Id;
    int Width;
    const wchar_t* Name;
} s_columns[COLUMN_COUNT] = {
    { COL_CODE, 80,                       L"Code" },
    { COL_NAME, LVSCW_AUTOSIZE_USEHEADER, L"Language" }
};

//static const int s_nColDisplayName;
//static const int s_nColCodeName;

static const bool s_bDefaultSortAsc = true;

// CLanguageDlg dialog

static CLanguageDlg* s_pThis;

bool CLanguageDlg::LangEnumCallback(LANGUAGE_DESC_WIDEDATA* pData, void* pUserData)
{
    bool bResult = false;

    CLanguageDlg* pThis = reinterpret_cast<CLanguageDlg*>(pUserData);

    if (pData && pThis)
    {
        LVITEM lvi = {0};

        lvi.mask = LVIF_TEXT;
        lvi.iItem = 0;

        lvi.iSubItem = COL_CODE;
        lvi.pszText = pData->wszCodeName;
        ListView_InsertItem(pThis->GetLangListHwnd(), &lvi);

        ListView_SetItemText(pThis->GetLangListHwnd(), lvi.iItem, COL_NAME, pData->wszDisplayName);

        bResult = true;
    }

    return bResult;
}

CLanguageDlg::CLanguageDlg(TolonSpellCheck::CSession* pSession, HWND hwndParent /*=NULL*/) :
    m_pSession(pSession),
    m_hwnd(NULL),
    m_hwndParent(hwndParent),
    m_hwndLangList(NULL)
{
    m_SortOptions.bAsc = s_bDefaultSortAsc;
    m_SortOptions.nCol = COL_NAME;
}

CLanguageDlg::~CLanguageDlg()
{
}

int CLanguageDlg::DoModal()
{
    s_pThis = this;
    DialogBox(g_hInstDll, MAKEINTRESOURCE(CLanguageDlg::IDD), m_hwndParent, CLanguageDlg::WndProc);
    return 0;
}

// CLanguageDlg message handlers
BOOL CLanguageDlg::OnInitDialog()
{
    // Sort out controls
    InitLangList();

    // Populate language list
    m_pSession->EnumLanguages(static_cast<LanguageEnumFn>(&CLanguageDlg::LangEnumCallback), this);

    // Fix up language list now it's populated with data
    ListView_SetColumnWidth(GetLangListHwnd(), 0, LVSCW_AUTOSIZE);
    ListView_SortItemsEx(GetLangListHwnd(), &CompareFunc,  reinterpret_cast<LPARAM>(this));

    // Get default language
    UpdateLanguageDisplay();

    return TRUE;
}

void CLanguageDlg::InitLangList()
{
    assert(m_hwnd);

    if (!m_hwnd)
        return;

    // Obtain HWND
    m_hwndLangList = GetDlgItem(IDC_DIC_LIST);

    // Set styles
    ListView_SetExtendedListViewStyleEx(GetLangListHwnd(), LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

    // Set up columns
    LVCOLUMN lvc = {0};
    lvc.mask = LVCF_TEXT;
    int nItem = 0;

    for ( int i = 0; i < COLUMN_COUNT; ++i )
    {
        lvc.pszText = const_cast<wchar_t*>(s_columns[i].Name);
        nItem = ListView_InsertColumn(GetLangListHwnd(), s_columns[i].Id, &lvc);
        ListView_SetColumnWidth(GetLangListHwnd(), nItem, s_columns[i].Width);
    }
}

int CALLBACK CLanguageDlg::WndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    BOOL bHandled = FALSE;

    if (!s_pThis)
        return 0;

    if (!s_pThis->m_hwnd)
        s_pThis->m_hwnd = hDlg;

    switch (message)
    {
    case WM_COMMAND:
        {
            const WORD nCmd = LOWORD(wParam);

            if (nCmd == IDOK)
            {
                s_pThis->OnCmdOk();
                EndDialog(hDlg, nCmd);
            }
            else if (nCmd == IDCANCEL)
            {
                s_pThis->OnCmdCancel();
                EndDialog(hDlg, nCmd);
            }
            else if (nCmd == IDC_MAKEDEFAULT_BTN)
            { s_pThis->OnCmdMakeDefault(); }

            bHandled = TRUE;

            break;
        }

    case WM_INITDIALOG:
        return s_pThis->OnInitDialog();

    case WM_NOTIFY:
        switch (((LPNMHDR)lParam)->code)
        {
        case LVN_COLUMNCLICK:
            if (((LPNMHDR)lParam)->idFrom == IDC_DIC_LIST)
            {
                s_pThis->OnDicListColClick(lParam);
                return TRUE;
            }
            break;
        break;
        }
    }

    return bHandled;
}

void CLanguageDlg::OnCmdOk()
{
    TolonSpellCheck::CSession* pSession = GetSession();

    if (!pSession)
    {
        ::MessageBox(GetHwnd(), L"Internal Error. Could not set the chosen dictionary.", L"TolonSpellCheck", MB_OK | MB_ICONEXCLAMATION);
        return;
    }
    else
    {
        string sChosenLang;

        // Get chosen language
        GetChosenLanguage(sChosenLang);

        // Set it on the session
        tsc_result r = TSC_E_FAIL;
        r = pSession->SetLanguage(sChosenLang.c_str());

        if (TSC_FAILED(r))
        {
            ::MessageBox(GetHwnd(), L"Failed to set new language. :(", L"TolonSpellCheckLib", MB_OK | MB_ICONEXCLAMATION);
        }
    }
}

void CLanguageDlg::OnCmdCancel()
{
}

void CLanguageDlg::OnCmdMakeDefault()
{
}

void CLanguageDlg::GetChosenLanguage(string& sLang)
{
    int nItem = -1;
    nItem = ListView_GetNextItem(GetLangListHwnd(), -1, LVNI_SELECTED);

    if (nItem != -1)
    {
        const int nBufLen = 13;
        wchar_t wszBuf[nBufLen] = L"\0";

        ListView_GetItemText( GetLangListHwnd(),
                              nItem,
                              COL_CODE,
                              &wszBuf[0],
                              nBufLen );

        char szBuffer[nBufLen] = "\0";
        ::WideCharToMultiByte(CP_UTF8, 0, wszBuf, -1, szBuffer, nBufLen, NULL, NULL);

        sLang.assign(szBuffer);
    }
}

void CLanguageDlg::UpdateLanguageDisplay()
{
    TolonSpellCheck::CSession* pSession = GetSession();

    if (pSession)
    {
        tsc_result result = TSC_S_OK;

        LANGUAGE_DESC_WIDEDATA ldwd;
        wchar_t wszLang[13];
        memset(&ldwd, 0, sizeof(LANGUAGE_DESC_WIDEDATA));
        memset(wszLang, 0, sizeof(wszLang));

        ldwd.cbSize = sizeof(LANGUAGE_DESC_WIDEDATA);
        result = pSession->GetCurrentLanguage(wszLang);

        if (TSC_SUCCEEDED(result))
        {
            result = m_pSession->DescribeLanguage(wszLang, &ldwd);

            if (TSC_SUCCEEDED(result))
            {
                HWND hwnd = GetDlgItem(IDC_DEFAULTLANG_STATIC);
                if (hwnd)
                {
                    wstring ws(L"Default: ");
                    ws.append(ldwd.wszDisplayName);
                    SetWindowText(hwnd, ws.c_str());
                }
            }
        }
    }
}

HWND CLanguageDlg::GetDlgItem(int nDlgItem) const
{
    HWND hMain = GetHwnd();
    HWND hItem = NULL;

    if (hMain)
    {
        hItem = ::GetDlgItem(hMain, nDlgItem);
    }

    assert(hItem != NULL);
    return hItem;
}

HWND CLanguageDlg::GetHwnd() const
{
    assert(m_hwnd != NULL);
    return m_hwnd;
}

HWND CLanguageDlg::GetLangListHwnd() const
{
    assert(m_hwndLangList != NULL);
    return m_hwndLangList;
}

TolonSpellCheck::CSession* CLanguageDlg::GetSession() const
{
    assert(m_pSession != NULL);
    return m_pSession;
}

//! @param lParam1 The list view index of the first item to compare.
//! @param lParam2 The list view index of the second item to compare.
//! @param lParamSort Indicates the order of the sort; ascending or descending.
int CALLBACK CLanguageDlg::CompareFunc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
{
    CLanguageDlg* pThis = reinterpret_cast<CLanguageDlg*>(lParamSort);
    const size_t nTextBufferLength = 64;
    static wchar_t sText1[nTextBufferLength], sText2[nTextBufferLength];

    const int nSortColumn = pThis->m_SortOptions.nCol;
    const bool bAsc = pThis->m_SortOptions.bAsc;

    sText1[0] = L'\0';
    ListView_GetItemText( pThis->GetLangListHwnd(), lParam1, nSortColumn, sText1, nTextBufferLength );
    sText2[0] = L'\0';
    ListView_GetItemText( pThis->GetLangListHwnd(), lParam2, nSortColumn, sText2, nTextBufferLength );

    // We're dealing with a windows user interface, so use the Win32 string comparison.
    int nComp = CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE, sText1, wcslen(sText1), sText2, wcslen(sText2) );

    // CompareString documentation says substract 2 to return standard <0, ==0, and >0 values.
    nComp = nComp - 2;
    if (bAsc)
        return nComp;
    else
        return -nComp;
}

void CLanguageDlg::OnDicListColClick(LPARAM lParam)
{
    const NMLISTVIEW* pnmlv = reinterpret_cast<const NMLISTVIEW*>(lParam);

    if (pnmlv->iSubItem == m_SortOptions.nCol)
    {
        m_SortOptions.bAsc = !m_SortOptions.bAsc;
    }
    else
    {
        m_SortOptions.nCol = pnmlv->iSubItem;
    }

    ListView_SortItemsEx(GetLangListHwnd(), &CompareFunc,  reinterpret_cast<LPARAM>(this));
}
<|endoftext|>


<commit_before>// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %dynamiclib -fPIC %ld_flags_rpath_so
// RUN: %clangxx_asan -fsanitize-coverage=func %s %ld_flags_rpath_exe -o %t
// RUN: rm -rf %T/coverage && mkdir -p %T/coverage && cd %T/coverage
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 2>&1         | FileCheck %s --check-prefix=CHECK-main
// RUN: %sancov print `ls coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t foo 2>&1     | FileCheck %s --check-prefix=CHECK-foo
// RUN: %sancov print `ls coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t bar 2>&1     | FileCheck %s --check-prefix=CHECK-bar
// RUN: %sancov print `ls *coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar
// RUN: %sancov print `ls *coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2
// RUN: %sancov print `ls *coverage.*sancov | grep '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1
// RUN: %sancov merge `ls *coverage.*sancov | grep -v '.so'` > merged-cov
// RUN: %sancov print merged-cov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2
// RUN: %env_asan_opts=coverage=1:verbosity=1 not %run %t foo bar 4    2>&1 | FileCheck %s --check-prefix=CHECK-report
// RUN: %env_asan_opts=coverage=1:verbosity=1 not %run %t foo bar 4 5  2>&1 | FileCheck %s --check-prefix=CHECK-segv
// RUN: rm -r %T/coverage
//
// https://code.google.com/p/address-sanitizer/issues/detail?id=263
// XFAIL: android

#include <sanitizer/coverage_interface.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#ifdef SHARED
void bar() { printf("bar\n"); }
#else
__attribute__((noinline))
void foo() { printf("foo\n"); }
extern void bar();

int G[4];

int main(int argc, char **argv) {
  fprintf(stderr, "PID: %d\n", getpid());
  for (int i = 1; i < argc; i++) {
    if (!strcmp(argv[i], "foo")) {
      uintptr_t old_coverage = __sanitizer_get_total_unique_coverage();
      foo();
      uintptr_t new_coverage = __sanitizer_get_total_unique_coverage();
      assert(new_coverage > old_coverage);
    }
    if (!strcmp(argv[i], "bar"))
      bar();
  }
  if (argc == 5) {
    static volatile char *zero = 0;
    *zero = 0;  // SEGV if argc == 5.
  }
  return G[argc];  // Buffer overflow if argc >= 4.
}
#endif

// CHECK-main: PID: [[PID:[0-9]+]]
// CHECK-main: [[PID]].sancov: 1 PCs written
// CHECK-main-NOT: .so.[[PID]]
//
// CHECK-foo: PID: [[PID:[0-9]+]]
// CHECK-foo: [[PID]].sancov: 2 PCs written
// CHECK-foo-NOT: .so.[[PID]]
//
// CHECK-bar: PID: [[PID:[0-9]+]]
// CHECK-bar: .so.[[PID]].sancov: 1 PCs written
// CHECK-bar: [[PID]].sancov: 1 PCs written
//
// CHECK-foo-bar: PID: [[PID:[0-9]+]]
// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written
// CHECK-foo-bar: [[PID]].sancov: 2 PCs written
//
// CHECK-report: AddressSanitizer: global-buffer-overflow
// CHECK-report: PCs written
//
// CHECK-segv: AddressSanitizer: SEGV
// CHECK-segv: PCs written
//
// CHECK-SANCOV1: 1 PCs total
// CHECK-SANCOV2: 2 PCs total
<commit_msg>avoid a subshell.<commit_after>// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %dynamiclib -fPIC %ld_flags_rpath_so
// RUN: %clangxx_asan -fsanitize-coverage=func %s %ld_flags_rpath_exe -o %t
// RUN: rm -rf %T/coverage && mkdir -p %T/coverage && cd %T/coverage
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 2>&1         | FileCheck %s --check-prefix=CHECK-main
// RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t foo 2>&1     | FileCheck %s --check-prefix=CHECK-foo
// RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t bar 2>&1     | FileCheck %s --check-prefix=CHECK-bar
// RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar
// RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2
// RUN: %sancov print libcoverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1
// RUN: %sancov merge coverage.*sancov > merged-cov
// RUN: %sancov print merged-cov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2
// RUN: %env_asan_opts=coverage=1:verbosity=1 not %run %t foo bar 4    2>&1 | FileCheck %s --check-prefix=CHECK-report
// RUN: %env_asan_opts=coverage=1:verbosity=1 not %run %t foo bar 4 5  2>&1 | FileCheck %s --check-prefix=CHECK-segv
// RUN: rm -r %T/coverage
//
// https://code.google.com/p/address-sanitizer/issues/detail?id=263
// XFAIL: android

#include <sanitizer/coverage_interface.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#ifdef SHARED
void bar() { printf("bar\n"); }
#else
__attribute__((noinline))
void foo() { printf("foo\n"); }
extern void bar();

int G[4];

int main(int argc, char **argv) {
  fprintf(stderr, "PID: %d\n", getpid());
  for (int i = 1; i < argc; i++) {
    if (!strcmp(argv[i], "foo")) {
      uintptr_t old_coverage = __sanitizer_get_total_unique_coverage();
      foo();
      uintptr_t new_coverage = __sanitizer_get_total_unique_coverage();
      assert(new_coverage > old_coverage);
    }
    if (!strcmp(argv[i], "bar"))
      bar();
  }
  if (argc == 5) {
    static volatile char *zero = 0;
    *zero = 0;  // SEGV if argc == 5.
  }
  return G[argc];  // Buffer overflow if argc >= 4.
}
#endif

// CHECK-main: PID: [[PID:[0-9]+]]
// CHECK-main: [[PID]].sancov: 1 PCs written
// CHECK-main-NOT: .so.[[PID]]
//
// CHECK-foo: PID: [[PID:[0-9]+]]
// CHECK-foo: [[PID]].sancov: 2 PCs written
// CHECK-foo-NOT: .so.[[PID]]
//
// CHECK-bar: PID: [[PID:[0-9]+]]
// CHECK-bar: .so.[[PID]].sancov: 1 PCs written
// CHECK-bar: [[PID]].sancov: 1 PCs written
//
// CHECK-foo-bar: PID: [[PID:[0-9]+]]
// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written
// CHECK-foo-bar: [[PID]].sancov: 2 PCs written
//
// CHECK-report: AddressSanitizer: global-buffer-overflow
// CHECK-report: PCs written
//
// CHECK-segv: AddressSanitizer: SEGV
// CHECK-segv: PCs written
//
// CHECK-SANCOV1: 1 PCs total
// CHECK-SANCOV2: 2 PCs total
<|endoftext|>


<commit_before>/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "PlanMasterController.h"
#include "QGCApplication.h"
#include "MultiVehicleManager.h"
#include "SettingsManager.h"
#include "AppSettings.h"
#include "JsonHelper.h"
#include "MissionManager.h"

#include <QJsonDocument>
#include <QFileInfo>

QGC_LOGGING_CATEGORY(PlanMasterControllerLog, "PlanMasterControllerLog")

const int   PlanMasterController::_planFileVersion =            1;
const char* PlanMasterController::_planFileType =               "Plan";
const char* PlanMasterController::_jsonMissionObjectKey =       "mission";
const char* PlanMasterController::_jsonGeoFenceObjectKey =      "geoFence";
const char* PlanMasterController::_jsonRallyPointsObjectKey =   "rallyPoints";

PlanMasterController::PlanMasterController(QObject* parent)
    : QObject(parent)
    , _multiVehicleMgr(qgcApp()->toolbox()->multiVehicleManager())
    , _controllerVehicle(new Vehicle((MAV_AUTOPILOT)qgcApp()->toolbox()->settingsManager()->appSettings()->offlineEditingFirmwareType()->rawValue().toInt(), (MAV_TYPE)qgcApp()->toolbox()->settingsManager()->appSettings()->offlineEditingVehicleType()->rawValue().toInt(), qgcApp()->toolbox()->firmwarePluginManager()))
    , _managerVehicle(_controllerVehicle)
    , _editMode(false)
    , _offline(true)
    , _missionController(this)
    , _geoFenceController(this)
    , _rallyPointController(this)
    , _loadGeoFence(false)
    , _loadRallyPoints(false)
    , _sendGeoFence(false)
    , _sendRallyPoints(false)
    , _syncInProgress(false)
{
    connect(&_missionController,    &MissionController::dirtyChanged,       this, &PlanMasterController::dirtyChanged);
    connect(&_geoFenceController,   &GeoFenceController::dirtyChanged,      this, &PlanMasterController::dirtyChanged);
    connect(&_rallyPointController, &RallyPointController::dirtyChanged,    this, &PlanMasterController::dirtyChanged);

    connect(&_missionController,    &MissionController::containsItemsChanged,       this, &PlanMasterController::containsItemsChanged);
    connect(&_geoFenceController,   &GeoFenceController::containsItemsChanged,      this, &PlanMasterController::containsItemsChanged);
    connect(&_rallyPointController, &RallyPointController::containsItemsChanged,    this, &PlanMasterController::containsItemsChanged);

    connect(&_missionController,    &MissionController::syncInProgressChanged,      this, &PlanMasterController::syncInProgressChanged);
    connect(&_geoFenceController,   &GeoFenceController::syncInProgressChanged,     this, &PlanMasterController::syncInProgressChanged);
    connect(&_rallyPointController, &RallyPointController::syncInProgressChanged,   this, &PlanMasterController::syncInProgressChanged);
}

PlanMasterController::~PlanMasterController()
{

}

void PlanMasterController::start(bool editMode)
{
    _editMode = editMode;
    _missionController.start(editMode);
    _geoFenceController.start(editMode);
    _rallyPointController.start(editMode);

    connect(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &PlanMasterController::_activeVehicleChanged);
    _activeVehicleChanged(_multiVehicleMgr->activeVehicle());
}

void PlanMasterController::startStaticActiveVehicle(Vehicle* vehicle)
{
    _editMode = false;
    _activeVehicleChanged(vehicle);
}

void PlanMasterController::_activeVehicleChanged(Vehicle* activeVehicle)
{
    if (_managerVehicle == activeVehicle) {
        // We are already setup for this vehicle
        return;
    }

    qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged" << activeVehicle;

    bool newOffline = false;
    if (activeVehicle == NULL) {
        // Since there is no longer an active vehicle we use the offline controller vehicle as the manager vehicle
        _managerVehicle = _controllerVehicle;
        newOffline = true;
    } else {
        newOffline = false;
        _managerVehicle = activeVehicle;

        // Update controllerVehicle to the currently connected vehicle
        AppSettings* appSettings = qgcApp()->toolbox()->settingsManager()->appSettings();
        appSettings->offlineEditingFirmwareType()->setRawValue(AppSettings::offlineEditingFirmwareTypeFromFirmwareType(_managerVehicle->firmwareType()));
        appSettings->offlineEditingVehicleType()->setRawValue(AppSettings::offlineEditingVehicleTypeFromVehicleType(_managerVehicle->vehicleType()));

        // We use these signals to sequence upload and download to the multiple controller/managers
        connect(_managerVehicle->missionManager(),      &MissionManager::newMissionItemsAvailable,  this, &PlanMasterController::_loadMissionComplete);
        connect(_managerVehicle->geoFenceManager(),     &GeoFenceManager::loadComplete,             this, &PlanMasterController::_loadGeoFenceComplete);
        connect(_managerVehicle->rallyPointManager(),   &RallyPointManager::loadComplete,           this, &PlanMasterController::_loadRallyPointsComplete);
        connect(_managerVehicle->missionManager(),      &MissionManager::sendComplete,              this, &PlanMasterController::_sendMissionComplete);
        connect(_managerVehicle->geoFenceManager(),     &GeoFenceManager::sendComplete,             this, &PlanMasterController::_sendGeoFenceComplete);
        connect(_managerVehicle->rallyPointManager(),   &RallyPointManager::sendComplete,           this, &PlanMasterController::_sendRallyPointsComplete);
    }
    if (newOffline != _offline) {
        _offline = newOffline;
        emit offlineEditingChanged(newOffline);
    }

    _missionController.managerVehicleChanged(_managerVehicle);
    _geoFenceController.managerVehicleChanged(_managerVehicle);
    _rallyPointController.managerVehicleChanged(_managerVehicle);

    if (_editMode) {
        if (!offline()) {
            // We are in Plan view and we have a newly connected vehicle:
            //  - If there is no plan available in Plan view show the one from the vehicle
            //  - Otherwise leave the current plan alone
            if (!containsItems()) {
                qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Plan view is empty so loading from manager";
                _showPlanFromManagerVehicle();
            }
        }
    } else {
        if (offline()) {
            // No more active vehicle, clear mission
            qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Fly view is offline clearing plan";
            removeAll();
        } else {
            // Fly view has changed to a new active vehicle, update to show correct mission
            qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Fly view is online so loading from manager";
            _showPlanFromManagerVehicle();
        }
    }
}

void PlanMasterController::loadFromVehicle(void)
{
    if (offline()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called while offline";
    } else if (!_editMode) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called from Fly view";
    } else if (syncInProgress()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called while syncInProgress";
    } else {
        _loadGeoFence = true;
        _syncInProgress = true;
        _missionController.loadFromVehicle();
        setDirty(false);
    }
}

void PlanMasterController::_loadMissionComplete(void)
{
    if (_editMode && _loadGeoFence) {
        _loadGeoFence = false;
        _loadRallyPoints = true;
        _geoFenceController.loadFromVehicle();
        setDirty(false);
    }
}

void PlanMasterController::_loadGeoFenceComplete(void)
{
    if (_editMode && _loadRallyPoints) {
        _loadRallyPoints = false;
        _rallyPointController.loadFromVehicle();
        setDirty(false);
    }
}

void PlanMasterController::_loadRallyPointsComplete(void)
{
    if (_editMode) {
        _syncInProgress = false;
        emit syncInProgressChanged(false);
    }
}

void PlanMasterController::_sendMissionComplete(void)
{
    if (_editMode && _sendGeoFence) {
        qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start fence sendToVehicle";
        _sendGeoFence = false;
        _sendRallyPoints = true;
        _geoFenceController.sendToVehicle();
        setDirty(false);
    }
}

void PlanMasterController::_sendGeoFenceComplete(void)
{
    if (_editMode && _sendRallyPoints) {
        qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start rally sendToVehicle";
        _sendRallyPoints = false;
        _rallyPointController.sendToVehicle();
    }
}

void PlanMasterController::_sendRallyPointsComplete(void)
{
    if (_editMode && _syncInProgress) {
        qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle rally point send complete";
        _syncInProgress = false;
        emit syncInProgressChanged(false);
    }
}

void PlanMasterController::sendToVehicle(void)
{
    if (offline()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called while offline";
    } else if (!_editMode) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called from Fly view";
    } else if (syncInProgress()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called while syncInProgress";
    } else {
        qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start mission sendToVehicle";
        _sendGeoFence = true;
        _missionController.sendToVehicle();
        setDirty(false);
    }
}

void PlanMasterController::loadFromFile(const QString& filename)
{
    QString errorString;
    QString errorMessage = tr("Error reading Plan file (%1). %2").arg(filename).arg("%1");

    if (filename.isEmpty()) {
        return;
    }

    QFile file(filename);

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        errorString = file.errorString() + QStringLiteral(" ") + filename;
        return;
    }

    QString fileExtension(".%1");
    if (filename.endsWith(fileExtension.arg(AppSettings::planFileExtension))) {
        QJsonDocument   jsonDoc;
        QByteArray      bytes = file.readAll();

        if (!JsonHelper::isJsonFile(bytes, jsonDoc, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return;
        }

        int version;
        QJsonObject json = jsonDoc.object();
        if (!JsonHelper::validateQGCJsonFile(json, _planFileType, _planFileVersion, _planFileVersion, version, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return;
        }

        QList<JsonHelper::KeyValidateInfo> rgKeyInfo = {
            { _jsonMissionObjectKey,        QJsonValue::Object, true },
            { _jsonGeoFenceObjectKey,       QJsonValue::Object, true },
            { _jsonRallyPointsObjectKey,    QJsonValue::Object, true },
        };
        if (!JsonHelper::validateKeys(json, rgKeyInfo, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return;
        }

        if (!_missionController.load(json[_jsonMissionObjectKey].toObject(), errorString) ||
                !_geoFenceController.load(json[_jsonGeoFenceObjectKey].toObject(), errorString) ||
                !_rallyPointController.load(json[_jsonRallyPointsObjectKey].toObject(), errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
        }
    } else if (filename.endsWith(fileExtension.arg(AppSettings::missionFileExtension))) {
        if (!_missionController.loadJsonFile(file, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
        }
    } else if (filename.endsWith(fileExtension.arg(AppSettings::waypointsFileExtension)) ||
               filename.endsWith(fileExtension.arg(QStringLiteral("txt")))) {
        if (!_missionController.loadTextFile(file, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
        }
    }

    if (!offline()) {
        setDirty(true);
    }
}

void PlanMasterController::saveToFile(const QString& filename)
{
    if (filename.isEmpty()) {
        return;
    }

    QString planFilename = filename;
    if (!QFileInfo(filename).fileName().contains(".")) {
        planFilename += QString(".%1").arg(fileExtension());
    }

    QFile file(planFilename);

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qgcApp()->showMessage(tr("Plan save error %1 : %2").arg(filename).arg(file.errorString()));
    } else {
        QJsonObject planJson;
        QJsonObject missionJson;
        QJsonObject fenceJson;
        QJsonObject rallyJson;

        JsonHelper::saveQGCJsonFileHeader(planJson, _planFileType, _planFileVersion);
        _missionController.save(missionJson);
        _geoFenceController.save(fenceJson);
        _rallyPointController.save(rallyJson);
        planJson[_jsonMissionObjectKey] = missionJson;
        planJson[_jsonGeoFenceObjectKey] = fenceJson;
        planJson[_jsonRallyPointsObjectKey] = rallyJson;

        QJsonDocument saveDoc(planJson);
        file.write(saveDoc.toJson());
    }

    // Only clear dirty bit if we are offline
    if (offline()) {
        setDirty(false);
    }
}

void PlanMasterController::removeAll(void)
{
    _missionController.removeAll();
    _geoFenceController.removeAll();
    _rallyPointController.removeAll();
}

void PlanMasterController::removeAllFromVehicle(void)
{
    if (!offline()) {
        _missionController.removeAllFromVehicle();
        _geoFenceController.removeAllFromVehicle();
        _rallyPointController.removeAllFromVehicle();
    } else {
        qWarning() << "PlanMasterController::removeAllFromVehicle called while offline";
    }
}

bool PlanMasterController::containsItems(void) const
{
    return _missionController.containsItems() || _geoFenceController.containsItems() || _rallyPointController.containsItems();
}

bool PlanMasterController::dirty(void) const
{
    return _missionController.dirty() || _geoFenceController.dirty() || _rallyPointController.dirty();
}

void PlanMasterController::setDirty(bool dirty)
{
    _missionController.setDirty(dirty);
    _geoFenceController.setDirty(dirty);
    _rallyPointController.setDirty(dirty);
}

QString PlanMasterController::fileExtension(void) const
{
    return AppSettings::planFileExtension;
}

QStringList PlanMasterController::loadNameFilters(void) const
{
    QStringList filters;

    filters << tr("Supported types (*.%1 *.%2 *.%3 *.%4)").arg(AppSettings::planFileExtension).arg(AppSettings::missionFileExtension).arg(AppSettings::waypointsFileExtension).arg("*.txt") <<
               tr("All Files (*.*)");
    return filters;
}

QStringList PlanMasterController::saveNameFilters(void) const
{
    QStringList filters;

    filters << tr("Plan Files (*.%1)").arg(fileExtension()) << tr("All Files (*.*)");
    return filters;
}

void PlanMasterController::sendPlanToVehicle(Vehicle* vehicle, const QString& filename)
{
    // Use a transient PlanMasterController to accomplish this
    PlanMasterController* controller = new PlanMasterController();
    controller->startStaticActiveVehicle(vehicle);
    controller->loadFromFile(filename);
    delete controller;
}

void PlanMasterController::_showPlanFromManagerVehicle(void)
{
    // The crazy if structure is to handle the load propogating by itself through the system
    if (!_missionController.showPlanFromManagerVehicle()) {
        if (!_geoFenceController.showPlanFromManagerVehicle()) {
            _rallyPointController.showPlanFromManagerVehicle();
        }
    }
}
<commit_msg>Typo in file extensions<commit_after>/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "PlanMasterController.h"
#include "QGCApplication.h"
#include "MultiVehicleManager.h"
#include "SettingsManager.h"
#include "AppSettings.h"
#include "JsonHelper.h"
#include "MissionManager.h"

#include <QJsonDocument>
#include <QFileInfo>

QGC_LOGGING_CATEGORY(PlanMasterControllerLog, "PlanMasterControllerLog")

const int   PlanMasterController::_planFileVersion =            1;
const char* PlanMasterController::_planFileType =               "Plan";
const char* PlanMasterController::_jsonMissionObjectKey =       "mission";
const char* PlanMasterController::_jsonGeoFenceObjectKey =      "geoFence";
const char* PlanMasterController::_jsonRallyPointsObjectKey =   "rallyPoints";

PlanMasterController::PlanMasterController(QObject* parent)
    : QObject(parent)
    , _multiVehicleMgr(qgcApp()->toolbox()->multiVehicleManager())
    , _controllerVehicle(new Vehicle((MAV_AUTOPILOT)qgcApp()->toolbox()->settingsManager()->appSettings()->offlineEditingFirmwareType()->rawValue().toInt(), (MAV_TYPE)qgcApp()->toolbox()->settingsManager()->appSettings()->offlineEditingVehicleType()->rawValue().toInt(), qgcApp()->toolbox()->firmwarePluginManager()))
    , _managerVehicle(_controllerVehicle)
    , _editMode(false)
    , _offline(true)
    , _missionController(this)
    , _geoFenceController(this)
    , _rallyPointController(this)
    , _loadGeoFence(false)
    , _loadRallyPoints(false)
    , _sendGeoFence(false)
    , _sendRallyPoints(false)
    , _syncInProgress(false)
{
    connect(&_missionController,    &MissionController::dirtyChanged,       this, &PlanMasterController::dirtyChanged);
    connect(&_geoFenceController,   &GeoFenceController::dirtyChanged,      this, &PlanMasterController::dirtyChanged);
    connect(&_rallyPointController, &RallyPointController::dirtyChanged,    this, &PlanMasterController::dirtyChanged);

    connect(&_missionController,    &MissionController::containsItemsChanged,       this, &PlanMasterController::containsItemsChanged);
    connect(&_geoFenceController,   &GeoFenceController::containsItemsChanged,      this, &PlanMasterController::containsItemsChanged);
    connect(&_rallyPointController, &RallyPointController::containsItemsChanged,    this, &PlanMasterController::containsItemsChanged);

    connect(&_missionController,    &MissionController::syncInProgressChanged,      this, &PlanMasterController::syncInProgressChanged);
    connect(&_geoFenceController,   &GeoFenceController::syncInProgressChanged,     this, &PlanMasterController::syncInProgressChanged);
    connect(&_rallyPointController, &RallyPointController::syncInProgressChanged,   this, &PlanMasterController::syncInProgressChanged);
}

PlanMasterController::~PlanMasterController()
{

}

void PlanMasterController::start(bool editMode)
{
    _editMode = editMode;
    _missionController.start(editMode);
    _geoFenceController.start(editMode);
    _rallyPointController.start(editMode);

    connect(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &PlanMasterController::_activeVehicleChanged);
    _activeVehicleChanged(_multiVehicleMgr->activeVehicle());
}

void PlanMasterController::startStaticActiveVehicle(Vehicle* vehicle)
{
    _editMode = false;
    _activeVehicleChanged(vehicle);
}

void PlanMasterController::_activeVehicleChanged(Vehicle* activeVehicle)
{
    if (_managerVehicle == activeVehicle) {
        // We are already setup for this vehicle
        return;
    }

    qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged" << activeVehicle;

    bool newOffline = false;
    if (activeVehicle == NULL) {
        // Since there is no longer an active vehicle we use the offline controller vehicle as the manager vehicle
        _managerVehicle = _controllerVehicle;
        newOffline = true;
    } else {
        newOffline = false;
        _managerVehicle = activeVehicle;

        // Update controllerVehicle to the currently connected vehicle
        AppSettings* appSettings = qgcApp()->toolbox()->settingsManager()->appSettings();
        appSettings->offlineEditingFirmwareType()->setRawValue(AppSettings::offlineEditingFirmwareTypeFromFirmwareType(_managerVehicle->firmwareType()));
        appSettings->offlineEditingVehicleType()->setRawValue(AppSettings::offlineEditingVehicleTypeFromVehicleType(_managerVehicle->vehicleType()));

        // We use these signals to sequence upload and download to the multiple controller/managers
        connect(_managerVehicle->missionManager(),      &MissionManager::newMissionItemsAvailable,  this, &PlanMasterController::_loadMissionComplete);
        connect(_managerVehicle->geoFenceManager(),     &GeoFenceManager::loadComplete,             this, &PlanMasterController::_loadGeoFenceComplete);
        connect(_managerVehicle->rallyPointManager(),   &RallyPointManager::loadComplete,           this, &PlanMasterController::_loadRallyPointsComplete);
        connect(_managerVehicle->missionManager(),      &MissionManager::sendComplete,              this, &PlanMasterController::_sendMissionComplete);
        connect(_managerVehicle->geoFenceManager(),     &GeoFenceManager::sendComplete,             this, &PlanMasterController::_sendGeoFenceComplete);
        connect(_managerVehicle->rallyPointManager(),   &RallyPointManager::sendComplete,           this, &PlanMasterController::_sendRallyPointsComplete);
    }
    if (newOffline != _offline) {
        _offline = newOffline;
        emit offlineEditingChanged(newOffline);
    }

    _missionController.managerVehicleChanged(_managerVehicle);
    _geoFenceController.managerVehicleChanged(_managerVehicle);
    _rallyPointController.managerVehicleChanged(_managerVehicle);

    if (_editMode) {
        if (!offline()) {
            // We are in Plan view and we have a newly connected vehicle:
            //  - If there is no plan available in Plan view show the one from the vehicle
            //  - Otherwise leave the current plan alone
            if (!containsItems()) {
                qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Plan view is empty so loading from manager";
                _showPlanFromManagerVehicle();
            }
        }
    } else {
        if (offline()) {
            // No more active vehicle, clear mission
            qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Fly view is offline clearing plan";
            removeAll();
        } else {
            // Fly view has changed to a new active vehicle, update to show correct mission
            qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Fly view is online so loading from manager";
            _showPlanFromManagerVehicle();
        }
    }
}

void PlanMasterController::loadFromVehicle(void)
{
    if (offline()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called while offline";
    } else if (!_editMode) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called from Fly view";
    } else if (syncInProgress()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called while syncInProgress";
    } else {
        _loadGeoFence = true;
        _syncInProgress = true;
        _missionController.loadFromVehicle();
        setDirty(false);
    }
}

void PlanMasterController::_loadMissionComplete(void)
{
    if (_editMode && _loadGeoFence) {
        _loadGeoFence = false;
        _loadRallyPoints = true;
        _geoFenceController.loadFromVehicle();
        setDirty(false);
    }
}

void PlanMasterController::_loadGeoFenceComplete(void)
{
    if (_editMode && _loadRallyPoints) {
        _loadRallyPoints = false;
        _rallyPointController.loadFromVehicle();
        setDirty(false);
    }
}

void PlanMasterController::_loadRallyPointsComplete(void)
{
    if (_editMode) {
        _syncInProgress = false;
        emit syncInProgressChanged(false);
    }
}

void PlanMasterController::_sendMissionComplete(void)
{
    if (_editMode && _sendGeoFence) {
        qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start fence sendToVehicle";
        _sendGeoFence = false;
        _sendRallyPoints = true;
        _geoFenceController.sendToVehicle();
        setDirty(false);
    }
}

void PlanMasterController::_sendGeoFenceComplete(void)
{
    if (_editMode && _sendRallyPoints) {
        qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start rally sendToVehicle";
        _sendRallyPoints = false;
        _rallyPointController.sendToVehicle();
    }
}

void PlanMasterController::_sendRallyPointsComplete(void)
{
    if (_editMode && _syncInProgress) {
        qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle rally point send complete";
        _syncInProgress = false;
        emit syncInProgressChanged(false);
    }
}

void PlanMasterController::sendToVehicle(void)
{
    if (offline()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called while offline";
    } else if (!_editMode) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called from Fly view";
    } else if (syncInProgress()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called while syncInProgress";
    } else {
        qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start mission sendToVehicle";
        _sendGeoFence = true;
        _missionController.sendToVehicle();
        setDirty(false);
    }
}

void PlanMasterController::loadFromFile(const QString& filename)
{
    QString errorString;
    QString errorMessage = tr("Error reading Plan file (%1). %2").arg(filename).arg("%1");

    if (filename.isEmpty()) {
        return;
    }

    QFile file(filename);

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        errorString = file.errorString() + QStringLiteral(" ") + filename;
        return;
    }

    QString fileExtension(".%1");
    if (filename.endsWith(fileExtension.arg(AppSettings::planFileExtension))) {
        QJsonDocument   jsonDoc;
        QByteArray      bytes = file.readAll();

        if (!JsonHelper::isJsonFile(bytes, jsonDoc, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return;
        }

        int version;
        QJsonObject json = jsonDoc.object();
        if (!JsonHelper::validateQGCJsonFile(json, _planFileType, _planFileVersion, _planFileVersion, version, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return;
        }

        QList<JsonHelper::KeyValidateInfo> rgKeyInfo = {
            { _jsonMissionObjectKey,        QJsonValue::Object, true },
            { _jsonGeoFenceObjectKey,       QJsonValue::Object, true },
            { _jsonRallyPointsObjectKey,    QJsonValue::Object, true },
        };
        if (!JsonHelper::validateKeys(json, rgKeyInfo, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return;
        }

        if (!_missionController.load(json[_jsonMissionObjectKey].toObject(), errorString) ||
                !_geoFenceController.load(json[_jsonGeoFenceObjectKey].toObject(), errorString) ||
                !_rallyPointController.load(json[_jsonRallyPointsObjectKey].toObject(), errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
        }
    } else if (filename.endsWith(fileExtension.arg(AppSettings::missionFileExtension))) {
        if (!_missionController.loadJsonFile(file, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
        }
    } else if (filename.endsWith(fileExtension.arg(AppSettings::waypointsFileExtension)) ||
               filename.endsWith(fileExtension.arg(QStringLiteral("txt")))) {
        if (!_missionController.loadTextFile(file, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
        }
    }

    if (!offline()) {
        setDirty(true);
    }
}

void PlanMasterController::saveToFile(const QString& filename)
{
    if (filename.isEmpty()) {
        return;
    }

    QString planFilename = filename;
    if (!QFileInfo(filename).fileName().contains(".")) {
        planFilename += QString(".%1").arg(fileExtension());
    }

    QFile file(planFilename);

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qgcApp()->showMessage(tr("Plan save error %1 : %2").arg(filename).arg(file.errorString()));
    } else {
        QJsonObject planJson;
        QJsonObject missionJson;
        QJsonObject fenceJson;
        QJsonObject rallyJson;

        JsonHelper::saveQGCJsonFileHeader(planJson, _planFileType, _planFileVersion);
        _missionController.save(missionJson);
        _geoFenceController.save(fenceJson);
        _rallyPointController.save(rallyJson);
        planJson[_jsonMissionObjectKey] = missionJson;
        planJson[_jsonGeoFenceObjectKey] = fenceJson;
        planJson[_jsonRallyPointsObjectKey] = rallyJson;

        QJsonDocument saveDoc(planJson);
        file.write(saveDoc.toJson());
    }

    // Only clear dirty bit if we are offline
    if (offline()) {
        setDirty(false);
    }
}

void PlanMasterController::removeAll(void)
{
    _missionController.removeAll();
    _geoFenceController.removeAll();
    _rallyPointController.removeAll();
}

void PlanMasterController::removeAllFromVehicle(void)
{
    if (!offline()) {
        _missionController.removeAllFromVehicle();
        _geoFenceController.removeAllFromVehicle();
        _rallyPointController.removeAllFromVehicle();
    } else {
        qWarning() << "PlanMasterController::removeAllFromVehicle called while offline";
    }
}

bool PlanMasterController::containsItems(void) const
{
    return _missionController.containsItems() || _geoFenceController.containsItems() || _rallyPointController.containsItems();
}

bool PlanMasterController::dirty(void) const
{
    return _missionController.dirty() || _geoFenceController.dirty() || _rallyPointController.dirty();
}

void PlanMasterController::setDirty(bool dirty)
{
    _missionController.setDirty(dirty);
    _geoFenceController.setDirty(dirty);
    _rallyPointController.setDirty(dirty);
}

QString PlanMasterController::fileExtension(void) const
{
    return AppSettings::planFileExtension;
}

QStringList PlanMasterController::loadNameFilters(void) const
{
    QStringList filters;

    filters << tr("Supported types (*.%1 *.%2 *.%3 *.%4)").arg(AppSettings::planFileExtension).arg(AppSettings::missionFileExtension).arg(AppSettings::waypointsFileExtension).arg("txt") <<
               tr("All Files (*.*)");
    return filters;
}

QStringList PlanMasterController::saveNameFilters(void) const
{
    QStringList filters;

    filters << tr("Plan Files (*.%1)").arg(fileExtension()) << tr("All Files (*.*)");
    return filters;
}

void PlanMasterController::sendPlanToVehicle(Vehicle* vehicle, const QString& filename)
{
    // Use a transient PlanMasterController to accomplish this
    PlanMasterController* controller = new PlanMasterController();
    controller->startStaticActiveVehicle(vehicle);
    controller->loadFromFile(filename);
    delete controller;
}

void PlanMasterController::_showPlanFromManagerVehicle(void)
{
    // The crazy if structure is to handle the load propogating by itself through the system
    if (!_missionController.showPlanFromManagerVehicle()) {
        if (!_geoFenceController.showPlanFromManagerVehicle()) {
            _rallyPointController.showPlanFromManagerVehicle();
        }
    }
}
<|endoftext|>