changed indentation to use spaces only
[wmaker-crm.git] / WINGs / Extras / wtableview.c
blob94a2c3bb1df952e6109814ab83bd1eb5c146a7f0
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;
29 static void handleResize(W_ViewDelegate *self, WMView *view);
31 static void rearrangeHeader(WMTableView *table);
33 static WMRange rowsInRect(WMTableView *table, WMRect rect);
36 WMTableColumn*
37 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
61 WMSetTableColumnId(WMTableColumn *column, void *id)
63 column->id = id;
67 void*
68 WMGetTableColumnId(WMTableColumn *column)
70 return column->id;
74 void
75 WMSetTableColumnWidth(WMTableColumn *column, unsigned width)
77 if (column->maxWidth == 0)
78 column->width = WMAX(column->minWidth, width);
79 else
80 column->width = WMAX(column->minWidth, WMIN(column->maxWidth, width));
82 if (column->table) {
83 rearrangeHeader(column->table);
88 void
89 WMSetTableColumnDelegate(WMTableColumn *column, WMTableColumnDelegate *delegate)
91 column->delegate = delegate;
95 void
96 WMSetTableColumnConstraints(WMTableColumn *column, unsigned minWidth,
97 unsigned maxWidth)
99 wassertr(maxWidth == 0 || minWidth <= maxWidth);
101 column->minWidth = minWidth;
102 column->maxWidth = maxWidth;
104 if (column->width < column->minWidth)
105 WMSetTableColumnWidth(column, column->minWidth);
106 else if (column->width > column->maxWidth && column->maxWidth != 0)
107 WMSetTableColumnWidth(column, column->maxWidth);
111 void
112 WMSetTableColumnEditable(WMTableColumn *column, Bool flag)
114 column->editable = ((flag==0) ? 0 : 1);
118 WMTableView*
119 WMGetTableColumnTableView(WMTableColumn *column)
121 return column->table;
126 struct W_TableView {
127 W_Class widgetClass;
128 WMView *view;
130 WMFrame *header;
132 WMLabel *corner;
134 WMScroller *hscroll;
135 WMScroller *vscroll;
136 WMView *tableView;
138 WMPixmap *viewBuffer;
140 WMArray *columns;
141 WMArray *splitters;
143 WMArray *selectedRows;
145 int tableWidth;
147 int rows;
149 WMColor *backColor;
151 GC gridGC;
152 WMColor *gridColor;
154 Cursor splitterCursor;
156 void *dataSource;
158 WMTableViewDelegate *delegate;
160 WMAction *action;
161 void *clientData;
163 void *clickedColumn;
164 int clickedRow;
166 int editingRow;
168 unsigned headerHeight;
170 unsigned rowHeight;
172 unsigned dragging:1;
173 unsigned drawsGrid:1;
174 unsigned canSelectRow:1;
175 unsigned canSelectMultiRows:1;
176 unsigned canDeselectRow:1;
178 unsigned int hasVScroller:1;
179 unsigned int hasHScroller:1;
182 static W_Class tableClass = 0;
185 static W_ViewDelegate viewDelegate = {
186 NULL,
187 NULL,
188 handleResize,
189 NULL,
190 NULL
195 static void reorganizeInterior(WMTableView *table);
198 static void handleEvents(XEvent *event, void *data);
199 static void handleTableEvents(XEvent *event, void *data);
200 static void repaintTable(WMTableView *table);
202 static WMSize
203 getTotalSize(WMTableView *table)
205 WMSize size;
206 int i;
208 /* get width from columns */
209 size.width = 0;
210 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
211 WMTableColumn *column;
213 column = WMGetFromArray(table->columns, i);
215 size.width += column->width;
218 /* get height from rows */
219 size.height = table->rows * table->rowHeight;
221 return size;
225 static WMRect
226 getVisibleRect(WMTableView *table)
228 WMSize size = getTotalSize(table);
229 WMRect rect;
231 if (table->vscroll) {
232 rect.size.height = size.height * WMGetScrollerKnobProportion(table->vscroll);
233 rect.pos.y = (size.height - rect.size.height) * WMGetScrollerValue(table->vscroll);
234 } else {
235 rect.size.height = size.height;
236 rect.pos.y = 0;
239 if (table->hscroll) {
240 rect.size.width = size.width * WMGetScrollerKnobProportion(table->hscroll);
241 rect.pos.x = (size.width - rect.size.width) * WMGetScrollerValue(table->hscroll);
242 } else {
243 rect.size.width = size.width;
244 rect.pos.x = 0;
247 return rect;
251 static void
252 scrollToPoint(WMTableView *table, int x, int y)
254 WMSize size = getTotalSize(table);
255 int i;
256 float value, prop;
258 if (table->hscroll) {
259 if (size.width > W_VIEW_WIDTH(table->tableView)) {
260 prop = (float)W_VIEW_WIDTH(table->tableView) / (float)size.width;
261 value = (float)x / (float)(size.width - W_VIEW_WIDTH(table->tableView));
262 } else {
263 prop = 1.0;
264 value = 0.0;
266 WMSetScrollerParameters(table->hscroll, value, prop);
269 if (table->vscroll) {
270 if (size.height > W_VIEW_HEIGHT(table->tableView)) {
271 prop = (float)W_VIEW_HEIGHT(table->tableView) / (float)size.height;
272 value = (float)y / (float)(size.height - W_VIEW_HEIGHT(table->tableView));
273 } else {
274 prop = 1.0;
275 value = 0.0;
278 WMSetScrollerParameters(table->vscroll, value, prop);
282 if (table->editingRow >= 0) {
283 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
284 WMTableColumn *column;
286 column = WMGetFromArray(table->columns, i);
288 if (column->delegate && column->delegate->beginCellEdit)
289 (*column->delegate->beginCellEdit)(column->delegate, column,
290 table->editingRow);
294 repaintTable(table);
298 static void
299 adjustScrollers(WMTableView *table)
301 WMSize size = getTotalSize(table);
302 WMSize vsize = WMGetViewSize(table->tableView);
303 float prop, value;
304 float oprop, ovalue;
306 if (table->hscroll) {
307 if (size.width <= vsize.width) {
308 value = 0.0;
309 prop = 1.0;
310 } else {
311 oprop = WMGetScrollerKnobProportion(table->hscroll);
312 if (oprop == 0.0)
313 oprop = 1.0;
314 ovalue = WMGetScrollerValue(table->hscroll);
316 prop = (float)vsize.width/(float)size.width;
317 value = prop*ovalue / oprop;
319 WMSetScrollerParameters(table->hscroll, value, prop);
322 if (table->vscroll) {
323 if (size.height <= vsize.height) {
324 value = 0.0;
325 prop = 1.0;
326 } else {
327 oprop = WMGetScrollerKnobProportion(table->vscroll);
328 if (oprop == 0.0)
329 oprop = 1.0;
330 ovalue = WMGetScrollerValue(table->vscroll);
332 prop = (float)vsize.height/(float)size.height;
333 value = prop*ovalue / oprop;
335 WMSetScrollerParameters(table->vscroll, value, prop);
340 static void
341 doScroll(WMWidget *self, void *data)
343 WMTableView *table = (WMTableView*)data;
344 float value;
345 float vpsize;
346 float size;
347 WMSize ts = getTotalSize(table);
349 value = WMGetScrollerValue(self);
351 if (table->hscroll == (WMScroller *)self) {
352 vpsize = W_VIEW_WIDTH(table->tableView);
353 size = ts.width;
354 } else {
355 vpsize = W_VIEW_HEIGHT(table->tableView);
356 size = ts.height;
359 switch (WMGetScrollerHitPart(self)) {
360 case WSDecrementWheel:
361 case WSDecrementLine:
362 value -= (float)table->rowHeight / size;
363 if (value < 0)
364 value = 0.0;
365 WMSetScrollerParameters(self, value,
366 WMGetScrollerKnobProportion(self));
367 repaintTable(table);
368 break;
370 case WSIncrementWheel:
371 case WSIncrementLine:
372 value += (float)table->rowHeight / size;
373 if (value > 1.0)
374 value = 1.0;
375 WMSetScrollerParameters(self, value,
376 WMGetScrollerKnobProportion(self));
377 repaintTable(table);
378 break;
380 case WSKnob:
381 repaintTable(table);
382 break;
384 case WSDecrementPage:
385 value -= vpsize / size;
386 if (value < 0.0)
387 value = 0.0;
388 WMSetScrollerParameters(self, value,
389 WMGetScrollerKnobProportion(self));
390 repaintTable(table);
391 break;
393 case WSIncrementPage:
394 value += vpsize / size;
395 if (value > 1.0)
396 value = 1.0;
397 WMSetScrollerParameters(self, value,
398 WMGetScrollerKnobProportion(self));
399 repaintTable(table);
400 break;
403 case WSNoPart:
404 case WSKnobSlot:
405 break;
408 if (table->editingRow >= 0) {
409 int i;
410 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
411 WMTableColumn *column;
413 column = WMGetFromArray(table->columns, i);
415 if (column->delegate && column->delegate->beginCellEdit)
416 (*column->delegate->beginCellEdit)(column->delegate, column,
417 table->editingRow);
422 if (table->hscroll == self) {
423 int x = 0;
424 int i;
425 WMRect rect = getVisibleRect(table);
427 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
428 WMTableColumn *column;
429 WMView *splitter;
431 column = WMGetFromArray(table->columns, i);
433 WMMoveWidget(column->titleW, x - rect.pos.x, 0);
435 x += W_VIEW_WIDTH(WMWidgetView(column->titleW)) + 1;
437 splitter = WMGetFromArray(table->splitters, i);
438 W_MoveView(splitter, x - rect.pos.x - 1, 0);
444 static void
445 splitterHandler(XEvent *event, void *data)
447 WMTableColumn *column = (WMTableColumn*)data;
448 WMTableView *table = column->table;
449 int done = 0;
450 int cx, ox, offsX;
451 WMPoint pos;
452 WMScreen *scr = WMWidgetScreen(table);
453 GC gc = scr->ixorGC;
454 Display *dpy = WMScreenDisplay(scr);
455 int h = WMWidgetHeight(table) - 22;
456 Window w = WMViewXID(table->view);
458 pos = WMGetViewPosition(WMWidgetView(column->titleW));
460 offsX = pos.x + column->width;
462 ox = cx = offsX;
464 XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
466 while (!done) {
467 XEvent ev;
469 WMMaskEvent(dpy, ButtonMotionMask|ButtonReleaseMask, &ev);
471 switch (ev.type) {
472 case MotionNotify:
473 ox = cx;
475 if (column->width + ev.xmotion.x < column->minWidth)
476 cx = pos.x + column->minWidth;
477 else if (column->maxWidth > 0
478 && column->width + ev.xmotion.x > column->maxWidth)
479 cx = pos.x + column->maxWidth;
480 else
481 cx = offsX + ev.xmotion.x;
483 XDrawLine(dpy, w, gc, ox+20, 0, ox+20, h);
484 XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
485 break;
487 case ButtonRelease:
488 column->width = cx - pos.x;
489 done = 1;
490 break;
494 XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
495 rearrangeHeader(table);
496 repaintTable(table);
500 static void
501 realizeTable(void *data, WMNotification *notif)
503 repaintTable(data);
507 WMTableView*
508 WMCreateTableView(WMWidget *parent)
510 WMTableView *table = wmalloc(sizeof(WMTableView));
511 WMScreen *scr = WMWidgetScreen(parent);
513 memset(table, 0, sizeof(WMTableView));
515 if (!tableClass) {
516 tableClass = W_RegisterUserWidget();
518 table->widgetClass = tableClass;
520 table->view = W_CreateView(W_VIEW(parent));
521 if (!table->view)
522 goto error;
523 table->view->self = table;
525 table->view->delegate = &viewDelegate;
527 table->headerHeight = 20;
529 table->hscroll = WMCreateScroller(table);
530 WMSetScrollerAction(table->hscroll, doScroll, table);
531 WMMoveWidget(table->hscroll, 1, 2+table->headerHeight);
532 WMMapWidget(table->hscroll);
534 table->hasHScroller = 1;
536 table->vscroll = WMCreateScroller(table);
537 WMSetScrollerArrowsPosition(table->vscroll, WSAMaxEnd);
538 WMSetScrollerAction(table->vscroll, doScroll, table);
539 WMMoveWidget(table->vscroll, 1, 2+table->headerHeight);
540 WMMapWidget(table->vscroll);
542 table->hasVScroller = 1;
544 table->header = WMCreateFrame(table);
545 WMMoveWidget(table->header, 22, 2);
546 WMMapWidget(table->header);
547 WMSetFrameRelief(table->header, WRFlat);
549 table->corner = WMCreateLabel(table);
550 WMResizeWidget(table->corner, 20, table->headerHeight);
551 WMMoveWidget(table->corner, 2, 2);
552 WMMapWidget(table->corner);
553 WMSetLabelRelief(table->corner, WRRaised);
554 WMSetWidgetBackgroundColor(table->corner, scr->darkGray);
557 table->tableView = W_CreateView(table->view);
558 if (!table->tableView)
559 goto error;
560 table->tableView->self = table;
561 W_MapView(table->tableView);
563 WMAddNotificationObserver(realizeTable, table, WMViewRealizedNotification,
564 table->tableView);
566 table->tableView->flags.dontCompressExpose = 1;
568 table->gridColor = WMCreateNamedColor(scr, "#cccccc", False);
569 /* table->gridColor = WMGrayColor(scr);*/
572 XGCValues gcv;
574 table->backColor = WMWhiteColor(scr);
576 gcv.foreground = WMColorPixel(table->gridColor);
577 gcv.dashes = 1;
578 gcv.line_style = LineOnOffDash;
579 table->gridGC = XCreateGC(WMScreenDisplay(scr), W_DRAWABLE(scr),
580 GCForeground, &gcv);
583 table->editingRow = -1;
584 table->clickedRow = -1;
586 table->drawsGrid = 1;
587 table->rowHeight = 16;
589 table->tableWidth = 1;
591 table->columns = WMCreateArray(4);
592 table->splitters = WMCreateArray(4);
594 table->selectedRows = WMCreateArray(16);
596 table->splitterCursor = XCreateFontCursor(WMScreenDisplay(scr),
597 XC_sb_h_double_arrow);
599 table->canSelectRow = 1;
601 WMCreateEventHandler(table->view, ExposureMask|StructureNotifyMask,
602 handleEvents, table);
604 WMCreateEventHandler(table->tableView, ExposureMask|ButtonPressMask|
605 ButtonReleaseMask|ButtonMotionMask,
606 handleTableEvents, table);
608 WMResizeWidget(table, 50, 50);
610 return table;
612 error:
613 if (table->tableView)
614 W_DestroyView(table->tableView);
615 if (table->view)
616 W_DestroyView(table->view);
617 wfree(table);
618 return NULL;
622 void
623 WMAddTableViewColumn(WMTableView *table, WMTableColumn *column)
625 WMScreen *scr = WMWidgetScreen(table);
627 column->table = table;
629 WMAddToArray(table->columns, column);
631 if (!column->titleW) {
632 column->titleW = WMCreateLabel(table);
633 WMSetLabelRelief(column->titleW, WRRaised);
634 WMSetLabelFont(column->titleW, scr->boldFont);
635 WMSetLabelTextColor(column->titleW, scr->white);
636 WMSetWidgetBackgroundColor(column->titleW, scr->darkGray);
637 WMSetLabelText(column->titleW, column->title);
638 W_ReparentView(WMWidgetView(column->titleW),
639 WMWidgetView(table->header), 0, 0);
640 if (W_VIEW_REALIZED(table->view))
641 WMRealizeWidget(column->titleW);
642 WMMapWidget(column->titleW);
646 WMView *splitter = W_CreateView(WMWidgetView(table->header));
648 W_SetViewBackgroundColor(splitter, WMWhiteColor(scr));
650 if (W_VIEW_REALIZED(table->view))
651 W_RealizeView(splitter);
653 W_ResizeView(splitter, 2, table->headerHeight);
654 W_MapView(splitter);
656 W_SetViewCursor(splitter, table->splitterCursor);
657 WMCreateEventHandler(splitter, ButtonPressMask|ButtonReleaseMask,
658 splitterHandler, column);
660 WMAddToArray(table->splitters, splitter);
663 rearrangeHeader(table);
667 void
668 WMSetTableViewHeaderHeight(WMTableView *table, unsigned height)
670 table->headerHeight = height;
672 handleResize(NULL, table->view);
676 void
677 WMSetTableViewDelegate(WMTableView *table, WMTableViewDelegate *delegate)
679 table->delegate = delegate;
683 void
684 WMSetTableViewAction(WMTableView *table, WMAction *action, void *clientData)
686 table->action = action;
688 table->clientData = clientData;
692 void*
693 WMGetTableViewClickedColumn(WMTableView *table)
695 return table->clickedColumn;
700 WMGetTableViewClickedRow(WMTableView *table)
702 return table->clickedRow;
706 WMArray*
707 WMGetTableViewSelectedRows(WMTableView *table)
709 return table->selectedRows;
713 WMView*
714 WMGetTableViewDocumentView(WMTableView *table)
716 return table->tableView;
720 void*
721 WMTableViewDataForCell(WMTableView *table, WMTableColumn *column, int row)
723 return (*table->delegate->valueForCell)(table->delegate, column, row);
727 void
728 WMSetTableViewDataForCell(WMTableView *table, WMTableColumn *column, int row,
729 void *data)
731 (*table->delegate->setValueForCell)(table->delegate, column, row, data);
735 WMRect
736 WMTableViewRectForCell(WMTableView *table, WMTableColumn *column, int row)
738 WMRect rect;
739 int i;
741 rect.pos.x = 0;
742 rect.pos.y = row * table->rowHeight;
743 rect.size.height = table->rowHeight;
745 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
746 WMTableColumn *col;
747 col = WMGetFromArray(table->columns, i);
749 if (col == column) {
750 rect.size.width = col->width;
751 break;
754 rect.pos.x += col->width;
758 WMRect r = getVisibleRect(table);
760 rect.pos.y -= r.pos.y;
761 rect.pos.x -= r.pos.x;
764 return rect;
768 void
769 WMSetTableViewDataSource(WMTableView *table, void *source)
771 table->dataSource = source;
775 void*
776 WMGetTableViewDataSource(WMTableView *table)
778 return table->dataSource;
783 void
784 WMSetTableViewHasHorizontalScroller(WMTableView *tPtr, Bool flag)
786 if (flag) {
787 if (tPtr->hasHScroller)
788 return;
789 tPtr->hasHScroller = 1;
791 tPtr->hscroll = WMCreateScroller(tPtr);
792 WMSetScrollerAction(tPtr->hscroll, doScroll, tPtr);
793 WMSetScrollerArrowsPosition(tPtr->hscroll, WSAMaxEnd);
794 /* make it a horiz. scroller */
795 WMResizeWidget(tPtr->hscroll, 1, 2);
797 if (W_VIEW_REALIZED(tPtr->view)) {
798 WMRealizeWidget(tPtr->hscroll);
801 reorganizeInterior(tPtr);
803 WMMapWidget(tPtr->hscroll);
804 } else {
805 if (!tPtr->hasHScroller)
806 return;
807 tPtr->hasHScroller = 0;
809 WMUnmapWidget(tPtr->hscroll);
810 WMDestroyWidget(tPtr->hscroll);
811 tPtr->hscroll = NULL;
813 reorganizeInterior(tPtr);
817 #if 0
818 /* not supported by now */
819 void
820 WMSetTableViewHasVerticalScroller(WMTableView *tPtr, Bool flag)
822 if (flag) {
823 if (tPtr->hasVScroller)
824 return;
825 tPtr->hasVScroller = 1;
827 tPtr->vscroll = WMCreateScroller(tPtr);
828 WMSetScrollerAction(tPtr->vscroll, doScroll, tPtr);
829 WMSetScrollerArrowsPosition(tPtr->vscroll, WSAMaxEnd);
830 /* make it a vert. scroller */
831 WMResizeWidget(tPtr->vscroll, 1, 2);
833 if (W_VIEW_REALIZED(tPtr->view)) {
834 WMRealizeWidget(tPtr->vscroll);
837 reorganizeInterior(tPtr);
839 WMMapWidget(tPtr->vscroll);
840 } else {
841 if (!tPtr->hasVScroller)
842 return;
843 tPtr->hasVScroller = 0;
845 WMUnmapWidget(tPtr->vscroll);
846 WMDestroyWidget(tPtr->vscroll);
847 tPtr->vscroll = NULL;
849 reorganizeInterior(tPtr);
852 #endif
854 void
855 WMSetTableViewBackgroundColor(WMTableView *table, WMColor *color)
857 W_SetViewBackgroundColor(table->tableView, color);
859 if (table->backColor)
860 WMReleaseColor(table->backColor);
862 table->backColor = WMRetainColor(color);
864 repaintTable(table);
868 void
869 WMSetTableViewGridColor(WMTableView *table, WMColor *color)
871 WMReleaseColor(table->gridColor);
872 table->gridColor = WMRetainColor(color);
873 XSetForeground(WMScreenDisplay(WMWidgetScreen(table)), table->gridGC,
874 WMColorPixel(color));
875 repaintTable(table);
880 void
881 WMSetTableViewRowHeight(WMTableView *table, int height)
883 table->rowHeight = height;
885 repaintTable(table);
889 void
890 WMScrollTableViewRowToVisible(WMTableView *table, int row)
892 WMScroller *scroller;
893 WMRange range;
894 WMRect rect;
895 int newY, tmp;
897 rect = getVisibleRect(table);
898 range = rowsInRect(table, rect);
900 scroller = table->vscroll;
902 if (row < range.position) {
903 newY = row * table->rowHeight - rect.size.height / 2;
904 } else if (row >= range.position + range.count) {
905 newY = row * table->rowHeight - rect.size.height / 2;
906 } else {
907 return;
909 tmp = table->rows*table->rowHeight - rect.size.height;
910 newY = WMAX(0, WMIN(newY, tmp));
912 scrollToPoint(table, rect.pos.x, newY);
917 static void
918 drawGrid(WMTableView *table, WMRect rect)
920 WMScreen *scr = WMWidgetScreen(table);
921 Display *dpy = WMScreenDisplay(scr);
922 int i;
923 int y1, y2;
924 int x1, x2;
925 int xx;
926 Drawable d = WMGetPixmapXID(table->viewBuffer);
927 GC gc = table->gridGC;
929 #if 0
930 char dashl[1] = {1};
932 XSetDashes(dpy, gc, 0, dashl, 1);
934 y1 = (rect.pos.y/table->rowHeight - 1)*table->rowHeight;
935 y2 = y1 + (rect.size.height/table->rowHeight+2)*table->rowHeight;
936 #endif
937 y1 = 0;
938 y2 = W_VIEW_HEIGHT(table->tableView);
940 xx = -rect.pos.x;
941 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
942 WMTableColumn *column;
944 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
946 column = WMGetFromArray(table->columns, i);
947 xx += column->width;
949 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
951 x1 = 0;
952 x2 = rect.size.width;
954 if (x2 <= x1)
955 return;
956 #if 0
957 XSetDashes(dpy, gc, (rect.pos.x&1), dashl, 1);
958 #endif
960 y1 = -rect.pos.y%table->rowHeight;
961 y2 = y1 + rect.size.height + table->rowHeight;
963 for (i = y1; i <= y2; i += table->rowHeight) {
964 XDrawLine(dpy, d, gc, x1, i, x2, i);
969 static WMRange
970 columnsInRect(WMTableView *table, WMRect rect)
972 WMTableColumn *column;
973 int pos;
974 int i, found;
975 int totalColumns = WMGetArrayItemCount(table->columns);
976 WMRange range;
978 pos = 0;
979 found = 0;
980 for (i = 0; i < totalColumns; i++) {
981 column = WMGetFromArray(table->columns, i);
982 if (!found) {
983 if (rect.pos.x >= pos && rect.pos.x < pos + column->width) {
984 range.position = i;
985 range.count = 1;
986 found = 1;
988 } else {
989 if (pos > rect.pos.x + rect.size.width) {
990 break;
992 range.count++;
994 pos += column->width;
996 range.count = WMAX(1, WMIN(range.count, totalColumns - range.position));
997 return range;
1001 static WMRange
1002 rowsInRect(WMTableView *table, WMRect rect)
1004 WMRange range;
1005 int rh = table->rowHeight;
1006 int dif;
1008 dif = rect.pos.y % rh;
1010 range.position = WMAX(0, (rect.pos.y - dif) / rh);
1011 range.count = WMAX(1, WMIN((rect.size.height + dif) / rh, table->rows));
1013 return range;
1017 static void
1018 drawRow(WMTableView *table, int row, WMRect clipRect)
1020 int i;
1021 WMRange cols = columnsInRect(table, clipRect);
1022 WMTableColumn *column;
1023 Drawable d = WMGetPixmapXID(table->viewBuffer);
1025 for (i = cols.position; i < cols.position+cols.count; i++) {
1026 column = WMGetFromArray(table->columns, i);
1028 if (!column->delegate || !column->delegate->drawCell)
1029 continue;
1031 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
1032 (*column->delegate->drawSelectedCell)(column->delegate, column, row, d);
1033 else
1034 (*column->delegate->drawCell)(column->delegate, column, row, d);
1039 #if 0
1040 static void
1041 drawFullRow(WMTableView *table, int row)
1043 int i;
1044 WMTableColumn *column;
1045 Drawable d = WMGetPixmapXID(table->viewBuffer);
1047 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1048 column = WMGetFromArray(table->columns, i);
1050 if (!column->delegate || !column->delegate->drawCell)
1051 continue;
1053 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
1054 (*column->delegate->drawSelectedCell)(column->delegate, column, row, d);
1055 else
1056 (*column->delegate->drawCell)(column->delegate, column, row, d);
1059 #endif
1062 static void
1063 setRowSelected(WMTableView *table, unsigned row, Bool flag)
1065 int repaint = 0;
1067 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound) {
1068 if (!flag) {
1069 WMRemoveFromArray(table->selectedRows, (void*)row);
1070 repaint = 1;
1072 } else {
1073 if (flag) {
1074 WMAddToArray(table->selectedRows, (void*)row);
1075 repaint = 1;
1078 if (repaint && row < table->rows) {
1079 /*drawFullRow(table, row);*/
1080 repaintTable(table);
1085 static void
1086 repaintTable(WMTableView *table)
1088 WMRect rect;
1089 WMRange rows;
1090 WMScreen *scr = WMWidgetScreen(table);
1091 int i;
1093 if (!table->delegate || !W_VIEW_REALIZED(table->view))
1094 return;
1096 wassertr(table->delegate->numberOfRows);
1098 if (!table->viewBuffer) {
1099 table->viewBuffer = WMCreatePixmap(scr,
1100 W_VIEW_WIDTH(table->tableView),
1101 W_VIEW_HEIGHT(table->tableView),
1102 WMScreenDepth(scr), 0);
1105 XFillRectangle(scr->display,
1106 WMGetPixmapXID(table->viewBuffer),
1107 WMColorGC(table->backColor), 0, 0,
1108 W_VIEW_WIDTH(table->tableView),
1109 W_VIEW_HEIGHT(table->tableView));
1111 rect = getVisibleRect(table);
1113 if (table->drawsGrid) {
1114 drawGrid(table, rect);
1117 rows = rowsInRect(table, rect);
1118 for (i = rows.position;
1119 i < WMIN(rows.position+rows.count + 1, table->rows);
1120 i++) {
1121 drawRow(table, i, rect);
1124 XSetWindowBackgroundPixmap(scr->display, table->tableView->window,
1125 WMGetPixmapXID(table->viewBuffer));
1126 XClearWindow(scr->display, table->tableView->window);
1130 static void
1131 stopRowEdit(WMTableView *table, int row)
1133 int i;
1134 WMTableColumn *column;
1136 table->editingRow = -1;
1137 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1138 column = WMGetFromArray(table->columns, i);
1140 if (column->delegate && column->delegate->endCellEdit)
1141 (*column->delegate->endCellEdit)(column->delegate, column, row);
1147 void
1148 WMEditTableViewRow(WMTableView *table, int row)
1150 int i;
1151 WMTableColumn *column;
1153 if (table->editingRow >= 0) {
1154 stopRowEdit(table, table->editingRow);
1157 table->editingRow = row;
1159 if (row < 0)
1160 return;
1162 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1163 column = WMGetFromArray(table->columns, i);
1165 if (column->delegate && column->delegate->beginCellEdit)
1166 (*column->delegate->beginCellEdit)(column->delegate, column, row);
1171 void
1172 WMSelectTableViewRow(WMTableView *table, int row)
1174 if (table->clickedRow >= 0)
1175 setRowSelected(table, table->clickedRow, False);
1177 if (row >= table->rows) {
1178 return;
1181 setRowSelected(table, row, True);
1182 table->clickedRow = row;
1184 if (table->action)
1185 (*table->action)(table, table->clientData);
1186 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
1187 table, NULL);
1191 void
1192 WMReloadTableView(WMTableView *table)
1194 if (table->editingRow >= 0)
1195 stopRowEdit(table, table->editingRow);
1197 /* when this is called, nothing in the table can be assumed to be
1198 * like the last time we accessed it (ie, rows might have disappeared) */
1200 WMEmptyArray(table->selectedRows);
1202 if (table->clickedRow >= 0) {
1203 if (table->action)
1204 (*table->action)(table, table->clientData);
1205 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
1206 table, NULL);
1207 table->clickedRow = -1;
1210 if (table->delegate && table->delegate->numberOfRows) {
1211 int rows;
1213 rows = (*table->delegate->numberOfRows)(table->delegate, table);
1215 if (rows != table->rows) {
1216 table->rows = rows;
1217 handleResize(table->view->delegate, table->view);
1218 } else {
1219 repaintTable(table);
1225 void
1226 WMNoteTableViewNumberOfRowsChanged(WMTableView *table)
1228 WMReloadTableView(table);
1232 static void
1233 handleTableEvents(XEvent *event, void *data)
1235 WMTableView *table = (WMTableView*)data;
1236 int row;
1238 switch (event->type) {
1239 case ButtonPress:
1240 if (event->xbutton.button == Button1) {
1241 WMRect rect = getVisibleRect(table);
1243 row = (event->xbutton.y + rect.pos.y)/table->rowHeight;
1244 if (row != table->clickedRow) {
1245 setRowSelected(table, table->clickedRow, False);
1246 setRowSelected(table, row, True);
1247 table->clickedRow = row;
1248 table->dragging = 1;
1249 } else {
1250 table->dragging = 1;
1253 break;
1255 case MotionNotify:
1256 if (table->dragging && event->xmotion.y >= 0) {
1257 WMRect rect = getVisibleRect(table);
1259 row = (event->xmotion.y + rect.pos.y)/table->rowHeight;
1260 if (table->clickedRow != row && row >= 0 && row < table->rows) {
1261 setRowSelected(table, table->clickedRow, False);
1262 setRowSelected(table, row, True);
1263 table->clickedRow = row;
1266 break;
1268 case ButtonRelease:
1269 if (event->xbutton.button == Button1) {
1270 if (table->action)
1271 (*table->action)(table, table->clientData);
1272 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
1273 table, NULL);
1274 table->dragging = 0;
1276 break;
1281 static void
1282 handleEvents(XEvent *event, void *data)
1284 WMTableView *table = (WMTableView*)data;
1285 WMScreen *scr = WMWidgetScreen(table);
1287 switch (event->type) {
1288 case Expose:
1289 W_DrawRelief(scr, W_VIEW_DRAWABLE(table->view), 0, 0,
1290 W_VIEW_WIDTH(table->view), W_VIEW_HEIGHT(table->view),
1291 WRSunken);
1292 break;
1297 static void
1298 handleResize(W_ViewDelegate *self, WMView *view)
1300 reorganizeInterior(view->self);
1304 static void
1305 reorganizeInterior(WMTableView *table)
1307 int width;
1308 int height;
1309 WMSize size = getTotalSize(table);
1310 WMView *view = table->view;
1311 int vw, vh;
1312 int hsThickness, vsThickness;
1314 if (table->vscroll)
1315 vsThickness = WMWidgetWidth(table->vscroll);
1316 if (table->hscroll)
1317 hsThickness = WMWidgetHeight(table->hscroll);
1319 width = W_VIEW_WIDTH(view) - 2;
1320 height = W_VIEW_HEIGHT(view) - 3;
1322 height -= table->headerHeight; /* table header */
1324 if (table->corner)
1325 WMResizeWidget(table->corner, 20, table->headerHeight);
1327 WMMoveWidget(table->vscroll, 1, table->headerHeight + 1);
1328 WMResizeWidget(table->vscroll, 20, height + 1);
1330 if (table->hscroll) {
1331 WMMoveWidget(table->hscroll, vsThickness, W_VIEW_HEIGHT(view) - hsThickness - 1);
1332 WMResizeWidget(table->hscroll, width-(vsThickness+1), hsThickness);
1335 if (table->header)
1336 WMResizeWidget(table->header, width-(vsThickness+1), table->headerHeight);
1338 if (table->viewBuffer) {
1339 WMReleasePixmap(table->viewBuffer);
1340 table->viewBuffer = NULL;
1343 width -= vsThickness;
1344 height -= hsThickness;
1347 vw = WMIN(size.width, width);
1348 vh = WMIN(size.height, height);
1350 W_MoveView(table->tableView, vsThickness+1, 1+table->headerHeight+1);
1351 W_ResizeView(table->tableView, WMAX(vw, 1), WMAX(vh, 1)+1);
1353 adjustScrollers(table);
1355 repaintTable(table);
1359 static void
1360 rearrangeHeader(WMTableView *table)
1362 int width;
1363 int count;
1364 int i;
1365 /*WMRect rect = WMGetScrollViewVisibleRect(table->scrollView);*/
1367 width = 0;
1369 count = WMGetArrayItemCount(table->columns);
1370 for (i = 0; i < count; i++) {
1371 WMTableColumn *column = WMGetFromArray(table->columns, i);
1372 WMView *splitter = WMGetFromArray(table->splitters, i);
1374 WMMoveWidget(column->titleW, width, 0);
1375 WMResizeWidget(column->titleW, column->width-1, table->headerHeight);
1377 width += column->width;
1378 W_MoveView(splitter, width-1, 0);
1381 wassertr(table->delegate && table->delegate->numberOfRows);
1383 table->rows = table->delegate->numberOfRows(table->delegate, table);
1385 table->tableWidth = width + 1;
1387 handleResize(table->view->delegate, table->view);