mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-19 12:28:22 +01:00
- Also tested the backward compatibility ability of the WINGs proplist code which seems to work quite well. Starting with this moment, Window Maker no longer needs libPropList and is now using the better and much more robust proplist code from WINGs. Also the WINGs based proplist code is actively maintained while the old libPropList code is practically dead and flawed by the fact that it borrowed concepts from the UserDefaults which conflicted with the retain/release mechanism, making some problems that libPropList had, practically unsolvable without a complete redesign (which can be found in the more robust WINGs code).
1234 lines
28 KiB
C
1234 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);
|
|
}
|
|
}
|
|
|
|
|
|
#if 0
|
|
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);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
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)
|
|
{
|
|
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);
|
|
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);
|
|
}
|