// --------------------------------------------------------------------
// Dialogs
// --------------------------------------------------------------------
/*

    This file is part of the extensible drawing editor Ipe.
    Copyright (C) 1993-2004  Otfried Cheong

    Ipe 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; either version 2 of the License, or
    (at your option) any later version.

    As a special exception, you have permission to link Ipe with the
    CGAL library and distribute executables, as long as you follow the
    requirements of the Gnu General Public License in regard to all of
    the software in the executable aside from CGAL.

    Ipe 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.

    You should have received a copy of the GNU General Public License
    along with Ipe; if not, you can find it at
    "http://www.gnu.org/copyleft/gpl.html", or write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "dialogs.h"
#include "ipeq.h"
#include "ipestyle.h"
#include "ipeprefs.h"

#include <qcheckbox.h>
#include <qlineedit.h>
#include <qmultilineedit.h>
#include <qlabel.h>
#include <qwhatsthis.h>
#include <qspinbox.h>
#include <qgroupbox.h>
#include <qtoolbutton.h>
#include <qlistbox.h>
#include <qfiledialog.h>
#include <qcombobox.h>
#include <qfontdialog.h>

// --------------------------------------------------------------------

/*! \class DialogDocumentProperties
  \brief Dialog to edit document properties
*/

//! The argument \c data is only updated if the dialog is accepted.
DialogDocumentProperties::DialogDocumentProperties
(QWidget* parent, IpeDocument::SProperties &data, const IpeStyleSheet *sheet)
  : DlgDocumentProperties(parent, "dlgdocumentproperties", true),
    iData(data), iSheet(sheet)
{
  iAuthorEditor->setText(QIpe(iData.iAuthor));
  iTitleEditor->setText(QIpe(iData.iTitle));
  iSubjectEditor->setText(QIpe(iData.iSubject));
  iKeywordsEditor->setText(QIpe(iData.iKeywords));
  iLatexPreambleEditor->setText(QIpe(iData.iPreamble));
  iFullScreenBox->setChecked(iData.iFullScreen);
  iCropBox->setChecked(iData.iCropBox);
  iCreated->setText(QIpe(iData.iCreated));
  iModified->setText(QIpe(iData.iModified));

  IpeVector size = iData.iMedia.Max() - iData.iMedia.Min();
  IpeAttributeSeq seq;
  iSheet->AllNames(IpeAttribute::EMedia, seq);
  int curMedia = -1;
  for (uint i = 0; i < seq.size(); ++i) {
    iMedia->insertItem(QIpe(iSheet->Repository()->String(seq[i])));
    IpeAttribute abs = iSheet->Find(seq[i]);
    IpeVector media = iSheet->Repository()->ToVector(abs);
    if ((media - size).SqLen() < 1.0)
      curMedia = i;
  }
  if (curMedia >= 0) {
    iMedia->setCurrentItem(curMedia);
    iCustomMedia = false;
  } else {
    // need custom media
    QString s = QString("Custom (%1 x %2)")
      .arg(int(size.iX + 0.5)).arg(int(size.iY + 0.5));
    iMedia->insertItem(s, 0);
    iMedia->setCurrentItem(0);
    iCustomMedia = true;
  }
}

void DialogDocumentProperties::UpdateData()
{
  iData.iAuthor = IpeQ(iAuthorEditor->text());
  iData.iTitle = IpeQ(iTitleEditor->text());
  iData.iSubject = IpeQ(iSubjectEditor->text());
  iData.iKeywords = IpeQ(iKeywordsEditor->text());
  iData.iPreamble = IpeQ(iLatexPreambleEditor->text());
  iData.iFullScreen = iFullScreenBox->isChecked();
  iData.iCropBox = iCropBox->isChecked();
  IpeVector media = GetMedia();
  iData.iMedia = IpeRect(iData.iMedia.Min(), iData.iMedia.Min() + media);
}

IpeVector DialogDocumentProperties::GetMedia()
{
  int curMedia = iMedia->currentItem();
  if (iCustomMedia)
    --curMedia;
  if (curMedia >= 0) {
    IpeAttributeSeq seq;
    iSheet->AllNames(IpeAttribute::EMedia, seq);
    IpeAttribute abs = iSheet->Find(seq[curMedia]);
    return iSheet->Repository()->ToVector(abs);
  } else
    return iData.iMedia.Max() - iData.iMedia.Min();
}

void DialogDocumentProperties::ShowHelp()
{
  QWhatsThis::enterWhatsThisMode();
}

void DialogDocumentProperties::SetDefaultMedia()
{
  IpePreferences *prefs = IpePreferences::Static();
  prefs->iMedia = GetMedia();
  ipeDebug("Setting default media %g x %g",
	   prefs->iMedia.iX, prefs->iMedia.iY);
  prefs->Save();
}

// --------------------------------------------------------------------

/*! \class DialogDirPreferences
  \brief Dialog to display Ipe directory settings (read only)
*/

//! Create dialog. \c data is only updated if dialog is accepted.
DialogDirPreferences::DialogDirPreferences(QWidget* parent,
					   IpePreferences &data)
  : DlgDirectories(parent, "dialogdirpreferences", true), iData(data)
{
  // copy data
  iPreferencesEditor->setText(iData.iPrefsFileName);
  iPreferencesEditor->setReadOnly(true);
  iLatexDirEditor->setText(iData.iLatexDir);
  iLatexDirEditor->setReadOnly(true);
  iPdfLatexEditor->setText(iData.iPdfLatex);
  iPdfLatexEditor->setReadOnly(true);
  iDocDirEditor->setText(iData.iDocDir);
  iDocDirEditor->setReadOnly(true);
  iFontmapEditor->setText(iData.iFontmap);
  iFontmapEditor->setReadOnly(true);
  iIpeletListBox->clear();
  iIpeletListBox->insertStringList(iData.iIpeletPath);
}

void DialogDirPreferences::ShowHelp()
{
  QWhatsThis::enterWhatsThisMode();
}

// --------------------------------------------------------------------

/*! \class DialogPreferences
  \brief Dialog to edit Ipe preferences
*/

//! Create dialog. \c data is only updated if dialog is accepted.
DialogPreferences::DialogPreferences(QWidget* parent, IpePreferences &data,
				     int gridSize)
  : DlgPreferences(parent, "dialogpreferences", true),
    iData(data), iGridSize(gridSize)
{
  // copy data
  iCompress->setChecked(iData.iCompressLevel > 0);
  iSelectDistance->setValue(iData.iSelectDistance);
  iSnapDistance->setValue(iData.iSnapDistance);
  iWhitePaper->setChecked(iData.iWhitePaper);
  iBigToolButtons->setChecked(iData.iBigToolButtons);
  iTransformable->setChecked(iData.iTransformable);
  iAntiAlias->setChecked(iData.iAntiAlias);
  iGridVisible->setChecked(iData.iGridVisible);
  iMaximize->setChecked(iData.iMaximize);

  iStyleSheet->setReadOnly(true);
  iStyleSheetPath = iData.iStyleSheet;
  if (!iStyleSheetPath.isNull())
    iStyleSheet->setText(iStyleSheetPath);
  // find all languages
  QDir dir(iData.iLangDir);
  dir.setFilter(QDir::Files);
  dir.setNameFilter("ipe-*.qm");
  QStringList langs = dir.entryList();
  iLanguage->insertItem("English (US)");
  iLanguage->setCurrentItem(0);
  for (uint j = 0; j < langs.count(); ++j) {
    QString fn = dir.filePath(langs[j]);
    QFileInfo finfo(fn);
    iLanguage->insertItem(finfo.baseName().mid(4));
    if (finfo.baseName().mid(4) == iData.iLanguage)
      iLanguage->setCurrentItem(iLanguage->count() - 1);
  }
}

void DialogPreferences::ShowHelp()
{
  QWhatsThis::enterWhatsThisMode();
}

void DialogPreferences::ShowDirectories()
{
  DialogDirPreferences *dialog = new DialogDirPreferences(this, iData);
  dialog->exec();
}

void DialogPreferences::SetStyleSheet()
{
  QString startIn = iStyleSheetPath;
  if (startIn.isNull())
    startIn = IpePreferences::Static()->iDialogDir;
  QString fn = QFileDialog::getOpenFileName
    (startIn, tr("Ipe style sheets (*.isy *.xml)"), this, 0,
     tr("Select default style sheet"));
  if (!fn.isEmpty()) {
    iStyleSheetPath = fn;
    iStyleSheet->setText(fn);
  }
}

void DialogPreferences::SetDefaultGrid()
{
  IpePreferences *prefs = IpePreferences::Static();
  prefs->iGridSize = iGridSize;
  prefs->Save();
}

void DialogPreferences::ClearStyleSheet()
{
  iStyleSheetPath = QString::null;
  iStyleSheet->setText("");
}

void DialogPreferences::SetFont()
{
  iData.iFont = QFontDialog::getFont(0, iData.iFont);
}

void DialogPreferences::SetTextFont()
{
  iData.iTextFont = QFontDialog::getFont(0, iData.iTextFont);
}

void DialogPreferences::UpdateData()
{
  // copy data
  iData.iCompressLevel = iCompress->isChecked() ? 9 : 0;
  iData.iSelectDistance = iSelectDistance->value();
  iData.iSnapDistance = iSnapDistance->value();
  iData.iWhitePaper = iWhitePaper->isChecked();
  iData.iBigToolButtons = iBigToolButtons->isChecked();
  iData.iTransformable = iTransformable->isChecked();
  iData.iAntiAlias = iAntiAlias->isChecked();
  iData.iGridVisible = iGridVisible->isChecked();
  iData.iMaximize = iMaximize->isChecked();
  iData.iStyleSheet = iStyleSheetPath;
  if (iLanguage->currentItem() == 0)
    iData.iLanguage = QString::null;
  else
    iData.iLanguage = iLanguage->currentText();
}

// --------------------------------------------------------------------

/*! \class DialogPageViews
  \brief Dialog to edit page presentation
*/

/*! Create dialog, given layer names (read-only), and an IpeViewSeq
  object.  Note that \c data is updated during the execution of the
  dialog, and must be discarded if the return value of exec() is not
  QDialog::Accepted. */
DialogPageViews::DialogPageViews(QWidget* parent, QStringList layers,
				 IpeViewSeq &data, QStringList &sections)
  : DlgPagePresentation(parent, "dialogpageviews", true),
    iLayers(layers), iData(data), iSections(sections)
{
  // init effects combo box
  iEffectComboBox->insertItem(tr("No effect"));
  iEffectComboBox->insertItem(tr("Split horizontally inwards"));
  iEffectComboBox->insertItem(tr("Split horizontally outwards"));
  iEffectComboBox->insertItem(tr("Split vertically inwards"));
  iEffectComboBox->insertItem(tr("Split vertically outwards"));
  iEffectComboBox->insertItem(tr("Blinds horizontally"));
  iEffectComboBox->insertItem(tr("Blinds vertically"));
  iEffectComboBox->insertItem(tr("Box inwards"));
  iEffectComboBox->insertItem(tr("Box outwards"));
  iEffectComboBox->insertItem(tr("Wipe left-to-right"));
  iEffectComboBox->insertItem(tr("Wipe bottom-to-top"));
  iEffectComboBox->insertItem(tr("Wipe right-to-left"));
  iEffectComboBox->insertItem(tr("Wipe top-to-bottom"));
  iEffectComboBox->insertItem(tr("Dissolve"));
  iEffectComboBox->insertItem(tr("Glitter left-to-right"));
  iEffectComboBox->insertItem(tr("Glitter top-to-bottom"));
  iEffectComboBox->insertItem(tr("Glitter diagonally"));
  // set layers
  iLayerListBox->clear();
  iLayerListBox->insertStringList(layers);
  // dialog state
  iCurView = 0;
  iInternal = true;
  // init views list
  iViewsListBox->clear();
  for (uint i = 0; i < iData.size(); i++) {
    iViewsListBox->insertItem("");
    UpdateViewLine(i);
  }
  UpdateViewState();
  UpdateFields();
  iSectionEditor->setText(iSections[0]);
  iSubsectionEditor->setText(iSections[1]);
}

//! Update the enabled state of the view buttons and the current line
void DialogPageViews::UpdateViewState()
{
  iViewsListBox->setCurrentItem(iCurView);
  iViewsDelButton->setEnabled(iData.size() > 1);
  iViewsUpButton->setEnabled(iCurView > 0);
  iViewsDownButton->setEnabled(iCurView < int(iData.size() - 1));
}

//! Update the view description lines
void DialogPageViews::UpdateViewLine(int itemno)
{
  IpeView &p = iData[itemno];
  QString s;
  s.sprintf("View #%d: ", itemno + 1);
  for (int i = 0; i < p.CountLayers(); ++i) {
    if (i > 0)
      s.append(", ");
    if (p.Layer(i) == p.Active())
      s.append("<" + QIpe(p.Layer(i)) + ">");
    else
      s.append(QIpe(p.Layer(i)));
  }
  if (p.Duration() > 0)
    s.append("  (" + QString::number(p.Duration()) + " sec.)");
  iViewsListBox->changeItem(s, itemno);
}

void DialogPageViews::AddNewView()
{
  iInternal = true;
  IpeView pres = iData[iCurView];
  ++iCurView;
  IpeViewSeq::iterator it = iData.begin() + iCurView;
  iData.insert(it, pres);
  iViewsListBox->insertItem("", iCurView);
  UpdateViewLine(iCurView);
  UpdateViewState();
  UpdateFields();
}

void DialogPageViews::RemoveView()
{
  if (iData.size() == 0) return;
  iInternal = true;
  iData.erase(iData.begin() + iCurView);
  iViewsListBox->removeItem(iCurView);
  if (iCurView == int(iData.size()))
    --iCurView;
  UpdateViewState();
  UpdateFields();
}

void DialogPageViews::MoveViewUp()
{
  if (iCurView == 0) return;
  iInternal = true;
  IpeViewSeq::iterator it = iData.begin() + iCurView;
  IpeView p = *it;
  it = iData.erase(it);
  --iCurView; --it; // previous line
  iData.insert(it, p);
  UpdateViewLine(iCurView);
  UpdateViewLine(iCurView + 1);
  UpdateViewState();
  iInternal = false;
}

void DialogPageViews::MoveViewDown()
{
  if (iCurView == int(iViewsListBox->count() - 1)) return;
  iInternal = true;
  IpeViewSeq::iterator it = iData.begin() + iCurView;
  IpeView p = *it;
  it = iData.erase(it);
  ++iCurView; ++it; // next line
  iData.insert(it, p);
  UpdateViewLine(iCurView);
  UpdateViewLine(iCurView - 1);
  UpdateViewState();
  iInternal = false;
}

void DialogPageViews::CurrentViewChanged(QListBoxItem *)
{
  if (iInternal) return;
  int itemno = iViewsListBox->currentItem();
  if (itemno >= 0)
    iCurView = itemno;
  UpdateFields();
  UpdateViewState();
}

// --------------------------------------------------------------------

//! Update the view fields when the current view has changed.
void DialogPageViews::UpdateFields()
{
  iInternal = true;
  IpeView &p = iData[iCurView];
  iDurationSpinBox->setValue(p.Duration());
  iTransitionSpinBox->setValue(p.TransitionTime());
  iEffectComboBox->setCurrentItem(int(p.Effect()));
  for (uint i = 0; i < iLayerListBox->count(); ++i) {
    iLayerListBox->setSelected(i, p.HasLayer(IpeQ(iLayerListBox->text(i))));
  }
  iTransitionSpinBox->setEnabled(p.Effect() != IpeView::ENormal);
  iInternal = false;
}

void DialogPageViews::LayerHighlighted()
{
  if (!iInternal) {
    iData[iCurView].ClearLayers();
    for (uint i = 0; i < iLayerListBox->count(); ++i) {
      if (iLayerListBox->isSelected(i)) {
	iData[iCurView].AddLayer(IpeQ(iLayerListBox->text(i)));
      }
    }
    UpdateViewLine(iCurView);
  }
}

void DialogPageViews::EffectChanged(int effectNo)
{
  iTransitionSpinBox->setEnabled(effectNo != 0);
  if (!iInternal)
    iData[iCurView].SetEffect(IpeView::TEffect(effectNo));
}

void DialogPageViews::TransitionChanged(int val)
{
  if (!iInternal)
    iData[iCurView].SetTransitionTime(val);
}

void DialogPageViews::DurationChanged(int val)
{
  if (!iInternal) {
    iData[iCurView].SetDuration(val);
    UpdateViewLine(iCurView);
  }
}

void DialogPageViews::UpdateData()
{
  iSections[0] = iSectionEditor->text();
  iSections[1] = iSubsectionEditor->text();
}

void DialogPageViews::ShowHelp()
{
  QWhatsThis::enterWhatsThisMode();
}

// --------------------------------------------------------------------

DialogLatexError::DialogLatexError(QWidget* parent, QString log)
  : DlgLatexErr(parent, "dlglatexerr", false)
{
  iLogEditor->setText(log);
  iLogEditor->setFocus();
}

// --------------------------------------------------------------------

