You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1876 lines
45 KiB

/* This file is part of the KDE libraries
Copyright (C) 1997, 1998, 1999 The KDE Team
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
// Written by Stefan Taferner <taferner@kde.org>
// Modified by Alessandro Russo <axela@bigfoot.org>
//
// BugFix:
// -The resize cursor appared in the wrong position when the tab is
// scrolled to the right.
// -Sometimes some columns became suddenly pressed.
// -Corrected resizing of the last column for fill the real size of the table.
//
// New Features:
// -Drag&Slide of the columns bar like Window95.
// -Complete key Bindings (Must be enable by enableKey(). )
// -Three Mode to Order the content of table
// (this mode are column specific, but you should'nt mix SimpleOrder with
// ComplexOrder for obvious reason):
// NoOrder = Old mode, do not order.
// SimpleOrder = An arrow appear near the title of column, are all
// disabled except the current selected column.
// ComplexOrder= Use a new widget KNumCheckButton, the behaviour is very
// complex:
// Single Left Click : set to the next free number if the
// button is blank,or else do nothing.
// Single Right Click: clear the button and adjust the others.
// Double Left Click : clear all buttons and set the current
// to '1'.
// Double Right Click: clear all buttons.
//
//
// Todo:
// - Save the current status of checkbutton/arrows to the config file.
#include <kconfig.h>
#include <qfontmetrics.h>
#include <qpainter.h>
#include <qkeycode.h>
#include <qbitmap.h>
#include <qdrawutil.h>
#include <qscrollbar.h>
#include <qdragobject.h>
#include <kglobal.h>
#include <kstddirs.h>
// This is only for flushKeys().
#include <X11/Xlib.h>
#include <stdarg.h>
#define INIT_MAX_ITEMS 16
#define ARROW_SPACE 15
#define BUTTON_SPACE 4
#define MINIMUM_SPACE 9
#include "ktablistbox.h"
template class QDict<QPixmap>;
//=============================================================================
//
// C L A S S KTabListBoxItem
//
//=============================================================================
KTabListBoxItem::KTabListBoxItem(int aColumns)
{
columns = aColumns;
txt = new QString[columns];
mark = -2;
}
//-----------------------------------------------------------------------------
KTabListBoxItem::~KTabListBoxItem()
{
if (txt) delete[] txt;
txt = 0L;
}
//-----------------------------------------------------------------------------
KTabListBoxItem& KTabListBoxItem::operator=(const KTabListBoxItem& from)
{
int i;
for (i=0; i<columns; i++)
txt[i] = from.txt[i];
fgColor = from.fgColor;
return *this;
}
//=============================================================================
//
// C L A S S KTabListBoxColumn
//
//=============================================================================
//-----------------------------------------------------------------------------
KTabListBoxColumn::KTabListBoxColumn(KTabListBox* pa, int w): QObject()
{
initMetaObject();
iwidth = w;
idefwidth = w;
colType = KTabListBox::TextColumn;
ordmode = KTabListBox::Descending;
ordtype = KTabListBox::NoOrder;
parent = pa;
inumber=0;
vline=false;
mbut=0L;
}
//-----------------------------------------------------------------------------
KTabListBoxColumn::~KTabListBoxColumn()
{
if(mbut) delete mbut;
}
//-----------------------------------------------------------------------------
bool KTabListBoxColumn::changeMode()
{
if(inumber>0)
{
if(ordmode==KTabListBox::Ascending)
ordmode=KTabListBox::Descending;
else
ordmode=KTabListBox::Ascending;
return true;
} else if(ordtype==KTabListBox::SimpleOrder)
{
parent->clearAllNum();
inumber=1;
return true;
}
return false;
}
//-----------------------------------------------------------------------------
void KTabListBoxColumn::setNumber(int num)
{
bool changed=false;
if((inumber==0 && num!=0) || (inumber!=0 && num==0) ) changed=true;
if(num>9) num=0;
inumber=num;
char tmp[2];
tmp[0]=48+inumber; tmp[1]=0;
if(!inumber) tmp[0]=' ';
if(mbut) mbut->setText(tmp);
if(changed) parent->reorderRows();
}
//-----------------------------------------------------------------------------
void KTabListBoxColumn::setOrder(KTabListBox::OrderType ort,
KTabListBox::OrderMode mode)
{
ordtype = ort;
ordmode = mode;
if(ordtype==KTabListBox::ComplexOrder && !mbut)
{
mbut = new KNumCheckButton(parent);
connect(mbut,SIGNAL(selected() ),this,SLOT(setButton() ) );
connect(mbut,SIGNAL(deselected() ),this,SLOT(resetButton() ) );
connect(mbut,SIGNAL(doubleclick(bool)),this,SLOT(clearAll(bool) ) );
}
}
//-----------------------------------------------------------------------------
void KTabListBoxColumn::paintCell(QPainter* paint, int row,
const QString& string, bool marked)
{
QFontMetrics fm = paint->fontMetrics();
QPixmap* pix = 0L;
int beg, end, x;
QPen pen, oldPen;
if (marked)
{
paint->fillRect(0, 0, iwidth, parent->cellHeight(row),
parent->highlightColor);
pen.setColor(parent->colorGroup().highlightedText());
oldPen = paint->pen();
paint->setPen(pen);
}
if (!string.isEmpty())
switch(colType)
{
case KTabListBox::PixmapColumn:
if (!string.isNull()) pix = parent->dict().find(string);
if (pix && !pix->isNull())
{
paint->drawPixmap(0, 0, *pix);
break;
}
/*else output as string*/
case KTabListBox::TextColumn:
paint->drawText(1, fm.ascent() +(fm.leading()), string);
break;
case KTabListBox::MixedColumn:
QString pixName;
for (x=0, beg=0; string[beg] == '\t'; x+=parent->tabPixels, beg++)
;
end = beg-1;
while(string[beg] == '{')
{
end = string.find('}', beg+1);
if (end >= 0)
{
pixName = string.mid(beg+1, end-beg-1);
pix = parent->dict().find(pixName);
if (!pix)
{
debug("KTabListBox %s"
":\nno pixmap for\n`%s' registered.", name(), pixName.latin1());
}
if (!pix->isNull()) paint->drawPixmap(x, 0, *pix);
x += pix->width()+1;
beg = end+1;
}
else
break;
}
paint->drawText(x+1, fm.ascent() +(fm.leading()),
string.mid(beg, string.length()-beg));
break;
}
if (marked)
{
paint->fillRect(iwidth-6, 0, iwidth, 128, parent->highlightColor);
paint->setPen(oldPen);
}
else
paint->eraseRect(iwidth-6, 0, iwidth, 128);
}
//-----------------------------------------------------------------------------
void KTabListBoxColumn::paint(QPainter* paint)
{
int t=3;
QPoint a,b(0,0);
QFontMetrics fm = paint->fontMetrics();
if(ordtype!=KTabListBox::NoOrder)
{
t+=15;
if(inumber>0)
if(ordmode==KTabListBox::Ascending)
paint->drawPixmap(-3, -2, parent->upPix);
else
paint->drawPixmap(-3, -1, parent->downPix);
else
if(ordmode==KTabListBox::Ascending)
paint->drawPixmap(-3, -2, parent->disabledUpPix);
else
paint->drawPixmap(-3, -1, parent->disabledDownPix);
if(ordtype==KTabListBox::ComplexOrder)
{
a=paint->xForm(b);
mbut->move(a.x()+15,a.y()+2);
if(!mbut->isVisible()) mbut->show();
t+=parent->labelHeight-4;
}
}
paint->drawText(t,(parent->labelHeight-4+fm.ascent())/2, getCaption());
}
//-----------------------------------------------------------------------------
void KTabListBoxColumn::setButton()
{
if(inumber<=0)
{
parent->lastSelectedColumn++;
inumber=parent->lastSelectedColumn;
char tmp[2];
tmp[0]=48+inumber; tmp[1]=0;
if(mbut) mbut->setText(tmp);
parent->reorderRows();
}
}
//-----------------------------------------------------------------------------
void KTabListBoxColumn::resetButton()
{
if(inumber>0)
{
if(parent->lastSelectedColumn==inumber)
{
parent->lastSelectedColumn--;
inumber=0;
if(mbut) mbut->setText(QString::fromLatin1(" "));
if(parent->lastSelectedColumn)
parent->repaint();
else
// This is for simulating a NoSort (useful for special cases like
// the trash folder of kmail?)
parent->reorderRows();
}
else
{
parent->adjustNumber(inumber);
inumber=0;
if(mbut) mbut->setText(QString::fromLatin1(" "));
parent->reorderRows();
}
}
}
//-----------------------------------------------------------------------------
void KTabListBoxColumn::clearAll(bool leftbutton)
{
parent->clearAllNum();
if(leftbutton) setButton();
}
//=============================================================================
//
// C L A S S KTabListBox
//
//=============================================================================
KTabListBox::KTabListBox(QWidget *parent, const char *name, int columns,
WFlags _f):
QWidget(parent, name, _f), lbox(this)
{
QFontMetrics fm = fontMetrics();
QString f;
QColorGroup g = colorGroup();
initMetaObject();
f = locate("data", QString::fromLatin1("khtml/pics/khtml_dnd.xpm"));
dndDefaultPixmap.load(f);
f = locate("toolbar", QString::fromLatin1("up.png"));
upPix.load(f);
f = locate("toolbar", QString::fromLatin1("down.png"));
downPix.load(f);
QPalette pal = palette();
QColorGroup g1 = pal.disabled();
// Prepare the disabledPixmap for drawing
// Maybe we can semplify it a bit, some suggestions??
disabledUpPix.resize(upPix.width(), upPix.height());
disabledDownPix.resize(downPix.width(), downPix.height());
disabledUpPix.fill( g1.background() );
disabledDownPix.fill( g1.background() );
const QBitmap *mask = upPix.mask();
const QBitmap *mask1= downPix.mask();
bool allocated = false;
if (!mask) {// This shouldn't occur anymore!
mask = new QBitmap(upPix.createHeuristicMask());
allocated = true;
}
bool allocated1 = false;
if (!mask1) {// This shouldn't occur anymore!
mask1 = new QBitmap(downPix.createHeuristicMask());
allocated1 = true;
}
QBitmap bitmap = *mask; // YES! make a DEEP copy before setting the mask!
QBitmap bitmap1 = *mask1;
bitmap.setMask(*mask);
bitmap1.setMask(*mask1);
QPainter p;
p.begin( &disabledUpPix );
p.setPen( g1.light() );
p.drawPixmap(1, 1, bitmap);
p.setPen( g1.mid() );
p.drawPixmap(0, 0, bitmap);
p.end();
QPainter p1;
p1.begin( &disabledDownPix );
p1.setPen( g1.light() );
p1.drawPixmap(1, 1, bitmap1);
p1.setPen( g1.mid() );
p1.drawPixmap(0, 0, bitmap1);
p1.end();
if (allocated) // This shouldn't occur anymore!
delete mask;
if(allocated1)
delete mask1;
lastSelectedColumn=0;
tabPixels = 10;
maxItems = 0;
nMarked=0;
current = -1;
colList = 0L;
colShowList =0L;
itemList = 0L;
itemShowList=0L;
sepChar = '\n';
labelHeight = fm.height() + 4;
columnPadding = fm.height() / 2;
highlightColor = colorGroup().highlight();
mResizeCol = false;
stopOrdering=false;
needsSort=false;
mSortCol = -1;
numColumns = columns;
mMouseCol=-1;
mMouseColLeft=0;
mMouseColWidth=0;
mMouseAction=false;
mMouseDragColumn=false;
setMouseTracking(TRUE);
lbox.setGeometry(0, labelHeight, width(), height()-labelHeight);
if (columns > 0) setNumCols(columns);
}
//-----------------------------------------------------------------------------
KTabListBox::~KTabListBox()
{
int i;
if (colList)
{
for (i=0; i<numColumns; i++)
{
delete colList[i];
colList[i] = NULL;
}
delete[] colList;
delete[] colShowList;
}
if (itemList)
{
for (i=0; i<maxItems; i++)
{
delete itemList[i];
itemList[i] = NULL;
}
delete[] itemList;
delete[] itemShowList;
}
}
//-----------------------------------------------------------------------------
void KTabListBox::setNumRows(int aRows)
{
lbox.setNumRows(aRows);
}
//-----------------------------------------------------------------------------
void KTabListBox::setTabWidth(int aTabWidth)
{
tabPixels = aTabWidth;
}
//-----------------------------------------------------------------------------
void KTabListBox::setNumCols(int aCols)
{
int i;
if (colList)
{
for (i=0; i<numColumns; i++)
delete colList[i];
delete [] colList;
delete [] colShowList;
}
if (itemList)
{
for (i=0; i<maxItems; i++)
delete itemList[i];
delete [] itemList;
}
if (itemShowList)
delete [] itemShowList;
colList = 0L;
colShowList=0L;
itemList = 0L;
itemShowList=0L;
maxItems = 0;
if (aCols < 0) aCols = 0;
lbox.setNumCols(aCols);
numColumns = aCols;
if (aCols <= 0) return;
colList = new KTabListBoxColumnPtr[aCols];
colShowList = new int[aCols];
for (i=0; i<aCols; i++)
{
colList[i] = newKTabListBoxColumn();
colShowList[i] = i;
}
itemList = new KTabListBoxItemPtr[INIT_MAX_ITEMS];
itemShowList = new int[INIT_MAX_ITEMS];
for (i=0; i<INIT_MAX_ITEMS; i++)
{
itemList[i] = new KTabListBoxItem(aCols);
itemShowList[i]=i;
}
maxItems = INIT_MAX_ITEMS;
}
//-----------------------------------------------------------------------------
KTabListBoxColumn* KTabListBox::newKTabListBoxColumn()
{
return (new KTabListBoxColumn(this));
}
//-----------------------------------------------------------------------------
void KTabListBox::readConfig()
{
KConfig* conf = KGlobal::config();
int beg, end, i, w;
int cols = numCols(),fixedmin=MINIMUM_SPACE;
QString str, substr;
conf->setGroup(QString::fromLatin1(name()));
str = conf->readEntry(QString::fromLatin1("colwidth"));
if (!str.isEmpty())
for (i=0, beg=0, end=0; i<cols;)
{
end = str.find(',', beg);
if (end < 0) break;
w = str.mid(beg,end-beg).toInt();
if (colList[i]->orderType()==ComplexOrder)
fixedmin+=ARROW_SPACE+labelHeight-BUTTON_SPACE;
else
if (colList[i]->orderType()==SimpleOrder)
fixedmin+=ARROW_SPACE;
if (w < fixedmin) w = fixedmin;
colList[i]->setWidth(w);
colList[i]->setDefaultWidth(w);
i++;
beg = end+1;
}
else
for(i=0;i<cols;i++)
{
colList[i]->setWidth(60);
colList[i]->setDefaultWidth(60);
}
}
//-----------------------------------------------------------------------------
void KTabListBox::writeConfig()
{
KConfig* conf = KGlobal::config();
int t;
QString str;
conf->setGroup(QString::fromLatin1(name()));
QString tmp;
for(t=0;t<numCols();t++) {
tmp.setNum(colList[t]->defaultWidth());
str += tmp;
}
conf->writeEntry(QString::fromLatin1("colwidth"),str);
conf->sync();
}
//-----------------------------------------------------------------------------
void KTabListBox::setDefaultColumnWidth(int aWidth, ...)
{
va_list ap;
int i, cols;
cols = numColumns;
va_start(ap, aWidth);
for (i=0; aWidth && i<cols; i++)
{
colList[i]->setDefaultWidth(aWidth);
aWidth = va_arg(ap, int);
}
va_end(ap);
}
//-----------------------------------------------------------------------------
void KTabListBox::setColumnWidth(int col, int aWidth)
{
if (col<0 || col>=numCols()) return;
colList[col]->setWidth(aWidth);
colList[col]->setDefaultWidth(aWidth);
lbox.updateTableSize();
}
//-----------------------------------------------------------------------------
void KTabListBox::reorderRows()
{
int t=1,t1=0,n,maxVal,iColNum[10],c1;
int result,itmp,totRows=numRows();
bool change;
// Who need more than 9 columns of priority for sorting??
KTabListBoxColumn *pc[10];
int (*columnSort)(const QString&, const QString&);
OrderMode fmode;
needsSort=true;
if(!lbox.autoUpdate()) return;
needsSort=false;
for(n=0;n<10;n++) { pc[n]=0L; iColNum[n]=0;}
while(t<10)
{
if(colList[t1]->number()==t)
{
iColNum[t-1]=t1;
pc[t]=colList[t1];
t1=0;t++;
}
else t1++;
if(t1==numColumns) break;
}
maxVal=t-1;
// if none column is selected no sort.
if(!maxVal)
{
for(t=0;t<totRows;t++) itemShowList[t]=t;
repaint();
return;
}
columnSort=pc[1]->columnSort; c1=iColNum[0];
fmode=pc[1]->orderMode();
change=false;
for(n=0;n<totRows-1;n++)
{
result=columnSort(
itemList[itemShowList[n]]->text(c1),
itemList[itemShowList[n+1]]->text(c1) );
if(result==0)
{
if(maxVal>=2 && recursiveSort(2,n,pc,iColNum))
{
itmp=itemShowList[n];
itemShowList[n]=itemShowList[n+1];
itemShowList[n+1]=itmp;
change=true;
if(n>0) n-=2;
}
}
else if((result<0 && fmode==Ascending) ||
(result>0 && fmode==Descending) )
{
itmp=itemShowList[n];
itemShowList[n]=itemShowList[n+1];
itemShowList[n+1]=itmp;
change=true;
if(n>0) n-=2;
}
}
//We need a repaint also if change==false!!! We must redraw the arrows!
if(change) repaint();
else QWidget::repaint();
}
//-----------------------------------------------------------------------------
bool KTabListBox::recursiveSort(int level,int n,
KTabListBoxColumn **pc,int *iCol)
{
int result;
result=pc[level]->columnSort(
itemList[itemShowList[n]]->text(iCol[level-1]),
itemList[itemShowList[n+1]]->text(iCol[level-1]) );
if(result==0)
if(pc[level+1] && recursiveSort(level+1,n,pc,iCol))
return true;
else return false;
else
if( (result<0 && pc[level]->orderMode()==Ascending) ||
(result>0 && pc[level]->orderMode()==Descending) )
return true;
else
return false;
}
static int string_compare(const QString& a, const QString& b)
{
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
//-----------------------------------------------------------------------------
void KTabListBox::setColumn(int col, const QString& aName, int aWidth,
ColumnType aType, OrderType aOType,
OrderMode aOMode,bool verticalLine,
int (*compar)(const QString&, const QString&))
{
if (col<0 || col>=numCols()) return;
setColumnWidth(col,aWidth);
colList[col]->setCaption(aName);
colList[col]->setType(aType);
colList[col]->setOrder(aOType,aOMode);
colList[col]->vline=verticalLine;
if(compar)
colList[col]->columnSort=compar;
else
colList[col]->columnSort=string_compare;
update();
}
//-----------------------------------------------------------------------------
void KTabListBox::setCurrentItem(int idx, int colId)
{
int i;
if (idx>=numRows()) return;
unmarkAll();
if ( idx <= topItem() && idx < lbox.lastRowVisible() )
lbox.setTopCell( itemPosList(idx));
else if ( idx >= lbox.lastRowVisible() )
{
int i = itemPosList(idx)+1;
int y = 0;
while (i) y += cellHeight(--i);
y -= lbox.viewHeight();
lbox.setYOffset( y );
}
if (idx != current)
{
i = current;
current = idx;
updateItem(i,FALSE);
}
if (current >= 0)
{
markItem(idx);
emit highlighted(current, colId);
}
}
//-----------------------------------------------------------------------------
// This is an internal method...
void KTabListBox::setCItem(int idx)
{
int i;
if (idx >= numRows() || idx < 0) return;
if (idx != current)
{
unmarkItem(current);
i = current;
current = idx;
updateItem(i,FALSE);
}
unmarkAll();
markItem(idx);
}
//-----------------------------------------------------------------------------
bool KTabListBox::isMarked(int idx) const
{
return (itemList[idx]->marked() >= -1);
}
//-----------------------------------------------------------------------------
void KTabListBox::markItem(int idx, int colId)
{
if (itemList[idx]->marked()==colId) return;
itemList[idx]->setMarked(colId);
nMarked++;
updateItem(idx,FALSE);
}
//-----------------------------------------------------------------------------
void KTabListBox::unmarkItem(int idx)
{
int mark;
mark = itemList[idx]->marked();
itemList[idx]->setMarked(-2);
if (mark>=-1)
{
nMarked--;
updateItem(idx);
}
}
//-----------------------------------------------------------------------------
void KTabListBox::unmarkAll()
{
int i;
if(!nMarked) return;
for (i=numRows()-1; i>=0; i--)
unmarkItem(i);
nMarked=0;
}
//-----------------------------------------------------------------------------
const QString& KTabListBox::text(int row, int col) const
{
const KTabListBoxItem* item = getItem(row);
static QString str;
int i, cols;
if (!item)
{
str = QString::null;
return str;
}
if (col >= 0) return item->text(col);
cols = item->columns - 1;
for (str="",i=0; i<=cols; i++)
{
str += item->text(i);
if (i<cols) str += sepChar;
}
return str;
}
//-----------------------------------------------------------------------------
void KTabListBox::insertItem(const QString& aStr, int row)
{
KTabListBoxItemPtr it;
int i, newSize;
if (row < 0) row = numRows();
if (numRows() >= maxItems)
{
newSize = (maxItems << 1);
if (newSize <= row) newSize = row+2;
resizeList(newSize);
}
it = itemList[numRows()];
if(row!=numRows())
{
for (i=numRows()-1; i>=row; i--)
itemList[i+1] = itemList[i];
for(i=0;i<numRows();i++) if(itemShowList[i]>=row) itemShowList[i]++;
}
itemList[row] = it;
itemShowList[row]=row;
if (itemPosList(current) >itemPosList(row))
current=itemShowList[itemPosList(current+1)];
setNumRows(numRows()+1);
changeItem(aStr, row);
reorderRows();
if (needsUpdate(row))
lbox.repaint();
}
//-----------------------------------------------------------------------------
void KTabListBox::appendStrList( QStrList const *strLst )
{
bool update;
uint i;
if (!strLst) return;
QStrListIterator it(*strLst);
update = autoUpdate();
setAutoUpdate(FALSE);
for (i=0; i<strLst->count(); i++)
{
insertItem(it.current());
++it;
}
setAutoUpdate(update);
lbox.repaint();
}
//-----------------------------------------------------------------------------
void KTabListBox::changeItem(const QString& aStr, int row)
{
char sepStr[2];
if (row < 0 || row >= numRows()) return;
char *str = qstrdup(aStr.local8Bit()); // TODO: make it really QString using
sepStr[0] = sepChar;
sepStr[1] = '\0';
KTabListBoxItem* item = itemList[row];
char *pos = strtok(str, sepStr);
for (int i=0; pos && *pos && i<numCols(); i++)
{
item->setText(i, QString::fromLocal8Bit(pos));
pos = strtok(0L, sepStr);
}
item->setForeground(black);
if (needsUpdate(row)) lbox.repaint();
delete [] str;
}
//-----------------------------------------------------------------------------
void KTabListBox::changeItemPart(const QString& aStr, int row, int col)
{
if (row < 0 || row >= numRows()) return;
if (col < 0 || col >= numCols()) return;
itemList[row]->setText(col, aStr);
if (needsUpdate(row)) lbox.repaint();
}
//-----------------------------------------------------------------------------
void KTabListBox::changeItemColor(const QColor& newColor, int row)
{
if (row >= numRows()) return;
if (row < 0) row = numRows()-1;
itemList[row]->setForeground(newColor);
if (needsUpdate(row)) lbox.repaint();
}
//-----------------------------------------------------------------------------
void KTabListBox::removeItem(int row)
{
KTabListBoxItemPtr it;
int i, nr;
bool upd;
if (row < 0 || row >= numRows()) return;
upd = needsUpdate(row);
if (itemPosList(current) >itemPosList(row))
current=itemShowList[itemPosList(current-1)];
nr = numRows()-1;
it = itemList[row];
for (i=row; i<nr; i++)
itemList[i] = itemList[i+1];
for(i=itemPosList(row); i<nr;i++)
itemShowList[i]=itemShowList[i+1];
for(i=0;i<nr;i++) if(itemShowList[i]>row) itemShowList[i]--;
itemList[nr] = it;
setNumRows(nr);
if (nr==0) current = -1;
if (upd) lbox.repaint();
}
//-----------------------------------------------------------------------------
void KTabListBox::updateItem(int row, bool erase)
{
int i;
for (i=numCols()-1; i>=0; i--)
lbox.updateCell(itemPosList(row), i, erase);
}
//-----------------------------------------------------------------------------
bool KTabListBox::needsUpdate(int id)
{
return (lbox.autoUpdate() && itemVisible(itemPosList(id)));
}
//-----------------------------------------------------------------------------
void KTabListBox::changeMode(int col)
{
// Add optimization HERE!!!!
if(col >= lbox.numCols() || col < 0)
return;
if(colList[col]->changeMode())
reorderRows();
}
//-----------------------------------------------------------------------------
void KTabListBox::adjustNumber(int num)
{
int t;
for(t=0;t<numColumns;t++)
if(colList[t]->number()>num)
colList[t]->setNumber(colList[t]->number()-1);
lastSelectedColumn--;
}
//-----------------------------------------------------------------------------
int KTabListBox::colPosList(int cl)
{
int t;
if (cl<0) return cl;
for(t=0;t<numColumns;t++) if(cl==colShowList[t]) break;
return t;
}
//-----------------------------------------------------------------------------
int KTabListBox::itemPosList(int it)
{
int t;
for(t=0;t<maxItems;t++) if(it==itemShowList[t]) break;
return t;
}
//-----------------------------------------------------------------------------
void KTabListBox::clearAllNum()
{
int t;
for(t=0;t<numColumns;t++)
colList[t]->setNumber(0);
lastSelectedColumn=0;
}
//-----------------------------------------------------------------------------
void KTabListBox::clear()
{
int i;
for (i=maxItems-1; i>=0; i--)
{
if (itemList[i])
itemList[i]->setMarked(-2);
}
setNumRows(0);
lbox.setTopLeftCell(0,0);
current = -1;
}
//-----------------------------------------------------------------------------
void KTabListBox::repaint()
{
if(needsSort) reorderRows();
QWidget::repaint();
lbox.repaint();
}
//-----------------------------------------------------------------------------
void KTabListBox::resizeList(int newNumItems)
{
KTabListBoxItemPtr* newItemList;
int *newItemShowList;
int i, ih, nc, oldNum;
if (newNumItems < 0) newNumItems = (maxItems << 1);
if (newNumItems < INIT_MAX_ITEMS) newNumItems = INIT_MAX_ITEMS;
nc = numCols();
oldNum = numRows();
newItemList = new KTabListBoxItemPtr[newNumItems];
newItemShowList = new int[newNumItems];
ih = newNumItems < oldNum ? newNumItems : oldNum;
for (i = ih-1; i>=0; i--)
{
newItemList[i] = itemList[i];
newItemShowList[i] = itemShowList[i];
}
if (newNumItems > oldNum)
{
for (i = oldNum; i < newNumItems; i++) {
newItemList[i] = new KTabListBoxItem(nc);
newItemShowList[i] = 0;
}
}
else
{
for (i = newNumItems; i < oldNum; i++)
delete itemList[i];
}
if (itemList) delete [] itemList;
if (itemShowList) delete [] itemShowList;
itemList = newItemList;
itemShowList = newItemShowList;
maxItems = newNumItems;
setNumRows(ih);
}
//-----------------------------------------------------------------------------
// This should really go into kdecore. Currently it's used by khtml and this.
// It prevents the tablist to continue to scroll after the user has released
// the key.
void KTabListBox::flushKeys()
{
XEvent ev_return;
Display *dpy = qt_xdisplay();
while ( XCheckTypedEvent( dpy, KeyPress, &ev_return ) );
}
//-----------------------------------------------------------------------------
void KTabListBox::keyPressEvent( QKeyEvent *e )
{
QScrollBar *sY=(QScrollBar*)lbox.verticalScrollBar();
QScrollBar *sX=(QScrollBar*)lbox.horizontalScrollBar();
bool thereIsX=sX->isVisible();
//int pageJump = lbox.lastRowVisible()-lbox.topCell();
int t;
switch ( e->key() ) {
case Key_Left:
if(thereIsX) sX->setValue( sX->value() - sX->lineStep() );
flushKeys();
break;
case Key_Right:
if(thereIsX) sX->setValue( sX->value() + sX->lineStep() );
flushKeys();
break;
case Key_Up:
if(current==topCell())
sY->setValue( sY->value() - sY->lineStep() );
setCItem(itemShowList[itemPosList(current)-1]);
flushKeys();
break;
case Key_Down:
if(current==lastRowVisible())
sY->setValue( sY->value() + sY->lineStep() );
if((t=itemPosList(current)+1)<numRows())
setCItem(itemShowList[t]);
flushKeys();
break;
case Key_PageUp:
sY->setValue( sY->value() - sY->pageStep() );
t=lbox.lastRowVisible()-1;
if(lbox.topCell()==0) t=0;
/*if((t=itemPosList(current)-pageJump)>0)
setCItem(itemShowList[t]);
else
setCItem(itemShowList[0]);*/
setCItem(itemShowList[t]);
flushKeys();
break;
case Key_PageDown:
sY->setValue( sY->value() + sY->pageStep() );
t=lbox.topCell()+1;
if(lbox.lastRowVisible()==numRows()-1) t=numRows()-1;
/*if((t=itemPosList(current)+pageJump+1)<numRows())
setCItem(itemShowList[t]);
else
setCItem(itemShowList[numRows()-1]);*/
setCItem(itemShowList[t]);
flushKeys();
break;
case Key_Home:
sY->setValue( sY->minValue() );
setCItem(itemShowList[0]);
flushKeys();
break;
case Key_End:
sY->setValue( sY->maxValue() );
setCItem(itemShowList[numRows()-1]);
flushKeys();
break;
case Key_Enter:
case Key_Return:
emit selected(current, -1);
break;
case Key_Space:
emit highlighted(current, -1);
break;
default:
e->ignore();
break;
}
}
//-----------------------------------------------------------------------------
void KTabListBox::resizeEvent(QResizeEvent* e)
{
int i, w,t;
QWidget::resizeEvent(e);
for (i=0, w=0; i<numCols(); i++)
{
colList[i]->setWidth(colList[i]->defaultWidth());
w += colList[i]->defaultWidth();
}
t=lbox.width()-lbox.viewWidth();
if (w < e->size().width()-t)
colList[colShowList[numCols()-1]]->setWidth(
e->size().width()-w-t+colList[colShowList[numCols()-1]]->defaultWidth() );
lbox.setGeometry(0, labelHeight, e->size().width(),
e->size().height()-labelHeight);
lbox.reconnectSBSignals();
repaint();
}
//-----------------------------------------------------------------------------
void KTabListBox::paintEvent(QPaintEvent*)
{
int i, ih, x, w;
QPainter paint;
QWMatrix matrix;
QRect clipR;
QFont font, oldFont;
ih = numCols();
x = -lbox.xOffset();
matrix.translate(x, 0);
paint.begin(this);
for (i=0; i<ih; i++)
{
w = colList[colShowList[i]]->width();
if (w + x >= 0)
{
clipR.setRect(x, 0, w, labelHeight);
paint.setWorldMatrix(matrix, FALSE);
paint.setClipRect(clipR);
colList[colShowList[i]]->paint(&paint);
qDrawShadeLine(&paint, w, 0, w, labelHeight,
QWidget::colorGroup());
}
else colList[colShowList[i]]->hideCheckButton();
matrix.translate(w, 0);
x += w;
}
paint.resetXForm();
paint.setClipping (false);
if ( style() == MotifStyle )
qDrawShadePanel(&paint, 0, 0, width(), height(),
QWidget::colorGroup(), false, 1);
else
qDrawShadeRect (&paint, 0, 0, width(), height(),
QWidget::colorGroup(), true, 1);
paint.end();
}
//-----------------------------------------------------------------------------
void KTabListBox::mouseMoveEvent(QMouseEvent* e)
{
register int i, x, ex, ey;
bool mayResize = FALSE;
ex = e->pos().x();
ey = e->pos().y();
if ((e->state() & LeftButton))
{
if(mMouseDragColumn) doMouseMoveCol(e);
if (mResizeCol && abs(mMouseStart.x() - ex) > 4)
doMouseResizeCol(e);
else if (!mResizeCol &&
(ex < mMouseColLeft ||
ex > (mMouseColLeft+mMouseColWidth)))
{
mMouseDragColumn=true;
doMouseMoveCol(e);
}
QWidget::mouseMoveEvent(e);
return;
}
for (i=1, x=colList[colShowList[0]]->width()-lbox.xOffset(); ; i++)
{
if (ex >= x-4 && ex <= x+4)
{
mayResize = TRUE;
mMouseCol = colShowList[i-1];
mMouseColLeft = ex;
mMouseColWidth = lbox.cellWidth(i-1);
break;
}
if (i >= numColumns) break;
x += colList[colShowList[i]]->width();
}
if (mayResize)
{
if (!mResizeCol)
{
mResizeCol = TRUE;
setCursor(sizeHorCursor);
}
}
else
{
if (mResizeCol)
{
mResizeCol = FALSE;
setCursor(arrowCursor);
}
}
}
//-----------------------------------------------------------------------------
void KTabListBox::mousePressEvent(QMouseEvent* e)
{
if (!mResizeCol && e->button() == LeftButton)
{
mMouseStart = e->pos();
mMouseCol = lbox.findRealCol(e->pos().x());
if (mMouseCol < 0) return;
mMouseColWidth = colList[mMouseCol]->width();
mMouseColLeft = 0;
int t;
colPosList(mMouseCol);
for(t=0;t<colPosList(mMouseCol);t++)
mMouseColLeft+=colList[colShowList[t]]->width();
mLastX=mMouseColLeft;
}
QWidget::mousePressEvent(e);
}
//-----------------------------------------------------------------------------
void KTabListBox::mouseReleaseEvent(QMouseEvent* e)
{
if (e->button() == LeftButton)
{
if (!mMouseAction)
{
// user wants to select the column rather than drag or move it
if (mMouseCol >= 0)
{
emit headerClicked(mMouseCol);
// changeMode knows if to do a repaint or not.
changeMode(mMouseCol);
}
}
else if(mMouseDragColumn)
{
// The user had dragged a column! We need to change the order?
mMouseDragColumn=false;
mMouseCol=colPosList(mMouseCol);
int actualColumn=colPosList(lbox.findRealCol(e->pos().x()));
if(actualColumn>=0 && actualColumn!=mMouseCol)
{
int tmp, t;
tmp=colShowList[mMouseCol];
if(actualColumn>mMouseCol)
for(t=mMouseCol;t<actualColumn;t++)
colShowList[t]=colShowList[t+1];
else
for(t=mMouseCol;t>=actualColumn;t--)
colShowList[t]=colShowList[t-1];
colShowList[actualColumn]=tmp;
}
repaint();
}
else if(mResizeCol) // The user had resized the column, make a resize:
resize(width(),height());
mMouseCol = -1;
mMouseAction = FALSE;
}
QWidget::mouseReleaseEvent(e);
}
//-----------------------------------------------------------------------------
void KTabListBox::doMouseResizeCol(QMouseEvent* e)
{
int w, x,fixedmin=MINIMUM_SPACE;
if (!mMouseAction) mMouseAction = true;
if (mMouseCol >= 0)
{
x=e->pos().x();
w = mMouseColWidth + (x - mMouseColLeft);
if (colList[mMouseCol]->orderType()==ComplexOrder)
fixedmin+=ARROW_SPACE+labelHeight-BUTTON_SPACE;
else
if (colList[mMouseCol]->orderType()==SimpleOrder)
fixedmin+=ARROW_SPACE;
if (w < fixedmin) w = fixedmin;
if(w!=colList[mMouseCol]->width())
{
colList[mMouseCol]->setDefaultWidth(w);
colList[mMouseCol]->setWidth(w);
repaint();
if(x>width())
{
QScrollBar *sX=(QScrollBar*)lbox.horizontalScrollBar();
if(sX->isVisible())
sX->setValue( sX->value() +x -width() );
}
}
}
}
//-----------------------------------------------------------------------------
void KTabListBox::doMouseMoveCol(QMouseEvent* e)
{
int x,old;
QPainter paint;
if (!mMouseAction) mMouseAction = true;
x = e->pos().x();
if(x<0)
{
QScrollBar *sX=(QScrollBar*)lbox.horizontalScrollBar();
if(sX->isVisible())
{
old=sX->value();
sX->setValue( sX->value() + x );
if(old!=sX->value()) mLastX=-9999;
}
}
else if(x+mMouseColWidth>width())
{
QScrollBar *sX=(QScrollBar*)lbox.horizontalScrollBar();
if(sX->isVisible())
{
old=sX->value();
sX->setValue( sX->value() + x+mMouseColWidth-width() );
if(old!=sX->value()) mLastX=-9999;
}
}
paint.begin(this);
if(mLastX!=-9999)
paint.drawWinFocusRect(mLastX+lbox.xOffset(),0,mMouseColWidth,labelHeight);
paint.drawWinFocusRect(x,0,mMouseColWidth,labelHeight);
paint.end();
mLastX=x-lbox.xOffset();
}
//-----------------------------------------------------------------------------
bool KTabListBox::startDrag(int aRow, int aCol)
{
QTextDrag *td = new QTextDrag(text(aRow, aCol), this);
td->setPixmap(dndDefaultPixmap);
td->dragCopy();
return TRUE;
}
//-----------------------------------------------------------------------------
void KTabListBox::horSbValue(int)
{
// This update generates two paintEvent, why?
update();
}
//-----------------------------------------------------------------------------
void KTabListBox::horSbSlidingDone()
{
}
//=============================================================================
//
// C L A S S KTabListBoxTable
//
//=============================================================================
KTabListBoxTable::KTabListBoxTable(KTabListBox *parent):
KTabListBoxTableInherited(parent)
{
QFontMetrics fm = fontMetrics();
initMetaObject();
dragCol = -1;
dragRow = -1;
selIdx = -1;
setTableFlags(Tbl_autoVScrollBar|Tbl_autoHScrollBar|Tbl_smoothVScrolling|
Tbl_clipCellPainting);
switch(style())
{
case MotifStyle:
case WindowsStyle:
setBackgroundColor(colorGroup().base());
setFrameStyle( QFrame::WinPanel | QFrame::Sunken );
break;
default:
setLineWidth(1);
setFrameStyle(QFrame::Panel|QFrame::Plain);
}
setCellWidth(0);
setCellHeight(fm.lineSpacing() + 1);
setNumRows(0);
setCursor(arrowCursor);
setMouseTracking(FALSE);
setFocusPolicy(NoFocus);
// You can enable it with enableKey();
//setFocusPolicy(StrongFocus);
}
//-----------------------------------------------------------------------------
KTabListBoxTable::~KTabListBoxTable()
{
}
//----------------------------------------------------------------------------
void KTabListBoxTable::enableKey()
{
setFocusPolicy(StrongFocus);
}
//-----------------------------------------------------------------------------
int KTabListBoxTable::findRealCol(int xPos)
{
int column = -1;
int xPos1;
KTabListBox*owner =(KTabListBox*)parentWidget();
int maxCol=owner->numColumns;
if ( xPos >= minViewX() && xPos <= maxViewX() )
{
column = 0;
xPos1=xPos+xOffset();
int cumWidth= 2;
while ( column < maxCol )
{
cumWidth += cellWidth( column );
if ( xPos1 < cumWidth ) break;
column++;
}
}
if ( column >=maxCol ) return -1;
if(column==-1) return -1;
return owner->colShowList[column];
}
//-----------------------------------------------------------------------------
void KTabListBoxTable::paintCell(QPainter* p, int row, int col)
{
KTabListBox* owner =(KTabListBox*)parentWidget();
KTabListBoxItem* item = owner->getItem(owner->itemShowList[row]);
if (!item) return;
p->setPen(item->foreground());
int t=owner->colShowList[col];
owner->colList[t]->paintCell(p, row, item->text(t),(item->marked()==-1));
}
//-----------------------------------------------------------------------------
int KTabListBoxTable::cellWidth(int col)
{
KTabListBox* owner =(KTabListBox*)parentWidget();
return(owner->colList ? owner->colList[owner->colShowList[col]]->width() : 0);
}
//-----------------------------------------------------------------------------
void KTabListBoxTable::mouseDoubleClickEvent(QMouseEvent* e)
{
KTabListBox* owner =(KTabListBox*)parentWidget();
int idx, colnr;
if(findRow(e->pos().y())<0) return;
idx = owner->currentItem();
colnr = findRealCol(e->pos().x());
if (idx >= 0) emit owner->selected(idx,colnr);
}
//-----------------------------------------------------------------------------
void KTabListBoxTable::doItemSelection(QMouseEvent* e, int idx)
{
KTabListBox* owner =(KTabListBox*)parentWidget();
int i, di, colnr;
owner->unmarkAll();
if ((e->state()&ShiftButton)!=0 && owner->currentItem()>=0)
{
i = owner->itemPosList(owner->currentItem());
idx= owner->itemPosList(idx);
di =(i>idx ? -1 : 1);
while(1)
{
owner->markItem(owner->itemShowList[i]);
if (i == idx) break;
i += di;
}
}
else
{
colnr = findRealCol(e->pos().x());
owner->setCurrentItem(idx,colnr);
}
}
//-----------------------------------------------------------------------------
void KTabListBoxTable::mousePressEvent(QMouseEvent* e)
{
KTabListBox* owner =(KTabListBox*)parentWidget();
int row, col;
row=findRow(e->pos().y());
if(row<0) return;
row = owner->itemShowList[row];
col = findRealCol(e->pos().x());
if (e->button() == RightButton)
{
// handle popup menu
if (row >= 0 && col >= 0)
emit owner->popupMenu(row, col);
return;
}
else if (e->button() == MidButton)
{
// handle middle click
if (row >= 0 && col >= 0)
emit owner->midClick(row, col);
return;
}
// arm for possible dragging
dragStartPos = e->pos();
dragCol = col;
dragRow = row;
// handle item highlighting
if (row >= 0 && owner->getItem(row)->marked() < -1)
{
doItemSelection(e, row);
selIdx = row;
}
else selIdx = -1;
}
//-----------------------------------------------------------------------------
void KTabListBoxTable::mouseReleaseEvent(QMouseEvent* e)
{
KTabListBox* owner =(KTabListBox*)parentWidget();
int idx;
if (e->button() != LeftButton) return;
idx = findRow(e->pos().y());
if(idx<0) return;
idx = owner->itemShowList[idx];
if (idx >= 0 && selIdx < 0)
doItemSelection(e, idx);
}
//-----------------------------------------------------------------------------
void KTabListBoxTable::mouseMoveEvent(QMouseEvent* e)
{
KTabListBox* owner =(KTabListBox*)parentWidget();
if ((e->state() &(RightButton|LeftButton|MidButton)) != 0)
{
if (dragRow >= 0 && dragCol >= 0 &&
(abs(e->pos().x()-dragStartPos.x()) >= 5 ||
abs(e->pos().y()-dragStartPos.y()) >= 5))
{
// we have a liftoff !
owner->startDrag(dragRow, dragCol);
dragRow = dragCol = -1;
return;
}
}
}
//-----------------------------------------------------------------------------
void KTabListBoxTable::reconnectSBSignals()
{
QScrollBar* hsb = (QScrollBar*)horizontalScrollBar();
KTabListBox* owner =(KTabListBox*)parentWidget();
if (!hsb) return;
hsb->setTracking(TRUE);
connect(hsb, SIGNAL(valueChanged(int)), owner, SLOT(horSbValue(int)));
connect(hsb, SIGNAL(sliderReleased()), owner, SLOT(horSbSlidingDone()));
}
//-----------------------------------------------------------------------------
void KTabListBoxTable::focusInEvent(QFocusEvent*)
{
// Just do nothing here to avoid the annoying flicker whick happens due
// to a redraw() call per default.
}
//-----------------------------------------------------------------------------
void KTabListBoxTable::focusOutEvent(QFocusEvent*)
{
// Just do nothing here to avoid the annoying flicker whick happens due
// to a redraw() call per default.
}
KTabListBoxItem* KTabListBox :: getItem (int idx)
{
return ((idx>=0 && idx<maxItems) ? itemList[idx] : (KTabListBoxItem*)0L);
}
const KTabListBoxItem* KTabListBox :: getItem (int idx) const
{
return ((idx>=0 && idx<maxItems) ? itemList[idx] : (KTabListBoxItem*)0L);
}
//=============================================================================
//
// C L A S S KNumCheckButton
//
//=============================================================================
KNumCheckButton::KNumCheckButton( QWidget *_parent, const char *name )
: QWidget(_parent, name)
{
int t=fontMetrics().height();
resize(t,t);
setText(QString::fromLatin1(" "));
raised = FALSE;
setFocusPolicy( NoFocus );
}
void KNumCheckButton::setText( const QString& text )
{
btext=text;
repaint();
}
void KNumCheckButton::enterEvent( QEvent* )
{
raised = TRUE;
repaint(FALSE);
}
void KNumCheckButton::leaveEvent( QEvent * )
{
if( raised != FALSE )
{
raised = FALSE;
repaint();
}
}
void KNumCheckButton::mousePressEvent( QMouseEvent *e)
{
if(e->button() == LeftButton)
emit selected();
else if(e->button() == RightButton)
emit deselected();
}
void KNumCheckButton::mouseDoubleClickEvent (QMouseEvent *e)
{
if(e->button() == LeftButton)
emit doubleclick(true);
else if(e->button() == RightButton)
emit doubleclick(false);
}
void KNumCheckButton::paintEvent( QPaintEvent *event )
{
QPainter painter;
QFont font,oldfont;
painter.begin( this );
painter.setClipRect( event->rect() );
if ( raised )
{
QBrush brush( white );
qDrawShadeRect( &painter, 0, 0, width(), height(), colorGroup(),
TRUE, 1,1, &brush );
}
else
qDrawShadeRect( &painter, 0, 0, width(), height(), colorGroup(),
TRUE, 1,1, 0L );
int tf = AlignCenter;
oldfont=painter.font();
font=oldfont;
font.setPointSize(height()-7);
painter.setFont(font);
painter.drawText(0, 0, width(), height(), tf, btext);
painter.setFont(oldfont);
painter.end();
}
#include "ktablistbox.moc"