added table widget
[wmaker-crm.git] / WINGs / Extras / wtableview.c
blob4dbd9e2f6c4a5deb132326838d4fe33c781d1ecb
3 #include <WINGsP.h>
4 #include <X11/cursorfont.h>
6 #include "wtableview.h"
8 struct W_TableColumn {
9 WMTableView *table;
10 WMWidget *titleW;
11 char *title;
12 int width;
13 int minWidth;
14 int maxWidth;
16 void *id;
18 WMTableColumnDelegate *delegate;
20 unsigned resizable:1;
21 unsigned editable:1;
26 static void handleResize(W_ViewDelegate *self, WMView *view);
29 WMTableColumn *WMCreateTableColumn(char *title)
31 WMTableColumn *col = wmalloc(sizeof(WMTableColumn));
33 col->table = NULL;
34 col->titleW = NULL;
35 col->width = 50;
36 col->minWidth = 5;
37 col->maxWidth = 0;
39 col->id = NULL;
41 col->title = wstrdup(title);
43 col->delegate = NULL;
45 col->resizable = 1;
46 col->editable = 0;
48 return col;
52 void WMSetTableColumnId(WMTableColumn *column, void *id)
54 column->id = id;
58 void *WMGetTableColumnId(WMTableColumn *column)
60 return column->id;
64 void WMSetTableColumnWidth(WMTableColumn *column, unsigned width)
66 if (column->maxWidth == 0)
67 column->width = WMAX(column->minWidth, width);
68 else
69 column->width = WMAX(column->minWidth, WMIN(column->maxWidth, width));
71 if (column->table) {
72 handleResize(NULL, W_VIEW(column->table));
77 void WMSetTableColumnDelegate(WMTableColumn *column,
78 WMTableColumnDelegate *delegate)
80 column->delegate = delegate;
84 void WMSetTableColumnConstraints(WMTableColumn *column,
85 unsigned minWidth, unsigned maxWidth)
87 wassertr(minWidth <= maxWidth);
89 column->minWidth = minWidth;
90 column->maxWidth = maxWidth;
92 if (column->width < column->minWidth)
93 WMSetTableColumnWidth(column, column->minWidth);
94 else if (column->width > column->maxWidth || column->maxWidth == 0)
95 WMSetTableColumnWidth(column, column->maxWidth);
99 void WMSetTableColumnEditable(WMTableColumn *column, Bool flag)
101 column->editable = flag;
105 WMTableView *WMGetTableColumnTableView(WMTableColumn *column)
107 return column->table;
112 struct W_TableView {
113 W_Class widgetClass;
114 WMView *view;
116 WMFrame *header;
118 WMLabel *corner;
120 WMScrollView *scrollView;
121 WMView *tableView;
123 WMArray *columns;
124 WMArray *splitters;
126 int tableWidth;
128 int rows;
130 GC gridGC;
131 WMColor *gridColor;
133 Cursor splitterCursor;
135 void *dataSource;
137 WMTableViewDelegate *delegate;
139 unsigned headerHeight;
141 unsigned rowHeight;
143 unsigned drawsGrid:1;
146 static W_Class tableClass = 0;
149 static W_ViewDelegate viewDelegate = {
150 NULL,
151 NULL,
152 handleResize,
153 NULL,
154 NULL
161 static void handleEvents(XEvent *event, void *data);
162 static void handleTableEvents(XEvent *event, void *data);
165 static void scrollObserver(void *self, WMNotification *notif)
167 WMTableView *table = (WMTableView*)self;
168 WMRect rect;
169 int i, x;
171 rect = WMGetScrollViewVisibleRect(table->scrollView);
173 x = 0;
174 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
175 WMTableColumn *column;
177 column = WMGetFromArray(table->columns, i);
179 WMMoveWidget(column->titleW, x + rect.pos.x, 0);
181 if (i > 0) {
182 WMView *splitter;
184 splitter = WMGetFromArray(table->splitters, i-1);
185 W_MoveView(splitter, x + rect.pos.x - 1, 0);
188 x += W_VIEW_WIDTH(WMWidgetView(column->titleW)) + 1;
193 static void splitterHandler(XEvent *event, void *data)
195 WMTableView *table = (WMTableView*)data;
202 WMTableView *WMCreateTableView(WMWidget *parent)
204 WMTableView *table = wmalloc(sizeof(WMTableView));
205 WMScreen *scr = WMWidgetScreen(parent);
207 memset(table, 0, sizeof(WMTableView));
209 if (!tableClass) {
210 tableClass = W_RegisterUserWidget();
212 table->widgetClass = tableClass;
214 table->view = W_CreateView(W_VIEW(parent));
215 if (!table->view)
216 goto error;
217 table->view->self = table;
219 table->view->delegate = &viewDelegate;
221 table->headerHeight = 20;
223 table->scrollView = WMCreateScrollView(table);
224 if (!table->scrollView)
225 goto error;
226 WMResizeWidget(table->scrollView, 10, 10);
227 WMSetScrollViewHasVerticalScroller(table->scrollView, True);
228 WMSetScrollViewHasHorizontalScroller(table->scrollView, True);
230 WMScroller *scroller;
231 scroller = WMGetScrollViewHorizontalScroller(table->scrollView);
232 WMAddNotificationObserver(scrollObserver, table,
233 WMScrollerDidScrollNotification,
234 scroller);
236 WMMoveWidget(table->scrollView, 1, 2+table->headerHeight);
237 WMMapWidget(table->scrollView);
239 table->header = WMCreateFrame(table);
240 WMMoveWidget(table->header, 22, 2);
241 WMMapWidget(table->header);
242 WMSetFrameRelief(table->header, WRFlat);
243 table->corner = WMCreateLabel(table);
244 WMResizeWidget(table->corner, 20, table->headerHeight);
245 WMMoveWidget(table->corner, 2, 2);
246 WMMapWidget(table->corner);
247 WMSetLabelRelief(table->corner, WRRaised);
248 WMSetWidgetBackgroundColor(table->corner, scr->darkGray);
251 table->tableView = W_CreateView(W_VIEW(parent));
252 if (!table->tableView)
253 goto error;
254 W_ResizeView(table->tableView, 100, 1000);
255 W_MapView(table->tableView);
257 WMSetScrollViewContentView(table->scrollView, table->tableView);
259 table->tableView->flags.dontCompressExpose = 1;
261 table->gridColor = WMDarkGrayColor(scr);
264 XGCValues gcv;
266 gcv.foreground = WMColorPixel(table->gridColor);
267 gcv.dashes = 1;
268 gcv.line_style = LineOnOffDash;
269 table->gridGC = XCreateGC(WMScreenDisplay(scr), W_DRAWABLE(scr),
270 GCForeground, &gcv);
273 table->drawsGrid = 1;
274 table->rowHeight = 16;
276 table->tableWidth = 1;
278 table->columns = WMCreateArray(4);
279 table->splitters = WMCreateArray(4);
281 table->splitterCursor = XCreateFontCursor(WMScreenDisplay(scr),
282 XC_sb_h_double_arrow);
284 WMCreateEventHandler(table->view, ExposureMask|StructureNotifyMask,
285 handleEvents, table);
287 WMCreateEventHandler(table->tableView, ExposureMask,
288 handleTableEvents, table);
290 return table;
292 error:
293 if (table->scrollView)
294 WMDestroyWidget(table->scrollView);
295 if (table->tableView)
296 W_DestroyView(table->tableView);
297 if (table->view)
298 W_DestroyView(table->view);
299 wfree(table);
300 return NULL;
304 void WMAddTableViewColumn(WMTableView *table, WMTableColumn *column)
306 int width;
307 int i;
308 WMScreen *scr = WMWidgetScreen(table);
309 int count;
311 column->table = table;
313 WMAddToArray(table->columns, column);
315 if (!column->titleW) {
316 column->titleW = WMCreateLabel(table);
317 WMSetLabelRelief(column->titleW, WRRaised);
318 WMSetLabelFont(column->titleW, scr->boldFont);
319 WMSetLabelTextColor(column->titleW, scr->white);
320 WMSetWidgetBackgroundColor(column->titleW, scr->darkGray);
321 WMSetLabelText(column->titleW, column->title);
322 W_ReparentView(WMWidgetView(column->titleW),
323 WMWidgetView(table->header), 0, 0);
324 if (W_VIEW_REALIZED(table->view))
325 WMRealizeWidget(column->titleW);
326 WMMapWidget(column->titleW);
329 if (WMGetArrayItemCount(table->columns) > 1) {
330 WMView *splitter = W_CreateView(WMWidgetView(table->header));
332 W_SetViewBackgroundColor(splitter, WMWhiteColor(scr));
334 if (W_VIEW_REALIZED(table->view))
335 W_RealizeView(splitter);
337 W_ResizeView(splitter, 2, table->headerHeight-1);
338 W_MapView(splitter);
340 W_SetViewCursor(splitter, table->splitterCursor);
341 WMCreateEventHandler(splitter, ButtonPressMask|ButtonReleaseMask
342 |PointerMotionMask, splitterHandler, table);
344 WMAddToArray(table->splitters, splitter);
347 count = WMGetArrayItemCount(table->columns);
348 for (i = 0, width = 0; i < count; i++) {
349 WMTableColumn *column = WMGetFromArray(table->columns, i);
351 WMMoveWidget(column->titleW, width, 0);
352 WMResizeWidget(column->titleW, column->width-1, table->headerHeight);
354 if (i > 0) {
355 WMView *splitter = WMGetFromArray(table->splitters, i-1);
357 W_MoveView(splitter, width-1, 0);
359 width += column->width;
362 wassertr(table->delegate && table->delegate->numberOfRows);
364 table->rows = table->delegate->numberOfRows(table->delegate, table);
366 W_ResizeView(table->tableView, width+1,
367 table->rows * table->rowHeight + 1);
369 table->tableWidth = width + 1;
373 void WMSetTableViewHeaderHeight(WMTableView *table, unsigned height)
375 table->headerHeight = height;
377 handleResize(NULL, table->view);
381 void WMSetTableViewDelegate(WMTableView *table, WMTableViewDelegate *delegate)
383 table->delegate = delegate;
387 WMView *WMGetTableViewDocumentView(WMTableView *table)
389 return table->tableView;
393 void *WMTableViewDataForCell(WMTableView *table, WMTableColumn *column,
394 int row)
396 return (*table->delegate->valueForCell)(table->delegate, column, row);
400 WMRect WMTableViewRectForCell(WMTableView *table, WMTableColumn *column,
401 int row)
403 WMRect rect;
404 int i;
406 rect.pos.x = 0;
407 rect.pos.y = row * table->rowHeight;
408 rect.size.height = table->rowHeight;
410 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
411 WMTableColumn *col;
412 col = WMGetFromArray(table->columns, i);
414 if (col == column) {
415 rect.size.width = col->width;
416 break;
419 rect.pos.x += col->width;
421 return rect;
425 void WMSetTableViewDataSource(WMTableView *table, void *source)
427 table->dataSource = source;
431 void *WMGetTableViewDataSource(WMTableView *table)
433 return table->dataSource;
437 void WMSetTableViewBackgroundColor(WMTableView *table, WMColor *color)
439 W_SetViewBackgroundColor(table->tableView, color);
443 void WMSetTableViewGridColor(WMTableView *table, WMColor *color)
445 WMReleaseColor(table->gridColor);
446 table->gridColor = WMRetainColor(color);
447 XSetForeground(WMScreenDisplay(WMWidgetScreen(table)), table->gridGC, WMColorPixel(color));
451 static void drawGrid(WMTableView *table, WMRect rect)
453 WMScreen *scr = WMWidgetScreen(table);
454 Display *dpy = WMScreenDisplay(scr);
455 int i;
456 int y1, y2;
457 int x1, x2;
458 int xx;
459 Drawable d = W_VIEW_DRAWABLE(table->tableView);
460 GC gc = table->gridGC;
462 #if 0
463 char dashl[1] = {1};
465 XSetDashes(dpy, gc, 0, dashl, 1);
467 y1 = (rect.pos.y/table->rowHeight - 1)*table->rowHeight;
468 y2 = y1 + (rect.size.height/table->rowHeight+2)*table->rowHeight;
469 #endif
470 y1 = 0;
471 y2 = W_VIEW_HEIGHT(table->tableView);
473 xx = 0;
474 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
475 WMTableColumn *column;
477 if (xx >= rect.pos.x && xx <= rect.pos.x+rect.size.width) {
478 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
480 column = WMGetFromArray(table->columns, i);
481 xx += column->width;
483 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
486 x1 = rect.pos.x;
487 x2 = WMIN(x1 + rect.size.width, xx);
489 if (x2 <= x1)
490 return;
491 #if 0
492 XSetDashes(dpy, gc, (rect.pos.x&1), dashl, 1);
493 #endif
495 y1 = rect.pos.y - rect.pos.y%table->rowHeight;
496 y2 = y1 + rect.size.height + table->rowHeight;
498 for (i = y1; i <= y2; i += table->rowHeight) {
499 XDrawLine(dpy, d, gc, x1, i, x2, i);
504 static WMRange columnsInRect(WMTableView *table, WMRect rect)
506 WMTableColumn *column;
507 int width;
508 int i , j;
509 int totalColumns = WMGetArrayItemCount(table->columns);
510 WMRange range;
512 j = 0;
513 width = 0;
514 for (i = 0; i < totalColumns; i++) {
515 column = WMGetFromArray(table->columns, i);
516 if (j == 0) {
517 if (width <= rect.pos.x && width + column->width > rect.pos.x) {
518 range.position = i;
519 j = 1;
521 } else {
522 if (width > rect.pos.x + rect.size.width) {
523 range.count = i - range.position;
524 break;
527 width += column->width;
530 range.count = WMAX(1, WMIN(range.count, totalColumns - range.position));
532 return range;
536 static WMRange rowsInRect(WMTableView *table, WMRect rect)
538 WMRange range;
539 int rh = table->rowHeight;
541 range.position = WMAX(0, rect.pos.y / rh - 1);
542 range.count = WMAX(1, WMIN(rect.size.height / rh + 3, table->rows));
544 return range;
548 static void drawRow(WMTableView *table, int row, WMRect clipRect)
550 int i;
551 WMRange cols = columnsInRect(table, clipRect);
552 WMTableColumn *column;
554 for (i = cols.position; i < cols.position+cols.count; i++) {
555 column = WMGetFromArray(table->columns, i);
557 wassertr(column->delegate && column->delegate->drawCell);
559 (*column->delegate->drawCell)(column->delegate, column, row);
564 static void repaintTable(WMTableView *table, int x, int y,
565 int width, int height)
567 WMRect rect;
568 WMRange rows;
569 int i;
571 wassertr(table->delegate && table->delegate->numberOfRows);
572 i = (*table->delegate->numberOfRows)(table->delegate, table);
574 if (i != table->rows) {
575 table->rows = i;
576 W_ResizeView(table->tableView, table->tableWidth,
577 table->rows * table->rowHeight + 1);
581 rect.pos = wmkpoint(x,y);
582 rect.size = wmksize(width, height);
584 if (table->drawsGrid) {
585 drawGrid(table, rect);
588 rows = rowsInRect(table, rect);
589 for (i = rows.position;
590 i < WMIN(rows.position+rows.count, table->rows);
591 i++) {
592 drawRow(table, i, rect);
597 static void handleTableEvents(XEvent *event, void *data)
599 WMTableView *table = (WMTableView*)data;
601 switch (event->type) {
602 case Expose:
603 repaintTable(table, event->xexpose.x, event->xexpose.y,
604 event->xexpose.width, event->xexpose.height);
605 break;
610 static void handleEvents(XEvent *event, void *data)
612 WMTableView *table = (WMTableView*)data;
613 WMScreen *scr = WMWidgetScreen(table);
615 switch (event->type) {
616 case Expose:
617 W_DrawRelief(scr, W_VIEW_DRAWABLE(table->view), 0, 0,
618 W_VIEW_WIDTH(table->view), W_VIEW_HEIGHT(table->view),
619 WRSunken);
620 break;
625 static void handleResize(W_ViewDelegate *self, WMView *view)
627 int width;
628 int height;
629 WMTableView *table = view->self;
631 width = W_VIEW_WIDTH(view) - 2;
632 height = W_VIEW_HEIGHT(view) - 3;
634 height -= table->headerHeight; /* table header */
636 if (table->corner)
637 WMResizeWidget(table->corner, 20, table->headerHeight);
639 if (table->scrollView) {
640 WMMoveWidget(table->scrollView, 1, table->headerHeight + 2);
641 WMResizeWidget(table->scrollView, width, height);
643 if (table->header)
644 WMResizeWidget(table->header, width - 21, table->headerHeight);