mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-19 04:20:27 +01:00
1235 lines
28 KiB
C
1235 lines
28 KiB
C
|
|
|
|
#include <WINGs/WINGsP.h>
|
|
#include <X11/cursorfont.h>
|
|
|
|
#include "wtableview.h"
|
|
|
|
|
|
const char *WMTableViewSelectionDidChangeNotification = "WMTableViewSelectionDidChangeNotification";
|
|
|
|
|
|
struct W_TableColumn {
|
|
WMTableView *table;
|
|
WMWidget *titleW;
|
|
char *title;
|
|
int width;
|
|
int minWidth;
|
|
int maxWidth;
|
|
|
|
void *id;
|
|
|
|
WMTableColumnDelegate *delegate;
|
|
|
|
unsigned resizable:1;
|
|
unsigned editable:1;
|
|
};
|
|
|
|
|
|
|
|
static void handleResize(W_ViewDelegate *self, WMView *view);
|
|
|
|
static void rearrangeHeader(WMTableView *table);
|
|
|
|
static WMRange rowsInRect(WMTableView *table, WMRect rect);
|
|
|
|
|
|
WMTableColumn *WMCreateTableColumn(char *title)
|
|
{
|
|
WMTableColumn *col = wmalloc(sizeof(WMTableColumn));
|
|
|
|
col->table = NULL;
|
|
col->titleW = NULL;
|
|
col->width = 50;
|
|
col->minWidth = 5;
|
|
col->maxWidth = 0;
|
|
|
|
col->id = NULL;
|
|
|
|
col->title = wstrdup(title);
|
|
|
|
col->delegate = NULL;
|
|
|
|
col->resizable = 1;
|
|
col->editable = 0;
|
|
|
|
return col;
|
|
}
|
|
|
|
|
|
void WMSetTableColumnId(WMTableColumn *column, void *id)
|
|
{
|
|
column->id = id;
|
|
}
|
|
|
|
|
|
void *WMGetTableColumnId(WMTableColumn *column)
|
|
{
|
|
return column->id;
|
|
}
|
|
|
|
|
|
void WMSetTableColumnWidth(WMTableColumn *column, unsigned width)
|
|
{
|
|
if (column->maxWidth == 0)
|
|
column->width = WMAX(column->minWidth, width);
|
|
else
|
|
column->width = WMAX(column->minWidth, WMIN(column->maxWidth, width));
|
|
|
|
if (column->table) {
|
|
rearrangeHeader(column->table);
|
|
}
|
|
}
|
|
|
|
|
|
void WMSetTableColumnDelegate(WMTableColumn *column,
|
|
WMTableColumnDelegate *delegate)
|
|
{
|
|
column->delegate = delegate;
|
|
}
|
|
|
|
|
|
void WMSetTableColumnConstraints(WMTableColumn *column,
|
|
unsigned minWidth, unsigned maxWidth)
|
|
{
|
|
wassertr(maxWidth == 0 || minWidth <= maxWidth);
|
|
|
|
column->minWidth = minWidth;
|
|
column->maxWidth = maxWidth;
|
|
|
|
if (column->width < column->minWidth)
|
|
WMSetTableColumnWidth(column, column->minWidth);
|
|
else if (column->width > column->maxWidth && column->maxWidth != 0)
|
|
WMSetTableColumnWidth(column, column->maxWidth);
|
|
}
|
|
|
|
|
|
void WMSetTableColumnEditable(WMTableColumn *column, Bool flag)
|
|
{
|
|
column->editable = flag;
|
|
}
|
|
|
|
|
|
WMTableView *WMGetTableColumnTableView(WMTableColumn *column)
|
|
{
|
|
return column->table;
|
|
}
|
|
|
|
|
|
|
|
struct W_TableView {
|
|
W_Class widgetClass;
|
|
WMView *view;
|
|
|
|
WMFrame *header;
|
|
|
|
WMLabel *corner;
|
|
|
|
WMScroller *hscroll;
|
|
WMScroller *vscroll;
|
|
WMView *tableView;
|
|
|
|
WMPixmap *viewBuffer;
|
|
|
|
WMArray *columns;
|
|
WMArray *splitters;
|
|
|
|
WMArray *selectedRows;
|
|
|
|
int tableWidth;
|
|
|
|
int rows;
|
|
|
|
WMColor *backColor;
|
|
|
|
GC gridGC;
|
|
WMColor *gridColor;
|
|
|
|
Cursor splitterCursor;
|
|
|
|
void *dataSource;
|
|
|
|
WMTableViewDelegate *delegate;
|
|
|
|
WMAction *action;
|
|
void *clientData;
|
|
|
|
void *clickedColumn;
|
|
int clickedRow;
|
|
|
|
int editingRow;
|
|
|
|
unsigned headerHeight;
|
|
|
|
unsigned rowHeight;
|
|
|
|
unsigned dragging:1;
|
|
unsigned drawsGrid:1;
|
|
unsigned canSelectRow:1;
|
|
unsigned canSelectMultiRows:1;
|
|
unsigned canDeselectRow:1;
|
|
};
|
|
|
|
static W_Class tableClass = 0;
|
|
|
|
|
|
static W_ViewDelegate viewDelegate = {
|
|
NULL,
|
|
NULL,
|
|
handleResize,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void handleEvents(XEvent *event, void *data);
|
|
static void handleTableEvents(XEvent *event, void *data);
|
|
static void repaintTable(WMTableView *table);
|
|
|
|
static WMSize getTotalSize(WMTableView *table)
|
|
{
|
|
WMSize size;
|
|
int i;
|
|
|
|
/* get width from columns */
|
|
size.width = 0;
|
|
for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
|
|
WMTableColumn *column;
|
|
|
|
column = WMGetFromArray(table->columns, i);
|
|
|
|
size.width += column->width;
|
|
}
|
|
|
|
/* get height from rows */
|
|
size.height = table->rows * table->rowHeight;
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
static WMRect getVisibleRect(WMTableView *table)
|
|
{
|
|
WMSize size = getTotalSize(table);
|
|
WMRect rect;
|
|
|
|
rect.size.height = size.height * WMGetScrollerKnobProportion(table->vscroll);
|
|
rect.size.width = size.width * WMGetScrollerKnobProportion(table->hscroll);
|
|
|
|
rect.pos.x = (size.width - rect.size.width) * WMGetScrollerValue(table->hscroll);
|
|
rect.pos.y = (size.height - rect.size.height) * WMGetScrollerValue(table->vscroll);
|
|
|
|
return rect;
|
|
}
|
|
|
|
|
|
static void scrollToPoint(WMTableView *table, int x, int y)
|
|
{
|
|
WMSize size = getTotalSize(table);
|
|
int i;
|
|
float value, prop;
|
|
|
|
if (size.width > W_VIEW_WIDTH(table->tableView)) {
|
|
prop = (float)W_VIEW_WIDTH(table->tableView) / (float)size.width;
|
|
value = (float)x / (float)(size.width - W_VIEW_WIDTH(table->tableView));
|
|
} else {
|
|
prop = 1.0;
|
|
value = 0.0;
|
|
}
|
|
WMSetScrollerParameters(table->hscroll, value, prop);
|
|
|
|
|
|
if (size.height > W_VIEW_HEIGHT(table->tableView)) {
|
|
prop = (float)W_VIEW_HEIGHT(table->tableView) / (float)size.height;
|
|
value = (float)y / (float)(size.height - W_VIEW_HEIGHT(table->tableView));
|
|
} else {
|
|
prop = 1.0;
|
|
value = 0.0;
|
|
}
|
|
|
|
WMSetScrollerParameters(table->vscroll, value, prop);
|
|
|
|
|
|
|
|
if (table->editingRow >= 0) {
|
|
for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
|
|
WMTableColumn *column;
|
|
|
|
column = WMGetFromArray(table->columns, i);
|
|
|
|
if (column->delegate && column->delegate->beginCellEdit)
|
|
(*column->delegate->beginCellEdit)(column->delegate, column,
|
|
table->editingRow);
|
|
}
|
|
}
|
|
|
|
repaintTable(table);
|
|
}
|
|
|
|
|
|
static void adjustScrollers(WMTableView *table)
|
|
{
|
|
WMSize size = getTotalSize(table);
|
|
WMSize vsize = WMGetViewSize(table->tableView);
|
|
float prop, value;
|
|
float oprop, ovalue;
|
|
|
|
if (size.width <= vsize.width) {
|
|
value = 0.0;
|
|
prop = 1.0;
|
|
} else {
|
|
oprop = WMGetScrollerKnobProportion(table->hscroll);
|
|
if (oprop == 0.0)
|
|
oprop = 1.0;
|
|
ovalue = WMGetScrollerValue(table->hscroll);
|
|
|
|
prop = (float)vsize.width/(float)size.width;
|
|
value = prop*ovalue / oprop;
|
|
}
|
|
WMSetScrollerParameters(table->hscroll, value, prop);
|
|
|
|
if (size.height <= vsize.height) {
|
|
value = 0.0;
|
|
prop = 1.0;
|
|
} else {
|
|
oprop = WMGetScrollerKnobProportion(table->vscroll);
|
|
oprop = WMGetScrollerKnobProportion(table->hscroll);
|
|
if (oprop == 0.0)
|
|
oprop = 1.0;
|
|
ovalue = WMGetScrollerValue(table->vscroll);
|
|
|
|
prop = (float)vsize.height/(float)size.height;
|
|
value = prop*ovalue / oprop;
|
|
}
|
|
WMSetScrollerParameters(table->vscroll, value, prop);
|
|
}
|
|
|
|
|
|
static void doScroll(WMWidget *self, void *data)
|
|
{
|
|
WMTableView *table = (WMTableView*)data;
|
|
float value;
|
|
float vpsize;
|
|
float size;
|
|
WMSize ts = getTotalSize(table);
|
|
|
|
value = WMGetScrollerValue(self);
|
|
|
|
if (table->hscroll == (WMScroller *)self) {
|
|
vpsize = W_VIEW_WIDTH(table->tableView);
|
|
size = ts.width;
|
|
} else {
|
|
vpsize = W_VIEW_HEIGHT(table->tableView);
|
|
size = ts.height;
|
|
}
|
|
|
|
switch (WMGetScrollerHitPart(self)) {
|
|
case WSDecrementWheel:
|
|
case WSDecrementLine:
|
|
value -= (float)table->rowHeight / size;
|
|
if (value < 0)
|
|
value = 0.0;
|
|
WMSetScrollerParameters(self, value,
|
|
WMGetScrollerKnobProportion(self));
|
|
repaintTable(table);
|
|
break;
|
|
|
|
case WSIncrementWheel:
|
|
case WSIncrementLine:
|
|
value += (float)table->rowHeight / size;
|
|
if (value > 1.0)
|
|
value = 1.0;
|
|
WMSetScrollerParameters(self, value,
|
|
WMGetScrollerKnobProportion(self));
|
|
repaintTable(table);
|
|
break;
|
|
|
|
case WSKnob:
|
|
repaintTable(table);
|
|
break;
|
|
|
|
case WSDecrementPage:
|
|
value -= vpsize / size;
|
|
if (value < 0.0)
|
|
value = 0.0;
|
|
WMSetScrollerParameters(self, value,
|
|
WMGetScrollerKnobProportion(self));
|
|
repaintTable(table);
|
|
break;
|
|
|
|
case WSIncrementPage:
|
|
value += vpsize / size;
|
|
if (value > 1.0)
|
|
value = 1.0;
|
|
WMSetScrollerParameters(self, value,
|
|
WMGetScrollerKnobProportion(self));
|
|
repaintTable(table);
|
|
break;
|
|
|
|
|
|
case WSNoPart:
|
|
case WSKnobSlot:
|
|
break;
|
|
}
|
|
|
|
if (table->editingRow >= 0) {
|
|
int i;
|
|
for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
|
|
WMTableColumn *column;
|
|
|
|
column = WMGetFromArray(table->columns, i);
|
|
|
|
if (column->delegate && column->delegate->beginCellEdit)
|
|
(*column->delegate->beginCellEdit)(column->delegate, column,
|
|
table->editingRow);
|
|
}
|
|
}
|
|
|
|
|
|
if (table->hscroll == self) {
|
|
int x = 0;
|
|
int i;
|
|
WMRect rect = getVisibleRect(table);
|
|
|
|
for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
|
|
WMTableColumn *column;
|
|
WMView *splitter;
|
|
|
|
column = WMGetFromArray(table->columns, i);
|
|
|
|
WMMoveWidget(column->titleW, x - rect.pos.x, 0);
|
|
|
|
x += W_VIEW_WIDTH(WMWidgetView(column->titleW)) + 1;
|
|
|
|
splitter = WMGetFromArray(table->splitters, i);
|
|
W_MoveView(splitter, x - rect.pos.x - 1, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void splitterHandler(XEvent *event, void *data)
|
|
{
|
|
WMTableColumn *column = (WMTableColumn*)data;
|
|
WMTableView *table = column->table;
|
|
int done = 0;
|
|
int cx, ox, offsX;
|
|
WMPoint pos;
|
|
WMScreen *scr = WMWidgetScreen(table);
|
|
GC gc = scr->ixorGC;
|
|
Display *dpy = WMScreenDisplay(scr);
|
|
int h = WMWidgetHeight(table) - 22;
|
|
Window w = WMViewXID(table->view);
|
|
|
|
pos = WMGetViewPosition(WMWidgetView(column->titleW));
|
|
|
|
offsX = pos.x + column->width;
|
|
|
|
ox = cx = offsX;
|
|
|
|
XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
|
|
|
|
while (!done) {
|
|
XEvent ev;
|
|
|
|
WMMaskEvent(dpy, ButtonMotionMask|ButtonReleaseMask, &ev);
|
|
|
|
switch (ev.type) {
|
|
case MotionNotify:
|
|
ox = cx;
|
|
|
|
if (column->width + ev.xmotion.x < column->minWidth)
|
|
cx = pos.x + column->minWidth;
|
|
else if (column->maxWidth > 0
|
|
&& column->width + ev.xmotion.x > column->maxWidth)
|
|
cx = pos.x + column->maxWidth;
|
|
else
|
|
cx = offsX + ev.xmotion.x;
|
|
|
|
XDrawLine(dpy, w, gc, ox+20, 0, ox+20, h);
|
|
XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
|
|
break;
|
|
|
|
case ButtonRelease:
|
|
column->width = cx - pos.x;
|
|
done = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
|
|
rearrangeHeader(table);
|
|
repaintTable(table);
|
|
}
|
|
|
|
|
|
static void realizeTable(void *data, WMNotification *notif)
|
|
{
|
|
repaintTable(data);
|
|
}
|
|
|
|
|
|
WMTableView *WMCreateTableView(WMWidget *parent)
|
|
{
|
|
WMTableView *table = wmalloc(sizeof(WMTableView));
|
|
WMScreen *scr = WMWidgetScreen(parent);
|
|
|
|
memset(table, 0, sizeof(WMTableView));
|
|
|
|
if (!tableClass) {
|
|
tableClass = W_RegisterUserWidget();
|
|
}
|
|
table->widgetClass = tableClass;
|
|
|
|
table->view = W_CreateView(W_VIEW(parent));
|
|
if (!table->view)
|
|
goto error;
|
|
table->view->self = table;
|
|
|
|
table->view->delegate = &viewDelegate;
|
|
|
|
table->headerHeight = 20;
|
|
|
|
table->hscroll = WMCreateScroller(table);
|
|
WMSetScrollerAction(table->hscroll, doScroll, table);
|
|
WMMoveWidget(table->hscroll, 1, 2+table->headerHeight);
|
|
WMMapWidget(table->hscroll);
|
|
|
|
table->vscroll = WMCreateScroller(table);
|
|
WMSetScrollerArrowsPosition(table->vscroll, WSAMaxEnd);
|
|
WMSetScrollerAction(table->vscroll, doScroll, table);
|
|
WMMoveWidget(table->vscroll, 1, 2+table->headerHeight);
|
|
WMMapWidget(table->vscroll);
|
|
|
|
|
|
table->header = WMCreateFrame(table);
|
|
WMMoveWidget(table->header, 22, 2);
|
|
WMMapWidget(table->header);
|
|
WMSetFrameRelief(table->header, WRFlat);
|
|
|
|
table->corner = WMCreateLabel(table);
|
|
WMResizeWidget(table->corner, 20, table->headerHeight);
|
|
WMMoveWidget(table->corner, 2, 2);
|
|
WMMapWidget(table->corner);
|
|
WMSetLabelRelief(table->corner, WRRaised);
|
|
WMSetWidgetBackgroundColor(table->corner, scr->darkGray);
|
|
|
|
|
|
table->tableView = W_CreateView(table->view);
|
|
if (!table->tableView)
|
|
goto error;
|
|
table->tableView->self = table;
|
|
W_MapView(table->tableView);
|
|
|
|
WMAddNotificationObserver(realizeTable, table, WMViewRealizedNotification,
|
|
table->tableView);
|
|
|
|
table->tableView->flags.dontCompressExpose = 1;
|
|
|
|
table->gridColor = WMCreateNamedColor(scr, "#cccccc", False);
|
|
/* table->gridColor = WMGrayColor(scr);*/
|
|
|
|
{
|
|
XGCValues gcv;
|
|
|
|
table->backColor = WMWhiteColor(scr);
|
|
|
|
gcv.foreground = WMColorPixel(table->gridColor);
|
|
gcv.dashes = 1;
|
|
gcv.line_style = LineOnOffDash;
|
|
table->gridGC = XCreateGC(WMScreenDisplay(scr), W_DRAWABLE(scr),
|
|
GCForeground, &gcv);
|
|
}
|
|
|
|
table->editingRow = -1;
|
|
table->clickedRow = -1;
|
|
|
|
table->drawsGrid = 1;
|
|
table->rowHeight = 16;
|
|
|
|
table->tableWidth = 1;
|
|
|
|
table->columns = WMCreateArray(4);
|
|
table->splitters = WMCreateArray(4);
|
|
|
|
table->selectedRows = WMCreateArray(16);
|
|
|
|
table->splitterCursor = XCreateFontCursor(WMScreenDisplay(scr),
|
|
XC_sb_h_double_arrow);
|
|
|
|
table->canSelectRow = 1;
|
|
|
|
WMCreateEventHandler(table->view, ExposureMask|StructureNotifyMask,
|
|
handleEvents, table);
|
|
|
|
WMCreateEventHandler(table->tableView, ExposureMask|ButtonPressMask|
|
|
ButtonReleaseMask|ButtonMotionMask,
|
|
handleTableEvents, table);
|
|
|
|
WMResizeWidget(table, 50, 50);
|
|
|
|
return table;
|
|
|
|
error:
|
|
if (table->tableView)
|
|
W_DestroyView(table->tableView);
|
|
if (table->view)
|
|
W_DestroyView(table->view);
|
|
wfree(table);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void WMAddTableViewColumn(WMTableView *table, WMTableColumn *column)
|
|
{
|
|
WMScreen *scr = WMWidgetScreen(table);
|
|
|
|
column->table = table;
|
|
|
|
WMAddToArray(table->columns, column);
|
|
|
|
if (!column->titleW) {
|
|
column->titleW = WMCreateLabel(table);
|
|
WMSetLabelRelief(column->titleW, WRRaised);
|
|
WMSetLabelFont(column->titleW, scr->boldFont);
|
|
WMSetLabelTextColor(column->titleW, scr->white);
|
|
WMSetWidgetBackgroundColor(column->titleW, scr->darkGray);
|
|
WMSetLabelText(column->titleW, column->title);
|
|
W_ReparentView(WMWidgetView(column->titleW),
|
|
WMWidgetView(table->header), 0, 0);
|
|
if (W_VIEW_REALIZED(table->view))
|
|
WMRealizeWidget(column->titleW);
|
|
WMMapWidget(column->titleW);
|
|
}
|
|
|
|
{
|
|
WMView *splitter = W_CreateView(WMWidgetView(table->header));
|
|
|
|
W_SetViewBackgroundColor(splitter, WMWhiteColor(scr));
|
|
|
|
if (W_VIEW_REALIZED(table->view))
|
|
W_RealizeView(splitter);
|
|
|
|
W_ResizeView(splitter, 2, table->headerHeight);
|
|
W_MapView(splitter);
|
|
|
|
W_SetViewCursor(splitter, table->splitterCursor);
|
|
WMCreateEventHandler(splitter, ButtonPressMask|ButtonReleaseMask,
|
|
splitterHandler, column);
|
|
|
|
WMAddToArray(table->splitters, splitter);
|
|
}
|
|
|
|
rearrangeHeader(table);
|
|
}
|
|
|
|
|
|
void WMSetTableViewHeaderHeight(WMTableView *table, unsigned height)
|
|
{
|
|
table->headerHeight = height;
|
|
|
|
handleResize(NULL, table->view);
|
|
}
|
|
|
|
|
|
void WMSetTableViewDelegate(WMTableView *table, WMTableViewDelegate *delegate)
|
|
{
|
|
table->delegate = delegate;
|
|
}
|
|
|
|
|
|
void WMSetTableViewAction(WMTableView *table, WMAction *action, void *clientData)
|
|
{
|
|
table->action = action;
|
|
|
|
table->clientData = clientData;
|
|
}
|
|
|
|
|
|
void *WMGetTableViewClickedColumn(WMTableView *table)
|
|
{
|
|
return table->clickedColumn;
|
|
}
|
|
|
|
|
|
int WMGetTableViewClickedRow(WMTableView *table)
|
|
{
|
|
return table->clickedRow;
|
|
}
|
|
|
|
|
|
WMArray *WMGetTableViewSelectedRows(WMTableView *table)
|
|
{
|
|
return table->selectedRows;
|
|
}
|
|
|
|
|
|
WMView *WMGetTableViewDocumentView(WMTableView *table)
|
|
{
|
|
return table->tableView;
|
|
}
|
|
|
|
|
|
void *WMTableViewDataForCell(WMTableView *table, WMTableColumn *column,
|
|
int row)
|
|
{
|
|
return (*table->delegate->valueForCell)(table->delegate, column, row);
|
|
}
|
|
|
|
|
|
void WMSetTableViewDataForCell(WMTableView *table, WMTableColumn *column,
|
|
int row, void *data)
|
|
{
|
|
(*table->delegate->setValueForCell)(table->delegate, column, row, data);
|
|
}
|
|
|
|
|
|
WMRect WMTableViewRectForCell(WMTableView *table, WMTableColumn *column,
|
|
int row)
|
|
{
|
|
WMRect rect;
|
|
int i;
|
|
|
|
rect.pos.x = 0;
|
|
rect.pos.y = row * table->rowHeight;
|
|
rect.size.height = table->rowHeight;
|
|
|
|
for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
|
|
WMTableColumn *col;
|
|
col = WMGetFromArray(table->columns, i);
|
|
|
|
if (col == column) {
|
|
rect.size.width = col->width;
|
|
break;
|
|
}
|
|
|
|
rect.pos.x += col->width;
|
|
}
|
|
|
|
{
|
|
WMRect r = getVisibleRect(table);
|
|
|
|
rect.pos.y -= r.pos.y;
|
|
rect.pos.x -= r.pos.x;
|
|
}
|
|
|
|
return rect;
|
|
}
|
|
|
|
|
|
void WMSetTableViewDataSource(WMTableView *table, void *source)
|
|
{
|
|
table->dataSource = source;
|
|
}
|
|
|
|
|
|
void *WMGetTableViewDataSource(WMTableView *table)
|
|
{
|
|
return table->dataSource;
|
|
}
|
|
|
|
|
|
void WMSetTableViewBackgroundColor(WMTableView *table, WMColor *color)
|
|
{
|
|
W_SetViewBackgroundColor(table->tableView, color);
|
|
|
|
if (table->backColor)
|
|
WMReleaseColor(table->backColor);
|
|
|
|
table->backColor = WMRetainColor(color);
|
|
|
|
repaintTable(table);
|
|
}
|
|
|
|
|
|
void WMSetTableViewGridColor(WMTableView *table, WMColor *color)
|
|
{
|
|
WMReleaseColor(table->gridColor);
|
|
table->gridColor = WMRetainColor(color);
|
|
XSetForeground(WMScreenDisplay(WMWidgetScreen(table)), table->gridGC,
|
|
WMColorPixel(color));
|
|
repaintTable(table);
|
|
}
|
|
|
|
|
|
|
|
void WMSetTableViewRowHeight(WMTableView *table, int height)
|
|
{
|
|
table->rowHeight = height;
|
|
|
|
repaintTable(table);
|
|
}
|
|
|
|
|
|
void WMScrollTableViewRowToVisible(WMTableView *table, int row)
|
|
{
|
|
WMScroller *scroller;
|
|
WMRange range;
|
|
WMRect rect;
|
|
int newY, tmp;
|
|
|
|
rect = getVisibleRect(table);
|
|
range = rowsInRect(table, rect);
|
|
|
|
scroller = table->vscroll;
|
|
|
|
if (row < range.position) {
|
|
newY = row * table->rowHeight - rect.size.height / 2;
|
|
} else if (row >= range.position + range.count) {
|
|
newY = row * table->rowHeight - rect.size.height / 2;
|
|
} else {
|
|
return;
|
|
}
|
|
tmp = table->rows*table->rowHeight - rect.size.height;
|
|
newY = WMAX(0, WMIN(newY, tmp));
|
|
|
|
scrollToPoint(table, rect.pos.x, newY);
|
|
}
|
|
|
|
|
|
|
|
static void drawGrid(WMTableView *table, WMRect rect)
|
|
{
|
|
WMScreen *scr = WMWidgetScreen(table);
|
|
Display *dpy = WMScreenDisplay(scr);
|
|
int i;
|
|
int y1, y2;
|
|
int x1, x2;
|
|
int xx;
|
|
Drawable d = WMGetPixmapXID(table->viewBuffer);
|
|
GC gc = table->gridGC;
|
|
|
|
#if 0
|
|
char dashl[1] = {1};
|
|
|
|
XSetDashes(dpy, gc, 0, dashl, 1);
|
|
|
|
y1 = (rect.pos.y/table->rowHeight - 1)*table->rowHeight;
|
|
y2 = y1 + (rect.size.height/table->rowHeight+2)*table->rowHeight;
|
|
#endif
|
|
y1 = 0;
|
|
y2 = W_VIEW_HEIGHT(table->tableView);
|
|
|
|
xx = -rect.pos.x;
|
|
for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
|
|
WMTableColumn *column;
|
|
|
|
XDrawLine(dpy, d, gc, xx, y1, xx, y2);
|
|
|
|
column = WMGetFromArray(table->columns, i);
|
|
xx += column->width;
|
|
}
|
|
XDrawLine(dpy, d, gc, xx, y1, xx, y2);
|
|
|
|
x1 = 0;
|
|
x2 = rect.size.width;
|
|
|
|
if (x2 <= x1)
|
|
return;
|
|
#if 0
|
|
XSetDashes(dpy, gc, (rect.pos.x&1), dashl, 1);
|
|
#endif
|
|
|
|
y1 = -rect.pos.y%table->rowHeight;
|
|
y2 = y1 + rect.size.height + table->rowHeight;
|
|
|
|
for (i = y1; i <= y2; i += table->rowHeight) {
|
|
XDrawLine(dpy, d, gc, x1, i, x2, i);
|
|
}
|
|
}
|
|
|
|
|
|
static WMRange columnsInRect(WMTableView *table, WMRect rect)
|
|
{
|
|
WMTableColumn *column;
|
|
int width;
|
|
int i , j;
|
|
int totalColumns = WMGetArrayItemCount(table->columns);
|
|
WMRange range;
|
|
|
|
j = 0;
|
|
width = 0;
|
|
for (i = 0; i < totalColumns; i++) {
|
|
column = WMGetFromArray(table->columns, i);
|
|
if (j == 0) {
|
|
if (width <= rect.pos.x && width + column->width > rect.pos.x) {
|
|
range.position = i;
|
|
j = 1;
|
|
}
|
|
} else {
|
|
range.count++;
|
|
if (width > rect.pos.x + rect.size.width) {
|
|
break;
|
|
}
|
|
}
|
|
width += column->width;
|
|
}
|
|
range.count = WMAX(1, WMIN(range.count, totalColumns - range.position));
|
|
return range;
|
|
}
|
|
|
|
|
|
static WMRange rowsInRect(WMTableView *table, WMRect rect)
|
|
{
|
|
WMRange range;
|
|
int rh = table->rowHeight;
|
|
int dif;
|
|
|
|
dif = rect.pos.y % rh;
|
|
|
|
range.position = WMAX(0, (rect.pos.y - dif) / rh);
|
|
range.count = WMAX(1, WMIN((rect.size.height + dif) / rh, table->rows));
|
|
|
|
return range;
|
|
}
|
|
|
|
|
|
static void drawRow(WMTableView *table, int row, WMRect clipRect)
|
|
{
|
|
int i;
|
|
WMRange cols = columnsInRect(table, clipRect);
|
|
WMTableColumn *column;
|
|
Drawable d = WMGetPixmapXID(table->viewBuffer);
|
|
|
|
for (i = cols.position; i < cols.position+cols.count; i++) {
|
|
column = WMGetFromArray(table->columns, i);
|
|
|
|
if (!column->delegate || !column->delegate->drawCell)
|
|
continue;
|
|
|
|
if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
|
|
(*column->delegate->drawSelectedCell)(column->delegate, column, row, d);
|
|
else
|
|
(*column->delegate->drawCell)(column->delegate, column, row, d);
|
|
}
|
|
}
|
|
|
|
|
|
static void drawFullRow(WMTableView *table, int row)
|
|
{
|
|
int i;
|
|
WMTableColumn *column;
|
|
Drawable d = WMGetPixmapXID(table->viewBuffer);
|
|
|
|
for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
|
|
column = WMGetFromArray(table->columns, i);
|
|
|
|
if (!column->delegate || !column->delegate->drawCell)
|
|
continue;
|
|
|
|
if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
|
|
(*column->delegate->drawSelectedCell)(column->delegate, column, row, d);
|
|
else
|
|
(*column->delegate->drawCell)(column->delegate, column, row, d);
|
|
}
|
|
}
|
|
|
|
|
|
static void setRowSelected(WMTableView *table, unsigned row, Bool flag)
|
|
{
|
|
int repaint = 0;
|
|
|
|
if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound) {
|
|
if (!flag) {
|
|
WMRemoveFromArray(table->selectedRows, (void*)row);
|
|
repaint = 1;
|
|
}
|
|
} else {
|
|
if (flag) {
|
|
WMAddToArray(table->selectedRows, (void*)row);
|
|
repaint = 1;
|
|
}
|
|
}
|
|
if (repaint && row < table->rows) {
|
|
/*drawFullRow(table, row);*/
|
|
repaintTable(table);
|
|
}
|
|
}
|
|
|
|
|
|
static void repaintTable(WMTableView *table)
|
|
{
|
|
WMRect rect;
|
|
WMRange rows;
|
|
WMScreen *scr = WMWidgetScreen(table);
|
|
int i;
|
|
|
|
if (!table->delegate || !W_VIEW_REALIZED(table->view))
|
|
return;
|
|
|
|
wassertr(table->delegate->numberOfRows);
|
|
|
|
if (!table->viewBuffer) {
|
|
table->viewBuffer = WMCreatePixmap(scr,
|
|
W_VIEW_WIDTH(table->tableView),
|
|
W_VIEW_HEIGHT(table->tableView),
|
|
WMScreenDepth(scr), 0);
|
|
}
|
|
|
|
XFillRectangle(scr->display,
|
|
WMGetPixmapXID(table->viewBuffer),
|
|
WMColorGC(table->backColor), 0, 0,
|
|
W_VIEW_WIDTH(table->tableView),
|
|
W_VIEW_HEIGHT(table->tableView));
|
|
|
|
rect = getVisibleRect(table);
|
|
|
|
if (table->drawsGrid) {
|
|
drawGrid(table, rect);
|
|
}
|
|
|
|
rows = rowsInRect(table, rect);
|
|
for (i = rows.position;
|
|
i < WMIN(rows.position+rows.count + 1, table->rows);
|
|
i++) {
|
|
drawRow(table, i, rect);
|
|
}
|
|
|
|
XSetWindowBackgroundPixmap(scr->display, table->tableView->window,
|
|
WMGetPixmapXID(table->viewBuffer));
|
|
XClearWindow(scr->display, table->tableView->window);
|
|
}
|
|
|
|
|
|
static void stopRowEdit(WMTableView *table, int row)
|
|
{
|
|
int i;
|
|
WMTableColumn *column;
|
|
|
|
table->editingRow = -1;
|
|
for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
|
|
column = WMGetFromArray(table->columns, i);
|
|
|
|
if (column->delegate && column->delegate->endCellEdit)
|
|
(*column->delegate->endCellEdit)(column->delegate, column, row);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void WMEditTableViewRow(WMTableView *table, int row)
|
|
{
|
|
int i;
|
|
WMTableColumn *column;
|
|
|
|
if (table->editingRow >= 0) {
|
|
stopRowEdit(table, table->editingRow);
|
|
}
|
|
|
|
table->editingRow = row;
|
|
|
|
if (row < 0)
|
|
return;
|
|
|
|
for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
|
|
column = WMGetFromArray(table->columns, i);
|
|
|
|
if (column->delegate && column->delegate->beginCellEdit)
|
|
(*column->delegate->beginCellEdit)(column->delegate, column, row);
|
|
}
|
|
}
|
|
|
|
|
|
void WMSelectTableViewRow(WMTableView *table, int row)
|
|
{
|
|
if (table->clickedRow >= 0)
|
|
setRowSelected(table, table->clickedRow, False);
|
|
|
|
if (row >= table->rows) {
|
|
return;
|
|
}
|
|
|
|
setRowSelected(table, row, True);
|
|
table->clickedRow = row;
|
|
|
|
if (table->action)
|
|
(*table->action)(table, table->clientData);
|
|
WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
|
|
table, NULL);
|
|
}
|
|
|
|
|
|
void WMReloadTableView(WMTableView *table)
|
|
{
|
|
WMRect rect = getVisibleRect(table);
|
|
|
|
if (table->editingRow >= 0)
|
|
stopRowEdit(table, table->editingRow);
|
|
|
|
/* when this is called, nothing in the table can be assumed to be
|
|
* like the last time we accessed it (ie, rows might have disappeared) */
|
|
|
|
WMEmptyArray(table->selectedRows);
|
|
|
|
if (table->clickedRow >= 0) {
|
|
table->clickedRow = -1;
|
|
|
|
if (table->action)
|
|
(*table->action)(table, table->clientData);
|
|
WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
|
|
table, NULL);
|
|
}
|
|
|
|
if (table->delegate && table->delegate->numberOfRows) {
|
|
int rows;
|
|
|
|
rows = (*table->delegate->numberOfRows)(table->delegate, table);
|
|
|
|
if (rows != table->rows) {
|
|
table->rows = rows;
|
|
handleResize(table->view->delegate, table->view);
|
|
} else {
|
|
repaintTable(table);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void WMNoteTableViewNumberOfRowsChanged(WMTableView *table)
|
|
{
|
|
WMReloadTableView(table);
|
|
}
|
|
|
|
|
|
static void handleTableEvents(XEvent *event, void *data)
|
|
{
|
|
WMTableView *table = (WMTableView*)data;
|
|
int row;
|
|
|
|
switch (event->type) {
|
|
case ButtonPress:
|
|
if (event->xbutton.button == Button1) {
|
|
WMRect rect = getVisibleRect(table);
|
|
|
|
row = (event->xbutton.y + rect.pos.y)/table->rowHeight;
|
|
if (row != table->clickedRow) {
|
|
setRowSelected(table, table->clickedRow, False);
|
|
setRowSelected(table, row, True);
|
|
table->clickedRow = row;
|
|
table->dragging = 1;
|
|
} else {
|
|
table->dragging = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MotionNotify:
|
|
if (table->dragging && event->xmotion.y >= 0) {
|
|
WMRect rect = getVisibleRect(table);
|
|
|
|
row = (event->xmotion.y + rect.pos.y)/table->rowHeight;
|
|
if (table->clickedRow != row && row >= 0 && row < table->rows) {
|
|
setRowSelected(table, table->clickedRow, False);
|
|
setRowSelected(table, row, True);
|
|
table->clickedRow = row;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ButtonRelease:
|
|
if (event->xbutton.button == Button1) {
|
|
if (table->action)
|
|
(*table->action)(table, table->clientData);
|
|
WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
|
|
table, NULL);
|
|
table->dragging = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void handleEvents(XEvent *event, void *data)
|
|
{
|
|
WMTableView *table = (WMTableView*)data;
|
|
WMScreen *scr = WMWidgetScreen(table);
|
|
|
|
switch (event->type) {
|
|
case Expose:
|
|
W_DrawRelief(scr, W_VIEW_DRAWABLE(table->view), 0, 0,
|
|
W_VIEW_WIDTH(table->view), W_VIEW_HEIGHT(table->view),
|
|
WRSunken);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void handleResize(W_ViewDelegate *self, WMView *view)
|
|
{
|
|
int width;
|
|
int height;
|
|
WMTableView *table = view->self;
|
|
WMSize size = getTotalSize(table);
|
|
WMScreen *scr = WMWidgetScreen(table);
|
|
int vw, vh;
|
|
|
|
width = W_VIEW_WIDTH(view) - 2;
|
|
height = W_VIEW_HEIGHT(view) - 3;
|
|
|
|
height -= table->headerHeight; /* table header */
|
|
|
|
if (table->corner)
|
|
WMResizeWidget(table->corner, 20, table->headerHeight);
|
|
|
|
WMMoveWidget(table->vscroll, 1, table->headerHeight + 1);
|
|
WMResizeWidget(table->vscroll, 20, height + 1);
|
|
|
|
WMMoveWidget(table->hscroll, 20, W_VIEW_HEIGHT(view) - 20 - 1);
|
|
WMResizeWidget(table->hscroll, width-20+1, 20);
|
|
|
|
if (table->header)
|
|
WMResizeWidget(table->header, width - 21, table->headerHeight);
|
|
|
|
if (table->viewBuffer) {
|
|
WMReleasePixmap(table->viewBuffer);
|
|
table->viewBuffer = NULL;
|
|
}
|
|
|
|
width -= 20;
|
|
height -= 20;
|
|
|
|
vw = WMIN(size.width, width);
|
|
vh = WMIN(size.height, height);
|
|
|
|
W_MoveView(table->tableView, 21, 1+table->headerHeight+1);
|
|
W_ResizeView(table->tableView, WMAX(vw, 1), WMAX(vh, 1)+1);
|
|
|
|
adjustScrollers(table);
|
|
|
|
repaintTable(table);
|
|
}
|
|
|
|
|
|
static void rearrangeHeader(WMTableView *table)
|
|
{
|
|
int width;
|
|
int count;
|
|
int i;
|
|
/*WMRect rect = WMGetScrollViewVisibleRect(table->scrollView);*/
|
|
|
|
width = 0;
|
|
|
|
count = WMGetArrayItemCount(table->columns);
|
|
for (i = 0; i < count; i++) {
|
|
WMTableColumn *column = WMGetFromArray(table->columns, i);
|
|
WMView *splitter = WMGetFromArray(table->splitters, i);
|
|
|
|
WMMoveWidget(column->titleW, width, 0);
|
|
WMResizeWidget(column->titleW, column->width-1, table->headerHeight);
|
|
|
|
width += column->width;
|
|
W_MoveView(splitter, width-1, 0);
|
|
}
|
|
|
|
wassertr(table->delegate && table->delegate->numberOfRows);
|
|
|
|
table->rows = table->delegate->numberOfRows(table->delegate, table);
|
|
|
|
table->tableWidth = width + 1;
|
|
|
|
handleResize(table->view->delegate, table->view);
|
|
}
|