Projekt

Allgemein

Profil

Herunterladen (57,8 KB) Statistiken
| Zweig: | Markierung: | Revision:
/***************************************************************************
* This file is part of the project AqFoxExt. *
* AqFoxExt (c) by 2011 Martin Preuss, all rights reserved. *
* *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
* MA 02111-1307 USA *
* *
***************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif


#include "fxx_table.hpp"

#include <gwen-gui-fox16/fox16_htmlctx.hpp>
#include <gwen-gui-fox16/theme.h>

#include <fxkeys.h>

#include <gwenhywfar/debug.h>

#include <assert.h>


#define CURRENT_BORDER_LINE_WIDTH 2


/* -----------------------------------------------------------------------------
* ColumnInfo
* ----------------------------------------------------------------------------- */


FXXTable::ColumnInfo::ColumnInfo(FXXTable *t, int row, int col)
:m_table(t)
,m_row(row)
,m_column(col)
,m_x(0)
,m_width(0)
,m_useSpecialColor(0)
{

}


FXXTable::ColumnInfo::~ColumnInfo() {
}



int FXXTable::ColumnInfo::getHeightForWidth(int forWidth) {
DBG_ERROR(0, "virtual function");
return 0;
}



void FXXTable::ColumnInfo::draw(FXDC &dc, int x, int y, int w, int h) {
DBG_ERROR(0, "virtual function");
}



void FXXTable::ColumnInfo::update() {
DBG_ERROR(0, "virtual function");
}





/* -----------------------------------------------------------------------------
* ColumnInfoText
* ----------------------------------------------------------------------------- */


FXXTable::ColumnInfoText::ColumnInfoText(FXXTable *t, int row, int col,
const FXString &s,
int useSpecialColor)
:ColumnInfo(t, row, col), m_text(s), m_htmlCtx(NULL) {
m_useSpecialColor=useSpecialColor;
m_backGround=FXRGB(255, 255, 255);
m_htmlCtx=new FOX16_HtmlCtx(0);
//m_html->setFont(t->getFont());
#if 0
if (t->isRowSelected(row)) {
m_htmlCtx->setBackgroundColor(t->getSelBackColor());
m_htmlCtx->setForegroundColor(t->getSelTextColor());
}
else {
if (m_useSpecialColor) {
m_backGround=t->getMarkColor(m_useSpecialColor-1);
m_htmlCtx->setBackgroundColor(t->getMarkColor(m_useSpecialColor-1));
m_htmlCtx->setForegroundColor(t->getTextColor());
}
else {
m_backGround=t->getCellBackColor(row, col);
m_htmlCtx->setBackgroundColor(t->getCellBackColor(row, col));
m_htmlCtx->setForegroundColor(t->getTextColor());
}
}
#else
if (useSpecialColor==-1) {
m_backGround=t->getCellBackColor(row, col);
m_htmlCtx->setBackgroundColor(t->getCellBackColor(row, col));
m_htmlCtx->setForegroundColor(t->getTextColor());
}
else if (useSpecialColor==0)
m_backGround=t->getCellBackColor(row, col);
else
m_backGround=t->getMarkColor(m_useSpecialColor-1, row, col);
#endif

m_htmlCtx->setText(s.text());
}



FXXTable::ColumnInfoText::ColumnInfoText(FXXTable *t, int row, int col,
const FXString &s,
FXColor bg)
:ColumnInfo(t, row, col), m_text(s), m_htmlCtx(NULL) {
m_useSpecialColor=-1;
m_backGround=bg;
m_htmlCtx=new FOX16_HtmlCtx(0);
//m_html->setFont(t->getFont());
if (t->isRowSelected(row)) {
m_htmlCtx->setBackgroundColor(t->getSelBackColor());
m_htmlCtx->setForegroundColor(t->getSelTextColor());
}
else {
m_htmlCtx->setBackgroundColor(bg);
m_htmlCtx->setForegroundColor(t->getTextColor());
}

m_htmlCtx->setText(s.text());
}



FXXTable::ColumnInfoText::~ColumnInfoText() {
if (m_htmlCtx) {
delete m_htmlCtx;
m_htmlCtx=NULL;
}
}



int FXXTable::ColumnInfoText::getHeightForWidth(int forWidth) {
m_htmlCtx->layout(forWidth, -1);
return m_htmlCtx->getHeight();
}



void FXXTable::ColumnInfoText::update() {
if (m_htmlCtx)
delete m_htmlCtx;
m_htmlCtx=new FOX16_HtmlCtx(0);
//m_htmlCtx->setFont(m_table->getFont());
if (m_table->isRowSelected(m_row)) {
m_htmlCtx->setBackgroundColor(m_table->getSelBackColor());
m_htmlCtx->setForegroundColor(m_table->getSelTextColor());
}
else {
#if 0
if (m_useSpecialColor) {
m_htmlCtx->setBackgroundColor(m_table->getMarkColor(m_useSpecialColor-1));
m_htmlCtx->setForegroundColor(m_table->getTextColor());
}
else {
m_htmlCtx->setBackgroundColor(m_table->getCellBackColor(m_row, m_column));
m_htmlCtx->setForegroundColor(m_table->getTextColor());
}
#else
m_htmlCtx->setBackgroundColor(m_backGround);
m_htmlCtx->setForegroundColor(m_table->getTextColor());
#endif
}

m_htmlCtx->setText(m_text.text());
}



void FXXTable::ColumnInfoText::draw(FXDC &dc, int x, int y, int w, int h) {
m_htmlCtx->paint(&dc, x, y);
}







/* -----------------------------------------------------------------------------
* ColumnInfoIcons
* ----------------------------------------------------------------------------- */

FXXTable::ColumnInfoIcons::ColumnInfoIcons(FXXTable *t, int row, int col,
int useSpecialColor)
:ColumnInfo(t, row, col), m_iconCount(0) {
int i;

m_useSpecialColor=useSpecialColor;
for (i=0; i<MAX_ICONS; i++)
m_iconPtrs[i]=NULL;

if (useSpecialColor==-1) {
m_backGround=t->getCellBackColor(row, col);
}
else if (useSpecialColor==0)
m_backGround=t->getCellBackColor(row, col);
else
m_backGround=t->getMarkColor(m_useSpecialColor-1, row, col);
}



FXXTable::ColumnInfoIcons::ColumnInfoIcons(FXXTable *t, int row, int col,
FXColor backGround)
:ColumnInfo(t, row, col), m_iconCount(0) {
int i;

m_useSpecialColor=-1;
m_backGround=backGround;
for (i=0; i<MAX_ICONS; i++)
m_iconPtrs[i]=NULL;
}



FXXTable::ColumnInfoIcons::~ColumnInfoIcons() {
}



int FXXTable::ColumnInfoIcons::getHeightForWidth(int forWidth) {
int i;
int x=0;
int y=0;
int maxh=0;

for (i=0; i<MAX_ICONS; i++) {
if (m_iconPtrs[i]) {
int w=m_iconPtrs[i]->getWidth();
int h=m_iconPtrs[i]->getHeight();

if (x!=0 && x+w>forWidth) {
/* icon doesn't fit into the current line */
y+=maxh+ICON_SPACING;
x=0;
maxh=0;
}

if (h>maxh)
maxh=h;
x+=w+ICON_SPACING;
}
}
y+=maxh;

return y;
}



void FXXTable::ColumnInfoIcons::draw(FXDC &dc, int x, int y, int w, int h) {
int i;
int dx=0;
int dy=0;
int maxh=0;

for (i=0; i<MAX_ICONS; i++) {
if (m_iconPtrs[i]) {
int dw=m_iconPtrs[i]->getWidth();
int dh=m_iconPtrs[i]->getHeight();

if (dx!=0 && dx+dw>w) {
/* icon doesn't fit into the current line */
dy+=maxh;
dx=0;
maxh=0;
}

dc.drawIcon(m_iconPtrs[i], x+dx, y+dy);
if (dh>maxh)
maxh=dh;
dx+=dw+ICON_SPACING;
}
}
}



void FXXTable::ColumnInfoIcons::update() {
}



void FXXTable::ColumnInfoIcons::setIcon(int idx, FXIcon *ic) {
if (idx<MAX_ICONS) {
m_iconPtrs[idx]=ic;
if (idx>=m_iconCount)
m_iconCount=idx+1;
}
}



void FXXTable::ColumnInfoIcons::addIcon(FXIcon *ic) {
if (m_iconCount+1<MAX_ICONS) {
m_iconPtrs[m_iconCount++]=ic;
}
}







/* -----------------------------------------------------------------------------
* RowInfo
* ----------------------------------------------------------------------------- */



FXXTable::RowInfo::RowInfo(FXXTable *t, int row)
:m_table(t)
,m_row(row)
,m_y(0)
,m_height(0)
,m_modified(false)
{
}



FXXTable::RowInfo::~RowInfo() {
std::list<ColumnInfo*>::iterator it;

for (it=m_columns.begin();
it!=m_columns.end();
it++) {
delete (*it);
}
m_columns.clear();
}



void FXXTable::RowInfo::addColumnInfo(ColumnInfo *ci) {
m_columns.push_back(ci);
}



FXXTable::ColumnInfo *FXXTable::RowInfo::getColumnInfo(int c) {
std::list<ColumnInfo*>::iterator it;

for (it=m_columns.begin();
it!=m_columns.end();
it++) {
if ((*it)->getColumn()==c)
return *it;
}

return NULL;
}






/* -----------------------------------------------------------------------------
* SelectionRange
* ----------------------------------------------------------------------------- */


FXXTable::SelectionRange::SelectionRange(int f, int l)
:first(f)
,last(l)
,previous(NULL)
,next(NULL) {
}



FXXTable::SelectionRange::~SelectionRange() {
}







/* -----------------------------------------------------------------------------
* Selection
* ----------------------------------------------------------------------------- */


FXXTable::Selection::Selection()
:m_first(NULL)
,m_last(NULL){

}



FXXTable::Selection::~Selection() {
clear();
}



void FXXTable::Selection::clear() {
SelectionRange *r=m_first;

while(r) {
SelectionRange *rNext;
rNext=r->next;
delete r;
r=rNext;
}

m_first=NULL;
m_last=NULL;
}



bool FXXTable::Selection::contains(int v) {
SelectionRange *r=m_first;

while(r) {
if (v>=r->first && v<=r->last)
return true;
r=r->next;
}

return false;
}



void FXXTable::Selection::unlinkRange(FXXTable::SelectionRange *r) {
/* unlink from neighbors */
if (r->previous)
r->previous->next=r->next;
if (r->next)
r->next->previous=r->previous;

/* unlink from Selection */
if (m_first==r)
m_first=r->next;
if (m_last==r)
m_last=r->previous;

r->previous=NULL;
r->next=NULL;

}



void FXXTable::Selection::appendRange(FXXTable::SelectionRange *r) {
r->next=NULL;
if (m_last) {
m_last->next=r;
r->previous=m_last;
m_last=r;
}
else {
m_first=r;
m_last=r;
}
}



void FXXTable::Selection::insertRange(FXXTable::SelectionRange *where,
FXXTable::SelectionRange *r) {
r->previous=where;
r->next=where->next;
if (where->next)
where->next->previous=r;
where->next=r;

if (m_last==where)
m_last=r;
}



void FXXTable::Selection::insertRangeBefore(FXXTable::SelectionRange *where,
FXXTable::SelectionRange *r) {
r->next=where;
r->previous=where->previous;
if (where->previous)
where->previous->next=r;
where->previous=r;

if (m_first==where)
m_first=r;
}



void FXXTable::Selection::combineRanges() {
SelectionRange *r;

/* find first overlapping entry */
r=m_first;
while(r) {
SelectionRange *rNext;

rNext=r->next;
while (rNext) {
if (r->last==(rNext->first-1)) {
/* combine */
#if 0
DBG_ERROR(0, "Combining: %d - %d with %d - %d",
r->first, r->last, rNext->first, rNext->last);
#endif
r->last=rNext->last;
unlinkRange(rNext);
delete rNext;
rNext=r->next;
}
else
break;
}
r=r->next;
}
}



void FXXTable::Selection::addRange(int first, int last) {
if (m_first==NULL) {
/* no selection, set this one */
SelectionRange *rNew=new SelectionRange(first, last);
m_first=rNew;
m_last=rNew;
}
else {
if (first<m_first->first && last>=m_last->last) {
/* completely replace current selection */
SelectionRange *rNew=new SelectionRange(first, last);
clear();
m_first=rNew;
m_last=rNew;
}
else {
SelectionRange *r;
SelectionRange *r1=NULL;

/* find first overlapping entry */
r=m_first;
while(r) {
if (r->first>last)
break;
if (r->last>=first && r->first<=last) {
r1=r;
break;
}
r=r->next;
}
if (r1==NULL) {
/* no entry overlaps the new range, so we need to create a new one
* r points to the first entry behind the new range */
SelectionRange *rNew=new SelectionRange(first, last);
if (r)
insertRangeBefore(r, rNew);
else
appendRange(rNew);
}
else {
/* combine all following area entries into r1 */
if (r1->first>first)
r1->first=first;
r=r1->next;
while(r) {
SelectionRange *rNext;

rNext=r->next;
if (r->first>last)
/* outside range, done */
break;
r1->last=r->last;
unlinkRange(r);
delete r;
r=rNext;
}
}
combineRanges();
}
}
}



void FXXTable::Selection::delRange(int first, int last) {
SelectionRange *r;

/* find range in which the new range begins */
r=m_first;
while(r) {
SelectionRange *rNext;

rNext=r->next;
if (r->first>last)
/* already behind the area, stop here */
break;

if (r->last>=first) {
if (r->first==first && r->last==last) {
/* r is completely identical with the new range; delete and done */
unlinkRange(r);
delete r;
break;
}
else if (r->first>=first && r->last<=last) {
/* r is completely included in new area, just remove it */
unlinkRange(r);
delete r;
}
else if (r->last>=first && r->last<=last) {
/* the end of r overlaps with the new range */
r->last=first-1;
}
else if (r->first>=first && r->first<=last) {
/* the beginning of r overlaps with the new range */
r->first=first+1;
}
else if (r->first<=first && r->last>=last) {
/* new range lies inside r; cut out and be done */
int rlast=r->last;

r->last=first-1;
if (rlast>last) {
/* there is something behind the new range, create new object for that */
SelectionRange *rNew=new SelectionRange(last+1, rlast);
insertRange(r, rNew);
break;
}
}
}

r=rNext;
}
}



void FXXTable::Selection::dump(FILE *f, int indent) const {
SelectionRange *r=m_first;
int i;

for (i=0; i<indent; i++) fprintf(f, " ");
fprintf(f, "Selection:\n");
while(r) {
for (i=0; i<indent; i++) fprintf(f, " ");
if (r->first==r->last)
fprintf(f, " Range %5d\n", r->first);
else
fprintf(f, " Range %5d - %5d\n", r->first, r->last);
r=r->next;
}
}








/* -----------------------------------------------------------------------------
* Table
* ----------------------------------------------------------------------------- */

FXDEFMAP(FXXTable) FXXTableMap[]={
FXMAPFUNC(SEL_PAINT, 0, FXXTable::onPaint),
FXMAPFUNC(SEL_COMMAND, FXXTable::ID_HSCROLLBAR, FXXTable::onHScrollerChanged),
FXMAPFUNC(SEL_CHANGED, FXXTable::ID_HSCROLLBAR, FXXTable::onHScrollerDragged),
FXMAPFUNC(SEL_COMMAND, FXXTable::ID_VSCROLLBAR, FXXTable::onVScrollerChanged),
FXMAPFUNC(SEL_CHANGED, FXXTable::ID_VSCROLLBAR, FXXTable::onVScrollerDragged),
FXMAPFUNC(SEL_COMMAND, FXXTable::ID_CHEADER, FXXTable::onHeaderItemClicked),
FXMAPFUNC(SEL_CHANGED, FXXTable::ID_CHEADER, FXXTable::onHeaderItemDragged),
FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, FXXTable::onLeftBtnPress),
FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, FXXTable::onLeftBtnRelease),
FXMAPFUNC(SEL_RIGHTBUTTONPRESS, 0, FXXTable::onRightBtnPress),
FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, 0, FXXTable::onRightBtnRelease),
FXMAPFUNC(SEL_DOUBLECLICKED, FXXTable::ID_TABLEITEM, FXXTable::onDoubleClicked),

FXMAPFUNC(SEL_KEYPRESS, 0, FXXTable::onKeyPress),
FXMAPFUNC(SEL_KEYRELEASE, 0, FXXTable::onKeyRelease),
FXMAPFUNC(SEL_MOUSEWHEEL,0, FXXTable::onMouseWheel),

FXMAPFUNC(SEL_FOCUSIN, 0, FXXTable::onFocusIn),
FXMAPFUNC(SEL_FOCUSOUT, 0, FXXTable::onFocusOut),

FXMAPFUNC(SEL_COMMAND,FXXTable::ID_MOVE_LEFT,FXXTable::onCmdMoveLeft),
FXMAPFUNC(SEL_COMMAND,FXXTable::ID_MOVE_RIGHT,FXXTable::onCmdMoveRight),
FXMAPFUNC(SEL_COMMAND,FXXTable::ID_MOVE_UP,FXXTable::onCmdMoveUp),
FXMAPFUNC(SEL_COMMAND,FXXTable::ID_MOVE_DOWN,FXXTable::onCmdMoveDown),
FXMAPFUNC(SEL_COMMAND,FXXTable::ID_MOVE_TOP,FXXTable::onCmdMoveTop),
FXMAPFUNC(SEL_COMMAND,FXXTable::ID_MOVE_BOTTOM,FXXTable::onCmdMoveBottom),
FXMAPFUNC(SEL_COMMAND,FXXTable::ID_MOVE_PAGEDOWN,FXXTable::onCmdMovePageDown),
FXMAPFUNC(SEL_COMMAND,FXXTable::ID_MOVE_PAGEUP,FXXTable::onCmdMovePageUp),

FXMAPFUNC(SEL_COMMAND, FXXTable::ID_START_INPUT, FXXTable::onCmdStartInput),

FXMAPFUNC(SEL_COMMAND,FXXTable::ID_DESELECT_ALL,FXXTable::onCmdDeselectAll),
FXMAPFUNC(SEL_COMMAND,FXXTable::ID_MARK,FXXTable::onCmdMark),
FXMAPFUNC(SEL_COMMAND,FXXTable::ID_EXTEND,FXXTable::onCmdExtend),
FXMAPFUNC(SEL_COMMAND,FXXTable::ID_UNEXTEND,FXXTable::onCmdUnextend),

};


FXIMPLEMENT(FXXTable, FXComposite, FXXTableMap, ARRAYNUMBER(FXXTableMap))




FXXTable::FXXTable(FXComposite *p, FXObject* tgt, FXSelector sel, FXuint opts,
FXint x, FXint y, FXint w, FXint h,
FXint pl, FXint pr,
FXint pt, FXint pb)
:FXComposite(p, opts, x, y, w, h)
,m_columnHeader(NULL)
,m_horizontalBar(NULL)
,m_verticalBar(NULL)
,m_scrollCorner(NULL)
,m_nrows(0)
,m_ncolumns(0)
,m_firstVisibleRow(0)
,m_lastVisibleRow(0)
,m_marginLeft(pl)
,m_marginRight(pr)
,m_marginTop(pt)
,m_marginBottom(pb)
,m_viewportX(0)
,m_viewportY(0)
,m_viewportWidth(0)
,m_viewportHeight(0)
,m_showGrid(true)
,m_gridWidth(DEFAULT_GRIDWIDTH)
,m_hSpacing(DEFAULT_HSPACING)
,m_vSpacing(DEFAULT_VSPACING)
,m_alignTop(true)
,m_currentRow(0)
,m_currentCol(0)
,m_editRow(-1)
,m_editCol(-1)
,m_editor(NULL)
,m_font(NULL)
,m_minRowHeight(0)
,m_needLayout(true)
,m_hasFocus(false)
{
int i;

flags|=FLAG_ENABLED;
flags|=FLAG_SHOWN;
target=tgt;
message=sel;

m_columnHeader=new FXHeader(this, this, ID_CHEADER,
HEADER_BUTTON | HEADER_RESIZE | HEADER_TRACKING |
FRAME_NORMAL);
m_horizontalBar=new FXScrollBar(this,
this, ID_HSCROLLBAR,
SCROLLBAR_WHEELJUMP |
SCROLLBAR_HORIZONTAL);
m_verticalBar=new FXScrollBar(this,
this, ID_VSCROLLBAR,
SCROLLBAR_WHEELJUMP |
SCROLLBAR_VERTICAL);
m_scrollCorner=new FXScrollCorner(this);

m_textColor=getApp()->getForeColor();
m_baseColor=getApp()->getBaseColor();
m_hiliteColor=getApp()->getHiliteColor();
m_shadowColor=getApp()->getShadowColor();
m_borderColor=getApp()->getBorderColor();
m_selbackColor=getApp()->getSelbackColor();
m_seltextColor=getApp()->getSelforeColor();
m_gridColor=getApp()->getBaseColor();
m_cellBorderColor=getApp()->getBorderColor();
m_cellBackColor[0][0]=getApp()->getBackColor(); // Even row, even column
m_cellBackColor[0][1]=getApp()->getBackColor(); // Even row, odd column
m_cellBackColor[1][0]=getApp()->getBackColor(); // Odd row, even column
m_cellBackColor[1][1]=getApp()->getBackColor(); // Odd row, odd column
m_stippleColor=FXRGB(255,0,0);
for (i=0; i<NUM_MARK_COLOR; i++) {
FXColor col;

col=getApp()->getSelbackColor();
m_markColor[i][0][0]=col;
m_markColor[i][0][1]=col;
m_markColor[i][1][0]=col;
m_markColor[i][1][1]=col;
}

m_font=getApp()->getNormalFont();
}



FXXTable::~FXXTable() {
std::list<RowInfo*>::iterator it;

for (it=m_rows.begin(); it!=m_rows.end(); it++) {
delete (*it);
}
m_rows.clear();
}



FXXTable::FXXTable()
:FXComposite()
,m_columnHeader(NULL)
,m_horizontalBar(NULL)
,m_verticalBar(NULL)
,m_scrollCorner(NULL)
,m_nrows(0)
,m_ncolumns(0)
,m_firstVisibleRow(0)
,m_lastVisibleRow(0)
,m_marginLeft(0)
,m_marginRight(0)
,m_marginTop(0)
,m_marginBottom(0)
,m_viewportX(0)
,m_viewportY(0)
,m_viewportWidth(0)
,m_viewportHeight(0)
,m_gridWidth(DEFAULT_GRIDWIDTH)
,m_hSpacing(DEFAULT_HSPACING)
,m_vSpacing(DEFAULT_VSPACING)
,m_alignTop(true)
,m_currentRow(0)
,m_currentCol(0)
,m_editRow(-1)
,m_editCol(-1)
,m_editor(NULL)
,m_font(NULL)
,m_minRowHeight(0)
,m_needLayout(true)
,m_hasFocus(false)
{
}



bool FXXTable::isEditable() const {
return (options & TABLE_READONLY)==0;
}



void FXXTable::create() {
FXComposite::create();
m_columnHeader->create();
m_horizontalBar->create();
m_verticalBar->create();
m_scrollCorner->create();
m_font->create();
}



void FXXTable::detach() {
m_scrollCorner->detach();
m_verticalBar->detach();
m_horizontalBar->detach();
m_columnHeader->detach();
FXComposite::detach();
m_font->detach();
}



void FXXTable::setNumberOfRows(int i) {
if (m_nrows!=i) {
m_nrows=i;
m_needLayout=true;
}
}



void FXXTable::addColumn(const FXString &s, int size) {
THEMEHEADERITEM *item=new THEMEHEADERITEM(s, NULL, size);
m_columnHeader->appendItem(item);
//m_columnHeader->appendItem(s, NULL, size);
m_ncolumns++;
m_needLayout=true;
releaseAllRowInfos();
update();
}



FXColor FXXTable::getCellBackColor(int r, int c) {
return m_cellBackColor[r & 1][c & 1];
}



void FXXTable::setCellBackColor(int r, int c, FXColor color) {
m_cellBackColor[r & 1][c & 1]=color;
}



FXint FXXTable::getDefaultWidth() {
return 100;
}



FXint FXXTable::getDefaultHeight() {
return 100;
}



void FXXTable::acceptInput(bool notify) {
FXTRACE((150,"%s::acceptInput %p\n",getClassName(),this));
if (m_editor) {
TableCoords pos(m_editRow, m_editCol);
RowInfo *ri;

//abort();
ri=getRowInfo(m_editRow, true);
if (ri) {
ColumnInfo *ci=ri->getColumnInfo(m_editCol);
if (ci) {
setFromEditor(ci, m_editor);
if(notify && target){
target->tryHandle(this, FXSEL(SEL_REPLACED, message), (void*) ci);
}

/* force rebuild of row/column info */
m_needLayout=true;
modifiedRowInfo(pos.row);
updateRow(pos.row);
}
}

releaseEditor(m_editRow, m_editCol, m_editor);
m_editor=NULL;
m_editRow=-1;
m_editCol=-1;
}
}



void FXXTable::cancelInput(bool notify) {
FXTRACE((150,"%s::cancelInput %p\n", getClassName(),this));
if (m_editor) {
TableCoords pos(m_editRow, m_editCol);

releaseEditor(m_editRow, m_editCol, m_editor);
m_editor=NULL;
m_editRow=-1;
m_editCol=-1;
updateCell(pos.row, pos.column);
}
}



void FXXTable::startInput(int r, int c) {
if (m_editor)
cancelInput(false);

if (isEditable()) {
RowInfo *ri;

makePositionVisible(r, c);
ri=getRowInfo(r, true);
if (ri) {
ColumnInfo *ci=ri->getColumnInfo(c);
if (ci) {
FXWindow *ed;

ed=getEditor(r, c);
if (ed) {
ed->position(ci->getX()+m_columnHeader->getPosition(),
ri->getY(),
ci->getWidth(),
ri->getHeight());
m_editRow=r;
m_editCol=c;
m_editor=ed;
ed->setFocus();
}
else {
DBG_ERROR(0, "No editor returned");
}
}
}
}
}



void FXXTable::setCurrentItem(int r, int c, bool notify) {
FXTRACE((150,"%s::setCurrentItem %p\n", getClassName(),this));
acceptInput(notify);
if (r>=0 && r<m_nrows && c>=0 && c<m_ncolumns) {
if (r!=m_currentRow || c!=m_currentCol) {
int pr, pc;

pr=m_currentRow;
pc=m_currentCol;
m_currentRow=r;
m_currentCol=c;

updateCell(pr, pc);
updateCell(r, c);

if (notify && target){
TableCoords pos(m_currentRow, m_currentCol);

target->tryHandle(this, FXSEL(SEL_CHANGED, message), (void*)&pos);
}
}
}
}



void FXXTable::makePositionVisible(int r, int c) {
int cx;
int cw;
bool modified=false;

/* handle row */
if (r==m_firstVisibleRow) {
if (!m_alignTop) {
/* first row without alignTop, so maybe the first line
* is not fully visible, update */
m_alignTop=true;
modified=true;
}
}
else if (r==m_lastVisibleRow) {
if (m_alignTop) {
/* last row with alignTop, so maybe the last line
* is not fully visible, update */
m_alignTop=false;
modified=true;
}
}
else
if (r>m_firstVisibleRow && r<m_lastVisibleRow) {
/* should already be visible */
}
else {
if (r<m_firstVisibleRow) {
/* row is above */
m_alignTop=true;
m_firstVisibleRow=r;
}
else if (r>m_lastVisibleRow) {
/* row is below */
m_alignTop=false;
m_lastVisibleRow=r;
}
modified=true;
}

/* handle column */
cx=m_columnHeader->getItemOffset(c);
cw=m_columnHeader->getItemSize(c);

if (cx>=0 && (cx+cw)<=m_viewportWidth) {
/* column already is visible */
}
else {
if (cx<0 || cw>m_viewportWidth) {
int nx;

nx=cx-m_columnHeader->getPosition();
/* cell begins left of the current viewport */
m_columnHeader->setPosition(-nx);
m_horizontalBar->setPosition(nx);
}
else {
int nx;

nx=cx-m_columnHeader->getPosition()+cw-m_viewportWidth;
m_columnHeader->setPosition(-nx);
m_horizontalBar->setPosition(nx);
}
modified=true;
}

if (modified) {
m_needLayout=true;
update();
}
}



bool FXXTable::isPositionVisible(int r, int c) const {
int cx;
int cw;

if (r<m_firstVisibleRow || r>m_lastVisibleRow)
return false;

/* handle column */
cx=m_columnHeader->getItemOffset(c);
cw=m_columnHeader->getItemSize(c);

if (cx>=m_viewportWidth || (cx+cw)<0)
return false;

return true;
}



void FXXTable::updateCell(int r, int c) {
if (isPositionVisible(r, c)) {
RowInfo *ri=getRowInfo(r, true);
if (ri) {
ColumnInfo *ci=ri->getColumnInfo(c);
if (ci) {
update(ci->getX()+m_columnHeader->getPosition()-m_hSpacing,
ri->getY()-m_vSpacing,
ci->getWidth()+m_hSpacing+m_hSpacing,
ri->getHeight()+m_vSpacing+m_vSpacing);
}
}
}
}



void FXXTable::updateRow(int r) {
std::list<RowInfo*>::iterator it;
RowInfo *ri=NULL;

for (it=m_rows.begin(); it!=m_rows.end(); it++) {
if ((*it)->getRow()==r) {
ri=*it;
break;
}
}
if (ri) {
update(m_viewportX,
ri->getY()-m_vSpacing,
m_viewportWidth,
ri->getHeight()+m_vSpacing+m_vSpacing);
}
}



void FXXTable::layout() {
int hsheight=0;
int vswidth=0;
int contentWidth;
int loop;

contentWidth=m_columnHeader->getTotalSize();

hsheight=m_horizontalBar->getDefaultHeight();
vswidth=m_verticalBar->getDefaultWidth();

m_viewportWidth=width-vswidth-m_marginLeft-m_marginRight;
m_viewportHeight=height-hsheight-m_marginTop-m_marginBottom-m_columnHeader->getDefaultHeight();
m_viewportX=m_marginLeft;
m_viewportY=m_marginTop+m_columnHeader->getDefaultHeight();

/* position column header */
m_columnHeader->position(m_marginLeft, m_marginTop,
m_viewportWidth,
m_columnHeader->getDefaultHeight());

/* handle horizontal bar */
if (m_columnHeader->getTotalSize()>m_viewportWidth) {
m_horizontalBar->position(0, height-hsheight, width-vswidth, hsheight);
m_horizontalBar->setRange(contentWidth);
m_horizontalBar->setPage(m_viewportWidth);
m_horizontalBar->show();
m_horizontalBar->raise();
}
else {
m_horizontalBar->hide();
m_columnHeader->setPosition(0);
}

for (loop=0; loop<2; loop++) {
if (m_alignTop) {
/* first visible row is fixed, determine last visible row */
if (m_viewportHeight) {
int h;
int i;
int y;

h=0;
y=m_columnHeader->getDefaultHeight()+m_marginTop;
i=m_firstVisibleRow;
while(h<m_viewportHeight) {
RowInfo *ri;

m_lastVisibleRow=i;

/* add top spacing and grid */
if (m_showGrid)
h+=m_gridWidth;
h+=m_vSpacing;

/* add height of the row */
ri=getRowInfo(i, false);
if (ri==NULL)
break;

/* layout row (to determine the height) */
layoutRowInfo(ri);
ri->setY(y+h);
h+=ri->getHeight();

/* add bottom spacing and grid */
h+=m_vSpacing;
if (m_showGrid)
h+=m_gridWidth;
i++;
if (i>=m_nrows)
break;
}
}
else
m_lastVisibleRow=m_firstVisibleRow;
break;
}
else {
/* last visible row is fixed, determine first visible row */
if (m_viewportHeight) {
int h;
int i;
int y;

h=m_viewportHeight;
y=m_columnHeader->getDefaultHeight()+m_marginTop;
i=m_lastVisibleRow;
while(h>0) {
RowInfo *ri;

m_firstVisibleRow=i;

/* sub spacing and grid */
if (m_showGrid)
h-=m_gridWidth;
h-=m_vSpacing;

/* sub row height */
ri=getRowInfo(i, false);
if (ri==NULL)
break;

/* layout row (to determine the height) */
layoutRowInfo(ri);

h-=ri->getHeight();
ri->setY(y+h);

/* sub spacing and grid */
h-=m_vSpacing;
if (m_showGrid)
h-=m_gridWidth;

if (i==0)
break;
i--;
}
if (m_firstVisibleRow<0)
m_firstVisibleRow=0;
if (h>(m_vSpacing+(m_showGrid?m_gridWidth:0))) {
/* there is too much room at the top, redo with alignTop */
m_alignTop=true;
m_firstVisibleRow=0;
}
else
break;
}
else {
m_firstVisibleRow=m_lastVisibleRow;
break;
}
}
} /* for */

#if 0
DBG_ERROR(0, "Visible rows: %d to %d (%s)",
m_firstVisibleRow, m_lastVisibleRow,
m_alignTop?"top":"bottom");
#endif

/* release invisible row infos */
releaseInvisibleRowInfos();

/* handle vertical scrollbar */
if ((m_lastVisibleRow-m_firstVisibleRow+1)<m_nrows) {
m_verticalBar->position(width-vswidth, m_columnHeader->getDefaultHeight(),
vswidth, m_viewportHeight);
m_verticalBar->setRange(m_nrows);
m_verticalBar->setPage(m_lastVisibleRow-m_firstVisibleRow+1);
m_verticalBar->setPosition(m_firstVisibleRow);
m_verticalBar->show();
m_verticalBar->raise();
}
else {
m_verticalBar->hide();
}

/* show or hide scroll corner */
if (hsheight && vswidth) {
m_scrollCorner->position(width-vswidth, height-hsheight, vswidth, hsheight);
m_scrollCorner->show();
m_scrollCorner->raise();
}
else {
m_scrollCorner->hide();
}

m_needLayout=false;

/* this is basically hijacking a SEL code, but we need to tell the target that the visible area has changed */
if (target)
target->tryHandle(this, FXSEL(SEL_DOCKED, message), NULL);
}



FXXTable::RowInfo *FXXTable::getRowInfo(int r, bool needCorrectLayout) {
std::list<RowInfo*>::iterator it;
RowInfo *ri=NULL;

for (it=m_rows.begin(); it!=m_rows.end(); it++) {
if ((*it)->getRow()==r) {
ri=*it;
break;
}
}
if (ri) {
/* ri found, return it */
if (ri->isModified()) {
std::list<ColumnInfo*>::iterator cit;

#if 0
DBG_ERROR(0, "Row %d was modified, updating (%s)",
r, needCorrectLayout?"layout":"no layout");
#endif

#if 0
for (cit=ri->getColumnInfos().begin();
cit!=ri->getColumnInfos().end();
cit++)
(*cit)->update();
if (needCorrectLayout)
layoutRowInfo(ri);
#else
int i;

/* don't update: recreate */
for (cit=ri->getColumnInfos().begin();
cit!=ri->getColumnInfos().end();
cit++)
releaseColumnInfo(*cit);
ri->getColumnInfos().clear();

for (i=0; i<m_ncolumns; i++) {
ColumnInfo *ci;

ci=getColumnInfo(r, i);
assert(ci);
ri->addColumnInfo(ci);
}

if (needCorrectLayout)
layoutRowInfo(ri);
#endif
ri->setModified(false);
}
return ri;
}
else {
if (r>=m_nrows)
return NULL;
else {
int i;

/* create row info, fill it with columns */
ri=new RowInfo(this, r);
for (i=0; i<m_ncolumns; i++) {
ColumnInfo *ci;

ci=getColumnInfo(r, i);
assert(ci);
ri->addColumnInfo(ci);
}

m_rows.push_back(ri);
return ri;
}
}
}



void FXXTable::layoutRowInfo(RowInfo *ri) {
int maxHeight=0;
std::list<ColumnInfo*>::iterator it;
int i;

for (it=ri->getColumnInfos().begin(), i=0;
it!=ri->getColumnInfos().end();
it++, i++) {
ColumnInfo *ci=*it;
int h;
int w;
int x;

/* get absolute item position */
x=m_columnHeader->getItemOffset(i)-m_columnHeader->getPosition();
w=m_columnHeader->getItemSize(i);

/* adjust width */
w-=m_hSpacing; /* spacing left */
w-=m_hSpacing; /* spacing right */
if (m_showGrid) {
w-=m_gridWidth; /* grid left */
w-=m_gridWidth; /* grid right */
}

/* adapt X position */
if (m_showGrid)
x+=m_gridWidth;
x+=m_hSpacing;

h=ci->getHeightForWidth(w);
ci->setX(x);
ci->setWidth(w);
if (h>maxHeight)
maxHeight=h;
}

if (m_minRowHeight>0 && maxHeight<m_minRowHeight)
maxHeight=m_minRowHeight;

/* store total height of the row */
ri->setHeight(maxHeight);
}



void FXXTable::releaseRowInfo(FXXTable::RowInfo *ri) {
#if 0
std::list<ColumnInfo*> &ciList=ri->getColumnInfos();
std::list<ColumnInfo*>::iterator cit;

for (cit=ciList.begin();
cit!=ciList.end();
cit++) {
releaseColumnInfo(*cit);
}
#endif
delete ri;
}



void FXXTable::modifiedRowInfo(int r) {
std::list<RowInfo*>::iterator it;

for (it=m_rows.begin(); it!=m_rows.end(); it++) {
if ((*it)->getRow()==r) {
(*it)->setModified(true);
break;
}
}
}



void FXXTable::releaseInvisibleRowInfos() {
bool keepRunning=true;

while(keepRunning) {
std::list<RowInfo*>::iterator it;

keepRunning=false;
for (it=m_rows.begin(); it!=m_rows.end(); it++) {
RowInfo *ri=*it;

if (ri->getRow()<m_firstVisibleRow ||
ri->getRow()>m_lastVisibleRow) {
m_rows.erase(it);
releaseRowInfo(ri);
keepRunning=true;
break;
}
} /* for */
} /* while keepRunning */
}



void FXXTable::releaseAllRowInfos() {
while(m_rows.size()) {
RowInfo *ri=m_rows.back();
m_rows.pop_back();
delete ri;
} /* while */
}



FXXTable::ColumnInfo *FXXTable::getColumnInfo(int r, int c) {
return new ColumnInfo(this, r, c);
}



void FXXTable::releaseColumnInfo(FXXTable::ColumnInfo *ci) {
delete ci;
}



FXWindow *FXXTable::getEditor(int r, int c) {
return NULL;
}



void FXXTable::setFromEditor(FXXTable::ColumnInfo *ci, FXWindow *ed) {
ci->update();
}



void FXXTable::releaseEditor(int r, int c, FXWindow *ed) {
}



int FXXTable::getRowAtY(int y) const {
std::list<RowInfo*>::const_iterator it;

for (it=m_rows.begin(); it!=m_rows.end(); it++) {
if (y>=(*it)->getY() && y<((*it)->getY()+(*it)->getHeight())) {
return (*it)->getRow();
}
}

return -1;
}



int FXXTable::getColumnAtX(int x) const {
int i;

for (i=0; i<m_ncolumns; i++) {
int cx;
int cw;

cx=m_columnHeader->getItemOffset(i);
cw=m_columnHeader->getItemSize(i);
if (x>=cx && x<(cx+cw)) {
return i;
}
}

return -1;
}



void FXXTable::restrictClipRectangle(FXDC &dc, int x, int y, int w, int h) {
const FXRectangle pc=dc.getClipRectangle();
int nx, ny, nw, nh;

/* test left border */
if (pc.x>x)
nx=pc.x;
else
nx=x;

/* test top border */
if (pc.y>y)
ny=pc.y;
else
ny=y;

/* test right border */
if ((pc.x+pc.w)<(x+w))
nw=(pc.x+pc.w)-nx;
else
nw=(x+w)-nx;

/* test bottom border */
if ((pc.y+pc.h)<(y+h))
nh=(pc.y+pc.h)-ny;
else
nh=(y+h)-ny;

dc.setClipRectangle(nx, ny, nw, nh);
}



void FXXTable::drawContents(FXDC &dc) {
int i;
FXRectangle prevClip=dc.getClipRectangle();

restrictClipRectangle(dc, m_viewportX, m_viewportY, m_viewportWidth, m_viewportHeight);
for (i=m_firstVisibleRow;
i<=m_lastVisibleRow;
i++) {
RowInfo *ri;

ri=getRowInfo(i, false); /* TODO: maybe we need TRUE here? */
if (ri==NULL)
break;
else {
if ((ri->getY()>(prevClip.y+prevClip.h)) ||
((ri->getY()+ri->getHeight())<prevClip.y)) {
/* no update needed for this row */
}
else {
std::list<ColumnInfo*>::iterator it;
bool rowIsSelected=isRowSelected(i);

for (it=ri->getColumnInfos().begin();
it!=ri->getColumnInfos().end();
it++) {
ColumnInfo *ci=*it;
int x;

x=ci->getX()+m_columnHeader->getPosition();

if (((x+ci->getWidth())<prevClip.x) ||
(x>(prevClip.x+prevClip.w))) {
/* no update needed for this column */
}
else {
FXRectangle clipBeforeCell=dc.getClipRectangle();

if (rowIsSelected)
dc.setForeground(getSelBackColor());
else {
#if 0
if (ci->getUseSpecialColor()==-1)
dc.setForeground(ci->getBackground());
else if (ci->getUseSpecialColor()==0)
dc.setForeground(getCellBackColor(ri->getRow(), ci->getColumn()));
else
dc.setForeground(m_markColor[ci->getUseSpecialColor()-1][ri->getRow()][ci->getColumn()]);
#else
dc.setForeground(ci->getBackground());
#endif
}
dc.fillRectangle(x-m_hSpacing,
ri->getY()-m_vSpacing,
ci->getWidth()+m_hSpacing*2,
ri->getHeight()+m_vSpacing*2);
restrictClipRectangle(dc,
x, ri->getY(),
ci->getWidth(), ri->getHeight());
ci->draw(dc,
x, ri->getY(),
ci->getWidth(), ri->getHeight());
dc.setClipRectangle(clipBeforeCell);
if (m_hasFocus &&
ci->getColumn()==m_currentCol &&
ri->getRow()==m_currentRow) {
FXuint dcLineWidth=dc.getLineWidth();
FXCapStyle dcCapStyle=dc.getLineCap();

dc.setLineWidth(CURRENT_BORDER_LINE_WIDTH);
dc.setLineCap(CAP_ROUND);
dc.setForeground(m_stippleColor);
dc.drawRectangle(x-1,
ri->getY()-1,
ci->getWidth()+2,
ri->getHeight()+2);
dc.setLineWidth(dcLineWidth);
dc.setLineCap(dcCapStyle);
}
} /* if column needs update */
}
} /* if row needs update */
}
}

dc.setClipRectangle(prevClip);
}



long FXXTable::onPaint(FXObject*, FXSelector, void* ptr){
FXEvent* event=(FXEvent*)ptr;
FXDCWindow dc(this, event);

dc.setForeground(backColor);
dc.fillRectangle(0, 0, width, height);

if (m_needLayout)
layout();
drawContents(dc);

return 1;
}



void FXXTable::rebuild() {
releaseAllRowInfos();
m_needLayout=true;
update();
}


long FXXTable::onHScrollerChanged(FXObject*, FXSelector, void* ptr){
FXint new_pos=-(FXint)(FXival)ptr;

if (new_pos!=m_columnHeader->getPosition()) {
m_columnHeader->setPosition(new_pos);
update();
}

flags&=~FLAG_TIP;
return 1;
}



long FXXTable::onHScrollerDragged(FXObject*, FXSelector, void* ptr){
if (options & TABLE_TRACK_SCROLLBARS) {
FXint new_pos=-(FXint)(FXival)ptr;

if (new_pos!=m_columnHeader->getPosition()) {
m_columnHeader->setPosition(new_pos);
update();
}
}

flags&=~FLAG_TIP;
return 1;
}



long FXXTable::onVScrollerChanged(FXObject*, FXSelector, void* ptr){
FXint new_pos=(FXint)(FXival)ptr;

if (new_pos<m_firstVisibleRow) {
/* new pos is above, fix at top */
m_alignTop=true;
m_firstVisibleRow=new_pos;
layout();
update();
}
else if (new_pos>m_firstVisibleRow) {
/* new pos is below, fix at bottom */
m_alignTop=false;
m_lastVisibleRow+=(new_pos-m_firstVisibleRow);
if (m_lastVisibleRow>=m_nrows)
m_lastVisibleRow=m_nrows-1;
if (m_lastVisibleRow<0)
m_lastVisibleRow=0;
m_needLayout=true;
update();
}

flags&=~FLAG_TIP;
return 1;
}



long FXXTable::onVScrollerDragged(FXObject*, FXSelector, void* ptr){
if (options & TABLE_TRACK_SCROLLBARS) {
FXint new_pos=(FXint)(FXival)ptr;

if (new_pos<m_firstVisibleRow) {
/* new pos is above, fix at top */
m_alignTop=true;
m_firstVisibleRow=new_pos;
layout();
update();
}
else if (new_pos>m_firstVisibleRow) {
/* new pos is below, fix at bottom */
m_alignTop=false;
m_lastVisibleRow+=(new_pos-m_firstVisibleRow);
if (m_lastVisibleRow>=m_nrows)
m_lastVisibleRow=m_nrows-1;
if (m_lastVisibleRow<0)
m_lastVisibleRow=0;
m_needLayout=true;
update();
}
}

flags&=~FLAG_TIP;
return 1;
}



long FXXTable::onHeaderItemDragged(FXObject*, FXSelector, void* ptr) {
//FXint i=(FXint)(FXival)ptr;

m_needLayout=true;
update();
return 1;
}



long FXXTable::onHeaderItemClicked(FXObject*, FXSelector, void* ptr) {
FXint idx=(FXint)(FXival)ptr;
FXbool b;
int i;

b=m_columnHeader->getArrowDir(idx);
for (i=0; i<m_columnHeader->getNumItems(); i++)
m_columnHeader->setArrowDir(i, MAYBE);

if (b==TRUE) {
/* was up, becomes down */
m_columnHeader->setArrowDir(idx, FALSE);
sortByColumn(idx, false);
}
else if (b==FALSE) {
/* was down, becomes MAYBE */
m_columnHeader->setArrowDir(idx, MAYBE);
}
else {
/* was MAYBE, becomes up */
m_columnHeader->setArrowDir(idx, TRUE);
sortByColumn(idx, true);
}
return 1;
}



long FXXTable::onFocusIn(FXObject *sender, FXSelector sel, void* ptr) {
FXTRACE((150,"%s::onFocusIn %p\n", getClassName(),this));
FXComposite::onFocusIn(sender, sel, ptr);
m_hasFocus=true;
updateRow(m_currentRow);
return 1;
}



long FXXTable::onFocusOut(FXObject *sender, FXSelector sel, void* ptr) {
FXTRACE((150,"%s::onFocusOut %p\n", getClassName(),this));
FXComposite::onFocusOut(sender, sel, ptr);
m_hasFocus=false;
updateRow(m_currentRow);
return 1;
}



long FXXTable::onKeyPress(FXObject *sender, FXSelector sel, void* ptr) {
FXEvent* event=(FXEvent*)ptr;
flags&=~FLAG_TIP;

FXTRACE((150,"%s::onKeyPress %p\n", getClassName(),this));
// Bounce to focus widget
if (getFocus() && getFocus()->handle(sender,sel,ptr)) {
return 1;
}

if (!isEnabled()) {
return 0;
}

// Try target first
if (target && target->tryHandle(this,FXSEL(SEL_KEYPRESS, message), ptr))
return 1;

// Eat keystroke
switch(event->code){
case KEY_Home:
case KEY_KP_Home:
if (!(event->state & SHIFTMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_DESELECT_ALL), NULL);
}
if (!(event->state & CONTROLMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_MOVE_TOP), NULL);
}
if (event->state & SHIFTMASK){
handle(this,FXSEL(SEL_COMMAND, ID_EXTEND),NULL);
}
else{
handle(this, FXSEL(SEL_COMMAND, ID_MARK), NULL);
}
return 1;

case KEY_End:
case KEY_KP_End:
if (!(event->state & SHIFTMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_DESELECT_ALL), NULL);
}
if (!(event->state & CONTROLMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_MOVE_BOTTOM), NULL);
}
if (event->state & SHIFTMASK){
handle(this,FXSEL(SEL_COMMAND, ID_EXTEND),NULL);
}
else{
handle(this, FXSEL(SEL_COMMAND, ID_MARK), NULL);
}
return 1;

case KEY_Page_Up:
case KEY_KP_Page_Up:
if (!(event->state & SHIFTMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_DESELECT_ALL), NULL);
}
if (!(event->state & CONTROLMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_MOVE_PAGEUP), NULL);
}
if (event->state & SHIFTMASK){
handle(this,FXSEL(SEL_COMMAND, ID_EXTEND),NULL);
}
else{
handle(this, FXSEL(SEL_COMMAND, ID_MARK), NULL);
}
return 1;

case KEY_Page_Down:
case KEY_KP_Page_Down:
if (!(event->state & SHIFTMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_DESELECT_ALL), NULL);
}
if (!(event->state & CONTROLMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_MOVE_PAGEDOWN), NULL);
}
if (event->state & SHIFTMASK){
handle(this,FXSEL(SEL_COMMAND, ID_EXTEND),NULL);
}
else{
handle(this, FXSEL(SEL_COMMAND, ID_MARK), NULL);
}
return 1;

case KEY_Up:
case KEY_KP_Up:
if (!(event->state & SHIFTMASK) &&
!(event->state & CONTROLMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_DESELECT_ALL), NULL);
}
handle(this, FXSEL(SEL_COMMAND, ID_MOVE_UP), NULL);
if (event->state & SHIFTMASK)
handle(this,FXSEL(SEL_COMMAND, ID_EXTEND),NULL);
return 1;

case KEY_Down:
case KEY_KP_Down:
if (!(event->state & SHIFTMASK) &&
!(event->state & CONTROLMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_DESELECT_ALL), NULL);
}
handle(this, FXSEL(SEL_COMMAND, ID_MOVE_DOWN), NULL);
if (event->state & SHIFTMASK)
handle(this,FXSEL(SEL_COMMAND, ID_EXTEND),NULL);
return 1;

case KEY_Tab:
handle(this, FXSEL(SEL_COMMAND, ID_DESELECT_ALL), NULL);
if (event->state & SHIFTMASK)
handle(this, FXSEL(SEL_COMMAND, ID_MOVE_LEFT), NULL);
else
handle(this, FXSEL(SEL_COMMAND, ID_MOVE_RIGHT), NULL);
handle(this, FXSEL(SEL_COMMAND, ID_MARK), NULL);
return 1;

case KEY_Right:
case KEY_KP_Right:
if (!(event->state & SHIFTMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_DESELECT_ALL), NULL);
}
if (!(event->state & CONTROLMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_MOVE_RIGHT), NULL);
}
if (event->state & SHIFTMASK){
handle(this,FXSEL(SEL_COMMAND, ID_EXTEND),NULL);
}
else{
handle(this, FXSEL(SEL_COMMAND, ID_MARK), NULL);
}
return 1;

case KEY_Left:
case KEY_KP_Left:
if (!(event->state & SHIFTMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_DESELECT_ALL), NULL);
}
if (!(event->state & CONTROLMASK)){
handle(this, FXSEL(SEL_COMMAND, ID_MOVE_LEFT), NULL);
}
if (event->state & SHIFTMASK){
handle(this,FXSEL(SEL_COMMAND, ID_EXTEND),NULL);
}
else{
handle(this, FXSEL(SEL_COMMAND, ID_MARK), NULL);
}
return 1;

case KEY_Return:
case KEY_KP_Enter:
case KEY_F2:
handle(this, FXSEL(SEL_COMMAND, ID_START_INPUT), NULL);
return 1;

case KEY_Escape:
handle(this,FXSEL(SEL_COMMAND,ID_CANCEL_INPUT),NULL);
return 1;

case KEY_a:
if (!(event->state & CONTROLMASK))
goto ins;
handle(this, FXSEL(SEL_COMMAND, ID_SELECT_ALL), NULL);
return 1;

case KEY_space:
if (!(event->state & CONTROLMASK))
goto ins;
if (isRowSelected(m_currentRow)) {
handle(this, FXSEL(SEL_COMMAND, ID_UNEXTEND), NULL);
}
else {
handle(this, FXSEL(SEL_COMMAND, ID_EXTEND), NULL);
}
return 1;

default:
ins:
if ((event->state & (CONTROLMASK|ALTMASK)) ||
((FXuchar)event->text[0]<32))
return 0;
handle(this, FXSEL(SEL_COMMAND, ID_START_INPUT), NULL);
if (getFocus() && getFocus()->handle(sender, sel, ptr))
return 1;
return 1;
} /* switch */

return 0;
}



long FXXTable::onKeyRelease(FXObject* sender, FXSelector sel, void* ptr) {
FXEvent* event=(FXEvent*)ptr;

// Bounce to focus widget
if (getFocus() && getFocus()->handle(sender, sel, ptr))
return 1;
if (!isEnabled())
return 0;
flags|=FLAG_UPDATE;

// Try target first
if (target && target->tryHandle(this, FXSEL(SEL_KEYRELEASE, message), ptr))
return 1;

// Eat keystroke
switch(event->code){
case KEY_Home:
case KEY_KP_Home:
case KEY_End:
case KEY_KP_End:
case KEY_Page_Up:
case KEY_KP_Page_Up:
case KEY_Page_Down:
case KEY_KP_Page_Down:
case KEY_Left:
case KEY_KP_Left:
case KEY_Right:
case KEY_KP_Right:
case KEY_Up:
case KEY_KP_Up:
case KEY_Down:
case KEY_KP_Down:
case KEY_Tab:
return 1;
case KEY_Return:
case KEY_KP_Enter:
case KEY_Escape:
case KEY_F2:
return 1;
case KEY_a:
return 1;
default:
if ((event->state & (CONTROLMASK | ALTMASK)) || ((FXuchar)event->text[0]<32))
return 0;
return 1;
}
return 0;
}



long FXXTable::onLeftBtnPress(FXObject*, FXSelector, void* ptr) {
FXEvent* event=(FXEvent*)ptr;

flags&=~FLAG_TIP;
handle(this, FXSEL(SEL_FOCUS_SELF, 0), ptr);

if (isEnabled()){
int row;
int col;

grab();
if (target && target->tryHandle(this, FXSEL(SEL_LEFTBUTTONPRESS, message), ptr))
return 1;

row=getRowAtY(event->win_y);
col=getColumnAtX(event->win_x);

if (row<0 || row>=m_nrows || col<0 || col>=m_ncolumns) {
setCurrentItem(m_currentRow, m_currentCol, true);
makePositionVisible(m_currentRow, m_currentCol);
return 1;
}

if (row!=m_currentRow || col!=m_currentCol) {
if (event->state & SHIFTMASK) {
m_selection.addRange(row, row);
updateRow(row);
modifiedRowInfo(row);
if (target)
target->tryHandle(this, FXSEL(SEL_SELECTED, message), NULL);
}
else {
unselectAll();
m_selection.clear();
m_selection.addRange(row, row);
updateRow(row);
modifiedRowInfo(row);
modifiedRowInfo(m_currentRow);
if (target)
target->tryHandle(this, FXSEL(SEL_SELECTED, message), NULL);
}
m_needLayout=true;

setCurrentItem(row, col, true);
makePositionVisible(m_currentRow, m_currentCol);
}

flags|=FLAG_PRESSED;
return 1;
}

return 0;
}



long FXXTable::onLeftBtnRelease(FXObject*, FXSelector, void* ptr) {
FXEvent* event=(FXEvent*)ptr;

if (isEnabled()){
TableCoords pos(m_currentRow, m_currentCol);

ungrab();

if (target) {
if (event->click_count==1){
target->tryHandle(this, FXSEL(SEL_CLICKED, message), (void*)&pos);
}
else if(event->click_count==2){
if (target==NULL || target->tryHandle(this, FXSEL(SEL_DOUBLECLICKED, message), (void*)&pos)==0)
handle(this, FXSEL(SEL_DOUBLECLICKED, ID_TABLEITEM), (void*)&pos);
}
else if(event->click_count==3){
target->tryHandle(this, FXSEL(SEL_TRIPLECLICKED, message), (void*)&pos);
}

target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)&pos);
}

flags&=~FLAG_PRESSED;
flags|=FLAG_UPDATE;
return 1;
}

return 0;
}



long FXXTable::onRightBtnPress(FXObject*, FXSelector, void* ptr) {
FXEvent* event=(FXEvent*)ptr;

flags&=~FLAG_TIP;
handle(this, FXSEL(SEL_FOCUS_SELF, 0), ptr);

if (isEnabled()){
if (target && target->tryHandle(this, FXSEL(SEL_RIGHTBUTTONPRESS, message), ptr))
return 1;
else {
int row;
int col;

row=getRowAtY(event->win_y);
col=getColumnAtX(event->win_x);

if (row<0 || row>=m_nrows || col<0 || col>=m_ncolumns) {
setCurrentItem(m_currentRow, m_currentCol, true);
makePositionVisible(m_currentRow, m_currentCol);
return 1;
}
setCurrentItem(row, col, true);
makePositionVisible(m_currentRow, m_currentCol);
return 1;
}
}

return 0;
}



long FXXTable::onRightBtnRelease(FXObject*, FXSelector, void* ptr) {
if (isEnabled()){
if (target && target->tryHandle(this, FXSEL(SEL_RIGHTBUTTONRELEASE, message), ptr))
return 1;

flags&=~FLAG_PRESSED;
flags|=FLAG_UPDATE;
return 1;
}

return 0;
}



long FXXTable::onMouseWheel(FXObject *sender, FXSelector sel, void *ptr) {
return m_verticalBar->handle(sender, sel, ptr);
}


long FXXTable::onCmdMoveRight(FXObject*,FXSelector,void*) {
if (m_nrows<1 || m_ncolumns<1)
return 1;
if (m_currentCol>(m_ncolumns-2))
return 1;

setCurrentItem(m_currentRow, m_currentCol+1, TRUE);
makePositionVisible(m_currentRow, m_currentCol);
return 1;
}



long FXXTable::onCmdMoveLeft(FXObject*,FXSelector,void*) {
if (m_nrows<1 || m_ncolumns<1)
return 1;
if (m_currentCol<1)
return 1;

setCurrentItem(m_currentRow, m_currentCol-1, TRUE);
makePositionVisible(m_currentRow, m_currentCol);
return 1;
}



long FXXTable::onCmdMoveUp(FXObject*,FXSelector,void*) {
if (m_nrows<1 || m_ncolumns<1)
return 1;
if (m_currentRow<1)
return 1;

//if (isRowSelected(m_currentRow))
updateRow(m_currentRow);

setCurrentItem(m_currentRow-1, m_currentCol, TRUE);
makePositionVisible(m_currentRow, m_currentCol);
return 1;
}



long FXXTable::onCmdMoveDown(FXObject*,FXSelector,void*) {
if (m_nrows<1 || m_ncolumns<1)
return 1;
if (m_currentRow>(m_nrows-2))
return 1;

//if (isRowSelected(m_currentRow))
updateRow(m_currentRow);

setCurrentItem(m_currentRow+1, m_currentCol, TRUE);
makePositionVisible(m_currentRow, m_currentCol);
return 1;
}



long FXXTable::onCmdMoveTop(FXObject*,FXSelector,void*) {
if (m_nrows<1 || m_ncolumns<1)
return 1;
setCurrentItem(0, m_currentCol, TRUE);
makePositionVisible(m_currentRow, m_currentCol);
return 1;
}



long FXXTable::onCmdMoveBottom(FXObject*,FXSelector,void*) {
if (m_nrows<1 || m_ncolumns<1)
return 1;
setCurrentItem(m_nrows-1, m_currentCol, TRUE);
makePositionVisible(m_currentRow, m_currentCol);
return 1;
}



long FXXTable::onCmdMovePageDown(FXObject*,FXSelector,void*) {
if (m_lastVisibleRow==(m_nrows-1)) {
setCurrentItem((m_nrows-1), m_currentCol, true);
makePositionVisible((m_nrows-1), m_currentCol);
updateRow(m_nrows-1);
}
else {
m_firstVisibleRow=m_lastVisibleRow;
m_alignTop=true;
layout();
setCurrentItem(m_firstVisibleRow, m_currentCol);
update();
}
return 1;
}



long FXXTable::onCmdMovePageUp(FXObject*,FXSelector,void*) {
if (m_firstVisibleRow==0) {
setCurrentItem(0, m_currentCol, true);
makePositionVisible(0, m_currentCol);
updateRow(0);
}
else {
m_lastVisibleRow=m_firstVisibleRow;
m_alignTop=false;
layout();
setCurrentItem(m_lastVisibleRow, m_currentCol, true);
update();
}
return 1;
}



long FXXTable::onCmdStartInput(FXObject*, FXSelector, void*) {
if (isEditable()){
startInput(m_currentRow, m_currentCol);
}
else{
getApp()->beep();
}
return 1;
}



long FXXTable::onCmdMark(FXObject*, FXSelector, void*) {
unselectAll();

m_selection.clear();
m_selection.addRange(m_currentRow, m_currentRow);

m_needLayout=true;
modifiedRowInfo(m_currentRow);
updateRow(m_currentRow);

if (target)
target->tryHandle(this, FXSEL(SEL_SELECTED, message), NULL);

return 1;
}



long FXXTable::onCmdExtend(FXObject*, FXSelector, void*) {
m_selection.addRange(m_currentRow, m_currentRow);
m_needLayout=true;
modifiedRowInfo(m_currentRow);
updateRow(m_currentRow);

if (target)
target->tryHandle(this, FXSEL(SEL_SELECTED, message), NULL);

return 1;
}



long FXXTable::onCmdUnextend(FXObject*, FXSelector, void*) {
m_selection.delRange(m_currentRow, m_currentRow);
m_needLayout=true;
modifiedRowInfo(m_currentRow);
updateRow(m_currentRow);

if (target)
target->tryHandle(this, FXSEL(SEL_SELECTED, message), NULL);

return 1;
}



long FXXTable::onCmdDeselectAll(FXObject*, FXSelector, void*) {
unselectAll();
m_selection.clear();
if (target)
target->tryHandle(this, FXSEL(SEL_SELECTED, message), NULL);
return 1;
}



long FXXTable::onDoubleClicked(FXObject*, FXSelector, void*) {
handle(this, FXSEL(SEL_COMMAND, ID_START_INPUT), NULL);
return 1;
}



long FXXTable::onEditorKeyPress(FXObject *sender, FXSelector sel, void* ptr) {
FXEvent* event=(FXEvent*)ptr;
flags&=~FLAG_TIP;

if (!isEnabled()) {
return 0;
}

#if 0
if (target && target->tryHandle(this,FXSEL(SEL_KEYPRESS, message), ptr))
return 1;
#endif

// Eat keystroke
switch(event->code){
case KEY_Escape:
cancelInput(true);
return 1;

default:
break;
} /* switch */

return 0;
}



long FXXTable::onEditorKeyRelease(FXObject* sender, FXSelector sel, void* ptr) {
FXEvent* event=(FXEvent*)ptr;

// Bounce to focus widget
if (getFocus() && getFocus()->handle(sender, sel, ptr))
return 1;
if (!isEnabled())
return 0;
flags|=FLAG_UPDATE;

#if 0
// Try target first
if (target && target->tryHandle(this, FXSEL(SEL_KEYRELEASE, message), ptr))
return 1;
#endif

// Eat keystroke
switch(event->code){
case KEY_Escape:
return 1;
default:
break;
}
return 0;
}



bool FXXTable::isRowSelected(int r) {
return m_selection.contains(r);
}



void FXXTable::unselectAll() {
const SelectionRange *r;

r=m_selection.getFirstRange();
while(r) {
int i;

m_needLayout=true;
for (i=r->first; i<=r->last; i++) {
updateRow(i);
modifiedRowInfo(i);
}

r=r->next;
}

if (target)
target->tryHandle(this, FXSEL(SEL_SELECTED, message), NULL);
}



int FXXTable::getHeaderSize(int idx) const {
return m_columnHeader->getItemSize(idx);
}



void FXXTable::setHeaderSize(int idx, int size) {
m_columnHeader->setItemSize(idx, size);
}



FXint FXXTable::getNumHeaders() const {
return m_columnHeader->getNumItems();
}



FXbool FXXTable::getHeaderArrowDir(int idx) {
return m_columnHeader->getArrowDir(idx);
}



void FXXTable::setHeaderArrowDir(int idx, FXbool b) {
m_columnHeader->setArrowDir(idx, b);
}



void FXXTable::clearItems() {
m_needLayout=true;
m_selection.clear();
releaseAllRowInfos();
update();
if (target)
target->tryHandle(this, FXSEL(SEL_SELECTED, message), NULL);
}



void FXXTable::sortByColumn(int idx, bool up) {
}



void FXXTable::sort() {
int i;

for (i=0; i<getNumHeaders(); i++) {
FXbool b;

b=getHeaderArrowDir(i);
if (b!=MAYBE) {
sortByColumn(i, b);
break;
}
}
}



FXColor FXXTable::getMarkColor(int idx) const {
if (idx>=NUM_MARK_COLOR)
return FXColor();
return m_markColor[idx][0][0];
}



void FXXTable::setMarkColor(int idx, FXColor c) {
if (idx<NUM_MARK_COLOR) {
m_markColor[idx][0][0]=c;
m_markColor[idx][0][1]=c;
m_markColor[idx][1][0]=c;
m_markColor[idx][1][1]=c;
}
}



FXColor FXXTable::getMarkColor(int idx, int r, int c) const {
if (idx>=NUM_MARK_COLOR)
return FXColor();
return m_markColor[idx][r & 1][c & 1];
}


void FXXTable::setMarkColor(int idx, FXColor col, int r, int c) {
if (idx<NUM_MARK_COLOR)
m_markColor[idx][r & 1][c & 1]=col;
}



bool FXXTable::canFocus() const {
return TRUE;
}


void FXXTable::setFocus(){
FXComposite::setFocus();
setDefault(TRUE);
}


// Out of focus chain
void FXXTable::killFocus(){
FXTRACE((150,"%s::killFocus %p\n", getClassName(),this));
FXComposite::killFocus();
setDefault(MAYBE);
acceptInput(TRUE);
}



(18-18/27)