Blob Blame Raw
#pragma once

#ifndef HEXCOLORNAMES_H
#define HEXCOLORNAMES_H

// TnzCore includes
#include "tcommon.h"
#include "tfilepath.h"
#include "tpixel.h"
#include "tpalette.h"
#include "tenv.h"

// TnzQt includes
#include "toonzqt/dvdialog.h"
#include "toonzqt/colorfield.h"

// Qt includes
#include <QString>
#include <QPushButton>
#include <QCheckBox>
#include <QLineEdit>
#include <QCompleter>
#include <QMap>
#include <QTreeWidget>
#include <QStyledItemDelegate>

#undef DVAPI
#undef DVVAR
#ifdef TOONZQT_EXPORTS
#define DVAPI DV_EXPORT_API
#define DVVAR DV_EXPORT_VAR
#else
#define DVAPI DV_IMPORT_API
#define DVVAR DV_IMPORT_VAR
#endif

namespace DVGui {

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

//! Manage all Hex and named colors conversions.
class HexColorNames final : public QObject {
  Q_OBJECT
  HexColorNames();

  static QMap<QString, QString> s_maincolornames;
  static QMap<QString, QString> s_usercolornames;
  static QMap<QString, QString> s_tempcolornames;

  static void loadColorTableXML(QMap<QString, QString> &table,
                                const TFilePath &fp);
  static void saveColorTableXML(QMap<QString, QString> &table,
                                const TFilePath &fp);
  static bool parseHexInternal(QString text, TPixel &outPixel);

public:

  //! iterator for accessing user entries
  class iterator {
    QMap<QString, QString>::iterator m_it;
    bool m_mutable;

  public:
    inline iterator() : m_it(nullptr), m_mutable(false) {}
    inline iterator(iterator *it)
        : m_it(it->m_it), m_mutable(it->m_mutable) {}
    inline iterator(QMap<QString, QString>::iterator it, bool isMutable)
        : m_it(it), m_mutable(isMutable) {}

    inline const QString &name() const { return m_it.key(); }
    inline const QString &value() const { return m_it.value(); }
    inline void setValue(const QString &value) { if (m_mutable) m_it.value() = value; }
    inline bool operator==(const iterator &o) const { return m_it == o.m_it; }
    inline bool operator!=(const iterator &o) const { return m_it != o.m_it; }
    inline iterator &operator++() {
      ++m_it;
      return *this;
    }
    inline iterator &operator--() {
      --m_it;
      return *this;
    }
    inline iterator operator++(int) {
      iterator r = *this;
      m_it++;
      return r;
    }
    inline iterator operator--(int) {
      iterator r = *this;
      m_it--;
      return r;
    }
  };

  //! Load entries from main colornames.txt file
  static bool loadMainFile(bool reload);

  //! Load entries from user colornames.txt file
  static bool loadUserFile(bool reload);

  //! Load temporary entries from custom file
  static bool loadTempFile(const TFilePath& fp);

  //! Verify if there's personal colornames.txt file
  static bool hasUserFile();

  //! Save entries to user colornames.txt file
  static bool saveUserFile();

  //! Load temporary entries from custom file
  static bool saveTempFile(const TFilePath &fp);

  //! Clear all user entries
  static void clearUserEntries();

  //! Clear all user entries
  static void clearTempEntries();

  //! Set single user entry
  static void setUserEntry(const QString &name, const QString &hex);

  //! Set single user entry
  static void setTempEntry(const QString &name, const QString &hex);

  //! Get first main entry
  static inline iterator beginMain() {
    return iterator(s_maincolornames.begin(), false);
  }

  //! Get last main entry
  static inline iterator endMain() {
    return iterator(s_maincolornames.end(), false);
  }

  //! Get first user entry
  static inline iterator beginUser() {
    return iterator(s_usercolornames.begin(), true);
  }

  //! Get last user entry
  static inline iterator endUser() {
    return iterator(s_usercolornames.end(), true);
  }

  //! Get first user entry
  static inline iterator beginTemp() {
    return iterator(s_tempcolornames.begin(), true);
  }

  //! Get last user entry
  static inline iterator endTemp() {
    return iterator(s_tempcolornames.end(), true);
  }

  //! Parse raw text into RGBA color
  static bool parseText(QString text, TPixel &outPixel);

  //! Parse hex format into RGBA color
  static bool parseHex(QString text, TPixel &outPixel);

  //! Generate hex format from RGBA color
  static QString generateHex(TPixel pixel);

  //! To access singleton for signaling
  static HexColorNames *instance();

  //! Emit that user entries were changed
  inline void emitChanged() { emit colorsChanged(); }
  inline void emitAutoComplete(bool enable) { emit autoCompleteChanged(enable); }

signals:
  void autoCompleteChanged(bool);
  void colorsChanged();
};

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

//! Hex line-edit widget
class DVAPI HexLineEdit : public QLineEdit {
  Q_OBJECT

public:
  HexLineEdit(const QString &contents, QWidget *parent);
  ~HexLineEdit() {}

  void setStyle(TColorStyle &style, int index);
  void updateColor();
  void setColor(TPixel color);
  TPixel getColor() { return m_color; }
  bool fromText(QString text);
  bool fromHex(QString text);

protected:
  void mousePressEvent(QMouseEvent *event) override;
  void focusOutEvent(QFocusEvent *event) override;
  void showEvent(QShowEvent *event) override;

  QCompleter *getCompleter();

  bool m_editing;
  TPixel m_color;
  QCompleter *m_completer;

protected slots:
  void onAutoCompleteChanged(bool);
  void onColorsChanged();
};

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

//! Editing delegate for editor
class HexColorNamesEditingDelegate : public QStyledItemDelegate {
  Q_OBJECT

public:
  HexColorNamesEditingDelegate(QObject *parent = nullptr)
      : QStyledItemDelegate(parent) {
    connect(this,
            SIGNAL(closeEditor(QWidget *,
                               QAbstractItemDelegate::EndEditHint)),
            this,
            SLOT(onCloseEditor(QWidget *, QAbstractItemDelegate::EndEditHint)));
  }
  virtual QWidget *createEditor(QWidget *parent,
                                const QStyleOptionViewItem &option,
                                const QModelIndex &index) const {
    QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
    emit editingStarted(index);
    return widget;
  }
  virtual void setModelData(QWidget *editor, QAbstractItemModel *model,
                            const QModelIndex &index) const {
    QStyledItemDelegate::setModelData(editor, model, index);
    emit editingFinished(index);
  }

signals:
  void editingStarted(const QModelIndex &) const;
  void editingFinished(const QModelIndex &) const;
  void editingClosed() const;

private slots:
  inline void onCloseEditor(QWidget *editor,
                            QAbstractItemDelegate::EndEditHint hint = NoHint) {
    emit editingClosed();
  }
};

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

//! Dialog: Hex color names editor
class HexColorNamesEditor final : public DVGui::Dialog {
  Q_OBJECT

  HexColorNamesEditingDelegate *m_userEditingDelegate;
  QTreeWidget *m_userTreeWidget;
  QTreeWidget *m_mainTreeWidget;
  QCheckBox *m_autoCompleteCb;
  QPushButton *m_addColorButton;
  QPushButton *m_delColorButton;
  QPushButton *m_unsColorButton;
  QPushButton *m_importButton;
  QPushButton *m_exportButton;
  HexLineEdit *m_hexLineEdit;
  ColorField *m_colorField;
  bool m_autoComplete;

  bool m_newEntry;
  int m_selectedColn;
  QTreeWidgetItem *m_selectedItem;
  QString m_selectedName, m_selectedHex;

  QTreeWidgetItem *addEntry(QTreeWidget *tree, const QString &name,
                            const QString &hex, bool editable);
  bool updateUserHexEntry(QTreeWidgetItem *treeItem, const TPixel32 &color);
  bool nameValid(const QString& name);
  bool nameExists(const QString &name, QTreeWidgetItem *self);
  void deselectItem(bool treeFocus);
  void deleteCurrentItem(bool deselect);
  void populateMainList(bool reload);
  void populateUserList(bool reload);

  void showEvent(QShowEvent *e) override;
  void keyPressEvent(QKeyEvent *event) override;
  void focusInEvent(QFocusEvent *e) override;
  void focusOutEvent(QFocusEvent *e) override;
  bool eventFilter(QObject *obj, QEvent *e) override;

private slots:
  void onCurrentItemChanged(QTreeWidgetItem *current,
                            QTreeWidgetItem *previous);
  void onEditingStarted(const QModelIndex &);
  void onEditingFinished(const QModelIndex &);
  void onEditingClosed();
  void onItemStarted(QTreeWidgetItem *item, int column);
  void onItemFinished(QTreeWidgetItem *item, int column);
  void onColorFieldChanged(const TPixel32 &color, bool isDragging);
  void onHexChanged();
  void onAddColor();
  void onDelColor();
  void onDeselect();
  void onImport();
  void onExport();
  void onOK();
  void onApply();

public:
  HexColorNamesEditor(QWidget *parent);
};

//-----------------------------------------------------------------------------
}  // namespace DVGui
//-----------------------------------------------------------------------------

#endif  // HEXCOLORNAMES_H