Make drawIconProc() static
[wmaker-crm.git] / WINGs / Extras / wtableview.c
blobdd193a9bc03ba08b2f4a662530b3bbcae3e7127e
3 #include <WINGs/WINGsP.h>
4 #include <X11/cursorfont.h>
5 #include <stdint.h>
7 #include "wtableview.h"
10 const char *WMTableViewSelectionDidChangeNotification = "WMTableViewSelectionDidChangeNotification";
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;
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*
38 WMCreateTableColumn(char *title)
40 WMTableColumn *col = wmalloc(sizeof(WMTableColumn));
42 col->table = NULL;
43 col->titleW = NULL;
44 col->width = 50;
45 col->minWidth = 5;
46 col->maxWidth = 0;
48 col->id = NULL;
50 col->title = wstrdup(title);
52 col->delegate = NULL;
54 col->resizable = 1;
55 col->editable = 0;
57 return col;
61 void
62 WMSetTableColumnId(WMTableColumn *column, void *id)
64 column->id = id;
68 void*
69 WMGetTableColumnId(WMTableColumn *column)
71 return column->id;
75 void
76 WMSetTableColumnWidth(WMTableColumn *column, unsigned width)
78 if (column->maxWidth == 0)
79 column->width = WMAX(column->minWidth, width);
80 else
81 column->width = WMAX(column->minWidth, WMIN(column->maxWidth, width));
83 if (column->table) {
84 rearrangeHeader(column->table);
89 void
90 WMSetTableColumnDelegate(WMTableColumn *column, WMTableColumnDelegate *delegate)
92 column->delegate = delegate;
96 void
97 WMSetTableColumnConstraints(WMTableColumn *column, unsigned minWidth,
98 unsigned maxWidth)
100 wassertr(maxWidth == 0 || minWidth <= maxWidth);
102 column->minWidth = minWidth;
103 column->maxWidth = maxWidth;
105 if (column->width < column->minWidth)
106 WMSetTableColumnWidth(column, column->minWidth);
107 else if (column->width > column->maxWidth && column->maxWidth != 0)
108 WMSetTableColumnWidth(column, column->maxWidth);
112 void
113 WMSetTableColumnEditable(WMTableColumn *column, Bool flag)
115 column->editable = ((flag==0) ? 0 : 1);
119 WMTableView*
120 WMGetTableColumnTableView(WMTableColumn *column)
122 return column->table;
127 struct W_TableView {
128 W_Class widgetClass;
129 WMView *view;
131 WMFrame *header;
133 WMLabel *corner;
135 WMScroller *hscroll;
136 WMScroller *vscroll;
137 WMView *tableView;
139 WMPixmap *viewBuffer;
141 WMArray *columns;
142 WMArray *splitters;
144 WMArray *selectedRows;
146 int tableWidth;
148 int rows;
150 WMColor *backColor;
152 GC gridGC;
153 WMColor *gridColor;
155 Cursor splitterCursor;
157 void *dataSource;
159 WMTableViewDelegate *delegate;
161 WMAction *action;
162 void *clientData;
164 void *clickedColumn;
165 int clickedRow;
167 int editingRow;
169 unsigned headerHeight;
171 unsigned rowHeight;
173 unsigned dragging:1;
174 unsigned drawsGrid:1;
175 unsigned canSelectRow:1;
176 unsigned canSelectMultiRows:1;
177 unsigned canDeselectRow:1;
179 unsigned int hasVScroller:1;
180 unsigned int hasHScroller:1;
183 static W_Class tableClass = 0;
186 static W_ViewDelegate viewDelegate = {
187 NULL,
188 NULL,
189 handleResize,
190 NULL,
191 NULL
196 static void reorganizeInterior(WMTableView *table);
199 static void handleEvents(XEvent *event, void *data);
200 static void handleTableEvents(XEvent *event, void *data);
201 static void repaintTable(WMTableView *table);
203 static WMSize
204 getTotalSize(WMTableView *table)
206 WMSize size;
207 int i;
209 /* get width from columns */
210 size.width = 0;
211 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
212 WMTableColumn *column;
214 column = WMGetFromArray(table->columns, i);
216 size.width += column->width;
219 /* get height from rows */
220 size.height = table->rows * table->rowHeight;
222 return size;
226 static WMRect
227 getVisibleRect(WMTableView *table)
229 WMSize size = getTotalSize(table);
230 WMRect rect;
232 if (table->vscroll) {
233 rect.size.height = size.height * WMGetScrollerKnobProportion(table->vscroll);
234 rect.pos.y = (size.height - rect.size.height) * WMGetScrollerValue(table->vscroll);
235 } else {
236 rect.size.height = size.height;
237 rect.pos.y = 0;
240 if (table->hscroll) {
241 rect.size.width = size.width * WMGetScrollerKnobProportion(table->hscroll);
242 rect.pos.x = (size.width - rect.size.width) * WMGetScrollerValue(table->hscroll);
243 } else {
244 rect.size.width = size.width;
245 rect.pos.x = 0;
248 return rect;
252 static void
253 scrollToPoint(WMTableView *table, int x, int y)
255 WMSize size = getTotalSize(table);
256 int i;
257 float value, prop;
259 if (table->hscroll) {
260 if (size.width > W_VIEW_WIDTH(table->tableView)) {
261 prop = (float)W_VIEW_WIDTH(table->tableView) / (float)size.width;
262 value = (float)x / (float)(size.width - W_VIEW_WIDTH(table->tableView));
263 } else {
264 prop = 1.0;
265 value = 0.0;
267 WMSetScrollerParameters(table->hscroll, value, prop);
270 if (table->vscroll) {
271 if (size.height > W_VIEW_HEIGHT(table->tableView)) {
272 prop = (float)W_VIEW_HEIGHT(table->tableView) / (float)size.height;
273 value = (float)y / (float)(size.height - W_VIEW_HEIGHT(table->tableView));
274 } else {
275 prop = 1.0;
276 value = 0.0;
279 WMSetScrollerParameters(table->vscroll, value, prop);
283 if (table->editingRow >= 0) {
284 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
285 WMTableColumn *column;
287 column = WMGetFromArray(table->columns, i);
289 if (column->delegate && column->delegate->beginCellEdit)
290 (*column->delegate->beginCellEdit)(column->delegate, column,
291 table->editingRow);
295 repaintTable(table);
299 static void
300 adjustScrollers(WMTableView *table)
302 WMSize size = getTotalSize(table);
303 WMSize vsize = WMGetViewSize(table->tableView);
304 float prop, value;
305 float oprop, ovalue;
307 if (table->hscroll) {
308 if (size.width <= vsize.width) {
309 value = 0.0;
310 prop = 1.0;
311 } else {
312 oprop = WMGetScrollerKnobProportion(table->hscroll);
313 if (oprop == 0.0)
314 oprop = 1.0;
315 ovalue = WMGetScrollerValue(table->hscroll);
317 prop = (float)vsize.width/(float)size.width;
318 value = prop*ovalue / oprop;
320 WMSetScrollerParameters(table->hscroll, value, prop);
323 if (table->vscroll) {
324 if (size.height <= vsize.height) {
325 value = 0.0;
326 prop = 1.0;
327 } else {
328 oprop = WMGetScrollerKnobProportion(table->vscroll);
329 if (oprop == 0.0)
330 oprop = 1.0;
331 ovalue = WMGetScrollerValue(table->vscroll);
333 prop = (float)vsize.height/(float)size.height;
334 value = prop*ovalue / oprop;
336 WMSetScrollerParameters(table->vscroll, value, prop);
341 static void
342 doScroll(WMWidget *self, void *data)
344 WMTableView *table = (WMTableView*)data;
345 float value;
346 float vpsize;
347 float size;
348 WMSize ts = getTotalSize(table);
350 value = WMGetScrollerValue(self);
352 if (table->hscroll == (WMScroller *)self) {
353 vpsize = W_VIEW_WIDTH(table->tableView);
354 size = ts.width;
355 } else {
356 vpsize = W_VIEW_HEIGHT(table->tableView);
357 size = ts.height;
360 switch (WMGetScrollerHitPart(self)) {
361 case WSDecrementWheel:
362 case WSDecrementLine:
363 value -= (float)table->rowHeight / size;
364 if (value < 0)
365 value = 0.0;
366 WMSetScrollerParameters(self, value,
367 WMGetScrollerKnobProportion(self));
368 repaintTable(table);
369 break;
371 case WSIncrementWheel:
372 case WSIncrementLine:
373 value += (float)table->rowHeight / size;
374 if (value > 1.0)
375 value = 1.0;
376 WMSetScrollerParameters(self, value,
377 WMGetScrollerKnobProportion(self));
378 repaintTable(table);
379 break;
381 case WSKnob:
382 repaintTable(table);
383 break;
385 case WSDecrementPage:
386 value -= vpsize / size;
387 if (value < 0.0)
388 value = 0.0;
389 WMSetScrollerParameters(self, value,
390 WMGetScrollerKnobProportion(self));
391 repaintTable(table);
392 break;
394 case WSIncrementPage:
395 value += vpsize / size;
396 if (value > 1.0)
397 value = 1.0;
398 WMSetScrollerParameters(self, value,
399 WMGetScrollerKnobProportion(self));
400 repaintTable(table);
401 break;
404 case WSNoPart:
405 case WSKnobSlot:
406 break;
409 if (table->editingRow >= 0) {
410 int i;
411 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
412 WMTableColumn *column;
414 column = WMGetFromArray(table->columns, i);
416 if (column->delegate && column->delegate->beginCellEdit)
417 (*column->delegate->beginCellEdit)(column->delegate, column,
418 table->editingRow);
423 if (table->hscroll == self) {
424 int x = 0;
425 int i;
426 WMRect rect = getVisibleRect(table);
428 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
429 WMTableColumn *column;
430 WMView *splitter;
432 column = WMGetFromArray(table->columns, i);
434 WMMoveWidget(column->titleW, x - rect.pos.x, 0);
436 x += W_VIEW_WIDTH(WMWidgetView(column->titleW)) + 1;
438 splitter = WMGetFromArray(table->splitters, i);
439 W_MoveView(splitter, x - rect.pos.x - 1, 0);
445 static void
446 splitterHandler(XEvent *event, void *data)
448 WMTableColumn *column = (WMTableColumn*)data;
449 WMTableView *table = column->table;
450 int done = 0;
451 int cx, ox, offsX;
452 WMPoint pos;
453 WMScreen *scr = WMWidgetScreen(table);
454 GC gc = scr->ixorGC;
455 Display *dpy = WMScreenDisplay(scr);
456 int h = WMWidgetHeight(table) - 22;
457 Window w = WMViewXID(table->view);
459 pos = WMGetViewPosition(WMWidgetView(column->titleW));
461 offsX = pos.x + column->width;
463 ox = cx = offsX;
465 XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
467 while (!done) {
468 XEvent ev;
470 WMMaskEvent(dpy, ButtonMotionMask|ButtonReleaseMask, &ev);
472 switch (ev.type) {
473 case MotionNotify:
474 ox = cx;
476 if (column->width + ev.xmotion.x < column->minWidth)
477 cx = pos.x + column->minWidth;
478 else if (column->maxWidth > 0
479 && column->width + ev.xmotion.x > column->maxWidth)
480 cx = pos.x + column->maxWidth;
481 else
482 cx = offsX + ev.xmotion.x;
484 XDrawLine(dpy, w, gc, ox+20, 0, ox+20, h);
485 XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
486 break;
488 case ButtonRelease:
489 column->width = cx - pos.x;
490 done = 1;
491 break;
495 XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
496 rearrangeHeader(table);
497 repaintTable(table);
501 static void
502 realizeTable(void *data, WMNotification *notif)
504 repaintTable(data);
508 WMTableView*
509 WMCreateTableView(WMWidget *parent)
511 WMTableView *table = wmalloc(sizeof(WMTableView));
512 WMScreen *scr = WMWidgetScreen(parent);
514 memset(table, 0, sizeof(WMTableView));
516 if (!tableClass) {
517 tableClass = W_RegisterUserWidget();
519 table->widgetClass = tableClass;
521 table->view = W_CreateView(W_VIEW(parent));
522 if (!table->view)
523 goto error;
524 table->view->self = table;
526 table->view->delegate = &viewDelegate;
528 table->headerHeight = 20;
530 table->hscroll = WMCreateScroller(table);
531 WMSetScrollerAction(table->hscroll, doScroll, table);
532 WMMoveWidget(table->hscroll, 1, 2+table->headerHeight);
533 WMMapWidget(table->hscroll);
535 table->hasHScroller = 1;
537 table->vscroll = WMCreateScroller(table);
538 WMSetScrollerArrowsPosition(table->vscroll, WSAMaxEnd);
539 WMSetScrollerAction(table->vscroll, doScroll, table);
540 WMMoveWidget(table->vscroll, 1, 2+table->headerHeight);
541 WMMapWidget(table->vscroll);
543 table->hasVScroller = 1;
545 table->header = WMCreateFrame(table);
546 WMMoveWidget(table->header, 22, 2);
547 WMMapWidget(table->header);
548 WMSetFrameRelief(table->header, WRFlat);
550 table->corner = WMCreateLabel(table);
551 WMResizeWidget(table->corner, 20, table->headerHeight);
552 WMMoveWidget(table->corner, 2, 2);
553 WMMapWidget(table->corner);
554 WMSetLabelRelief(table->corner, WRRaised);
555 WMSetWidgetBackgroundColor(table->corner, scr->darkGray);
558 table->tableView = W_CreateView(table->view);
559 if (!table->tableView)
560 goto error;
561 table->tableView->self = table;
562 W_MapView(table->tableView);
564 WMAddNotificationObserver(realizeTable, table, WMViewRealizedNotification,
565 table->tableView);
567 table->tableView->flags.dontCompressExpose = 1;
569 table->gridColor = WMCreateNamedColor(scr, "#cccccc", False);
570 /* table->gridColor = WMGrayColor(scr);*/
573 XGCValues gcv;
575 table->backColor = WMWhiteColor(scr);
577 gcv.foreground = WMColorPixel(table->gridColor);
578 gcv.dashes = 1;
579 gcv.line_style = LineOnOffDash;
580 table->gridGC = XCreateGC(WMScreenDisplay(scr), W_DRAWABLE(scr),
581 GCForeground, &gcv);
584 table->editingRow = -1;
585 table->clickedRow = -1;
587 table->drawsGrid = 1;
588 table->rowHeight = 16;
590 table->tableWidth = 1;
592 table->columns = WMCreateArray(4);
593 table->splitters = WMCreateArray(4);
595 table->selectedRows = WMCreateArray(16);
597 table->splitterCursor = XCreateFontCursor(WMScreenDisplay(scr),
598 XC_sb_h_double_arrow);
600 table->canSelectRow = 1;
602 WMCreateEventHandler(table->view, ExposureMask|StructureNotifyMask,
603 handleEvents, table);
605 WMCreateEventHandler(table->tableView, ExposureMask|ButtonPressMask|
606 ButtonReleaseMask|ButtonMotionMask,
607 handleTableEvents, table);
609 WMResizeWidget(table, 50, 50);
611 return table;
613 error:
614 if (table->tableView)
615 W_DestroyView(table->tableView);
616 if (table->view)
617 W_DestroyView(table->view);
618 wfree(table);
619 return NULL;
623 void
624 WMAddTableViewColumn(WMTableView *table, WMTableColumn *column)
626 WMScreen *scr = WMWidgetScreen(table);
628 column->table = table;
630 WMAddToArray(table->columns, column);
632 if (!column->titleW) {
633 column->titleW = WMCreateLabel(table);
634 WMSetLabelRelief(column->titleW, WRRaised);
635 WMSetLabelFont(column->titleW, scr->boldFont);
636 WMSetLabelTextColor(column->titleW, scr->white);
637 WMSetWidgetBackgroundColor(column->titleW, scr->darkGray);
638 WMSetLabelText(column->titleW, column->title);
639 W_ReparentView(WMWidgetView(column->titleW),
640 WMWidgetView(table->header), 0, 0);
641 if (W_VIEW_REALIZED(table->view))
642 WMRealizeWidget(column->titleW);
643 WMMapWidget(column->titleW);
647 WMView *splitter = W_CreateView(WMWidgetView(table->header));
649 W_SetViewBackgroundColor(splitter, WMWhiteColor(scr));
651 if (W_VIEW_REALIZED(table->view))
652 W_RealizeView(splitter);
654 W_ResizeView(splitter, 2, table->headerHeight);
655 W_MapView(splitter);
657 W_SetViewCursor(splitter, table->splitterCursor);
658 WMCreateEventHandler(splitter, ButtonPressMask|ButtonReleaseMask,
659 splitterHandler, column);
661 WMAddToArray(table->splitters, splitter);
664 rearrangeHeader(table);
668 void
669 WMSetTableViewHeaderHeight(WMTableView *table, unsigned height)
671 table->headerHeight = height;
673 handleResize(NULL, table->view);
677 void
678 WMSetTableViewDelegate(WMTableView *table, WMTableViewDelegate *delegate)
680 table->delegate = delegate;
684 void
685 WMSetTableViewAction(WMTableView *table, WMAction *action, void *clientData)
687 table->action = action;
689 table->clientData = clientData;
693 void*
694 WMGetTableViewClickedColumn(WMTableView *table)
696 return table->clickedColumn;
701 WMGetTableViewClickedRow(WMTableView *table)
703 return table->clickedRow;
707 WMArray*
708 WMGetTableViewSelectedRows(WMTableView *table)
710 return table->selectedRows;
714 WMView*
715 WMGetTableViewDocumentView(WMTableView *table)
717 return table->tableView;
721 void*
722 WMTableViewDataForCell(WMTableView *table, WMTableColumn *column, int row)
724 return (*table->delegate->valueForCell)(table->delegate, column, row);
728 void
729 WMSetTableViewDataForCell(WMTableView *table, WMTableColumn *column, int row,
730 void *data)
732 (*table->delegate->setValueForCell)(table->delegate, column, row, data);
736 WMRect
737 WMTableViewRectForCell(WMTableView *table, WMTableColumn *column, int row)
739 WMRect rect;
740 int i;
742 rect.pos.x = 0;
743 rect.pos.y = row * table->rowHeight;
744 rect.size.height = table->rowHeight;
746 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
747 WMTableColumn *col;
748 col = WMGetFromArray(table->columns, i);
750 if (col == column) {
751 rect.size.width = col->width;
752 break;
755 rect.pos.x += col->width;
759 WMRect r = getVisibleRect(table);
761 rect.pos.y -= r.pos.y;
762 rect.pos.x -= r.pos.x;
765 return rect;
769 void
770 WMSetTableViewDataSource(WMTableView *table, void *source)
772 table->dataSource = source;
776 void*
777 WMGetTableViewDataSource(WMTableView *table)
779 return table->dataSource;
784 void
785 WMSetTableViewHasHorizontalScroller(WMTableView *tPtr, Bool flag)
787 if (flag) {
788 if (tPtr->hasHScroller)
789 return;
790 tPtr->hasHScroller = 1;
792 tPtr->hscroll = WMCreateScroller(tPtr);
793 WMSetScrollerAction(tPtr->hscroll, doScroll, tPtr);
794 WMSetScrollerArrowsPosition(tPtr->hscroll, WSAMaxEnd);
795 /* make it a horiz. scroller */
796 WMResizeWidget(tPtr->hscroll, 1, 2);
798 if (W_VIEW_REALIZED(tPtr->view)) {
799 WMRealizeWidget(tPtr->hscroll);
802 reorganizeInterior(tPtr);
804 WMMapWidget(tPtr->hscroll);
805 } else {
806 if (!tPtr->hasHScroller)
807 return;
808 tPtr->hasHScroller = 0;
810 WMUnmapWidget(tPtr->hscroll);
811 WMDestroyWidget(tPtr->hscroll);
812 tPtr->hscroll = NULL;
814 reorganizeInterior(tPtr);
818 #if 0
819 /* not supported by now */
820 void
821 WMSetTableViewHasVerticalScroller(WMTableView *tPtr, Bool flag)
823 if (flag) {
824 if (tPtr->hasVScroller)
825 return;
826 tPtr->hasVScroller = 1;
828 tPtr->vscroll = WMCreateScroller(tPtr);
829 WMSetScrollerAction(tPtr->vscroll, doScroll, tPtr);
830 WMSetScrollerArrowsPosition(tPtr->vscroll, WSAMaxEnd);
831 /* make it a vert. scroller */
832 WMResizeWidget(tPtr->vscroll, 1, 2);
834 if (W_VIEW_REALIZED(tPtr->view)) {
835 WMRealizeWidget(tPtr->vscroll);
838 reorganizeInterior(tPtr);
840 WMMapWidget(tPtr->vscroll);
841 } else {
842 if (!tPtr->hasVScroller)
843 return;
844 tPtr->hasVScroller = 0;
846 WMUnmapWidget(tPtr->vscroll);
847 WMDestroyWidget(tPtr->vscroll);
848 tPtr->vscroll = NULL;
850 reorganizeInterior(tPtr);
853 #endif
855 void
856 WMSetTableViewBackgroundColor(WMTableView *table, WMColor *color)
858 W_SetViewBackgroundColor(table->tableView, color);
860 if (table->backColor)
861 WMReleaseColor(table->backColor);
863 table->backColor = WMRetainColor(color);
865 repaintTable(table);
869 void
870 WMSetTableViewGridColor(WMTableView *table, WMColor *color)
872 WMReleaseColor(table->gridColor);
873 table->gridColor = WMRetainColor(color);
874 XSetForeground(WMScreenDisplay(WMWidgetScreen(table)), table->gridGC,
875 WMColorPixel(color));
876 repaintTable(table);
881 void
882 WMSetTableViewRowHeight(WMTableView *table, int height)
884 table->rowHeight = height;
886 repaintTable(table);
890 void
891 WMScrollTableViewRowToVisible(WMTableView *table, int row)
893 WMScroller *scroller;
894 WMRange range;
895 WMRect rect;
896 int newY, tmp;
898 rect = getVisibleRect(table);
899 range = rowsInRect(table, rect);
901 scroller = table->vscroll;
903 if (row < range.position) {
904 newY = row * table->rowHeight - rect.size.height / 2;
905 } else if (row >= range.position + range.count) {
906 newY = row * table->rowHeight - rect.size.height / 2;
907 } else {
908 return;
910 tmp = table->rows*table->rowHeight - rect.size.height;
911 newY = WMAX(0, WMIN(newY, tmp));
913 scrollToPoint(table, rect.pos.x, newY);
918 static void
919 drawGrid(WMTableView *table, WMRect rect)
921 WMScreen *scr = WMWidgetScreen(table);
922 Display *dpy = WMScreenDisplay(scr);
923 int i;
924 int y1, y2;
925 int x1, x2;
926 int xx;
927 Drawable d = WMGetPixmapXID(table->viewBuffer);
928 GC gc = table->gridGC;
930 #if 0
931 char dashl[1] = {1};
933 XSetDashes(dpy, gc, 0, dashl, 1);
935 y1 = (rect.pos.y/table->rowHeight - 1)*table->rowHeight;
936 y2 = y1 + (rect.size.height/table->rowHeight+2)*table->rowHeight;
937 #endif
938 y1 = 0;
939 y2 = W_VIEW_HEIGHT(table->tableView);
941 xx = -rect.pos.x;
942 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
943 WMTableColumn *column;
945 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
947 column = WMGetFromArray(table->columns, i);
948 xx += column->width;
950 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
952 x1 = 0;
953 x2 = rect.size.width;
955 if (x2 <= x1)
956 return;
957 #if 0
958 XSetDashes(dpy, gc, (rect.pos.x&1), dashl, 1);
959 #endif
961 y1 = -rect.pos.y%table->rowHeight;
962 y2 = y1 + rect.size.height + table->rowHeight;
964 for (i = y1; i <= y2; i += table->rowHeight) {
965 XDrawLine(dpy, d, gc, x1, i, x2, i);
970 static WMRange
971 columnsInRect(WMTableView *table, WMRect rect)
973 WMTableColumn *column;
974 int pos;
975 int i, found;
976 int totalColumns = WMGetArrayItemCount(table->columns);
977 WMRange range;
979 pos = 0;
980 found = 0;
981 for (i = 0; i < totalColumns; i++) {
982 column = WMGetFromArray(table->columns, i);
983 if (!found) {
984 if (rect.pos.x >= pos && rect.pos.x < pos + column->width) {
985 range.position = i;
986 range.count = 1;
987 found = 1;
989 } else {
990 if (pos > rect.pos.x + rect.size.width) {
991 break;
993 range.count++;
995 pos += column->width;
997 range.count = WMAX(1, WMIN(range.count, totalColumns - range.position));
998 return range;
1002 static WMRange
1003 rowsInRect(WMTableView *table, WMRect rect)
1005 WMRange range;
1006 int rh = table->rowHeight;
1007 int dif;
1009 dif = rect.pos.y % rh;
1011 range.position = WMAX(0, (rect.pos.y - dif) / rh);
1012 range.count = WMAX(1, WMIN((rect.size.height + dif) / rh, table->rows));
1014 return range;
1018 static void
1019 drawRow(WMTableView *table, int row, WMRect clipRect)
1021 int i;
1022 WMRange cols = columnsInRect(table, clipRect);
1023 WMTableColumn *column;
1024 Drawable d = WMGetPixmapXID(table->viewBuffer);
1026 for (i = cols.position; i < cols.position+cols.count; i++) {
1027 column = WMGetFromArray(table->columns, i);
1029 if (!column->delegate || !column->delegate->drawCell)
1030 continue;
1032 if (WMFindInArray(table->selectedRows, NULL, (void*)(uintptr_t)row) != WANotFound)
1033 (*column->delegate->drawSelectedCell)(column->delegate, column, row, d);
1034 else
1035 (*column->delegate->drawCell)(column->delegate, column, row, d);
1040 #if 0
1041 static void
1042 drawFullRow(WMTableView *table, int row)
1044 int i;
1045 WMTableColumn *column;
1046 Drawable d = WMGetPixmapXID(table->viewBuffer);
1048 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1049 column = WMGetFromArray(table->columns, i);
1051 if (!column->delegate || !column->delegate->drawCell)
1052 continue;
1054 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
1055 (*column->delegate->drawSelectedCell)(column->delegate, column, row, d);
1056 else
1057 (*column->delegate->drawCell)(column->delegate, column, row, d);
1060 #endif
1063 static void
1064 setRowSelected(WMTableView *table, unsigned row, Bool flag)
1066 int repaint = 0;
1068 if (WMFindInArray(table->selectedRows, NULL, (void*)(uintptr_t)row) != WANotFound) {
1069 if (!flag) {
1070 WMRemoveFromArray(table->selectedRows, (void*)(uintptr_t)row);
1071 repaint = 1;
1073 } else {
1074 if (flag) {
1075 WMAddToArray(table->selectedRows, (void*)(uintptr_t)row);
1076 repaint = 1;
1079 if (repaint && row < table->rows) {
1080 /*drawFullRow(table, row);*/
1081 repaintTable(table);
1086 static void
1087 repaintTable(WMTableView *table)
1089 WMRect rect;
1090 WMRange rows;
1091 WMScreen *scr = WMWidgetScreen(table);
1092 int i;
1094 if (!table->delegate || !W_VIEW_REALIZED(table->view))
1095 return;
1097 wassertr(table->delegate->numberOfRows);
1099 if (!table->viewBuffer) {
1100 table->viewBuffer = WMCreatePixmap(scr,
1101 W_VIEW_WIDTH(table->tableView),
1102 W_VIEW_HEIGHT(table->tableView),
1103 WMScreenDepth(scr), 0);
1106 XFillRectangle(scr->display,
1107 WMGetPixmapXID(table->viewBuffer),
1108 WMColorGC(table->backColor), 0, 0,
1109 W_VIEW_WIDTH(table->tableView),
1110 W_VIEW_HEIGHT(table->tableView));
1112 rect = getVisibleRect(table);
1114 if (table->drawsGrid) {
1115 drawGrid(table, rect);
1118 rows = rowsInRect(table, rect);
1119 for (i = rows.position;
1120 i < WMIN(rows.position+rows.count + 1, table->rows);
1121 i++) {
1122 drawRow(table, i, rect);
1125 XSetWindowBackgroundPixmap(scr->display, table->tableView->window,
1126 WMGetPixmapXID(table->viewBuffer));
1127 XClearWindow(scr->display, table->tableView->window);
1131 static void
1132 stopRowEdit(WMTableView *table, int row)
1134 int i;
1135 WMTableColumn *column;
1137 table->editingRow = -1;
1138 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1139 column = WMGetFromArray(table->columns, i);
1141 if (column->delegate && column->delegate->endCellEdit)
1142 (*column->delegate->endCellEdit)(column->delegate, column, row);
1148 void
1149 WMEditTableViewRow(WMTableView *table, int row)
1151 int i;
1152 WMTableColumn *column;
1154 if (table->editingRow >= 0) {
1155 stopRowEdit(table, table->editingRow);
1158 table->editingRow = row;
1160 if (row < 0)
1161 return;
1163 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1164 column = WMGetFromArray(table->columns, i);
1166 if (column->delegate && column->delegate->beginCellEdit)
1167 (*column->delegate->beginCellEdit)(column->delegate, column, row);
1172 void
1173 WMSelectTableViewRow(WMTableView *table, int row)
1175 if (table->clickedRow >= 0)
1176 setRowSelected(table, table->clickedRow, False);
1178 if (row >= table->rows) {
1179 return;
1182 setRowSelected(table, row, True);
1183 table->clickedRow = row;
1185 if (table->action)
1186 (*table->action)(table, table->clientData);
1187 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
1188 table, NULL);
1192 void
1193 WMReloadTableView(WMTableView *table)
1195 if (table->editingRow >= 0)
1196 stopRowEdit(table, table->editingRow);
1198 /* when this is called, nothing in the table can be assumed to be
1199 * like the last time we accessed it (ie, rows might have disappeared) */
1201 WMEmptyArray(table->selectedRows);
1203 if (table->clickedRow >= 0) {
1204 if (table->action)
1205 (*table->action)(table, table->clientData);
1206 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
1207 table, NULL);
1208 table->clickedRow = -1;
1211 if (table->delegate && table->delegate->numberOfRows) {
1212 int rows;
1214 rows = (*table->delegate->numberOfRows)(table->delegate, table);
1216 if (rows != table->rows) {
1217 table->rows = rows;
1218 handleResize(table->view->delegate, table->view);
1219 } else {
1220 repaintTable(table);
1226 void
1227 WMNoteTableViewNumberOfRowsChanged(WMTableView *table)
1229 WMReloadTableView(table);
1233 static void
1234 handleTableEvents(XEvent *event, void *data)
1236 WMTableView *table = (WMTableView*)data;
1237 int row;
1239 switch (event->type) {
1240 case ButtonPress:
1241 if (event->xbutton.button == Button1) {
1242 WMRect rect = getVisibleRect(table);
1244 row = (event->xbutton.y + rect.pos.y)/table->rowHeight;
1245 if (row != table->clickedRow) {
1246 setRowSelected(table, table->clickedRow, False);
1247 setRowSelected(table, row, True);
1248 table->clickedRow = row;
1249 table->dragging = 1;
1250 } else {
1251 table->dragging = 1;
1254 break;
1256 case MotionNotify:
1257 if (table->dragging && event->xmotion.y >= 0) {
1258 WMRect rect = getVisibleRect(table);
1260 row = (event->xmotion.y + rect.pos.y)/table->rowHeight;
1261 if (table->clickedRow != row && row >= 0 && row < table->rows) {
1262 setRowSelected(table, table->clickedRow, False);
1263 setRowSelected(table, row, True);
1264 table->clickedRow = row;
1267 break;
1269 case ButtonRelease:
1270 if (event->xbutton.button == Button1) {
1271 if (table->action)
1272 (*table->action)(table, table->clientData);
1273 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
1274 table, NULL);
1275 table->dragging = 0;
1277 break;
1282 static void
1283 handleEvents(XEvent *event, void *data)
1285 WMTableView *table = (WMTableView*)data;
1286 WMScreen *scr = WMWidgetScreen(table);
1288 switch (event->type) {
1289 case Expose:
1290 W_DrawRelief(scr, W_VIEW_DRAWABLE(table->view), 0, 0,
1291 W_VIEW_WIDTH(table->view), W_VIEW_HEIGHT(table->view),
1292 WRSunken);
1293 break;
1298 static void
1299 handleResize(W_ViewDelegate *self, WMView *view)
1301 reorganizeInterior(view->self);
1305 static void
1306 reorganizeInterior(WMTableView *table)
1308 int width;
1309 int height;
1310 WMSize size = getTotalSize(table);
1311 WMView *view = table->view;
1312 int vw, vh;
1313 int hsThickness, vsThickness;
1315 if (table->vscroll)
1316 vsThickness = WMWidgetWidth(table->vscroll);
1317 if (table->hscroll)
1318 hsThickness = WMWidgetHeight(table->hscroll);
1320 width = W_VIEW_WIDTH(view) - 2;
1321 height = W_VIEW_HEIGHT(view) - 3;
1323 height -= table->headerHeight; /* table header */
1325 if (table->corner)
1326 WMResizeWidget(table->corner, 20, table->headerHeight);
1328 WMMoveWidget(table->vscroll, 1, table->headerHeight + 1);
1329 WMResizeWidget(table->vscroll, 20, height + 1);
1331 if (table->hscroll) {
1332 WMMoveWidget(table->hscroll, vsThickness, W_VIEW_HEIGHT(view) - hsThickness - 1);
1333 WMResizeWidget(table->hscroll, width-(vsThickness+1), hsThickness);
1336 if (table->header)
1337 WMResizeWidget(table->header, width-(vsThickness+1), table->headerHeight);
1339 if (table->viewBuffer) {
1340 WMReleasePixmap(table->viewBuffer);
1341 table->viewBuffer = NULL;
1344 width -= vsThickness;
1345 height -= hsThickness;
1348 vw = WMIN(size.width, width);
1349 vh = WMIN(size.height, height);
1351 W_MoveView(table->tableView, vsThickness+1, 1+table->headerHeight+1);
1352 W_ResizeView(table->tableView, WMAX(vw, 1), WMAX(vh, 1)+1);
1354 adjustScrollers(table);
1356 repaintTable(table);
1360 static void
1361 rearrangeHeader(WMTableView *table)
1363 int width;
1364 int count;
1365 int i;
1366 /*WMRect rect = WMGetScrollViewVisibleRect(table->scrollView);*/
1368 width = 0;
1370 count = WMGetArrayItemCount(table->columns);
1371 for (i = 0; i < count; i++) {
1372 WMTableColumn *column = WMGetFromArray(table->columns, i);
1373 WMView *splitter = WMGetFromArray(table->splitters, i);
1375 WMMoveWidget(column->titleW, width, 0);
1376 WMResizeWidget(column->titleW, column->width-1, table->headerHeight);
1378 width += column->width;
1379 W_MoveView(splitter, width-1, 0);
1382 wassertr(table->delegate && table->delegate->numberOfRows);
1384 table->rows = table->delegate->numberOfRows(table->delegate, table);
1386 table->tableWidth = width + 1;
1388 handleResize(table->view->delegate, table->view);