/*
 * htable.cpp
 * This file is part of qps -- Qt-based visual process status monitor
 *
 * Copyright 1997-1999 Mattias Engdegård
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */


//	very dirty and  complex code ( fasthyun@magicn.com)
//	Disaster !!

// TODO:
// * autoscroll speed proportional to distance from edge
// * interface to add/remove rows (for disclosure triangles)
// * interface to display pixmaps in cells (for disclosure triangles etc)
// * include sorting functionality here for more generality

#include "htable.h"
#include <QMouseEvent>
#include <QStyleOption>
#include <QShortcut>
#include <QPalette>

#define DEBUG qDebug
//#define DEBUG
VPointer *vp = NULL; // Temporary

VPointer::VPointer(QWidget *parent) : QWidget(parent)
{
    pix = new QPixmap(":/icon/vpointer.png");
    pix_vcross = new QPixmap(":/icon/vcross.png");
    int w = pix->width();
    bool flag_movable = true;
    setGeometry(0, 0, w, w);
}

void VPointer::paintEvent(QPaintEvent *event)
{
    QPainter p(this);
    p.setClipping(false); // not work
    if (flag_movable)
        p.drawPixmap(0, 0, *pix);
    else
        p.drawPixmap(0, 0, *pix_vcross);
}

FloatingHead::FloatingHead(QWidget *parent) : QWidget(parent) {}

void FloatingHead::setTitle(QString str, int w, int h)
{
    title = str;
    resize(w, h);
}
void FloatingHead::paintEvent(QPaintEvent *event)
{
    QPainter p(this);
    QStyleOptionHeader opt;
    p.setOpacity(0.7);
    opt.rect = rect();
    opt.text = title;
    opt.state = QStyle::State_Enabled;
    // if(htable->sortedCol()==col) opt.state= opt.state |
    // QStyle::State_Sunken;
    // CE_Header, CE_HeaderSection, CE_HeaderLabel
    style()->drawControl(QStyle::CE_Header, &opt, &p, this);
    return;
}

// TableHead: the horizontally scrollable table head
TableHead::TableHead(HeadedTable *parent)
    : QtTableView(parent), htable(parent), dragging(false)
{
    setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
    // setSizePolicy(QSizePolicy
    // (QSizePolicy::Minimum,QSizePolicy::Expanding));
    // setTableFlags(Tbl_smoothHScrolling | Tbl_scrollLastHCell);
    setTableFlags(Tbl_scrollLastHCell); // ?
    setNumRows(1);

    setMouseTracking(true); // for tooltip

    floatHead = new FloatingHead(parent); // tiny memory leak! don't care.
    floatHead->hide();
    // setMinimumHeight(20);//cellHeight()); //ZERO!! fault
}

// Description : draw a field name of table  when DRAG !!
// 		called by paintHeading()
// 		called by QtTableView::repaint()
void TableHead::paintCell(QPainter *p, int row, int col, bool use_cache)
{
    static int count = 0;
    /// printf(" paintcell %d,%d\n",row,col);
    int w = htable->max_widths[col];

    // if( isCellChanged (row,col,true)==false)
    {
        //	printf(" paintcell %d,%d\n",row,col);
        //	if(use_cache==true)  // check cached value
        {
            //	return;
        }
    }

    // if( col ==11 )	printf("head: %d  (%d) \n",col,count++);
    // w = cellUpdateR.width();
    {
        QStyleOptionHeader opt;
        QRect rectR(0, 0, w, height());
        opt.rect = rectR;
        opt.text = htable->title(col);
        opt.state = QStyle::State_Enabled;
        if (htable->sortedCol() == col)
            opt.state = opt.state | QStyle::State_Sunken;
        // CE_Header, CE_HeaderSection, CE_HeaderLabel
        style()->drawControl(QStyle::CE_Header, &opt, p, this);
    }
}

// works
void TableHead::eraseRight(QPainter *p, QRect &r)
{
    QStyleOptionHeader opt;
    //		printf("widt=%d \n",r.width());
    opt.rect = r;
    opt.text = "";
    style()->drawControl(QStyle::CE_Header, &opt, p, this);
}

// virtual !
int TableHead::cellWidth(int col) { return htable->max_widths[col]; }

//
void TableHead::scrollSideways(int val) { setXOffset(val); }

void TableHead::mousePressEvent(QMouseEvent *e)
{
    int col = findCol(e->x());
    //	printf("col=%d\n",col);
    if (col == -1)
        return;
    click_col = col;

    if (e->button() == Qt::RightButton)
    // && htable->options & HTBL_HEADING_CONTEXT_MENU)
    {
        emit rightClicked(e->globalPos(), col);
        return;
    }

    if (e->button() == Qt::LeftButton)
    {
        press = e->pos();
    }
}

void TableHead::dragEnterEvent(QDragEnterEvent *event)
{
    printf("dragEnterEvent()\n");
}
void TableHead::dragLeaveEvent(QDragLeaveEvent *event)
{
    printf("dragLeaveEvent()\n");
}

void TableHead::mouseMoveEvent(QMouseEvent *e)
{
    //	printf("mouseMoveEvent()\n");
    int col = findCol(e->x());
    if (col < 0)
    {
        emit htable->outOfHCell();
        return;
    }
    //	if( !dragging) return;
    emit htable->flyOnHCell(col);

    int thold = abs(press.x() - e->pos().x());

    if (e->buttons() == Qt::LeftButton //  Button state
        and htable->options & HTBL_REORDER_COLS)
    {
        {

            if (!dragging and thold > 5)
            {
                dragging = true;
                drag_offset =
                    press.x() - htable->colOffset(click_col) + xOffset();
                floatHead->setTitle(htable->title(click_col),
                                    cellWidth(click_col), height());
                floatHead->show();
                // vp->show();
            }

            if (dragging)
            {
                //	htable->body->drawGhostCol(drag_pos -
                // drag_offset, w);
                floatHead->move(e->x() - drag_offset, 0);

                int vcol = findColNoMinus(e->x() - drag_offset);
                int vcol_offx = htable->colOffset(vcol);
                static int old_pos = -1;
                //	printf("vcol=%d\n",vcol);

                // virtual Pointer move!
                if (old_pos != vcol_offx)
                {
                    QWidget *p = parentWidget(); // htable
                    //	vp->setMovable(htable->columnMovable(htable->columnMovable(vcol)
                    // and htable->columnMovable(click_col)
                    // ));
                    //	vp->move(vcol_offx -
                    // p->geometry().x()
                    //,p->geometry().y() -
                    // 13);
                    // vp->move(e->x()	,10);
                    old_pos = vcol_offx;
                }
                drag_pos = e->x(); // save old pos

                return;
            }
        }
    }

    if (col != -1) // ToolTip
    {
        //	for(int i=0;i<1024*1024;i++)	htable->tipText(col);
        //// memory leak
        // test
        QString s = htable->tipText(col);

        if (!s.isEmpty())
            QToolTip::showText(e->globalPos(), s);
    }
}

void TableHead::mouseReleaseEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton) // Returns the button that caused the
                                       // event.
    {
        int col = findCol(e->x());
        if (col < 0)
            col = (e->x() < 0) ? 0 : htable->ncols - 1; // good!

        if (!dragging and col == click_col)
        {
            // htable->emit_click_signal(col); // just click no drag
            emit htable->titleClicked(col);
        }

        if (dragging)
        {
            int vcol = findColNoMinus(e->x() - drag_offset);
            if (click_col >= 0)
            {
                htable->moveCol(click_col, vcol); // call
                                                  // Pstable::moveCol(int
                                                  // col, int place)
                                                  // should emit !!
                // emit colMoved(click_col, vcol);
            }
            ///	printf(" vcol2=%d \n",vcol);
        }
        dragging = false;
        floatHead->hide();
        // vp->hide();
    }
}

#include <QStyleFactory>

// TableBody: the table body, scrollable in all ways
TableBody::TableBody(HeadedTable *parent) : QtTableView(parent), htable(parent)
{

    // setTableFlags(Tbl_autoScrollBars | Tbl_smoothScrolling);
    setTableFlags(Tbl_snapToVGrid); // works
    setTableFlags(Tbl_hScrollBar | Tbl_vScrollBar);
    first_drag_row = prev_drag_row = -1;
    autoscrolling = false;
    gadget_click = false;
    setMouseTracking(true);

    QStyle *s = QStyleFactory::create("windows");
    if (s)
        setStyle(s);
}

// virtual from QtTableView
void TableBody::scrollTrigger(int rx, int ry)
{
    if (ry == 0)
        return;
    int n = ry / cellHeight();
    /// printf("ry=%d n=%d\n",ry,n);
    if (ry > 0)
        tablecache.cutDown(n); // error?
    else
        tablecache.cutUp(-n);
    //	printf("ry=%d cellheight=%d n=%d\n",ry,cellHeight(),n);
}

// new, possible COMMON?
bool TableBody::isCellChanged(int row, int col)
{
    QString str;
    int xpos;
    int fold, dep;
    bool selected;
    bool sorted;
    int lc;
    int width;

    {
        str = htable->text(row, col);
        xpos = htable->colXPos(col);
        selected = htable->isSelected(row);
        sorted = (htable->sortedCol() == col);
        width = htable->max_widths[col];
    }

    // int left = leftCell(), top = topCell();
    CellAttribute *attr = tablecache.value(row - topCell(), col - leftCell());

    if (attr == NULL) // never NULL
    {
        return true;
    }
    else
    {
        bool result = false;
        if (attr->text == str and attr->selected == selected and
            attr->sorted == sorted and attr->xpos == xpos and attr->w == width
            // and attr->size==tmp_size
            and
            (attr->size.height() >= tmp_size.height())
            //	and (attr->size.width()>=tmp_size.width())
            )
        {
            result = false;
        }
        else
        {
            //	attr->text=str;
            //	attr->selected=selected;
            //	attr->sorted=sorted;
            //	attr->xpos=xpos;
            //	attr->size=tmp_size;
            //	attr->w=width;

            //	if (row==29 and col==0)
            //	printf("(%d %d) xpos=%d %d sorted=%d %d sel=%d
            //%d str=%s
            //%s\n",attr->size.height(),tmp_size.height(),
            //			attr->xpos,xpos,attr->sorted,sorted,attr->selected,selected
            //			,qPrintable(attr->text),qPrintable(str));
            //	printf("true (%d
            //%d)\n",row-topCell(),col-leftCell());
            return true;
        }

        if (col == 0)
        {
            dep = htable->rowDepth(row); // folding_level
            fold = htable->folded(row);
            lc = htable->lastChild(row);

            if (attr->depth == dep /* Uninitial*/
                and attr->folded == fold and attr->lastchild == lc)
            {
                // false
            }
            else
            {
                //	attr->depth=dep;
                //	attr->folded=fold;
                result = true;
                //	printf("true (%d
                //%d)\n",row-topCell(),col-leftCell());
            }
        }
        return result;
    }
}

bool TableHead::isCellChanged(int row, int col)
{
    int xpos;
    int ypos;
    int w;
    int fold, dep;
    QString str;
    bool sorted = (htable->sortedCol() == col);

    w = htable->max_widths[col];
    xpos = htable->colXPos(col);

    {
        str = htable->title(col);
    }
    // int left = leftCell();
    // int top	 = topCell();
    CellAttribute *attr = tablecache.value(row - topCell(), col - leftCell());

    if (attr == NULL) // Uninitialed?
    {
        return true;
    }
    else
    {
        bool result = false;
        if (attr->text == str and attr->sorted == sorted and
            attr->size == tmp_size and attr->xpos == xpos
            //	and attr->w==w
            )
        {
            return false;
        }
        else
        {
            attr->text = str;
            attr->sorted = sorted;
            attr->xpos = xpos;
            //		attr->w=w;
            attr->size = tmp_size;
            //			if(head)	printf("head
            // true");
            return true;
        }
        // printf("return  %s x=%d\n",str.toAscii().data(),xpos);
    }
}

#include <Qt>

// DRAFT CODE !!  BOTTLENECK !!!
// Description : draw a cell of table
// 		1. draw the background of a cell
// 		2. draw the text of a cell
//
// called by
// 		1.QtTableView::paintEvent()
void TableBody::paintCell(QPainter *p, int row, int col, bool use_cache)
{
    // *** sequence important !!!
    //	if(isCellChanged(row,col)==false)	{
    // if(use_cache==true)	return;
    //}

    CellAttribute *attr =
        tablecache.value(row - topCell(), col - leftCell()); // save cache
    //	isCellChanged(row,col);

    bool sort;
    int h = cellHeight();
    int w = htable->max_widths[col];
    attr->w = w; //

    QColor baseColor = palette().brush(QPalette::Base).color();
    sort = (col == htable->sorted_col);
    attr->sorted = sort;

    // gridline
    QColor lineColor, textColor;
    QColor altColor = palette().brush(QPalette::AlternateBase).color();
    lineColor.setRgb((baseColor.red() * 0.5 + altColor.red() * 0.5),
                     (baseColor.green() * 0.4 + altColor.green() * 0.6),
                     (baseColor.blue() * 0.4 + altColor.blue() * 0.6));

    //	QStyleOption opt;
    //	opt.rect=QRect(0,0,w,h-1);
    //	opt.state = QStyle::State_Active; //?
    // QStyle::SH_ItemView_PaintAlternatingRowColorsForEmptyArea
    //  const int gridHint =
    //  style()->styleHint(QStyle::SH_Table_GridLineColor,
    //  &opt, this);

    p->setPen(lineColor);
    p->drawLine(0, h - 1, w, h - 1);

    // background
    if (htable->isSelected(row))
    {
        p->fillRect(0, 0, w, h - 1, palette().brush(QPalette::Highlight));
        p->setPen(palette().brush(QPalette::HighlightedText).color()); // text
        attr->selected = true;
    }
    else
    {
        attr->selected = false;
        if (sort)
        {
            p->fillRect(0, 0, w, h - 1, lineColor.dark(101));
        }
        else
        {
            p->fillRect(0, 0, w, h - 1, baseColor);
        }
        // style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p,
        // this);
        p->setPen(palette().brush(QPalette::Text).color()); // text
    }

    // tree gadget , treestep=height
    int gap = h / 4;
    int testFlag = 0;
    if (col == 0 and htable->treemode == true)
    {
        QStyleOption opt;
        int d = htable->rowDepth(row);
        attr->depth = d; //**
        int treestep = htable->treestep;
        if (true /*lines*/)
        {
            /// int dx = folding ? gadget_space : 6;
            for (int level = d, prow = row; level >= 0 and prow >= 0;
                 level--, prow = htable->parentRow(prow))
            {
                if (level == d)
                    continue;
                int x = gap + level * treestep;
                QRect branchR(x, 0, treestep, h);
                opt.rect = branchR;

                if (htable->lastChild(prow) == false)
                {
                    if (testFlag)
                    {
                        p->drawLine(x + treestep / 2, 0, x + treestep / 2, h);
                    }
                    else
                    {
                        opt.state = QStyle::State_Sibling; // | vertical
                                                           // line
                        style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt,
                                               p, this);
                    }
                }
            }

            QRect branchR(gap + d * treestep, 0, treestep, h);
            opt.rect = branchR;
            if (testFlag)
            {
                int x = gap + d * treestep + treestep / 2;

                p->drawLine(x, h / 2, x + treestep / 2, h / 2);
                if (!htable->lastChild(row))
                {
                    p->drawLine(x, 0, x, h);
                }
                else
                {
                    p->drawLine(x, 0, x, h / 2);
                }
            }
            else
            {
                opt.state = QStyle::State_Item; //?
                if (!htable->lastChild(row))
                    opt.state = opt.state | QStyle::State_Sibling; // |
            }
            attr->lastchild = htable->lastChild(row);
        }

        if (true /*folding*/)
        {
            HeadedTable::NodeState fs = htable->folded(row);
            attr->folded = fs; //**
            if (fs != HeadedTable::Leaf)
            {
                opt.state = opt.state | QStyle::State_Children;
                if (fs != HeadedTable::Closed)
                    opt.state = opt.state | QStyle::State_Open;
            }
            p->save(); // temp , save pencolor
            style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, p, this);
            p->restore();
        }
        gap = htable->gadget_space + d * treestep + 1;
    }

    // virtual int Pstable::alignment(int col)
    if (htable->alignment_col[col] == Qt::AlignRight) // using cache
    {
        w -= gap;
        gap = 0;
    }
    // Qt::IncludeTrailingSpaces
    // p->drawText(gap, 0, w , h, Qt::TextIncludeTrailingSpace |
    // Qt::IncludeTrailingSpaces | Qt::AlignVCenter |
    // htable->alignment_col[col],htable->text(row,col));
    // p->drawText(gap, 0, w , h, Qt::TextIncludeTrailingSpace |
    // Qt::AlignVCenter
    // | htable->alignment_col[col],htable->text(row,col));
    p->drawText(gap, 0, w, h, Qt::AlignVCenter | htable->alignment_col[col],
                htable->text(row, col));

    htable->overpaintCell(p, row, col, gap);

    // cache write!
    // attr->xpos=tmp_x ;
    attr->text = htable->text(row, col);
    attr->xpos = htable->colXPos(col);
    attr->size = tmp_size;
}

// DEL , Home key
void TableBody::jumpTop() { setYOffset(0); }

// DEL, End Key
void TableBody::jumpBottom() { setYOffset(maxYOffset()); }

/// !!!!
void TableBody::centerVertically(int row)
{
    int topcell = row - viewHeight() / (cellHeight() * 2);
    setTopCell(qMax(topcell, 0));
    update();
}

//??
void TableBody::showRange(int from, int to)
{
    int h = viewHeight() / cellHeight();
    if (to >= topCell() + h)
        setTopCell(qMax(0, qMin(from, to - h + 1)));
}

// virtual
// called by
//	1.
int TableBody::cellWidth(int col) { return htable->max_widths[col]; }

// **** fix !!
void TableBody::updateRow(int row)
{
    // ??
    // for(int col = 0; col < htable->ncols; col++)
    // updateCell(row, col,false);	updateCell(row, col);
}

void TableBody::mousePressEvent(QMouseEvent *e)
{
    if (numRows() == 0)
        return; // *** prevent out of range

    // printf("mousePressEvent() 1\n");
    static int last_row = -1;

    int row = findRow(e->y());
    if (row == -1)
    {
        //	printf("mousePressEvent\n");
        htable->clearAllSelections();
        if (e->y() >= 0)
            row = numRows(); // if SHIFT+click outside ~
        first_drag_row = prev_drag_row = row;
        return;
    }

    if (!(htable->options & HTBL_ROW_SELECTION))
        return;

    if (e->button() == Qt::LeftButton)
    {
        // folding
        if (htable->treemode && htable->folding &&
            e->x() < htable->gadget_space +
                         htable->treestep * htable->rowDepth(row) &&
            htable->folded(row) != HeadedTable::Leaf)
        {
            emit htable->foldSubTree(row);
            gadget_click = true;
            last_row = row;
            //	clearCache(); // dont use cache
            return;
        }

        if (e->modifiers() & Qt::ShiftModifier)
        {
            if (row < last_row)
                for (int i = row; i < last_row; i++)
                    htable->setSelected(i, true); // virtual
            else
                for (int i = last_row; i <= row; i++)
                    htable->setSelected(i, true); // virtual
        }
        else if (e->modifiers() & Qt::ControlModifier)
        {
            htable->setSelected(row, !htable->isSelected(row));
        }
        else
            htable->selectOnlyOne(row);

        first_drag_row = prev_drag_row = row;

        emit htable->selectionChanged();
    }

    else if (e->button() == Qt::RightButton)
    {
        if (!htable->isSelected(row))
            htable->selectOnlyOne(row);
        emit htable->selectionChanged();
        // better? 	emit htable->rightClicked(e->globalPos());
    }
    last_row = row;
    //	htable->repaint_changed();
    repaintChanged(); // repaint
                      // view->update(); // fast but use more CPU
}

void TableBody::mouseReleaseEvent(QMouseEvent *e)
{
    gadget_click = false;

    if (e->button() == Qt::LeftButton)
    {
        if (autoscrolling)
        {
            ////killTimers();		// no more autoscrolling
            first_drag_row = prev_drag_row = -1;
            autoscrolling = false;
        }
    }
    else if (e->button() == Qt::RightButton)
    {
        QPoint p(6, 3);
        p += e->globalPos();
        emit htable->rightClicked(p);
    }
}

void TableBody::mouseDoubleClickEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        int row = findRow(e->y());
        if (row != -1)
        {
            if (htable->options & HTBL_ROW_SELECTION &&
                !htable->isSelected(row))
                htable->selectOnlyOne(row);
            // htable->selectionNotify();
            emit htable->selectionChanged();
            //		htable->emit_double_click_signal(row);
            emit htable->doubleClicked(row);
        }
    }
}

// Bottle Neck ? no
void TableBody::leaveEvent(QEvent *) { emit htable->outOfCell(); }

void TableBody::mouseMoveEvent(QMouseEvent *e)
{
    if (numRows() == 0)
        return; // *** prevent out of range

    // DRAFT CODE !
    // Signal : find row, col(field name) emit flyOnEvent Signal
    int row = findRow(e->y());
    int col = findCol(e->x());

    if (row < 0 or col < 0)
    {
        emit htable->outOfCell(); // if...
        return;
    }

    emit htable->flyOnCell(row, col);

    if (e->buttons() == Qt::NoButton)
        return;
    if (e->buttons() == Qt::RightButton)
        return;

    if (e->buttons() & Qt::ControlModifier || gadget_click)
        return;

    if (row != prev_drag_row)
    {
        if (row == -1)
        {
            if (!autoscrolling)
            {
                // dragging outside table, cause scrolling
                scrolldir = (e->y() < 0) ? UP : DOWN;
                //	killTimers();
                //	startTimer(scroll_delay);
                autoscrolling = true;
            }
        }
        else
        {
            ////killTimers();
            autoscrolling = false;
            dragSelectTo(row);
        }
    }
    //	repaintRow(row);
    //	view->update();
    repaintChanged();
}

void TableBody::timerEvent(QTimerEvent *)
{
    // timer not proved !
    return;
    // printf("timer\n");
    if (!autoscrolling)
        return;
    ////killTimers();
    if (scrolldir == UP)
    {
        int top = topCell();
        setTopCell((top > 1) ? top - 1 : 0);
        dragSelectTo(topCell());
    }
    else
    {
        setTopCell(topCell() + 1);
        int bottom = lastRowVisible();
        dragSelectTo((bottom < numRows()) ? bottom : numRows() - 1);
    }
    startTimer(scroll_delay);
}

// change drag selection point from previous drag position to row
void TableBody::dragSelectTo(int row)
{
    int dir = (row > prev_drag_row) ? 1 : -1;
    if ((prev_drag_row - first_drag_row) * dir >= 0)
    {
        // moving away from start point
        for (int i = prev_drag_row + dir; i - dir != row; i += dir)
            htable->setSelected(i, true);
    }
    else
    {
        // moving towards start point
        for (int i = prev_drag_row; i != row; i += dir)
            htable->setSelected(i, false);
    }
    prev_drag_row = row;
    // htable->selectionNotify();
    emit htable->selectionChanged();
}

// heuristic for determining a good XOR color: This is in general a hard
// problem but since we know (most of) the background and the foreground,
// we can try. Note that this function might not return an allocated QColor,
// so it is only useful for XOR drawing.

QColor TableBody::getXorColor()
{
    QColor fg = palette().brush(QPalette::Base).color();

    return fg; /// QColor(0, fg.pixel() ^ backgroundColor().pixel());
}

void TableBody::drawGhostCol(int x, int w)
{
    static QColor xorcol = getXorColor();

    QPainter p(this);

    p.setPen(xorcol);
    // p.setRasterOp(XorROP);
    /// p.drawLine(x, 0, x, height());
    ////p.drawLine(x + w, 0, x + w, height());
}

// HeadedTable: the actually useable class
HeadedTable::HeadedTable(QWidget *parent, int opts) : QWidget(parent)
{
    // meaningless?
    options = HTBL_ROW_CONTEXT_MENU | HTBL_ROW_SELECTION |
              HTBL_ROW_DOUBLE_CLICK | HTBL_HEADING_TOOLTIPS |
              HTBL_HEADING_CONTEXT_MENU | HTBL_HEADING_CLICK |
              HTBL_REORDER_COLS;

    head = new TableHead(this);
    body = new TableBody(this);
    head->setObjectName("head");
    body->setObjectName("body");

    sorted_col = -1;
    treemode = false;
    treestep = 0;
    lines = true;
    folding = true;
    gadget_space = 0;
    nrows = ncols = 0; // hmm...

    // printf("style name=%s \n",qPrintable(style()->objectName()));
    // QApplication::setStyle(new QWindowsStyle);
    head->setFrameShape(QFrame::NoFrame);
    if (style()->objectName().contains("oxygen"))
        ;
    else
    {
        body->setFrameShape(QFrame::NoFrame);
    }

    QVBoxLayout *vlayout = new QVBoxLayout;
    vlayout->setSpacing(0); // distance between Widget and Widget

#if QT_VERSION < 0x040300
    vlayout->setMargin(0); // qt-4.2
#else
    vlayout->setContentsMargins(0, 0, 0, 0); // qt-4.3
#endif

    //	vlayout->setSizeConstraint ( SizeConstraint )
    vlayout->addWidget(head);
    vlayout->addWidget(body);
    setLayout(vlayout);

    // connect keyboard shortcuts
    QShortcut *c;
    c = new QShortcut(Qt::CTRL + Qt::Key_A, this, SLOT(selectAll()));
    // synchronize horizontal scrolling of head and body
    connect(body->horizontalScrollBar(), SIGNAL(valueChanged(int)), head,
            SLOT(scrollSideways(int)));
    fontChange(font()); // *** need for init
                        //	vp=new VPointer((QWidget *)QObject::parent());
                        //	vp->hide();		//memory leak!!
}

HeadedTable::~HeadedTable() {}

// ok : move to Qttableview
void HeadedTable::fontChange(const QFont &oldFont)
{
    // DEBUG("fontChange()\n");
    int fontHeight = fontMetrics().height() + 1;
    // printf("cell height =%d \n", cellH);
    if (fontHeight % 2 != 0)
        fontHeight++; // for pretty fold-lines
    head->setCellHeight(fontHeight + 5);
    head->setMaximumHeight(head->cellHeight());
    body->setCellHeight(fontHeight);
    treestep = fontHeight;
    gadget_space = folding ? fontHeight + (fontHeight / 2) : 0;
    //	QWidget::fontChange ( oldFont );
}

// DRAFT  virtual !
void HeadedTable::moveCol(int col, int place)
{
    // go to void Procview::moveColumn(int col, int place)
}

// distance (in table coords) from left table edge of physical column
int HeadedTable::colOffset(int col)
{
    int x = 0;
    for (int c = 0; c < col; c++)
        x += max_widths[c];
    // x += body->cellWidth(c);
    return x;
}

// OPTIZ
inline int HeadedTable::colXPos(int col)
{
    int x = 0; // + coloffset
    for (int c = 0; c < col; c++)
        x += max_widths[c];
    return x;
}

// repaint columns from col0 to col1. If col1 is -1, repaint all
// the way to the right edge of the table.
// called by
//          1.void Qps::update_table(int col)
void HeadedTable::repaintColumns(int col0, int col1)
{
    QRect bvr = body->viewRect();
    QRect hvr = head->viewRect();
    int x0 = colOffset(col0) - body->xOffset();
    if (x0 > hvr.width())
        return;
    if (x0 < 0)
        x0 = 0;
    bvr.setLeft(x0);
    hvr.setLeft(x0);
    if (col1 >= 0)
    {
        int x1 = colOffset(col1) + max_widths[col1] - body->xOffset();
        if (x1 < hvr.width())
        {
            hvr.setRight(x1);
            bvr.setRight(x1);
        }
    }
    head->repaint(hvr);
    body->repaint(bvr);
}

// DEL -> virtual
// 	called by Pstable::setTreeMode(bool)
void HeadedTable::setTreeMode(bool tm)
{
    treemode = tm;
    head->clearCache();
    body->clearCache();
}

// update table Head !!
void HeadedTable::setSortedCol(int col)
{
    {
        int old_sorted = sorted_col;
        sorted_col = col;
        //		printf("old_sorted=%d\n",old_sorted);
        //		printf("sorted_col=%d\n",sorted_col);
        if (old_sorted != -1 && old_sorted < ncols) // ncols bug
            updateHeading(old_sorted);
        if (col != -1 && col < ncols)
            updateHeading(col);
    }
}

// should be virtual. why?
void HeadedTable::clearAllSelections()
{
    for (int row = 0; row < nrows; row++)
        setSelected(row, false);
    body->view->update();
    //	update(); // not work why?
}

// BottleNeck ?
void HeadedTable::selectOnlyOne(int row)
{
    // this apply to the current list only!
    for (int r = 0; r < nrows; r++)
        setSelected(r, r == row);
}

void HeadedTable::selectAll()
{
    for (int r = 0; r < nrows; r++)
        setSelected(r, true);
    emit selectionChanged(); // notwork?
}

// default implementation returns a null string (no tip d)
QString HeadedTable::tipText(int) { return ""; }
char *HeadedTable::total_selectedRow(int col) { return 0; }

void HeadedTable::setNumRows(int rows)
{
    nrows = rows;
    head->setNumRows(1);
    body->setNumRows(rows);
}

void TableCache::setRow(int row)
{
    if (row < 0)
        return;

    int i, size = rows.size();
    if (row >= size)
    {
        //	rows.append(new TableRow);
    }

    if (row < nrow)
    {
        //		printf("row=%d,nrow=%d \n",row,nrow);
        if (size < nrow)
            nrow = size;
        for (i = row; i < nrow; i++)
        {
            for (int j = 0; j < 48; j++)
                rows[i]->cells[j].text = "";
        }
    }
    nrow = row;
}

void TableCache::setCol(int col)
{
    if (col < 0)
        return;
    if (col < ncol)
    {
        //	printf("clear cache\n");
        int i, size = rows.size();
        for (i = 0; i < size; i++)
            for (int j = col; j < ncol; j++)
                rows[i]->cells[j].text = "";
    }
    ncol = col;
}

// TEST
void TableHead::checkProfile()
{
    QRect viewR = viewRect();
    // if(viewR.y()!=0) printf(" qps: ooooohss....\n");
    int maxViewCol = findCol(viewR.width());
    if (maxViewCol < 0)
        maxViewCol = numCols();
    tablecache.setCol(maxViewCol);
    tablecache.setRow(1);
}

// TESt
void TableBody::checkProfile()
{
    htable->checkTableModel(); // important

    QRect viewR = viewRect();
    if (viewR.y() != 0)
        printf(" qps: ooooophss....\n");
    int maxViewRow = findRow(viewR.height());
    if (maxViewRow < 0)
        maxViewRow = numRows();
    int maxViewCol = findCol(viewR.width());
    if (maxViewCol < 0)
        maxViewCol = numCols();
    tablecache.setCol(maxViewCol);
    tablecache.setRow(maxViewRow);
}

void HeadedTable::setNumCols(int cols)
{
    // printf("cols=%d\n",cols);

    ncols = cols;
    // resetWidths();
    for (int i = 0; i < cols; i++)
    {
        alignment_col[i] = alignment(i);
        updateColWidth(i);
    }
    //	printf("ncols=%d\n",ncols);
    head->setNumCols(ncols);
    body->setNumCols(ncols);
}

/*
//virtual
int HeadedTable::colWidth(int col)
{
        int r = numRows();
        for(int i=0;i<r;i++)	text(i,col);
} */

// BOTTLENECK
// find Max width of a column
// called by
//		1.  void HeadedTable::setNumCols(int cols)
int HeadedTable::updateColWidth(int col)
{
    int w = 0;
    int sw = 0;
    int hw = 0;
    int i = 0;
    int rows = numRows();
    bool treecol = (treemode && col == 0);

    if ((w = sizeHintForColumn(col)) < 0) // width
    {
        if (w < 0)
            w = -w; // trick.
        // if(w<0) printf("w = %d\n",w);
        // harsh, if procs more than 1000
        for (i = 0; i < rows; i++)
        {
            sw = fontMetrics().width(text(i, col)) + 10;

            if (treecol)
                sw += treestep * rowDepth(i);
            if (sw > w)
                w = sw;
        }

        if (treecol)
        {
            w += gadget_space;
        }
    }
    // don't forget the width of the heading
    hw = fontMetrics().width(title(col)) + 12;
    // return 0;
    if (hw > w)
        w = hw;

    // sw=fontmetric.width("0") * colWidth(col);
    // if(sw>w) w=sw;

    max_widths[col] = w;
    return 0;
}

void HeadedTable::resetWidths()
{
    for (int i = 0; i < numCols() + 1; i++)
        max_widths[i] = 0;
}

// called by ?
void HeadedTable::repaintAll() // -> repaint()
{
    int count = 0, trow = 0;
    for (int row = 0; row < nrows; row++)
        if (isSelected(row))
        {
            count++;
            trow = row;
        }
    //	body->view->update();
    //	update(); // not work why?

    //	printf("Qps debug: repaintAll()\n");
    //	head->clearCache();
    //	body->clearCache();
    // update();
    body->view->update();
    head->view->update();
    if (count == 1)
        centerVertically(trow);
}

//  PROBLEM : update()  +  repaint() call = draw twice !!!
//	called by pstable::refresh()
//  DEL if (render_area smaller than full_arear ) then force draw
void HeadedTable::repaint_changed()
{
    //	printf("repaint_changed()\n");
    // body->setUpdatesEnabled(true);
    head->repaintChanged();
    body->repaintChanged();
}

void HeadedTable::hideEvent(QHideEvent *event) {}

void HeadedTable::paintEvent(QPaintEvent *e)
{
    static int c = 0;
    //	printf("HeadedTable paintEvent %d\n",c++); //works ?
    //	QWidget::paintEvent(e);
    // body->clearCache();
    //	head->update();
}

void HeadedTable::showEvent(QShowEvent *)
{
    //	printf("HeadedTable showEvent\n"); // works
    //  before paintEvent call
    head->clearCache();
    body->clearCache();
}

void HeadedTable::resizeEvent(QResizeEvent *e)
{
    // DEBUG("resizeEvent() HeadedTable %d\n",head->cellHeight());
    ////	head->repaintRow(0);
    QWidget::resizeEvent(e);
    head->clearCache();
    body->clearCache();
}
