- Made all changes for moving WINGs headers in the WINGs subdirectory.
[wmaker-crm.git] / WINGs / Extras / wtableview.c
blobd133b4b058f3f93808c61bd3efc7558eb06abbc1
3 #include <WINGs/WINGsP.h>
4 #include <X11/cursorfont.h>
6 #include "wtableview.h"
9 const char *WMTableViewSelectionDidChangeNotification = "WMTableViewSelectionDidChangeNotification";
12 struct W_TableColumn {
13 WMTableView *table;
14 WMWidget *titleW;
15 char *title;
16 int width;
17 int minWidth;
18 int maxWidth;
20 void *id;
22 WMTableColumnDelegate *delegate;
24 unsigned resizable:1;
25 unsigned editable:1;
30 static void handleResize(W_ViewDelegate *self, WMView *view);
32 static void rearrangeHeader(WMTableView *table);
34 static WMRange rowsInRect(WMTableView *table, WMRect rect);
37 WMTableColumn *WMCreateTableColumn(char *title)
39 WMTableColumn *col = wmalloc(sizeof(WMTableColumn));
41 col->table = NULL;
42 col->titleW = NULL;
43 col->width = 50;
44 col->minWidth = 5;
45 col->maxWidth = 0;
47 col->id = NULL;
49 col->title = wstrdup(title);
51 col->delegate = NULL;
53 col->resizable = 1;
54 col->editable = 0;
56 return col;
60 void WMSetTableColumnId(WMTableColumn *column, void *id)
62 column->id = id;
66 void *WMGetTableColumnId(WMTableColumn *column)
68 return column->id;
72 void WMSetTableColumnWidth(WMTableColumn *column, unsigned width)
74 if (column->maxWidth == 0)
75 column->width = WMAX(column->minWidth, width);
76 else
77 column->width = WMAX(column->minWidth, WMIN(column->maxWidth, width));
79 if (column->table) {
80 rearrangeHeader(column->table);
85 void WMSetTableColumnDelegate(WMTableColumn *column,
86 WMTableColumnDelegate *delegate)
88 column->delegate = delegate;
92 void WMSetTableColumnConstraints(WMTableColumn *column,
93 unsigned minWidth, unsigned maxWidth)
95 wassertr(minWidth <= maxWidth);
97 column->minWidth = minWidth;
98 column->maxWidth = maxWidth;
100 if (column->width < column->minWidth)
101 WMSetTableColumnWidth(column, column->minWidth);
102 else if (column->width > column->maxWidth || column->maxWidth == 0)
103 WMSetTableColumnWidth(column, column->maxWidth);
107 void WMSetTableColumnEditable(WMTableColumn *column, Bool flag)
109 column->editable = flag;
113 WMTableView *WMGetTableColumnTableView(WMTableColumn *column)
115 return column->table;
120 struct W_TableView {
121 W_Class widgetClass;
122 WMView *view;
124 WMFrame *header;
126 WMLabel *corner;
128 WMScrollView *scrollView;
129 WMView *tableView;
131 WMArray *columns;
132 WMArray *splitters;
134 WMArray *selectedRows;
136 int tableWidth;
138 int rows;
140 GC gridGC;
141 WMColor *gridColor;
143 Cursor splitterCursor;
145 void *dataSource;
147 WMTableViewDelegate *delegate;
149 WMAction *action;
150 void *clientData;
152 void *clickedColumn;
153 int clickedRow;
155 int editingRow;
157 unsigned headerHeight;
159 unsigned rowHeight;
161 unsigned dragging:1;
162 unsigned drawsGrid:1;
163 unsigned canSelectRow:1;
164 unsigned canSelectMultiRows:1;
165 unsigned canDeselectRow:1;
168 static W_Class tableClass = 0;
171 static W_ViewDelegate viewDelegate = {
172 NULL,
173 NULL,
174 handleResize,
175 NULL,
176 NULL
183 static void handleEvents(XEvent *event, void *data);
184 static void handleTableEvents(XEvent *event, void *data);
187 static void scrollObserver(void *self, WMNotification *notif)
189 WMTableView *table = (WMTableView*)self;
190 WMRect rect;
191 int i, x;
193 rect = WMGetScrollViewVisibleRect(table->scrollView);
195 x = 0;
196 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
197 WMTableColumn *column;
199 column = WMGetFromArray(table->columns, i);
201 WMMoveWidget(column->titleW, x - rect.pos.x, 0);
203 if (i > 0) {
204 WMView *splitter;
206 splitter = WMGetFromArray(table->splitters, i-1);
207 W_MoveView(splitter, x - rect.pos.x - 1, 0);
210 x += W_VIEW_WIDTH(WMWidgetView(column->titleW)) + 1;
215 static void splitterHandler(XEvent *event, void *data)
217 WMTableView *table = (WMTableView*)data;
218 int done = 0;
220 while (!done) {
221 XEvent ev;
223 WMMaskEvent(event->xany.display, ButtonMotionMask|ButtonReleaseMask,
224 &ev);
226 switch (event->type) {
227 case MotionNotify:
228 printf("%i\n", event->xmotion.x);
229 break;
231 case ButtonRelease:
232 done = 1;
233 break;
240 WMTableView *WMCreateTableView(WMWidget *parent)
242 WMTableView *table = wmalloc(sizeof(WMTableView));
243 WMScreen *scr = WMWidgetScreen(parent);
245 memset(table, 0, sizeof(WMTableView));
247 if (!tableClass) {
248 tableClass = W_RegisterUserWidget();
250 table->widgetClass = tableClass;
252 table->view = W_CreateView(W_VIEW(parent));
253 if (!table->view)
254 goto error;
255 table->view->self = table;
257 table->view->delegate = &viewDelegate;
259 table->headerHeight = 20;
261 table->scrollView = WMCreateScrollView(table);
262 if (!table->scrollView)
263 goto error;
264 WMResizeWidget(table->scrollView, 10, 10);
265 WMSetScrollViewHasVerticalScroller(table->scrollView, True);
266 WMSetScrollViewHasHorizontalScroller(table->scrollView, True);
268 WMScroller *scroller;
269 scroller = WMGetScrollViewHorizontalScroller(table->scrollView);
270 WMAddNotificationObserver(scrollObserver, table,
271 WMScrollerDidScrollNotification,
272 scroller);
274 WMMoveWidget(table->scrollView, 1, 2+table->headerHeight);
275 WMMapWidget(table->scrollView);
277 table->header = WMCreateFrame(table);
278 WMMoveWidget(table->header, 22, 2);
279 WMMapWidget(table->header);
280 WMSetFrameRelief(table->header, WRFlat);
282 table->corner = WMCreateLabel(table);
283 WMResizeWidget(table->corner, 20, table->headerHeight);
284 WMMoveWidget(table->corner, 2, 2);
285 WMMapWidget(table->corner);
286 WMSetLabelRelief(table->corner, WRRaised);
287 WMSetWidgetBackgroundColor(table->corner, scr->darkGray);
290 table->tableView = W_CreateView(W_VIEW(parent));
291 if (!table->tableView)
292 goto error;
293 table->tableView->self = table;
294 W_ResizeView(table->tableView, 100, 1000);
295 W_MapView(table->tableView);
297 WMSetScrollViewContentView(table->scrollView, table->tableView);
299 table->tableView->flags.dontCompressExpose = 1;
301 table->gridColor = WMCreateNamedColor(scr, "#cccccc", False);
302 /* table->gridColor = WMGrayColor(scr);*/
305 XGCValues gcv;
307 gcv.foreground = WMColorPixel(table->gridColor);
308 gcv.dashes = 1;
309 gcv.line_style = LineOnOffDash;
310 table->gridGC = XCreateGC(WMScreenDisplay(scr), W_DRAWABLE(scr),
311 GCForeground, &gcv);
314 table->editingRow = -1;
315 table->clickedRow = -1;
317 table->drawsGrid = 1;
318 table->rowHeight = 16;
320 WMSetScrollViewLineScroll(table->scrollView, table->rowHeight);
322 table->tableWidth = 1;
324 table->columns = WMCreateArray(4);
325 table->splitters = WMCreateArray(4);
327 table->selectedRows = WMCreateArray(16);
329 table->splitterCursor = XCreateFontCursor(WMScreenDisplay(scr),
330 XC_sb_h_double_arrow);
332 table->canSelectRow = 1;
334 WMCreateEventHandler(table->view, ExposureMask|StructureNotifyMask,
335 handleEvents, table);
337 WMCreateEventHandler(table->tableView, ExposureMask|ButtonPressMask|
338 ButtonReleaseMask|ButtonMotionMask,
339 handleTableEvents, table);
341 return table;
343 error:
344 if (table->scrollView)
345 WMDestroyWidget(table->scrollView);
346 if (table->tableView)
347 W_DestroyView(table->tableView);
348 if (table->view)
349 W_DestroyView(table->view);
350 wfree(table);
351 return NULL;
355 void WMAddTableViewColumn(WMTableView *table, WMTableColumn *column)
357 int width;
358 int i;
359 WMScreen *scr = WMWidgetScreen(table);
360 int count;
362 column->table = table;
364 WMAddToArray(table->columns, column);
366 if (!column->titleW) {
367 column->titleW = WMCreateLabel(table);
368 WMSetLabelRelief(column->titleW, WRRaised);
369 WMSetLabelFont(column->titleW, scr->boldFont);
370 WMSetLabelTextColor(column->titleW, scr->white);
371 WMSetWidgetBackgroundColor(column->titleW, scr->darkGray);
372 WMSetLabelText(column->titleW, column->title);
373 W_ReparentView(WMWidgetView(column->titleW),
374 WMWidgetView(table->header), 0, 0);
375 if (W_VIEW_REALIZED(table->view))
376 WMRealizeWidget(column->titleW);
377 WMMapWidget(column->titleW);
380 if (WMGetArrayItemCount(table->columns) > 1) {
381 WMView *splitter = W_CreateView(WMWidgetView(table->header));
383 W_SetViewBackgroundColor(splitter, WMWhiteColor(scr));
385 if (W_VIEW_REALIZED(table->view))
386 W_RealizeView(splitter);
388 W_ResizeView(splitter, 2, table->headerHeight-1);
389 W_MapView(splitter);
391 W_SetViewCursor(splitter, table->splitterCursor);
392 WMCreateEventHandler(splitter, ButtonPressMask,
393 splitterHandler, table);
395 WMAddToArray(table->splitters, splitter);
398 rearrangeHeader(table);
402 void WMSetTableViewHeaderHeight(WMTableView *table, unsigned height)
404 table->headerHeight = height;
406 handleResize(NULL, table->view);
410 void WMSetTableViewDelegate(WMTableView *table, WMTableViewDelegate *delegate)
412 table->delegate = delegate;
416 void WMSetTableViewAction(WMTableView *table, WMAction *action, void *clientData)
418 table->action = action;
420 table->clientData = clientData;
424 void *WMGetTableViewClickedColumn(WMTableView *table)
426 return table->clickedColumn;
430 int WMGetTableViewClickedRow(WMTableView *table)
432 return table->clickedRow;
436 WMArray *WMGetTableViewSelectedRows(WMTableView *table)
438 return table->selectedRows;
442 WMView *WMGetTableViewDocumentView(WMTableView *table)
444 return table->tableView;
448 void *WMTableViewDataForCell(WMTableView *table, WMTableColumn *column,
449 int row)
451 return (*table->delegate->valueForCell)(table->delegate, column, row);
455 void WMSetTableViewDataForCell(WMTableView *table, WMTableColumn *column,
456 int row, void *data)
458 (*table->delegate->setValueForCell)(table->delegate, column, row, data);
462 WMRect WMTableViewRectForCell(WMTableView *table, WMTableColumn *column,
463 int row)
465 WMRect rect;
466 int i;
468 rect.pos.x = 0;
469 rect.pos.y = row * table->rowHeight;
470 rect.size.height = table->rowHeight;
472 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
473 WMTableColumn *col;
474 col = WMGetFromArray(table->columns, i);
476 if (col == column) {
477 rect.size.width = col->width;
478 break;
481 rect.pos.x += col->width;
483 return rect;
487 void WMSetTableViewDataSource(WMTableView *table, void *source)
489 table->dataSource = source;
493 void *WMGetTableViewDataSource(WMTableView *table)
495 return table->dataSource;
499 void WMSetTableViewBackgroundColor(WMTableView *table, WMColor *color)
501 W_SetViewBackgroundColor(table->tableView, color);
505 void WMSetTableViewGridColor(WMTableView *table, WMColor *color)
507 WMReleaseColor(table->gridColor);
508 table->gridColor = WMRetainColor(color);
509 XSetForeground(WMScreenDisplay(WMWidgetScreen(table)), table->gridGC,
510 WMColorPixel(color));
515 void WMSetTableViewRowHeight(WMTableView *table, int height)
517 table->rowHeight = height;
519 WMRedisplayWidget(table);
523 void WMScrollTableViewRowToVisible(WMTableView *table, int row)
525 WMScroller *scroller;
526 WMRange range;
527 WMRect rect;
528 int newY, tmp;
530 rect = WMGetScrollViewVisibleRect(table->scrollView);
531 range = rowsInRect(table, rect);
533 scroller = WMGetScrollViewVerticalScroller(table->scrollView);
535 if (row < range.position) {
536 newY = row * table->rowHeight - rect.size.height / 2;
537 } else if (row >= range.position + range.count) {
538 newY = row * table->rowHeight - rect.size.height / 2;
539 } else {
540 return;
542 tmp = table->rows*table->rowHeight - rect.size.height;
543 newY = WMAX(0, WMIN(newY, tmp));
544 WMScrollViewScrollPoint(table->scrollView, rect.pos.x, newY);
549 static void drawGrid(WMTableView *table, WMRect rect)
551 WMScreen *scr = WMWidgetScreen(table);
552 Display *dpy = WMScreenDisplay(scr);
553 int i;
554 int y1, y2;
555 int x1, x2;
556 int xx;
557 Drawable d = W_VIEW_DRAWABLE(table->tableView);
558 GC gc = table->gridGC;
560 #if 0
561 char dashl[1] = {1};
563 XSetDashes(dpy, gc, 0, dashl, 1);
565 y1 = (rect.pos.y/table->rowHeight - 1)*table->rowHeight;
566 y2 = y1 + (rect.size.height/table->rowHeight+2)*table->rowHeight;
567 #endif
568 y1 = 0;
569 y2 = W_VIEW_HEIGHT(table->tableView);
571 xx = 0;
572 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
573 WMTableColumn *column;
575 if (xx >= rect.pos.x && xx <= rect.pos.x+rect.size.width) {
576 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
578 column = WMGetFromArray(table->columns, i);
579 xx += column->width;
581 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
584 x1 = rect.pos.x;
585 x2 = WMIN(x1 + rect.size.width, xx);
587 if (x2 <= x1)
588 return;
589 #if 0
590 XSetDashes(dpy, gc, (rect.pos.x&1), dashl, 1);
591 #endif
593 y1 = rect.pos.y - rect.pos.y%table->rowHeight;
594 y2 = y1 + rect.size.height + table->rowHeight;
596 for (i = y1; i <= y2; i += table->rowHeight) {
597 XDrawLine(dpy, d, gc, x1, i, x2, i);
602 static WMRange columnsInRect(WMTableView *table, WMRect rect)
604 WMTableColumn *column;
605 int width;
606 int i , j;
607 int totalColumns = WMGetArrayItemCount(table->columns);
608 WMRange range;
610 j = 0;
611 width = 0;
612 for (i = 0; i < totalColumns; i++) {
613 column = WMGetFromArray(table->columns, i);
614 if (j == 0) {
615 if (width <= rect.pos.x && width + column->width > rect.pos.x) {
616 range.position = i;
617 j = 1;
619 } else {
620 range.count++;
621 if (width > rect.pos.x + rect.size.width) {
622 break;
625 width += column->width;
627 range.count = WMAX(1, WMIN(range.count, totalColumns - range.position));
628 return range;
632 static WMRange rowsInRect(WMTableView *table, WMRect rect)
634 WMRange range;
635 int rh = table->rowHeight;
636 int dif;
638 dif = rect.pos.y % rh;
640 range.position = WMAX(0, (rect.pos.y - dif) / rh);
641 range.count = WMAX(1, WMIN((rect.size.height + dif) / rh, table->rows));
643 return range;
647 static void drawRow(WMTableView *table, int row, WMRect clipRect)
649 int i;
650 WMRange cols = columnsInRect(table, clipRect);
651 WMTableColumn *column;
653 for (i = cols.position; i < cols.position+cols.count; i++) {
654 column = WMGetFromArray(table->columns, i);
656 wassertr(column->delegate && column->delegate->drawCell);
658 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
659 (*column->delegate->drawSelectedCell)(column->delegate, column, row);
660 else
661 (*column->delegate->drawCell)(column->delegate, column, row);
666 static void drawFullRow(WMTableView *table, int row)
668 int i;
669 WMTableColumn *column;
671 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
672 column = WMGetFromArray(table->columns, i);
674 wassertr(column->delegate && column->delegate->drawCell);
676 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
677 (*column->delegate->drawSelectedCell)(column->delegate, column, row);
678 else
679 (*column->delegate->drawCell)(column->delegate, column, row);
684 static void setRowSelected(WMTableView *table, unsigned row, Bool flag)
686 int repaint = 0;
688 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound) {
689 if (!flag) {
690 WMRemoveFromArray(table->selectedRows, (void*)row);
691 repaint = 1;
693 } else {
694 if (flag) {
695 WMAddToArray(table->selectedRows, (void*)row);
696 repaint = 1;
699 if (repaint && row < table->rows) {
700 drawFullRow(table, row);
705 static void repaintTable(WMTableView *table, int x, int y,
706 int width, int height)
708 WMRect rect;
709 WMRange rows;
710 int i;
712 wassertr(table->delegate && table->delegate->numberOfRows);
713 i = (*table->delegate->numberOfRows)(table->delegate, table);
715 if (i != table->rows) {
716 table->rows = i;
717 W_ResizeView(table->tableView, table->tableWidth,
718 table->rows * table->rowHeight + 1);
722 rect.pos = wmkpoint(x,y);
723 rect.size = wmksize(width, height);
725 if (table->drawsGrid) {
726 drawGrid(table, rect);
729 rows = rowsInRect(table, rect);
730 for (i = rows.position;
731 i < WMIN(rows.position+rows.count + 1, table->rows);
732 i++) {
733 drawRow(table, i, rect);
738 static void stopRowEdit(WMTableView *table, int row)
740 int i;
741 WMTableColumn *column;
743 table->editingRow = -1;
744 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
745 column = WMGetFromArray(table->columns, i);
747 if (column->delegate && column->delegate->endCellEdit)
748 (*column->delegate->endCellEdit)(column->delegate, column, row);
754 void WMEditTableViewRow(WMTableView *table, int row)
756 int i;
757 WMTableColumn *column;
759 if (table->editingRow >= 0) {
760 stopRowEdit(table, table->editingRow);
763 table->editingRow = row;
764 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
765 column = WMGetFromArray(table->columns, i);
767 if (column->delegate && column->delegate->beginCellEdit)
768 (*column->delegate->beginCellEdit)(column->delegate, column, row);
773 void WMSelectTableViewRow(WMTableView *table, int row)
775 if (table->clickedRow >= 0)
776 setRowSelected(table, table->clickedRow, False);
778 if (row >= table->rows) {
779 return;
782 setRowSelected(table, row, True);
783 table->clickedRow = row;
785 if (table->action)
786 (*table->action)(table, table->clientData);
787 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
788 table, NULL);
792 void WMReloadTableView(WMTableView *table)
794 WMRect rect = WMGetScrollViewVisibleRect(table->scrollView);
796 /* when this is called, nothing in the table can be assumed to be
797 * like the last time we accessed it (ie, rows might have disappeared) */
799 WMEmptyArray(table->selectedRows);
801 if (table->clickedRow >= 0) {
802 table->clickedRow = -1;
804 if (table->action)
805 (*table->action)(table, table->clientData);
806 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
807 table, NULL);
810 repaintTable(table, 0, 0,
811 W_VIEW_WIDTH(table->tableView), rect.size.height);
815 void WMNoteTableViewNumberOfRowsChanged(WMTableView *table)
817 WMReloadTableView(table);
821 static void handleTableEvents(XEvent *event, void *data)
823 WMTableView *table = (WMTableView*)data;
824 int row;
826 switch (event->type) {
827 case ButtonPress:
828 if (event->xbutton.button == Button1) {
829 row = event->xbutton.y/table->rowHeight;
830 if (row != table->clickedRow) {
831 setRowSelected(table, table->clickedRow, False);
832 setRowSelected(table, row, True);
833 table->clickedRow = row;
834 table->dragging = 1;
837 break;
839 case MotionNotify:
840 if (table->dragging && event->xmotion.y >= 0) {
841 row = event->xmotion.y/table->rowHeight;
842 if (table->clickedRow != row && row >= 0) {
843 setRowSelected(table, table->clickedRow, False);
844 setRowSelected(table, row, True);
845 table->clickedRow = row;
848 break;
850 case ButtonRelease:
851 if (event->xbutton.button == Button1) {
852 if (table->action)
853 (*table->action)(table, table->clientData);
854 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
855 table, NULL);
856 table->dragging = 0;
858 break;
860 case Expose:
861 repaintTable(table, event->xexpose.x, event->xexpose.y,
862 event->xexpose.width, event->xexpose.height);
863 break;
868 static void handleEvents(XEvent *event, void *data)
870 WMTableView *table = (WMTableView*)data;
871 WMScreen *scr = WMWidgetScreen(table);
873 switch (event->type) {
874 case Expose:
875 W_DrawRelief(scr, W_VIEW_DRAWABLE(table->view), 0, 0,
876 W_VIEW_WIDTH(table->view), W_VIEW_HEIGHT(table->view),
877 WRSunken);
879 if (event->xexpose.serial == 0) {
880 WMRect rect = WMGetScrollViewVisibleRect(table->scrollView);
882 repaintTable(table, rect.pos.x, rect.pos.y,
883 rect.size.width, rect.size.height);
885 break;
890 static void handleResize(W_ViewDelegate *self, WMView *view)
892 int width;
893 int height;
894 WMTableView *table = view->self;
896 width = W_VIEW_WIDTH(view) - 2;
897 height = W_VIEW_HEIGHT(view) - 3;
899 height -= table->headerHeight; /* table header */
901 if (table->corner)
902 WMResizeWidget(table->corner, 20, table->headerHeight);
904 if (table->scrollView) {
905 WMMoveWidget(table->scrollView, 1, table->headerHeight + 2);
906 WMResizeWidget(table->scrollView, width, height);
908 if (table->header)
909 WMResizeWidget(table->header, width - 21, table->headerHeight);
913 static void rearrangeHeader(WMTableView *table)
915 int width;
916 int count;
917 int i;
918 width = 0;
920 count = WMGetArrayItemCount(table->columns);
921 for (i = 0; i < count; i++) {
922 WMTableColumn *column = WMGetFromArray(table->columns, i);
924 WMMoveWidget(column->titleW, width, 0);
925 WMResizeWidget(column->titleW, column->width-1, table->headerHeight);
927 if (i > 0) {
928 WMView *splitter = WMGetFromArray(table->splitters, i-1);
930 W_MoveView(splitter, width-1, 0);
932 width += column->width;
935 wassertr(table->delegate && table->delegate->numberOfRows);
937 table->rows = table->delegate->numberOfRows(table->delegate, table);
939 W_ResizeView(table->tableView, width+1,
940 table->rows * table->rowHeight + 1);
942 table->tableWidth = width + 1;