changed stringselector for enumeration selector
[wmaker-crm.git] / WINGs / Extras / wtableview.c
blob2f64d400583057c6dcbbbb28d1b2a183e339dba9
3 #include <WINGsP.h>
4 #include <X11/cursorfont.h>
6 #include "wtableview.h"
9 const char *WMTableViewRowWasSelectedNotification = "WMTableViewRowWasSelectedNotification";
10 const char *WMTableViewRowWasUnselectedNotification = "WMTableViewRowWasUnselectedNotification";
13 struct W_TableColumn {
14 WMTableView *table;
15 WMWidget *titleW;
16 char *title;
17 int width;
18 int minWidth;
19 int maxWidth;
21 void *id;
23 WMTableColumnDelegate *delegate;
25 unsigned resizable:1;
26 unsigned editable:1;
31 static void handleResize(W_ViewDelegate *self, WMView *view);
34 WMTableColumn *WMCreateTableColumn(char *title)
36 WMTableColumn *col = wmalloc(sizeof(WMTableColumn));
38 col->table = NULL;
39 col->titleW = NULL;
40 col->width = 50;
41 col->minWidth = 5;
42 col->maxWidth = 0;
44 col->id = NULL;
46 col->title = wstrdup(title);
48 col->delegate = NULL;
50 col->resizable = 1;
51 col->editable = 0;
53 return col;
57 void WMSetTableColumnId(WMTableColumn *column, void *id)
59 column->id = id;
63 void *WMGetTableColumnId(WMTableColumn *column)
65 return column->id;
69 void WMSetTableColumnWidth(WMTableColumn *column, unsigned width)
71 if (column->maxWidth == 0)
72 column->width = WMAX(column->minWidth, width);
73 else
74 column->width = WMAX(column->minWidth, WMIN(column->maxWidth, width));
76 if (column->table) {
77 handleResize(NULL, W_VIEW(column->table));
82 void WMSetTableColumnDelegate(WMTableColumn *column,
83 WMTableColumnDelegate *delegate)
85 column->delegate = delegate;
89 void WMSetTableColumnConstraints(WMTableColumn *column,
90 unsigned minWidth, unsigned maxWidth)
92 wassertr(minWidth <= maxWidth);
94 column->minWidth = minWidth;
95 column->maxWidth = maxWidth;
97 if (column->width < column->minWidth)
98 WMSetTableColumnWidth(column, column->minWidth);
99 else if (column->width > column->maxWidth || column->maxWidth == 0)
100 WMSetTableColumnWidth(column, column->maxWidth);
104 void WMSetTableColumnEditable(WMTableColumn *column, Bool flag)
106 column->editable = flag;
110 WMTableView *WMGetTableColumnTableView(WMTableColumn *column)
112 return column->table;
117 struct W_TableView {
118 W_Class widgetClass;
119 WMView *view;
121 WMFrame *header;
123 WMLabel *corner;
125 WMScrollView *scrollView;
126 WMView *tableView;
128 WMArray *columns;
129 WMArray *splitters;
131 WMArray *selectedRows;
133 int tableWidth;
135 int rows;
137 GC gridGC;
138 WMColor *gridColor;
140 Cursor splitterCursor;
142 void *dataSource;
144 WMTableViewDelegate *delegate;
146 int editingRow;
148 unsigned headerHeight;
150 unsigned rowHeight;
152 unsigned drawsGrid:1;
153 unsigned canSelectRow:1;
154 unsigned canSelectMultiRows:1;
155 unsigned canDeselectRow:1;
158 static W_Class tableClass = 0;
161 static W_ViewDelegate viewDelegate = {
162 NULL,
163 NULL,
164 handleResize,
165 NULL,
166 NULL
173 static void handleEvents(XEvent *event, void *data);
174 static void handleTableEvents(XEvent *event, void *data);
177 static void scrollObserver(void *self, WMNotification *notif)
179 WMTableView *table = (WMTableView*)self;
180 WMRect rect;
181 int i, x;
183 rect = WMGetScrollViewVisibleRect(table->scrollView);
185 x = 0;
186 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
187 WMTableColumn *column;
189 column = WMGetFromArray(table->columns, i);
191 WMMoveWidget(column->titleW, x + rect.pos.x, 0);
193 if (i > 0) {
194 WMView *splitter;
196 splitter = WMGetFromArray(table->splitters, i-1);
197 W_MoveView(splitter, x + rect.pos.x - 1, 0);
200 x += W_VIEW_WIDTH(WMWidgetView(column->titleW)) + 1;
205 static void splitterHandler(XEvent *event, void *data)
207 WMTableView *table = (WMTableView*)data;
208 int done = 0;
210 while (!done) {
211 XEvent ev;
213 WMMaskEvent(event->xany.display, ButtonMotionMask|ButtonReleaseMask,
214 &ev);
216 switch (event->type) {
217 case MotionNotify:
218 printf("%i\n", event->xmotion.x);
219 break;
221 case ButtonRelease:
222 done = 1;
223 break;
230 WMTableView *WMCreateTableView(WMWidget *parent)
232 WMTableView *table = wmalloc(sizeof(WMTableView));
233 WMScreen *scr = WMWidgetScreen(parent);
235 memset(table, 0, sizeof(WMTableView));
237 if (!tableClass) {
238 tableClass = W_RegisterUserWidget();
240 table->widgetClass = tableClass;
242 table->view = W_CreateView(W_VIEW(parent));
243 if (!table->view)
244 goto error;
245 table->view->self = table;
247 table->view->delegate = &viewDelegate;
249 table->headerHeight = 20;
251 table->scrollView = WMCreateScrollView(table);
252 if (!table->scrollView)
253 goto error;
254 WMResizeWidget(table->scrollView, 10, 10);
255 WMSetScrollViewHasVerticalScroller(table->scrollView, True);
256 WMSetScrollViewHasHorizontalScroller(table->scrollView, True);
258 WMScroller *scroller;
259 scroller = WMGetScrollViewHorizontalScroller(table->scrollView);
260 WMAddNotificationObserver(scrollObserver, table,
261 WMScrollerDidScrollNotification,
262 scroller);
264 WMMoveWidget(table->scrollView, 1, 2+table->headerHeight);
265 WMMapWidget(table->scrollView);
267 table->header = WMCreateFrame(table);
268 WMMoveWidget(table->header, 22, 2);
269 WMMapWidget(table->header);
270 WMSetFrameRelief(table->header, WRFlat);
271 table->corner = WMCreateLabel(table);
272 WMResizeWidget(table->corner, 20, table->headerHeight);
273 WMMoveWidget(table->corner, 2, 2);
274 WMMapWidget(table->corner);
275 WMSetLabelRelief(table->corner, WRRaised);
276 WMSetWidgetBackgroundColor(table->corner, scr->darkGray);
279 table->tableView = W_CreateView(W_VIEW(parent));
280 if (!table->tableView)
281 goto error;
282 table->tableView->self = table;
283 W_ResizeView(table->tableView, 100, 1000);
284 W_MapView(table->tableView);
286 WMSetScrollViewContentView(table->scrollView, table->tableView);
288 table->tableView->flags.dontCompressExpose = 1;
290 table->gridColor = WMDarkGrayColor(scr);
293 XGCValues gcv;
295 gcv.foreground = WMColorPixel(table->gridColor);
296 gcv.dashes = 1;
297 gcv.line_style = LineOnOffDash;
298 table->gridGC = XCreateGC(WMScreenDisplay(scr), W_DRAWABLE(scr),
299 GCForeground, &gcv);
302 table->editingRow = -1;
304 table->drawsGrid = 1;
305 table->rowHeight = 16;
307 table->tableWidth = 1;
309 table->columns = WMCreateArray(4);
310 table->splitters = WMCreateArray(4);
312 table->selectedRows = WMCreateArray(16);
314 table->splitterCursor = XCreateFontCursor(WMScreenDisplay(scr),
315 XC_sb_h_double_arrow);
317 table->canSelectRow = 1;
319 WMCreateEventHandler(table->view, ExposureMask|StructureNotifyMask,
320 handleEvents, table);
322 WMCreateEventHandler(table->tableView, ExposureMask|ButtonPressMask,
323 handleTableEvents, table);
325 return table;
327 error:
328 if (table->scrollView)
329 WMDestroyWidget(table->scrollView);
330 if (table->tableView)
331 W_DestroyView(table->tableView);
332 if (table->view)
333 W_DestroyView(table->view);
334 wfree(table);
335 return NULL;
339 void WMAddTableViewColumn(WMTableView *table, WMTableColumn *column)
341 int width;
342 int i;
343 WMScreen *scr = WMWidgetScreen(table);
344 int count;
346 column->table = table;
348 WMAddToArray(table->columns, column);
350 if (!column->titleW) {
351 column->titleW = WMCreateLabel(table);
352 WMSetLabelRelief(column->titleW, WRRaised);
353 WMSetLabelFont(column->titleW, scr->boldFont);
354 WMSetLabelTextColor(column->titleW, scr->white);
355 WMSetWidgetBackgroundColor(column->titleW, scr->darkGray);
356 WMSetLabelText(column->titleW, column->title);
357 W_ReparentView(WMWidgetView(column->titleW),
358 WMWidgetView(table->header), 0, 0);
359 if (W_VIEW_REALIZED(table->view))
360 WMRealizeWidget(column->titleW);
361 WMMapWidget(column->titleW);
364 if (WMGetArrayItemCount(table->columns) > 1) {
365 WMView *splitter = W_CreateView(WMWidgetView(table->header));
367 W_SetViewBackgroundColor(splitter, WMWhiteColor(scr));
369 if (W_VIEW_REALIZED(table->view))
370 W_RealizeView(splitter);
372 W_ResizeView(splitter, 2, table->headerHeight-1);
373 W_MapView(splitter);
375 W_SetViewCursor(splitter, table->splitterCursor);
376 WMCreateEventHandler(splitter, ButtonPressMask,
377 splitterHandler, table);
379 WMAddToArray(table->splitters, splitter);
382 count = WMGetArrayItemCount(table->columns);
383 for (i = 0, width = 0; i < count; i++) {
384 WMTableColumn *column = WMGetFromArray(table->columns, i);
386 WMMoveWidget(column->titleW, width, 0);
387 WMResizeWidget(column->titleW, column->width-1, table->headerHeight);
389 if (i > 0) {
390 WMView *splitter = WMGetFromArray(table->splitters, i-1);
392 W_MoveView(splitter, width-1, 0);
394 width += column->width;
397 wassertr(table->delegate && table->delegate->numberOfRows);
399 table->rows = table->delegate->numberOfRows(table->delegate, table);
401 W_ResizeView(table->tableView, width+1,
402 table->rows * table->rowHeight + 1);
404 table->tableWidth = width + 1;
408 void WMSetTableViewHeaderHeight(WMTableView *table, unsigned height)
410 table->headerHeight = height;
412 handleResize(NULL, table->view);
416 void WMSetTableViewDelegate(WMTableView *table, WMTableViewDelegate *delegate)
418 table->delegate = delegate;
422 WMView *WMGetTableViewDocumentView(WMTableView *table)
424 return table->tableView;
428 void *WMTableViewDataForCell(WMTableView *table, WMTableColumn *column,
429 int row)
431 return (*table->delegate->valueForCell)(table->delegate, column, row);
435 void WMSetTableViewDataForCell(WMTableView *table, WMTableColumn *column,
436 int row, void *data)
438 (*table->delegate->setValueForCell)(table->delegate, column, row, data);
442 WMRect WMTableViewRectForCell(WMTableView *table, WMTableColumn *column,
443 int row)
445 WMRect rect;
446 int i;
448 rect.pos.x = 0;
449 rect.pos.y = row * table->rowHeight;
450 rect.size.height = table->rowHeight;
452 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
453 WMTableColumn *col;
454 col = WMGetFromArray(table->columns, i);
456 if (col == column) {
457 rect.size.width = col->width;
458 break;
461 rect.pos.x += col->width;
463 return rect;
467 void WMSetTableViewDataSource(WMTableView *table, void *source)
469 table->dataSource = source;
473 void *WMGetTableViewDataSource(WMTableView *table)
475 return table->dataSource;
479 void WMSetTableViewBackgroundColor(WMTableView *table, WMColor *color)
481 W_SetViewBackgroundColor(table->tableView, color);
485 void WMSetTableViewGridColor(WMTableView *table, WMColor *color)
487 WMReleaseColor(table->gridColor);
488 table->gridColor = WMRetainColor(color);
489 XSetForeground(WMScreenDisplay(WMWidgetScreen(table)), table->gridGC, WMColorPixel(color));
494 static void drawGrid(WMTableView *table, WMRect rect)
496 WMScreen *scr = WMWidgetScreen(table);
497 Display *dpy = WMScreenDisplay(scr);
498 int i;
499 int y1, y2;
500 int x1, x2;
501 int xx;
502 Drawable d = W_VIEW_DRAWABLE(table->tableView);
503 GC gc = table->gridGC;
505 #if 0
506 char dashl[1] = {1};
508 XSetDashes(dpy, gc, 0, dashl, 1);
510 y1 = (rect.pos.y/table->rowHeight - 1)*table->rowHeight;
511 y2 = y1 + (rect.size.height/table->rowHeight+2)*table->rowHeight;
512 #endif
513 y1 = 0;
514 y2 = W_VIEW_HEIGHT(table->tableView);
516 xx = 0;
517 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
518 WMTableColumn *column;
520 if (xx >= rect.pos.x && xx <= rect.pos.x+rect.size.width) {
521 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
523 column = WMGetFromArray(table->columns, i);
524 xx += column->width;
526 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
529 x1 = rect.pos.x;
530 x2 = WMIN(x1 + rect.size.width, xx);
532 if (x2 <= x1)
533 return;
534 #if 0
535 XSetDashes(dpy, gc, (rect.pos.x&1), dashl, 1);
536 #endif
538 y1 = rect.pos.y - rect.pos.y%table->rowHeight;
539 y2 = y1 + rect.size.height + table->rowHeight;
541 for (i = y1; i <= y2; i += table->rowHeight) {
542 XDrawLine(dpy, d, gc, x1, i, x2, i);
547 static WMRange columnsInRect(WMTableView *table, WMRect rect)
549 WMTableColumn *column;
550 int width;
551 int i , j;
552 int totalColumns = WMGetArrayItemCount(table->columns);
553 WMRange range;
555 j = 0;
556 width = 0;
557 for (i = 0; i < totalColumns; i++) {
558 column = WMGetFromArray(table->columns, i);
559 if (j == 0) {
560 if (width <= rect.pos.x && width + column->width > rect.pos.x) {
561 range.position = i;
562 j = 1;
564 } else {
565 if (width > rect.pos.x + rect.size.width) {
566 range.count = i - range.position;
567 break;
570 width += column->width;
573 range.count = WMAX(1, WMIN(range.count, totalColumns - range.position));
575 return range;
579 static WMRange rowsInRect(WMTableView *table, WMRect rect)
581 WMRange range;
582 int rh = table->rowHeight;
584 range.position = WMAX(0, rect.pos.y / rh - 1);
585 range.count = WMAX(1, WMIN(rect.size.height / rh + 3, table->rows));
587 return range;
591 static void drawRow(WMTableView *table, int row, WMRect clipRect)
593 int i;
594 WMRange cols = columnsInRect(table, clipRect);
595 WMTableColumn *column;
597 for (i = cols.position; i < cols.position+cols.count; i++) {
598 column = WMGetFromArray(table->columns, i);
600 wassertr(column->delegate && column->delegate->drawCell);
602 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
603 (*column->delegate->drawSelectedCell)(column->delegate, column, row);
604 else
605 (*column->delegate->drawCell)(column->delegate, column, row);
610 static void drawFullRow(WMTableView *table, int row)
612 int i;
613 WMTableColumn *column;
615 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
616 column = WMGetFromArray(table->columns, i);
618 wassertr(column->delegate && column->delegate->drawCell);
620 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
621 (*column->delegate->drawSelectedCell)(column->delegate, column, row);
622 else
623 (*column->delegate->drawCell)(column->delegate, column, row);
628 static void setRowSelected(WMTableView *table, unsigned row, Bool flag)
630 int repaint = 0;
633 if (WMGetArrayItemCount(table->selectedRows) > 0
634 && !table->canSelectMultiRows) {
635 int r = (int)WMGetFromArray(table->selectedRows, 0);
637 WMDeleteFromArray(table->selectedRows, 0);
639 drawFullRow(table, r);
641 WMPostNotificationName(WMTableViewRowWasUnselectedNotification,
642 table, (void*)r);
645 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound) {
646 if (!flag) {
647 WMRemoveFromArray(table->selectedRows, (void*)row);
648 WMPostNotificationName(WMTableViewRowWasUnselectedNotification,
649 table, (void*)row);
650 repaint = 1;
652 } else {
653 if (flag) {
654 WMAddToArray(table->selectedRows, (void*)row);
655 WMPostNotificationName(WMTableViewRowWasSelectedNotification,
656 table, (void*)row);
657 repaint = 1;
661 if (repaint) {
662 drawFullRow(table, row);
667 static void repaintTable(WMTableView *table, int x, int y,
668 int width, int height)
670 WMRect rect;
671 WMRange rows;
672 int i;
674 wassertr(table->delegate && table->delegate->numberOfRows);
675 i = (*table->delegate->numberOfRows)(table->delegate, table);
677 if (i != table->rows) {
678 table->rows = i;
679 W_ResizeView(table->tableView, table->tableWidth,
680 table->rows * table->rowHeight + 1);
684 rect.pos = wmkpoint(x,y);
685 rect.size = wmksize(width, height);
687 if (table->drawsGrid) {
688 drawGrid(table, rect);
691 rows = rowsInRect(table, rect);
692 for (i = rows.position;
693 i < WMIN(rows.position+rows.count, table->rows);
694 i++) {
695 drawRow(table, i, rect);
700 static void stopRowEdit(WMTableView *table, int row)
702 int i;
703 WMTableColumn *column;
705 table->editingRow = -1;
706 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
707 column = WMGetFromArray(table->columns, i);
709 wassertr(column->delegate && column->delegate->endCellEdit);
711 (*column->delegate->endCellEdit)(column->delegate, column, row);
717 void WMEditTableViewRow(WMTableView *table, int row)
719 int i;
720 WMTableColumn *column;
722 if (table->editingRow >= 0) {
723 stopRowEdit(table, table->editingRow);
726 table->editingRow = row;
727 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
728 column = WMGetFromArray(table->columns, i);
730 wassertr(column->delegate && column->delegate->beginCellEdit);
732 (*column->delegate->beginCellEdit)(column->delegate, column, row);
737 static void handleTableEvents(XEvent *event, void *data)
739 WMTableView *table = (WMTableView*)data;
741 switch (event->type) {
742 case ButtonPress:
743 setRowSelected(table, event->xbutton.y/table->rowHeight, True);
744 break;
746 case Expose:
747 repaintTable(table, event->xexpose.x, event->xexpose.y,
748 event->xexpose.width, event->xexpose.height);
749 break;
754 static void handleEvents(XEvent *event, void *data)
756 WMTableView *table = (WMTableView*)data;
757 WMScreen *scr = WMWidgetScreen(table);
759 switch (event->type) {
760 case Expose:
761 W_DrawRelief(scr, W_VIEW_DRAWABLE(table->view), 0, 0,
762 W_VIEW_WIDTH(table->view), W_VIEW_HEIGHT(table->view),
763 WRSunken);
764 break;
769 static void handleResize(W_ViewDelegate *self, WMView *view)
771 int width;
772 int height;
773 WMTableView *table = view->self;
775 width = W_VIEW_WIDTH(view) - 2;
776 height = W_VIEW_HEIGHT(view) - 3;
778 height -= table->headerHeight; /* table header */
780 if (table->corner)
781 WMResizeWidget(table->corner, 20, table->headerHeight);
783 if (table->scrollView) {
784 WMMoveWidget(table->scrollView, 1, table->headerHeight + 2);
785 WMResizeWidget(table->scrollView, width, height);
787 if (table->header)
788 WMResizeWidget(table->header, width - 21, table->headerHeight);