|
/***************************************************************************
|
|
* 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);
|
|
}
|
|
|
|
|
|
|