/* -*- c++ -*- */
/*
 * Copyright 2012 Free Software Foundation, Inc.
 *
 * This file is part of GNU Radio
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 */

#ifndef FORM_MENUS_H
#define FORM_MENUS_H

#include <QtGui/QDoubleValidator>
#include <QtGui/QIntValidator>
#include <QtGui/QtGui>
#include <QComboBox>
#include <stdexcept>
#include <vector>

#if QT_VERSION >= 0x050000
#include <QtWidgets/QtWidgets>
#endif

#include <gnuradio/filter/firdes.h>
#include <gnuradio/qtgui/qtgui_types.h>
#include <gnuradio/qtgui/trigger_mode.h>
#include <qwt_symbol.h>

class LineColorMenu : public QMenu
{
    Q_OBJECT

public:
    LineColorMenu(unsigned int which, QWidget* parent)
        : QMenu("Line Color", parent), d_which(which)
    {
        d_grp = new QActionGroup(this);

        d_act.push_back(new QAction("Blue", this));
        d_act.push_back(new QAction("Red", this));
        d_act.push_back(new QAction("Green", this));
        d_act.push_back(new QAction("Black", this));
        d_act.push_back(new QAction("Cyan", this));
        d_act.push_back(new QAction("Magenta", this));
        d_act.push_back(new QAction("Yellow", this));
        d_act.push_back(new QAction("Gray", this));
        d_act.push_back(new QAction("Dark Red", this));
        d_act.push_back(new QAction("Dark Green", this));
        d_act.push_back(new QAction("Dark Blue", this));
        d_act.push_back(new QAction("Dark Gray", this));

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getBlue()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getRed()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getGreen()));
        connect(d_act[3], SIGNAL(triggered()), this, SLOT(getBlack()));
        connect(d_act[4], SIGNAL(triggered()), this, SLOT(getCyan()));
        connect(d_act[5], SIGNAL(triggered()), this, SLOT(getMagenta()));
        connect(d_act[6], SIGNAL(triggered()), this, SLOT(getYellow()));
        connect(d_act[7], SIGNAL(triggered()), this, SLOT(getGray()));
        connect(d_act[8], SIGNAL(triggered()), this, SLOT(getDarkRed()));
        connect(d_act[9], SIGNAL(triggered()), this, SLOT(getDarkGreen()));
        connect(d_act[10], SIGNAL(triggered()), this, SLOT(getDarkBlue()));
        connect(d_act[11], SIGNAL(triggered()), this, SLOT(getDarkGray()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }
    }

    ~LineColorMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("LineColorMenu::getAction: which out of range.");
    }

signals:
    void whichTrigger(unsigned int which, const QString& name);

public slots:
    void getBlue() { emit whichTrigger(d_which, "blue"); }
    void getRed() { emit whichTrigger(d_which, "red"); }
    void getGreen() { emit whichTrigger(d_which, "green"); }
    void getBlack() { emit whichTrigger(d_which, "black"); }
    void getCyan() { emit whichTrigger(d_which, "cyan"); }
    void getMagenta() { emit whichTrigger(d_which, "magenta"); }
    void getYellow() { emit whichTrigger(d_which, "yellow"); }
    void getGray() { emit whichTrigger(d_which, "gray"); }
    void getDarkRed() { emit whichTrigger(d_which, "darkred"); }
    void getDarkGreen() { emit whichTrigger(d_which, "darkgreen"); }
    void getDarkBlue() { emit whichTrigger(d_which, "darkblue"); }
    void getDarkGray() { emit whichTrigger(d_which, "darkgray"); }

private:
    QActionGroup* d_grp;
    QList<QAction*> d_act;
    int d_which;
};


/********************************************************************/


class LineWidthMenu : public QMenu
{
    Q_OBJECT

public:
    LineWidthMenu(unsigned int which, QWidget* parent)
        : QMenu("Line Width", parent), d_which(which)
    {
        d_grp = new QActionGroup(this);

        d_act.push_back(new QAction("1", this));
        d_act.push_back(new QAction("2", this));
        d_act.push_back(new QAction("3", this));
        d_act.push_back(new QAction("4", this));
        d_act.push_back(new QAction("5", this));
        d_act.push_back(new QAction("6", this));
        d_act.push_back(new QAction("7", this));
        d_act.push_back(new QAction("8", this));
        d_act.push_back(new QAction("9", this));
        d_act.push_back(new QAction("10", this));

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getOne()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getTwo()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getThree()));
        connect(d_act[3], SIGNAL(triggered()), this, SLOT(getFour()));
        connect(d_act[4], SIGNAL(triggered()), this, SLOT(getFive()));
        connect(d_act[5], SIGNAL(triggered()), this, SLOT(getSix()));
        connect(d_act[6], SIGNAL(triggered()), this, SLOT(getSeven()));
        connect(d_act[7], SIGNAL(triggered()), this, SLOT(getEight()));
        connect(d_act[8], SIGNAL(triggered()), this, SLOT(getNine()));
        connect(d_act[9], SIGNAL(triggered()), this, SLOT(getTen()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }
    }

    ~LineWidthMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("LineWidthMenu::getAction: which out of range.");
    }

signals:
    void whichTrigger(unsigned int which, unsigned int width);

public slots:
    void getOne() { emit whichTrigger(d_which, 1); }
    void getTwo() { emit whichTrigger(d_which, 2); }
    void getThree() { emit whichTrigger(d_which, 3); }
    void getFour() { emit whichTrigger(d_which, 4); }
    void getFive() { emit whichTrigger(d_which, 5); }
    void getSix() { emit whichTrigger(d_which, 6); }
    void getSeven() { emit whichTrigger(d_which, 7); }
    void getEight() { emit whichTrigger(d_which, 8); }
    void getNine() { emit whichTrigger(d_which, 9); }
    void getTen() { emit whichTrigger(d_which, 10); }

private:
    QActionGroup* d_grp;
    QList<QAction*> d_act;
    int d_which;
};


/********************************************************************/


class LineStyleMenu : public QMenu
{
    Q_OBJECT

public:
    LineStyleMenu(unsigned int which, QWidget* parent)
        : QMenu("Line Style", parent), d_which(which)
    {
        d_grp = new QActionGroup(this);

        d_act.push_back(new QAction("None", this));
        d_act.push_back(new QAction("Solid", this));
        d_act.push_back(new QAction("Dash", this));
        d_act.push_back(new QAction("Dots", this));
        d_act.push_back(new QAction("Dash-Dot", this));
        d_act.push_back(new QAction("Dash-Dot-Dot", this));

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getNone()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getSolid()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getDash()));
        connect(d_act[3], SIGNAL(triggered()), this, SLOT(getDots()));
        connect(d_act[4], SIGNAL(triggered()), this, SLOT(getDashDot()));
        connect(d_act[5], SIGNAL(triggered()), this, SLOT(getDashDotDot()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }
    }

    ~LineStyleMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("LineStyleMenu::getAction: which out of range.");
    }

signals:
    void whichTrigger(unsigned int which, Qt::PenStyle);

public slots:
    void getNone() { emit whichTrigger(d_which, Qt::NoPen); }
    void getSolid() { emit whichTrigger(d_which, Qt::SolidLine); }
    void getDash() { emit whichTrigger(d_which, Qt::DashLine); }
    void getDots() { emit whichTrigger(d_which, Qt::DotLine); }
    void getDashDot() { emit whichTrigger(d_which, Qt::DashDotLine); }
    void getDashDotDot() { emit whichTrigger(d_which, Qt::DashDotDotLine); }

private:
    QActionGroup* d_grp;
    QList<QAction*> d_act;
    int d_which;
};


/********************************************************************/


class LineMarkerMenu : public QMenu
{
    Q_OBJECT

public:
    LineMarkerMenu(unsigned int which, QWidget* parent)
        : QMenu("Line Marker", parent), d_which(which)
    {
        d_grp = new QActionGroup(this);

        d_act.push_back(new QAction("None", this));
        d_act.push_back(new QAction("Circle", this));
        d_act.push_back(new QAction("Rectangle", this));
        d_act.push_back(new QAction("Diamond", this));
        d_act.push_back(new QAction("Triangle", this));
        d_act.push_back(new QAction("Down Triangle", this));
        d_act.push_back(new QAction("Left Triangle", this));
        d_act.push_back(new QAction("Right Triangle", this));
        d_act.push_back(new QAction("Cross", this));
        d_act.push_back(new QAction("X-Cross", this));
        d_act.push_back(new QAction("Horiz. Line", this));
        d_act.push_back(new QAction("Vert. Line", this));
        d_act.push_back(new QAction("Star 1", this));
        d_act.push_back(new QAction("Star 2", this));
        d_act.push_back(new QAction("Hexagon", this));

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getNone()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getCircle()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getRect()));
        connect(d_act[3], SIGNAL(triggered()), this, SLOT(getDiamond()));
        connect(d_act[4], SIGNAL(triggered()), this, SLOT(getTriangle()));
        connect(d_act[5], SIGNAL(triggered()), this, SLOT(getDTriangle()));
        connect(d_act[6], SIGNAL(triggered()), this, SLOT(getLTriangle()));
        connect(d_act[7], SIGNAL(triggered()), this, SLOT(getRTriangle()));
        connect(d_act[8], SIGNAL(triggered()), this, SLOT(getCross()));
        connect(d_act[9], SIGNAL(triggered()), this, SLOT(getXCross()));
        connect(d_act[10], SIGNAL(triggered()), this, SLOT(getHLine()));
        connect(d_act[11], SIGNAL(triggered()), this, SLOT(getVLine()));
        connect(d_act[12], SIGNAL(triggered()), this, SLOT(getStar1()));
        connect(d_act[13], SIGNAL(triggered()), this, SLOT(getStar2()));
        connect(d_act[14], SIGNAL(triggered()), this, SLOT(getHexagon()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }
    }

    ~LineMarkerMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("LineMarkerMenu::getAction: which out of range.");
    }

signals:
    void whichTrigger(unsigned int which, QwtSymbol::Style);

public slots:
    void getNone() { emit whichTrigger(d_which, QwtSymbol::NoSymbol); }
    void getCircle() { emit whichTrigger(d_which, QwtSymbol::Ellipse); }
    void getRect() { emit whichTrigger(d_which, QwtSymbol::Rect); }
    void getDiamond() { emit whichTrigger(d_which, QwtSymbol::Diamond); }
    void getTriangle() { emit whichTrigger(d_which, QwtSymbol::Triangle); }
    void getDTriangle() { emit whichTrigger(d_which, QwtSymbol::DTriangle); }
    void getLTriangle() { emit whichTrigger(d_which, QwtSymbol::LTriangle); }
    void getRTriangle() { emit whichTrigger(d_which, QwtSymbol::RTriangle); }
    void getCross() { emit whichTrigger(d_which, QwtSymbol::Cross); }
    void getXCross() { emit whichTrigger(d_which, QwtSymbol::XCross); }
    void getHLine() { emit whichTrigger(d_which, QwtSymbol::HLine); }
    void getVLine() { emit whichTrigger(d_which, QwtSymbol::VLine); }
    void getStar1() { emit whichTrigger(d_which, QwtSymbol::Star1); }
    void getStar2() { emit whichTrigger(d_which, QwtSymbol::Star2); }
    void getHexagon() { emit whichTrigger(d_which, QwtSymbol::Hexagon); }

private:
    QActionGroup* d_grp;
    QList<QAction*> d_act;
    int d_which;
};


/********************************************************************/


class MarkerAlphaMenu : public QMenu
{
    Q_OBJECT

public:
    MarkerAlphaMenu(unsigned int which, QWidget* parent)
        : QMenu("Line Transparency", parent), d_which(which)
    {
        d_grp = new QActionGroup(this);

        d_act.push_back(new QAction("None", this));
        d_act.push_back(new QAction("Low", this));
        d_act.push_back(new QAction("Medium", this));
        d_act.push_back(new QAction("High", this));
        d_act.push_back(new QAction("Off", this));

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getNone()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getLow()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getMedium()));
        connect(d_act[3], SIGNAL(triggered()), this, SLOT(getHigh()));
        connect(d_act[4], SIGNAL(triggered()), this, SLOT(getOff()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }
    }

    ~MarkerAlphaMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("MarkerAlphaMenu::getAction: which out of range.");
    }

signals:
    void whichTrigger(unsigned int which, unsigned int);

public slots:
    void getNone() { emit whichTrigger(d_which, 255); }
    void getLow() { emit whichTrigger(d_which, 200); }
    void getMedium() { emit whichTrigger(d_which, 125); }
    void getHigh() { emit whichTrigger(d_which, 50); }
    void getOff() { emit whichTrigger(d_which, 0); }

private:
    QActionGroup* d_grp;
    QList<QAction*> d_act;
    int d_which;
};


/********************************************************************/


class LineTitleAction : public QAction
{
    Q_OBJECT

public:
    LineTitleAction(unsigned int which, QWidget* parent)
        : QAction("Line Title", parent), d_which(which)
    {
        d_diag = new QDialog(parent);
        d_diag->setModal(true);

        d_text = new QLineEdit();

        QGridLayout* layout = new QGridLayout(d_diag);
        QPushButton* btn_ok = new QPushButton(tr("OK"));
        QPushButton* btn_cancel = new QPushButton(tr("Cancel"));

        layout->addWidget(d_text, 0, 0, 1, 2);
        layout->addWidget(btn_ok, 1, 0);
        layout->addWidget(btn_cancel, 1, 1);

        connect(btn_ok, SIGNAL(clicked()), this, SLOT(getText()));
        connect(btn_cancel, SIGNAL(clicked()), d_diag, SLOT(close()));

        connect(this, SIGNAL(triggered()), this, SLOT(getTextDiag()));
    }

    ~LineTitleAction() override {}

signals:
    void whichTrigger(unsigned int which, const QString& text);

public slots:
    void getTextDiag() { d_diag->exec(); }

private slots:
    void getText()
    {
        emit whichTrigger(d_which, d_text->text());
        d_diag->accept();
    }

private:
    int d_which;

    QDialog* d_diag;
    QLineEdit* d_text;
};


/********************************************************************/


class AverageMenu : public QMenu
{
    Q_OBJECT

public:
    AverageMenu(const std::string& menuTitle, QWidget* parent)
        : QMenu(menuTitle.c_str(), parent)
    {
        d_grp = new QActionGroup(this);

        d_off = 1.0;
        d_high = 0.05;
        d_medium = 0.1;
        d_low = 0.2;

        d_act.push_back(new QAction("Off", this));
        d_act.push_back(new QAction("High", this));
        d_act.push_back(new QAction("Medium", this));
        d_act.push_back(new QAction("Low", this));

        d_grp = new QActionGroup(this);
        for (int t = 0; t < d_act.size(); t++) {
            d_act[t]->setCheckable(true);
            d_act[t]->setActionGroup(d_grp);
        }
        d_act[0]->setChecked(true);

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getOff()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getHigh()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getMedium()));
        connect(d_act[3], SIGNAL(triggered()), this, SLOT(getLow()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }
    }

    ~AverageMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("AverageMenu::getAction: which out of range.");
    }

    QAction* getActionFromAvg(float avg)
    {
        int which = 0;
        if (avg == d_off)
            which = 0;
        else if (avg == d_high)
            which = 1;
        else if (avg == d_medium)
            which = 2;
        else if (avg == d_low)
            which = 3;
        return d_act[static_cast<int>(which)];
    }

    void setHigh(float x) { d_high = x; }

    void setMedium(float x) { d_medium = x; }

    void setLow(float x) { d_low = x; }

signals:
    void whichTrigger(float alpha);

public slots:
    void getOff() { emit whichTrigger(d_off); }
    void getHigh() { emit whichTrigger(d_high); }
    void getMedium() { emit whichTrigger(d_medium); }
    void getLow() { emit whichTrigger(d_low); }
    void getOther(const QString& str)
    {
        float value = str.toFloat();
        emit whichTrigger(value);
    }

private:
    QList<QAction*> d_act;
    QActionGroup* d_grp;
    float d_off, d_high, d_medium, d_low;
};

/********************************************************************/

class FFTAverageMenu : public AverageMenu
{
public:
    FFTAverageMenu(QWidget* parent) : AverageMenu("FFT Average", parent)
    {
        // nop
    }

    ~FFTAverageMenu() override {}
};

/********************************************************************/


class FFTWindowMenu : public QMenu
{
    Q_OBJECT

public:
    FFTWindowMenu(QWidget* parent) : QMenu("FFT Window", parent)
    {
        d_act.push_back(new QAction("None", this));
        d_act.push_back(new QAction("Hamming", this));
        d_act.push_back(new QAction("Hann", this));
        d_act.push_back(new QAction("Blackman", this));
        d_act.push_back(new QAction("Blackman-harris", this));
        d_act.push_back(new QAction("Rectangular", this));
        d_act.push_back(new QAction("Kaiser", this));
        d_act.push_back(new QAction("Flat-top", this));

        d_grp = new QActionGroup(this);
        for (int t = 0; t < d_act.size(); t++) {
            d_act[t]->setCheckable(true);
            d_act[t]->setActionGroup(d_grp);
        }

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getNone()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getHamming()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getHann()));
        connect(d_act[3], SIGNAL(triggered()), this, SLOT(getBlackman()));
        connect(d_act[4], SIGNAL(triggered()), this, SLOT(getBlackmanharris()));
        connect(d_act[5], SIGNAL(triggered()), this, SLOT(getRectangular()));
        connect(d_act[6], SIGNAL(triggered()), this, SLOT(getKaiser()));
        connect(d_act[7], SIGNAL(triggered()), this, SLOT(getFlattop()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            addAction(a);
        }
    }

    ~FFTWindowMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("FFTWindowMenu::getAction: which out of range.");
    }

    QAction* getActionFromWindow(gr::fft::window::win_type type)
    {
        int which = 0;
        switch (static_cast<int>(type)) {
        case ((gr::fft::window::WIN_NONE)):
            which = 0;
            break;
        case ((gr::fft::window::WIN_HAMMING)):
            which = 1;
            break;
        case ((gr::fft::window::WIN_HANN)):
            which = 2;
            break;
        case ((gr::fft::window::WIN_BLACKMAN)):
            which = 3;
            break;
        case ((gr::fft::window::WIN_BLACKMAN_hARRIS)):
            which = 4;
            break;
        case ((gr::fft::window::WIN_RECTANGULAR)):
            which = 5;
            break;
        case ((gr::fft::window::WIN_KAISER)):
            which = 6;
            break;
        case ((gr::fft::window::WIN_FLATTOP)):
            which = 7;
            break;
        }
        return d_act[which];
    }

signals:
    void whichTrigger(const gr::fft::window::win_type type);

public slots:
    void getNone() { emit whichTrigger(gr::fft::window::WIN_NONE); }
    void getHamming() { emit whichTrigger(gr::fft::window::WIN_HAMMING); }
    void getHann() { emit whichTrigger(gr::fft::window::WIN_HANN); }
    void getBlackman() { emit whichTrigger(gr::fft::window::WIN_BLACKMAN); }
    void getBlackmanharris() { emit whichTrigger(gr::fft::window::WIN_BLACKMAN_hARRIS); }
    void getRectangular() { emit whichTrigger(gr::fft::window::WIN_RECTANGULAR); }
    void getKaiser() { emit whichTrigger(gr::fft::window::WIN_KAISER); }
    void getFlattop() { emit whichTrigger(gr::fft::window::WIN_FLATTOP); }

private:
    QList<QAction*> d_act;
    QActionGroup* d_grp;
};


/********************************************************************/


class NPointsMenu : public QAction
{
    Q_OBJECT

public:
    NPointsMenu(QWidget* parent) : QAction("Number of Points", parent)
    {
        d_diag = new QDialog(parent);
        d_diag->setWindowTitle("Number of Points");
        d_diag->setModal(true);

        d_text = new QLineEdit();

        QGridLayout* layout = new QGridLayout(d_diag);
        QPushButton* btn_ok = new QPushButton(tr("OK"));
        QPushButton* btn_cancel = new QPushButton(tr("Cancel"));

        layout->addWidget(d_text, 0, 0, 1, 2);
        layout->addWidget(btn_ok, 1, 0);
        layout->addWidget(btn_cancel, 1, 1);

        connect(btn_ok, SIGNAL(clicked()), this, SLOT(getText()));
        connect(btn_cancel, SIGNAL(clicked()), d_diag, SLOT(close()));

        connect(this, SIGNAL(triggered()), this, SLOT(getTextDiag()));
    }

    ~NPointsMenu() override {}

signals:
    void whichTrigger(const int npts);

public slots:
    void setDiagText(const int npts) { d_text->setText(QString().setNum(npts)); }

    void getTextDiag() { d_diag->show(); }

private slots:
    void getText()
    {
        emit whichTrigger(d_text->text().toInt());
        d_diag->accept();
    }

private:
    QDialog* d_diag;
    QLineEdit* d_text;
};


/********************************************************************/


class ColorMapMenu : public QMenu
{
    Q_OBJECT

public:
    ColorMapMenu(unsigned int which, QWidget* parent)
        : QMenu("Color Map", parent), d_which(which)
    {
        d_grp = new QActionGroup(this);

        d_act.push_back(new QAction("Multi-Color", this));
        d_act.push_back(new QAction("White Hot", this));
        d_act.push_back(new QAction("Black Hot", this));
        d_act.push_back(new QAction("Incandescent", this));
        d_act.push_back(new QAction("Sunset", this));
        d_act.push_back(new QAction("Cool", this));
        d_act.push_back(new QAction("Other", this));
        // d_act.push_back(new OtherDualAction("Min Intensity: ", "Max Intensity: ",
        // this));

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getMultiColor()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getWhiteHot()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getBlackHot()));
        connect(d_act[3], SIGNAL(triggered()), this, SLOT(getIncandescent()));
        connect(d_act[4], SIGNAL(triggered()), this, SLOT(getSunset()));
        connect(d_act[5], SIGNAL(triggered()), this, SLOT(getCool()));
        connect(d_act[6], SIGNAL(triggered()), this, SLOT(getOther()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }

        d_max_value = QColor("white");
        d_min_value = QColor("white");
    }

    ~ColorMapMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("ColorMapMenu::getAction: which out of range.");
    }

signals:
    void whichTrigger(unsigned int which,
                      const int type,
                      const QColor& min_color = QColor(),
                      const QColor& max_color = QColor());

public slots:
    void getMultiColor()
    {
        emit whichTrigger(d_which, gr::qtgui::INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR);
    }
    void getWhiteHot()
    {
        emit whichTrigger(d_which, gr::qtgui::INTENSITY_COLOR_MAP_TYPE_WHITE_HOT);
    }
    void getBlackHot()
    {
        emit whichTrigger(d_which, gr::qtgui::INTENSITY_COLOR_MAP_TYPE_BLACK_HOT);
    }
    void getIncandescent()
    {
        emit whichTrigger(d_which, gr::qtgui::INTENSITY_COLOR_MAP_TYPE_INCANDESCENT);
    }
    void getSunset()
    {
        emit whichTrigger(d_which, gr::qtgui::INTENSITY_COLOR_MAP_TYPE_SUNSET);
    }
    void getCool()
    {
        emit whichTrigger(d_which, gr::qtgui::INTENSITY_COLOR_MAP_TYPE_COOL);
    }
    // void getOther(d_which, const QString &min_str, const QString &max_str)
    void getOther()
    {
        QMessageBox::information(
            this,
            "Set low and high intensities",
            "In the next windows, select the low and then the high intensity colors.",
            QMessageBox::Ok);
        d_min_value = QColorDialog::getColor(d_min_value, this);
        d_max_value = QColorDialog::getColor(d_max_value, this);

        emit whichTrigger(d_which,
                          gr::qtgui::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED,
                          d_min_value,
                          d_max_value);
    }

private:
    QActionGroup* d_grp;
    QList<QAction*> d_act;
    QColor d_max_value, d_min_value;
    int d_which;
};


/********************************************************************/


class TriggerModeMenu : public QMenu
{
    Q_OBJECT

public:
    TriggerModeMenu(QWidget* parent) : QMenu("Mode", parent)
    {
        d_grp = new QActionGroup(this);
        d_act.push_back(new QAction("Free", this));
        d_act.push_back(new QAction("Auto", this));
        d_act.push_back(new QAction("Normal", this));
        d_act.push_back(new QAction("Tag", this));

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getFree()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getAuto()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getNorm()));
        connect(d_act[3], SIGNAL(triggered()), this, SLOT(getTag()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }
    }

    ~TriggerModeMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("TriggerModeMenu::getAction: which out of range.");
    }

    QAction* getAction(gr::qtgui::trigger_mode mode)
    {
        switch (mode) {
        case gr::qtgui::TRIG_MODE_FREE:
            return d_act[0];
            break;
        case gr::qtgui::TRIG_MODE_AUTO:
            return d_act[1];
            break;
        case gr::qtgui::TRIG_MODE_NORM:
            return d_act[2];
            break;
        case gr::qtgui::TRIG_MODE_TAG:
            return d_act[3];
            break;
        default:
            throw std::runtime_error("TriggerModeMenu::getAction: unknown trigger mode.");
        }
    }

signals:
    void whichTrigger(gr::qtgui::trigger_mode mode);

public slots:
    void getFree() { emit whichTrigger(gr::qtgui::TRIG_MODE_FREE); }
    void getAuto() { emit whichTrigger(gr::qtgui::TRIG_MODE_AUTO); }
    void getNorm() { emit whichTrigger(gr::qtgui::TRIG_MODE_NORM); }
    void getTag() { emit whichTrigger(gr::qtgui::TRIG_MODE_TAG); }

private:
    QList<QAction*> d_act;
    QActionGroup* d_grp;
};


/********************************************************************/


class TriggerSlopeMenu : public QMenu
{
    Q_OBJECT

public:
    TriggerSlopeMenu(QWidget* parent) : QMenu("Slope", parent)
    {
        d_grp = new QActionGroup(this);
        d_act.push_back(new QAction("Positive", this));
        d_act.push_back(new QAction("Negative", this));

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getPos()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getNeg()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }
    }

    ~TriggerSlopeMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("TriggerSlopeMenu::getAction: which out of range.");
    }

    QAction* getAction(gr::qtgui::trigger_slope slope)
    {
        switch (slope) {
        case gr::qtgui::TRIG_SLOPE_POS:
            return d_act[0];
            break;
        case gr::qtgui::TRIG_SLOPE_NEG:
            return d_act[1];
            break;
        default:
            throw std::runtime_error(
                "TriggerSlopeMenu::getAction: unknown trigger slope.");
        }
    }

signals:
    void whichTrigger(gr::qtgui::trigger_slope slope);

public slots:
    void getPos() { emit whichTrigger(gr::qtgui::TRIG_SLOPE_POS); }
    void getNeg() { emit whichTrigger(gr::qtgui::TRIG_SLOPE_NEG); }

private:
    QList<QAction*> d_act;
    QActionGroup* d_grp;
};


/********************************************************************/


class TriggerChannelMenu : public QMenu
{
    Q_OBJECT

public:
    TriggerChannelMenu(int nchans, QWidget* parent) : QMenu("Channel", parent)
    {
        d_grp = new QActionGroup(this);
        for (int i = 0; i < nchans; i++) {
            d_act.push_back(new QAction(QString().setNum(i), this));
            d_act[i]->setCheckable(true);
            d_act[i]->setActionGroup(d_grp);

            addAction(d_act[i]);
            connect(d_act[i], SIGNAL(triggered()), this, SLOT(getChannel()));
        }
    }

    ~TriggerChannelMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error(
                "TriggerChannelMenu::getAction: which out of range.");
    }


signals:
    void whichTrigger(int n);

public slots:
    void getChannel()
    {
        QAction* a = d_grp->checkedAction();
        int which = a->text().toInt();
        emit whichTrigger(which);
    }

private:
    QList<QAction*> d_act;
    QActionGroup* d_grp;
};


/********************************************************************/


class NumberLayoutMenu : public QMenu
{
    Q_OBJECT

public:
    NumberLayoutMenu(QWidget* parent) : QMenu("Layout", parent)
    {
        d_grp = new QActionGroup(this);
        d_act.push_back(new QAction("Horizontal", this));
        d_act.push_back(new QAction("Vertical", this));
        d_act.push_back(new QAction("None", this));

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getHoriz()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getVert()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getNone()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }
    }

    ~NumberLayoutMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("NumberLayoutMenu::getAction: which out of range.");
    }

    QAction* getAction(gr::qtgui::graph_t layout)
    {
        switch (layout) {
        case gr::qtgui::NUM_GRAPH_HORIZ:
            return d_act[0];
            break;
        case gr::qtgui::NUM_GRAPH_VERT:
            return d_act[1];
            break;
        case gr::qtgui::NUM_GRAPH_NONE:
            return d_act[1];
            break;
        default:
            throw std::runtime_error("NumberLayoutMenu::getAction: unknown layout type.");
        }
    }

signals:
    void whichTrigger(gr::qtgui::graph_t layout);

public slots:
    void getHoriz() { emit whichTrigger(gr::qtgui::NUM_GRAPH_HORIZ); }
    void getVert() { emit whichTrigger(gr::qtgui::NUM_GRAPH_VERT); }
    void getNone() { emit whichTrigger(gr::qtgui::NUM_GRAPH_NONE); }

private:
    QList<QAction*> d_act;
    QActionGroup* d_grp;
};


/********************************************************************/


class NumberColorMapMenu : public QMenu
{
    Q_OBJECT

public:
    NumberColorMapMenu(unsigned int which, QWidget* parent)
        : QMenu("Color Map", parent), d_which(which)
    {
        d_grp = new QActionGroup(this);

        d_act.push_back(new QAction("Black", this));
        d_act.push_back(new QAction("Blue-Red", this));
        d_act.push_back(new QAction("White Hot", this));
        d_act.push_back(new QAction("Black Hot", this));
        d_act.push_back(new QAction("Black-Red", this));
        d_act.push_back(new QAction("Other", this));

        connect(d_act[0], SIGNAL(triggered()), this, SLOT(getBlack()));
        connect(d_act[1], SIGNAL(triggered()), this, SLOT(getBlueRed()));
        connect(d_act[2], SIGNAL(triggered()), this, SLOT(getWhiteHot()));
        connect(d_act[3], SIGNAL(triggered()), this, SLOT(getBlackHot()));
        connect(d_act[4], SIGNAL(triggered()), this, SLOT(getBlackRed()));
        connect(d_act[5], SIGNAL(triggered()), this, SLOT(getOther()));

        QListIterator<QAction*> i(d_act);
        while (i.hasNext()) {
            QAction* a = i.next();
            a->setCheckable(true);
            a->setActionGroup(d_grp);
            addAction(a);
        }

        d_max_value = QColor("black");
        d_min_value = QColor("black");
    }

    ~NumberColorMapMenu() override {}

    int getNumActions() const { return d_act.size(); }

    QAction* getAction(unsigned int which)
    {
        if (which < static_cast<unsigned int>(d_act.size()))
            return d_act[which];
        else
            throw std::runtime_error("ColorMapMenu::getAction: which out of range.");
    }

signals:
    void
    whichTrigger(unsigned int which, const QColor& min_color, const QColor& max_color);

public slots:
    void getBlack() { emit whichTrigger(d_which, QColor("black"), QColor("black")); }
    void getBlueRed() { emit whichTrigger(d_which, QColor("blue"), QColor("red")); }
    void getWhiteHot() { emit whichTrigger(d_which, QColor("black"), QColor("white")); }
    void getBlackHot() { emit whichTrigger(d_which, QColor("white"), QColor("black")); }
    void getBlackRed() { emit whichTrigger(d_which, QColor("black"), QColor("red")); }
    void getOther()
    {
        QMessageBox::information(
            this,
            "Set low and high intensities",
            "In the next windows, select the low and then the high intensity colors.",
            QMessageBox::Ok);
        d_min_value = QColorDialog::getColor(d_min_value, this);
        d_max_value = QColorDialog::getColor(d_max_value, this);

        emit whichTrigger(d_which, d_min_value, d_max_value);
    }

private:
    QActionGroup* d_grp;
    QList<QAction*> d_act;
    QColor d_max_value, d_min_value;
    int d_which;
};


/********************************************************************/


class PopupMenu : public QAction
{
    Q_OBJECT

public:
    PopupMenu(QString desc, QWidget* parent) : QAction(desc, parent)
    {
        d_diag = new QDialog(parent);
        d_diag->setWindowTitle(desc);
        d_diag->setModal(true);

        d_text = new QLineEdit();

        QGridLayout* layout = new QGridLayout(d_diag);
        QPushButton* btn_ok = new QPushButton(tr("OK"));
        QPushButton* btn_cancel = new QPushButton(tr("Cancel"));

        layout->addWidget(d_text, 0, 0, 1, 2);
        layout->addWidget(btn_ok, 1, 0);
        layout->addWidget(btn_cancel, 1, 1);

        connect(btn_ok, SIGNAL(clicked()), this, SLOT(getText()));
        connect(btn_cancel, SIGNAL(clicked()), d_diag, SLOT(close()));

        connect(this, SIGNAL(triggered()), this, SLOT(getTextDiag()));
    }

    ~PopupMenu() override {}

    void setText(QString s) { d_text->setText(s); }

signals:
    void whichTrigger(const QString data);

public slots:
    void getTextDiag() { d_diag->show(); }

private slots:
    void getText()
    {
        emit whichTrigger(d_text->text());
        d_diag->accept();
    }

private:
    QDialog* d_diag;
    QLineEdit* d_text;
};


/********************************************************************/


class ItemFloatAct : public QAction
{
    Q_OBJECT

public:
    ItemFloatAct(unsigned int which, QString title, QWidget* parent)
        : QAction(title, parent), d_which(which)
    {
        d_diag = new QDialog(parent);
        d_diag->setWindowTitle(title);
        d_diag->setModal(true);

        d_text = new QLineEdit();

        QGridLayout* layout = new QGridLayout(d_diag);
        QPushButton* btn_ok = new QPushButton(tr("OK"));
        QPushButton* btn_cancel = new QPushButton(tr("Cancel"));

        layout->addWidget(d_text, 0, 0, 1, 2);
        layout->addWidget(btn_ok, 1, 0);
        layout->addWidget(btn_cancel, 1, 1);

        connect(btn_ok, SIGNAL(clicked()), this, SLOT(getText()));
        connect(btn_cancel, SIGNAL(clicked()), d_diag, SLOT(close()));

        connect(this, SIGNAL(triggered()), this, SLOT(getTextDiag()));
    }

    ~ItemFloatAct() override {}

    void setText(float f) { d_text->setText(QString("%1").arg(f)); }


signals:
    void whichTrigger(unsigned int which, float data);

public slots:
    void getTextDiag() { d_diag->show(); }

private slots:
    void getText()
    {
        emit whichTrigger(d_which, d_text->text().toFloat());
        d_diag->accept();
    }

private:
    int d_which;
    QDialog* d_diag;
    QLineEdit* d_text;
};


/********************************************************************/


#endif /* FORM_MENUS_H */
