/*
    Copyright (c) 2008-2024 Jan W. Krieger (<jan@jkrieger.de>)


    This software is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License (LGPL) as published by
    the Free Software Foundation, either version 2.1 of the License, or
    (at your option) any later version.

    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 Lesser General Public License (LGPL) for more details.

    You should have received a copy of the GNU Lesser General Public License (LGPL)
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/




#include "jkqtplotter/gui/jkqtpenhancedtableview.h"
#include <QAction>
#include <QApplication>
#include <QKeyEvent>
#include <QClipboard>
#include <QDialog>
#include <QGridLayout>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QHeaderView>
#ifndef JKQTPLOTTER_COMPILE_WITHOUT_PRINTSUPPORT
#  include <QPrintDialog>
#  include <QPrinter>
#endif
#include <QLabel>
#include "jkqtplotter/jkqtptools.h"
#include "jkqtcommon/jkqtpenhancedpainter.h"



JKQTPEnhancedTableView::JKQTPEnhancedTableView(QWidget *parent):
    QTableView(parent)
{
    setContextMenuPolicy(Qt::ActionsContextMenu);
    QAction* act;

    act=new QAction(QIcon(":/JKQTPlotter/jkqtp_copy16.png"), tr("Copy Selection to Clipboard (for Excel ...)"), this);
    connect(act, SIGNAL(triggered()), this, SLOT(copySelectionToExcel()));
    addAction(act);
    act=new QAction(QIcon(":/JKQTPlotter/jkqtp_copy16_nohead.png"), tr("Copy Selection to clipboard (for Excel ...) without header row/column"), this);
    connect(act, SIGNAL(triggered()), this, SLOT(copySelectionToExcelNoHead()));
    addAction(act);
    act=new QAction(QIcon(":/JKQTPlotter/jkqtp_copy16.png"), tr("Copy Selection to Clipboard (as CSV)"), this);
    connect(act, SIGNAL(triggered()), this, SLOT(copySelectionToCSV()));
    addAction(act);
    act=new QAction(QIcon(":/JKQTPlotter/jkqtp_copy16_nohead.png"), tr("Copy Selection to clipboard (as CSV ...) without header row/column"), this);
    connect(act, SIGNAL(triggered()), this, SLOT(copySelectionToCSVNoHead()));
    addAction(act);
#ifndef JKQTPLOTTER_COMPILE_WITHOUT_PRINTSUPPORT
    act=new QAction(QIcon(":/JKQTPlotter/jkqtp_printtable.png"), tr("Print Table"), this);
    connect(act, SIGNAL(triggered()), this, SLOT(print()));
    addAction(act);
    printAction=act;
#endif
}

JKQTPEnhancedTableView::~JKQTPEnhancedTableView()
= default;


QString JKQTPEnhancedTableView::toHtml(int borderWidth, bool /*non_breaking*/, int fontSizePt) const
{
    if (!model()) return "";
    QString fsstyle="";
    if (fontSizePt>0) {
        fsstyle=QString(" font-size: %1pt;").arg(fontSizePt);
    }
    QString html=QString("<table border=\"%1\" cellspacing=\"0\" cellpadding=\"0\">").arg(borderWidth);
    for (int row=-1; row<model()->rowCount(); row++) {
        html+="<tr>";
        if (row==-1) {
            for (int col=-1; col<model()->columnCount(); col++) {
                html+=QString("<th style=\"white-space: nowrap;%1\"><nobr>").arg(fsstyle);
                if (col>=0) {
                    html+=model()->headerData(col, Qt::Horizontal).toString();
                }
                html+="</nobr></th>";
            }
        } else {
            for (int col=-1; col<model()->columnCount(); col++) {
                if (col==-1) {
                    html+=QString("<th style=\"white-space: nowrap;%1\"><nobr>").arg(fsstyle);
                    html+=model()->headerData(row, Qt::Vertical).toString();
                    html+="</nobr></th>";
                } else {
                    QModelIndex index=model()->index(row, col);
                    QVariant check=index.data(Qt::CheckStateRole);
                    QBrush back=index.data(Qt::BackgroundRole).value<QBrush>();
                    QString style=fsstyle+"white-space: nowrap; ";
                    //qDebug()<<"r="<<row<<"\tc="<<col<<"\tcolor="<<back.color().name();
                    if (back.color()!=QColor(0,0,0) && index.data(Qt::BackgroundRole).isValid()) style=QString("background: %1;").arg(back.color().name());
                    if (style.isEmpty()) html+=QString("<td style=\"%1\"><nobr>").arg(fsstyle);
                    else html+=QString("<td style=\"%1\"><nobr>").arg(style);
                    if (check.isValid()) {
                        if (check.toInt()!=0) {
                            html+= QString("|&times;|&nbsp;&nbsp;");
                        } else {
                            html+= QString("|&nbsp;|&nbsp;&nbsp;");
                        }
                    }
                    html+=index.data().toString();
                    html+="</nobr></td>";
                }
            }
        }
        html+="</tr>";

    }
    html+="</table>";
    return html;
}

void JKQTPEnhancedTableView::copySelectionToExcel(int copyrole, bool storeHead)
{
    if (!model()) return;
    if (!selectionModel()) return;
    QModelIndexList sel=selectionModel()->selectedIndexes();
    QLocale loc=QLocale::system();
    loc.setNumberOptions(QLocale::OmitGroupSeparator);
    if (sel.size()==1) {
        QVariant vdata=sel[0].data(copyrole);
        QString txt="";
#if (QT_VERSION>=QT_VERSION_CHECK(6, 0, 0))
        switch (vdata.typeId()) {
            case QMetaType::Int:
            case QMetaType::LongLong:
            case QMetaType::UInt:
            case QMetaType::ULongLong:
            case QMetaType::Bool:
                txt=vdata.toString();
                break;
            case QMetaType::Double:
                txt=loc.toString(vdata.toDouble());
                break;
            case QMetaType::QPointF:
                txt=loc.toString(vdata.toPointF().x());
                break;
            default:
                txt=QString("\"%1\"").arg(vdata.toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " "));
                break;
        }
#else
        switch (vdata.type()) {
            case QVariant::Int:
            case QVariant::LongLong:
            case QVariant::UInt:
            case QVariant::ULongLong:
            case QVariant::Bool:
                txt=vdata.toString();
                break;
            case QVariant::Double:
                txt=loc.toString(vdata.toDouble());
                break;
            case QVariant::PointF:
                txt=loc.toString(vdata.toPointF().x());
                break;
            default:
                txt=QString("\"%1\"").arg(vdata.toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " "));
                break;
        }
#endif
         QApplication::clipboard()->setText(txt);
    } else {
        QSet<int> rows, cols;
        int colmin=0;
        int rowmin=0;
        for (int i=0; i<sel.size(); i++) {
            int r=sel[i].row();
            int c=sel[i].column();
            rows.insert(r);
            cols.insert(c);
            if (i==0) {
                colmin=c;
                rowmin=r;
            } else {
                if (c<colmin) colmin=c;
                if (r<rowmin) rowmin=r;
            }
        }
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
        QList<int> rowlist(rows.begin(), rows.end());
#else
        QList<int> rowlist=QList<int>::fromSet(rows);
#endif
        std::sort(rowlist.begin(), rowlist.end());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
        QList<int> collist(cols.begin(), cols.end());
#else
        QList<int> collist=QList<int>::fromSet(cols);
#endif
        std::sort(collist.begin(), collist.end());
        int rowcnt=rowlist.size();
        int colcnt=collist.size();
        QList<QStringList> data;

        // header row:
        //
        //  <EMPTY> | <HOR_HEDER1> | <HOR_HEADER2> | ...
        QStringList hrow;
        if (storeHead) {
            hrow.append(""); // empty header for first column (vertical headers!)
            for (int c=0; c<colcnt; c++) {
                hrow.append(QString("\"%1\"").arg(model()->headerData(collist[c], Qt::Horizontal).toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " ")));
            }
            data.append(hrow);
        }

        // now add dta rows:
        //
        //               <~~~~~~~~~ colcnt times ~~~~~~~~~~>
        //  <VER_HEADER> | <EMPTY> | <EMPTY> | ... | <EMPTY>
        for (int r=0; r<rowcnt; r++) {
            QStringList row;
            if (storeHead) row.append(QString("\"%1\"").arg(model()->headerData(rowlist[r], Qt::Vertical).toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " "))); // vertical header
            for (int c=0; c<colcnt; c++) {
                row.append(""); // empty columns for data
            }
            data.append(row);
        }
        for (int i=0; i<sel.size(); i++) {
            int r=rowlist.indexOf(sel[i].row());
            int c=collist.indexOf(sel[i].column());
            QVariant vdata=sel[i].data(copyrole);
            QString txt="";
#if (QT_VERSION>=QT_VERSION_CHECK(6, 0, 0))
            switch (vdata.typeId()) {
                case QMetaType::Int:
                case QMetaType::LongLong:
                case QMetaType::UInt:
                case QMetaType::ULongLong:
                case QMetaType::Bool:
                    txt=vdata.toString();
                    break;
                case QMetaType::Double:
                    txt=loc.toString(vdata.toDouble());
                    break;
                case QMetaType::QPointF:
                    txt=loc.toString(vdata.toPointF().x());
                    break;
                default:
                    txt=QString("\"%1\"").arg(vdata.toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " "));
                    break;
            }
#else
            switch (vdata.type()) {
                case QVariant::Int:
                case QVariant::LongLong:
                case QVariant::UInt:
                case QVariant::ULongLong:
                case QVariant::Bool:
                    txt=vdata.toString();
                    break;
                case QVariant::Double:
                    txt=loc.toString(vdata.toDouble());
                    break;
                case QVariant::PointF:
                    txt=loc.toString(vdata.toPointF().x());
                    break;
                default:
                    txt=QString("\"%1\"").arg(vdata.toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " "));
                    break;
            }
#endif
            int shift=0;
            if (storeHead) shift=1;
            if ((r>=0) && (c>=0) && (r<=data.size()) && (c<=colcnt))data[r+shift][c+shift]=txt;
        }

        QString result="";
        for (int r=0; r<data.size(); r++) {
            result+=data[r].join("\t")+"\n";
        }
        QApplication::clipboard()->setText(result);
    }
}

void JKQTPEnhancedTableView::copySelectionToExcelNoHead(int copyrole)
{
    copySelectionToExcel(copyrole, false);
}

void JKQTPEnhancedTableView::copySelectionToCSV(int copyrole, bool storeHead, const QString &separator, const QChar &decimalpoint)
{
    if (!model()) return;
    if (!selectionModel()) return;
    QModelIndexList sel=selectionModel()->selectedIndexes();
    QLocale loc=QLocale::c();
    loc.setNumberOptions(QLocale::OmitGroupSeparator);
    if (sel.size()==1) {
        QVariant vdata=sel[0].data(copyrole);
        QString txt="";
#if (QT_VERSION>=QT_VERSION_CHECK(6, 0, 0))
        switch (vdata.typeId()) {
            case QMetaType::Int:
            case QMetaType::LongLong:
            case QMetaType::UInt:
            case QMetaType::ULongLong:
            case QMetaType::Bool:
                txt=vdata.toString();
                break;
            case QMetaType::Double:
                txt=JKQTPDoubleToQString(vdata.toDouble(), 15, 'g', decimalpoint);
                break;
            case QMetaType::QPointF:
                txt=JKQTPDoubleToQString(vdata.toPointF().x(), 15, 'g', decimalpoint);
                break;
            default:
                txt=QString("\"%1\"").arg(vdata.toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " "));
                break;
        }
#else
        switch (vdata.type()) {
            case QVariant::Int:
            case QVariant::LongLong:
            case QVariant::UInt:
            case QVariant::ULongLong:
            case QVariant::Bool:
                txt=vdata.toString();
                break;
            case QVariant::Double:
                txt=JKQTPDoubleToQString(vdata.toDouble(), 15, 'g', decimalpoint);
                break;
            case QVariant::PointF:
                txt=JKQTPDoubleToQString(vdata.toPointF().x(), 15, 'g', decimalpoint);
                break;
            default:
                txt=QString("\"%1\"").arg(vdata.toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " "));
                break;
        }
#endif
         QApplication::clipboard()->setText(txt);
    } else {
        QSet<int> rows, cols;
        int colmin=0;
        int rowmin=0;
        for (int i=0; i<sel.size(); i++) {
            int r=sel[i].row();
            int c=sel[i].column();
            rows.insert(r);
            cols.insert(c);
            if (i==0) {
                colmin=c;
                rowmin=r;
            } else {
                if (c<colmin) colmin=c;
                if (r<rowmin) rowmin=r;
            }
        }
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
        QList<int> rowlist(rows.begin(), rows.end());
#else
        QList<int> rowlist=QList<int>::fromSet(rows);
#endif
        std::sort(rowlist.begin(), rowlist.end());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
        QList<int> collist(cols.begin(), cols.end());
#else
        QList<int> collist=QList<int>::fromSet(cols);
#endif
        std::sort(collist.begin(), collist.end());
        int rowcnt=rowlist.size();
        int colcnt=collist.size();
        QList<QStringList> data;

        // header row:
        //
        //  <EMPTY> | <HOR_HEDER1> | <HOR_HEADER2> | ...
        QStringList hrow;
        if (storeHead) {
            hrow.append(""); // empty header for first column (vertical headers!)
            for (int c=0; c<colcnt; c++) {
                hrow.append(QString("\"%1\"").arg(model()->headerData(collist[c], Qt::Horizontal).toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " ")));
            }
            data.append(hrow);
        }

        // now add dta rows:
        //
        //               <~~~~~~~~~ colcnt times ~~~~~~~~~~>
        //  <VER_HEADER> | <EMPTY> | <EMPTY> | ... | <EMPTY>
        for (int r=0; r<rowcnt; r++) {
            QStringList row;
            if (storeHead) row.append(QString("\"%1\"").arg(model()->headerData(rowlist[r], Qt::Vertical).toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " "))); // vertical header
            for (int c=0; c<colcnt; c++) {
                row.append(""); // empty columns for data
            }
            data.append(row);
        }
        for (int i=0; i<sel.size(); i++) {
            int r=rowlist.indexOf(sel[i].row());
            int c=collist.indexOf(sel[i].column());
            QVariant vdata=sel[i].data(copyrole);
            QString txt="";
#if (QT_VERSION>=QT_VERSION_CHECK(6, 0, 0))
                switch (vdata.typeId()) {
                    case QMetaType::Int:
                    case QMetaType::LongLong:
                    case QMetaType::UInt:
                    case QMetaType::ULongLong:
                    case QMetaType::Bool:
                        txt=vdata.toString();
                        break;
                    case QMetaType::Double:
                        txt=JKQTPDoubleToQString(vdata.toDouble(), 15, 'g', decimalpoint);
                        break;
                    case QMetaType::QPointF:
                        txt=JKQTPDoubleToQString(vdata.toPointF().x(), 15, 'g', decimalpoint);
                        break;
                    default:
                        txt=QString("\"%1\"").arg(vdata.toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " "));
                        break;
                }
#else
            switch (vdata.type()) {
                case QVariant::Int:
                case QVariant::LongLong:
                case QVariant::UInt:
                case QVariant::ULongLong:
                case QVariant::Bool:
                    txt=vdata.toString();
                    break;
                case QVariant::Double:
                    txt=JKQTPDoubleToQString(vdata.toDouble(), 15, 'g', decimalpoint);
                    break;
                case QVariant::PointF:
                    txt=JKQTPDoubleToQString(vdata.toPointF().x(), 15, 'g', decimalpoint);
                    break;
                default:
                    txt=QString("\"%1\"").arg(vdata.toString().replace('"', "''").replace('\n', "\\n ").replace('\r', "\\r ").replace('\t', " "));
                    break;
            }
#endif
            int shift=0;
            if (storeHead) shift=1;
            if ((r>=0) && (c>=0) && (r<=data.size()) && (c<=colcnt))data[r+shift][c+shift]=txt;
        }

        QString result="";
        for (int r=0; r<data.size(); r++) {
            result+=data[r].join(separator)+"\n";
        }
        QApplication::clipboard()->setText(result);
    }
}

void JKQTPEnhancedTableView::copySelectionToCSVNoHead(int copyrole, const QString &separator, const QChar &decimalpoint)
{
     copySelectionToCSV(copyrole, false, separator, decimalpoint);
}

void JKQTPEnhancedTableView::keyPressEvent(QKeyEvent *event)
{
    if (event->matches(QKeySequence::Copy)) {
        copySelectionToExcel(Qt::EditRole, false);
        event->accept();
#ifndef JKQTPLOTTER_COMPILE_WITHOUT_PRINTSUPPORT
    } else if (event->matches(QKeySequence::Print)) {
        print();
        event->accept();
#endif
    } else QTableView::keyPressEvent(event);
    emit keyPressed(event->key(), event->modifiers(), event->text());
}

#ifndef JKQTPLOTTER_COMPILE_WITHOUT_PRINTSUPPORT
void JKQTPEnhancedTableView::print()
{
    QPrinter* tablePrinter=getPrinter(nullptr);

    if (tablePrinter) {
        QDialog* dlg=new QDialog(this);
        dlg->setWindowTitle(tr("Table print options ..."));
        QGridLayout* lay=new QGridLayout();
        dlg->setLayout(lay);
        lay->addWidget(new QLabel(tr("<b>scaling:</b>")), 0,0);
        QCheckBox* chk1Wide=new QCheckBox(tr("one page wide"), dlg);
        QCheckBox* chk1High=new QCheckBox(tr("one page high"), dlg);
        lay->addWidget(chk1Wide, 0, 1);
        lay->addWidget(chk1High, 1, 1);

        QDialogButtonBox* btns=new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel,Qt::Horizontal,dlg);
        connect(btns, SIGNAL(accepted()), dlg, SLOT(accept()));
        connect(btns, SIGNAL(rejected()), dlg, SLOT(reject()));
        lay->addWidget(btns, 2, 0,1,2);

        if (dlg->exec()) {
            print(tablePrinter, chk1Wide->isChecked(), chk1High->isChecked());
        }
        delete dlg;
        delete tablePrinter;
    }
}
#endif

#ifndef JKQTPLOTTER_COMPILE_WITHOUT_PRINTSUPPORT
void JKQTPEnhancedTableView::print(QPrinter *printer, bool onePageWide, bool onePageHigh)
{
    QPrinter* p=printer;


     //p->setPageMargins(10,10,10,10,QPageLayout::Millimeter);

     /*if (width()>height()) {
         p->setPageOrientation(QPageLayout::Landscape);
     } else {
         p->setPageOrientation(QPageLayout::Portrait);
     }*/

     clearSelection();


     /// PRINT HERE //////////////////////////////////////////////////////////////////////////////////
     // calculate the total width/height table would need without scaling
     const int rows = model()->rowCount();
     const int cols = model()->columnCount();
     double vhw=verticalHeader()->width()+8;
     double totalWidth = vhw;
     double minWidth=1e33;
     double maxWidth=0;
     for (int c = -1; c < cols; ++c)
     {
         double w=columnWidth(c);
         totalWidth += w;
         if (w<minWidth) minWidth=w;
         if (w>maxWidth) maxWidth=w;
     }
     double hhh=horizontalHeader()->height()+8;
     double totalHeight = hhh;
     double minHeight=1e33;
     double maxHeight=0;
     for (int r = 0; r < rows; ++r)
     {
        double h=rowHeight(r);
        totalHeight += h;
        if (h<minHeight) minHeight=h;
        if (h>maxHeight) maxHeight=h;
     }
     double scale=1.0;
     // adjust scale, so the widest/highest column fits on one page
     /*if (maxWidth*scale>p->pageLayout().paintRectPixels(p->resolution()).width()) scale=p->pageLayout().paintRectPixels(p->resolution()).width()/maxWidth;
     if (maxHeight*scale>p->pageLayout().paintRectPixels(p->resolution()).height()) scale=p->pageLayout().paintRectPixels(p->resolution()).height()/maxHeight;*/
     if (onePageWide) {

         if (totalWidth>p->pageLayout().paintRectPixels(p->resolution()).width()) scale=p->pageLayout().paintRectPixels(p->resolution()).width()/totalWidth;
     }
     if (onePageHigh) {
         if (totalHeight>p->pageLayout().paintRectPixels(p->resolution()).height()) scale=qMin(scale, p->pageLayout().paintRectPixels(p->resolution()).height()/totalHeight);
     }

     //qDebug()<<scale;

     // print scaled pixmap

     int pagesWide=1;
     int pagesHigh=1;
     QList<int> pageCols, pageRows;
     pageCols<<0;
     pageRows<<0;
     { // find number of pages needed
         double x=vhw, x0=vhw;
         if (!onePageWide) {
             for (int c=0; c<cols; c++) {
                 double cw=columnWidth(c);
                 if (x+cw>p->pageLayout().paintRectPixels(p->resolution()).width()/scale) {
                     pagesWide++;
                     x=0;
                     pageCols<<c;
                 } else {
                     x=x+cw;
                     x0=x0+cw;
                 }
             }
         }
         if (pageCols.size()>0 && cols>pageCols.last()) pageCols<<cols;
         if (pageCols.size()==1) pageCols<<cols;
         double y=hhh, y0=hhh;
         if (!onePageHigh) {
             for (int r=0; r<rows; r++) {
                 double rh=rowHeight(r);
                 if (y+rh>p->pageLayout().paintRectPixels(p->resolution()).height()/scale) {
                     pagesHigh++;
                     pageRows<<r;
                     y=hhh;
                 } else {
                     y=y+rh;
                     y0=y0+rh;
                 }
             }
         }
         if (pageRows.size()>0 && rows>pageRows.last()) pageRows<<rows;
         if (pageRows.size()==1) pageRows<<rows;
     }

     JKQTPEnhancedPainter painter(p);
     paint(painter, scale, -1, hhh, vhw, pageCols, pageRows, p);
     painter.end();




     /// PRINT DONE //////////////////////////////////////////////////////////////////////////////////
}
#endif

void JKQTPEnhancedTableView::paint(QPainter &painter, QRect pageRect)
{
    QRect pageRec=pageRect;
    if (pageRec.width()==0 || pageRec.height()==0) pageRec=QRect(QPoint(0,0), getTotalSize().toSize());
    painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
    painter.translate(pageRec.topLeft());
    QSizeF size=getTotalSize();
    double vhw=verticalHeader()->width()+8;
    double hhh=horizontalHeader()->height()+8;
    double scaleX=size.width()/double(pageRec.width());
    double scaleY=size.height()/double(pageRec.height());
    double scale=qMin(scaleX, scaleY);
    QList<int>pageCols, pageRows;
    pageCols<<0<<model()->columnCount();
    pageRows<<0<<model()->rowCount();
    paint(painter, scale, -1, hhh, vhw, pageCols, pageRows);
    
}

QSizeF JKQTPEnhancedTableView::getTotalSize() const
{
    const int rows = model()->rowCount();
    const int cols = model()->columnCount();
    double vhw=verticalHeader()->width()+8;
    double totalWidth = vhw;
    for (int c = -1; c < cols; ++c)
    {
        double w=columnWidth(c);
        totalWidth += w;
    }
    double hhh=horizontalHeader()->height()+8;
    double totalHeight = hhh;
    for (int r = 0; r < rows; ++r)
    {
       double h=rowHeight(r);
       totalHeight += h;
    }

    return QSizeF((totalWidth), (totalHeight));
}

#ifndef JKQTPLOTTER_COMPILE_WITHOUT_PRINTSUPPORT
void JKQTPEnhancedTableView::paint(QPainter &painter, double scale, int page, double hhh, double vhw, const QList<int>& pageCols, const QList<int>& pageRows, QPrinter* p)
#else
void JKQTPEnhancedTableView::paint(QPainter &painter, double scale, int page, double hhh, double vhw, const QList<int>& pageCols, const QList<int>& pageRows)
#endif
{
    painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
    QStyleOptionViewItem option;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    initViewItemOption(&option);
#else
    option = viewOptions();
#endif
    painter.scale(scale, scale);
    QPen headerPen("black");
    headerPen.setWidth(2);
    QPen cellPen("gray");
    cellPen.setWidth(1);
    QFont headerFont=horizontalHeader()->font();
    headerFont.setBold(true);
    int pagesWide=pageCols.size()-1;
    int pagesHigh=pageRows.size()-1;
    //painter.translate(p->pageLayout().paintRectPixels(p->resolution()).topLeft());
    int pageCnt=0;
    for (int ph=0; ph<pageRows.size()-1; ph++) {
        for (int pw=0; pw<pageCols.size()-1; pw++) {
            if (page<0 || page==pageCnt) {
                //qDebug()<<"print page "<<ph<<"/"<<pageRows.size()<<pagesHigh<<"    "<<pw<<"/"<<pageCols.size()<<pagesWide;
                //painter.drawPicture(p->pageLayout().paintRectPixels(p->resolution()).topLeft(), pic);
                double y=0;
                if (ph==0) {
                    y=hhh;
                }
                //qDebug()<<"   rows = "<<pageRows[ph]<<"..."<<pageRows[ph+1]-1;
                //qDebug()<<"   cols = "<<pageCols[pw]<<"..."<<pageCols[pw+1]-1;
                for (int r=pageRows[ph]; r<pageRows[ph+1]; r++) {
                    double x=0;
                    int rh=rowHeight(r);
                    if (pw%pagesWide==0) {
                        x=0;
                        option.rect = QRect(static_cast<int>(x), static_cast<int>(y), static_cast<int>(vhw), rh);
                        //verticalHeader()->itemDelegate()->paint(&painter, option, model()->index(r, c, QModelIndex()));
                        x=vhw;
                    }
                    for (int c=pageCols[pw]; c<pageCols[pw+1]; c++) {
                        double cw=columnWidth(c);
                        option.rect = QRect(static_cast<int>(x), static_cast<int>(y), static_cast<int>(cw), rh);
                        itemDelegate()->paint(&painter, option, model()->index(r, c, QModelIndex()));
                        painter.setPen(cellPen);
                        painter.drawRect(option.rect);
                        x=x+cw;

                    }
                    y=y+rh;
                }

                if (ph==0) {
                    y=0;
                    int x=0;
                    if (pw%pagesWide==0) x=static_cast<int>(vhw);
                    painter.setPen(headerPen);
                    for (int c=pageCols[pw]; c<pageCols[pw+1]; c++) {
                        QRect rec=QRect(x, static_cast<int>(y), columnWidth(c), static_cast<int>(hhh));
                        painter.fillRect(rec, QColor("lightgrey"));
                        painter.setFont(headerFont);
                        painter.setPen(headerPen);
                        painter.drawText(QRect(rec.x()+4, rec.y()+4, rec.width()-8, rec.height()-8), model()->headerData(c, Qt::Horizontal).toString());
                        painter.drawRect(rec);
                        //if (x==vhw &&) painter.drawLine(rec.topLeft(), QPoint(rec.left(), p->pageLayout().paintRectPixels(p->resolution()).height()));
                        x=x+columnWidth(c);
                    }

                }
                if (pw%pagesWide==0) {
                    y=0;
                    int x=0;
                    if (ph==0) y=hhh;
                    for (int r=pageRows[ph]; r<pageRows[ph+1]; r++) {
                        QRect rec=QRect(x, static_cast<int>(y), static_cast<int>(vhw), rowHeight(r));
                        painter.fillRect(rec, QColor("lightgrey"));
                        painter.setPen(headerPen);
                        painter.setFont(headerFont);
                        painter.drawText(QRect(rec.x()+4, rec.y()+4, rec.width()-8, rec.height()-8), model()->headerData(r, Qt::Vertical).toString());
                        painter.drawRect(rec);
                        //if (x==vhw &&) painter.drawLine(rec.topLeft(), QPoint(rec.left(), p->pageLayout().paintRectPixels(p->resolution()).height()));
                        y=y+rowHeight(r);
                    }

                }


#ifndef JKQTPLOTTER_COMPILE_WITHOUT_PRINTSUPPORT
                if (p && pw<pagesWide-1) p->newPage();
#endif
            }
            pageCnt++;
        }
#ifndef JKQTPLOTTER_COMPILE_WITHOUT_PRINTSUPPORT
        if (p && ph<pagesHigh-1) p->newPage();
#endif
    }
    
}



#ifndef JKQTPLOTTER_COMPILE_WITHOUT_PRINTSUPPORT
QPrinter *JKQTPEnhancedTableView::getPrinter(QPrinter *printerIn, bool *localPrinter)
{
    QPrinter* p=printerIn;
    if (p==nullptr) {
        p=new QPrinter();
        if (localPrinter) *localPrinter=true;
    }

    QPrintDialog *dialog = new QPrintDialog(p, nullptr);
    dialog->setWindowTitle(tr("Print Table"));
    if (dialog->exec() != QDialog::Accepted) {
        if (localPrinter && *localPrinter) delete p;
        delete dialog;
        return printerIn;
    }
    p=dialog->printer();
    delete dialog;

    return p;
}
#endif
