- s/sprintf/snprintf
[wmaker-crm.git] / WINGs / Extras / wtableview.c
blob3f39b26215eaf6b7d9212feb545abe86029437f3
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(maxWidth == 0 || 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 WMScroller *hscroll;
129 WMScroller *vscroll;
130 WMView *tableView;
132 WMPixmap *viewBuffer;
134 WMArray *columns;
135 WMArray *splitters;
137 WMArray *selectedRows;
139 int tableWidth;
141 int rows;
143 WMColor *backColor;
145 GC gridGC;
146 WMColor *gridColor;
148 Cursor splitterCursor;
150 void *dataSource;
152 WMTableViewDelegate *delegate;
154 WMAction *action;
155 void *clientData;
157 void *clickedColumn;
158 int clickedRow;
160 int editingRow;
162 unsigned headerHeight;
164 unsigned rowHeight;
166 unsigned dragging:1;
167 unsigned drawsGrid:1;
168 unsigned canSelectRow:1;
169 unsigned canSelectMultiRows:1;
170 unsigned canDeselectRow:1;
173 static W_Class tableClass = 0;
176 static W_ViewDelegate viewDelegate = {
177 NULL,
178 NULL,
179 handleResize,
180 NULL,
181 NULL
188 static void handleEvents(XEvent *event, void *data);
189 static void handleTableEvents(XEvent *event, void *data);
190 static void repaintTable(WMTableView *table);
192 static WMSize getTotalSize(WMTableView *table)
194 WMSize size;
195 int i;
197 /* get width from columns */
198 size.width = 0;
199 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
200 WMTableColumn *column;
202 column = WMGetFromArray(table->columns, i);
204 size.width += column->width;
207 /* get height from rows */
208 size.height = table->rows * table->rowHeight;
210 return size;
214 static WMRect getVisibleRect(WMTableView *table)
216 WMSize size = getTotalSize(table);
217 WMRect rect;
219 rect.size.height = size.height * WMGetScrollerKnobProportion(table->vscroll);
220 rect.size.width = size.width * WMGetScrollerKnobProportion(table->hscroll);
222 rect.pos.x = (size.width - rect.size.width) * WMGetScrollerValue(table->hscroll);
223 rect.pos.y = (size.height - rect.size.height) * WMGetScrollerValue(table->vscroll);
225 return rect;
229 static void scrollToPoint(WMTableView *table, int x, int y)
231 WMSize size = getTotalSize(table);
232 int i;
233 float value, prop;
235 if (size.width > W_VIEW_WIDTH(table->tableView)) {
236 prop = (float)W_VIEW_WIDTH(table->tableView) / (float)size.width;
237 value = (float)x / (float)(size.width - W_VIEW_WIDTH(table->tableView));
238 } else {
239 prop = 1.0;
240 value = 0.0;
242 WMSetScrollerParameters(table->hscroll, value, prop);
245 if (size.height > W_VIEW_HEIGHT(table->tableView)) {
246 prop = (float)W_VIEW_HEIGHT(table->tableView) / (float)size.height;
247 value = (float)y / (float)(size.height - W_VIEW_HEIGHT(table->tableView));
248 } else {
249 prop = 1.0;
250 value = 0.0;
253 WMSetScrollerParameters(table->vscroll, value, prop);
257 if (table->editingRow >= 0) {
258 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
259 WMTableColumn *column;
261 column = WMGetFromArray(table->columns, i);
263 if (column->delegate && column->delegate->beginCellEdit)
264 (*column->delegate->beginCellEdit)(column->delegate, column,
265 table->editingRow);
269 repaintTable(table);
273 static void adjustScrollers(WMTableView *table)
275 WMSize size = getTotalSize(table);
276 WMSize vsize = WMGetViewSize(table->tableView);
277 float prop, value;
278 float oprop, ovalue;
280 if (size.width <= vsize.width) {
281 value = 0.0;
282 prop = 1.0;
283 } else {
284 oprop = WMGetScrollerKnobProportion(table->hscroll);
285 if (oprop == 0.0)
286 oprop = 1.0;
287 ovalue = WMGetScrollerValue(table->hscroll);
289 prop = (float)vsize.width/(float)size.width;
290 value = prop*ovalue / oprop;
292 WMSetScrollerParameters(table->hscroll, value, prop);
294 if (size.height <= vsize.height) {
295 value = 0.0;
296 prop = 1.0;
297 } else {
298 oprop = WMGetScrollerKnobProportion(table->vscroll);
299 oprop = WMGetScrollerKnobProportion(table->hscroll);
300 if (oprop == 0.0)
301 oprop = 1.0;
302 ovalue = WMGetScrollerValue(table->vscroll);
304 prop = (float)vsize.height/(float)size.height;
305 value = prop*ovalue / oprop;
307 WMSetScrollerParameters(table->vscroll, value, prop);
311 static void doScroll(WMWidget *self, void *data)
313 WMTableView *table = (WMTableView*)data;
314 float value;
315 float vpsize;
316 float size;
317 WMSize ts = getTotalSize(table);
319 value = WMGetScrollerValue(self);
321 if (table->hscroll == (WMScroller *)self) {
322 vpsize = W_VIEW_WIDTH(table->tableView);
323 size = ts.width;
324 } else {
325 vpsize = W_VIEW_HEIGHT(table->tableView);
326 size = ts.height;
329 switch (WMGetScrollerHitPart(self)) {
330 case WSDecrementWheel:
331 case WSDecrementLine:
332 value -= (float)table->rowHeight / size;
333 if (value < 0)
334 value = 0.0;
335 WMSetScrollerParameters(self, value,
336 WMGetScrollerKnobProportion(self));
337 repaintTable(table);
338 break;
340 case WSIncrementWheel:
341 case WSIncrementLine:
342 value += (float)table->rowHeight / size;
343 if (value > 1.0)
344 value = 1.0;
345 WMSetScrollerParameters(self, value,
346 WMGetScrollerKnobProportion(self));
347 repaintTable(table);
348 break;
350 case WSKnob:
351 repaintTable(table);
352 break;
354 case WSDecrementPage:
355 value -= vpsize / size;
356 if (value < 0.0)
357 value = 0.0;
358 WMSetScrollerParameters(self, value,
359 WMGetScrollerKnobProportion(self));
360 repaintTable(table);
361 break;
363 case WSIncrementPage:
364 value += vpsize / size;
365 if (value > 1.0)
366 value = 1.0;
367 WMSetScrollerParameters(self, value,
368 WMGetScrollerKnobProportion(self));
369 repaintTable(table);
370 break;
373 case WSNoPart:
374 case WSKnobSlot:
375 break;
378 if (table->editingRow >= 0) {
379 int i;
380 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
381 WMTableColumn *column;
383 column = WMGetFromArray(table->columns, i);
385 if (column->delegate && column->delegate->beginCellEdit)
386 (*column->delegate->beginCellEdit)(column->delegate, column,
387 table->editingRow);
392 if (table->hscroll == self) {
393 int x = 0;
394 int i;
395 WMRect rect = getVisibleRect(table);
397 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
398 WMTableColumn *column;
399 WMView *splitter;
401 column = WMGetFromArray(table->columns, i);
403 WMMoveWidget(column->titleW, x - rect.pos.x, 0);
405 x += W_VIEW_WIDTH(WMWidgetView(column->titleW)) + 1;
407 splitter = WMGetFromArray(table->splitters, i);
408 W_MoveView(splitter, x - rect.pos.x - 1, 0);
414 static void splitterHandler(XEvent *event, void *data)
416 WMTableColumn *column = (WMTableColumn*)data;
417 WMTableView *table = column->table;
418 int done = 0;
419 int cx, ox, offsX;
420 WMPoint pos;
421 WMScreen *scr = WMWidgetScreen(table);
422 GC gc = scr->ixorGC;
423 Display *dpy = WMScreenDisplay(scr);
424 int h = WMWidgetHeight(table) - 22;
425 Window w = WMViewXID(table->view);
427 pos = WMGetViewPosition(WMWidgetView(column->titleW));
429 offsX = pos.x + column->width;
431 ox = cx = offsX;
433 XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
435 while (!done) {
436 XEvent ev;
438 WMMaskEvent(dpy, ButtonMotionMask|ButtonReleaseMask, &ev);
440 switch (ev.type) {
441 case MotionNotify:
442 ox = cx;
444 if (column->width + ev.xmotion.x < column->minWidth)
445 cx = pos.x + column->minWidth;
446 else if (column->maxWidth > 0
447 && column->width + ev.xmotion.x > column->maxWidth)
448 cx = pos.x + column->maxWidth;
449 else
450 cx = offsX + ev.xmotion.x;
452 XDrawLine(dpy, w, gc, ox+20, 0, ox+20, h);
453 XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
454 break;
456 case ButtonRelease:
457 column->width = cx - pos.x;
458 done = 1;
459 break;
463 XDrawLine(dpy, w, gc, cx+20, 0, cx+20, h);
464 rearrangeHeader(table);
465 repaintTable(table);
469 static void realizeTable(void *data, WMNotification *notif)
471 repaintTable(data);
475 WMTableView *WMCreateTableView(WMWidget *parent)
477 WMTableView *table = wmalloc(sizeof(WMTableView));
478 WMScreen *scr = WMWidgetScreen(parent);
480 memset(table, 0, sizeof(WMTableView));
482 if (!tableClass) {
483 tableClass = W_RegisterUserWidget();
485 table->widgetClass = tableClass;
487 table->view = W_CreateView(W_VIEW(parent));
488 if (!table->view)
489 goto error;
490 table->view->self = table;
492 table->view->delegate = &viewDelegate;
494 table->headerHeight = 20;
496 table->hscroll = WMCreateScroller(table);
497 WMSetScrollerAction(table->hscroll, doScroll, table);
498 WMMoveWidget(table->hscroll, 1, 2+table->headerHeight);
499 WMMapWidget(table->hscroll);
501 table->vscroll = WMCreateScroller(table);
502 WMSetScrollerArrowsPosition(table->vscroll, WSAMaxEnd);
503 WMSetScrollerAction(table->vscroll, doScroll, table);
504 WMMoveWidget(table->vscroll, 1, 2+table->headerHeight);
505 WMMapWidget(table->vscroll);
508 table->header = WMCreateFrame(table);
509 WMMoveWidget(table->header, 22, 2);
510 WMMapWidget(table->header);
511 WMSetFrameRelief(table->header, WRFlat);
513 table->corner = WMCreateLabel(table);
514 WMResizeWidget(table->corner, 20, table->headerHeight);
515 WMMoveWidget(table->corner, 2, 2);
516 WMMapWidget(table->corner);
517 WMSetLabelRelief(table->corner, WRRaised);
518 WMSetWidgetBackgroundColor(table->corner, scr->darkGray);
521 table->tableView = W_CreateView(table->view);
522 if (!table->tableView)
523 goto error;
524 table->tableView->self = table;
525 W_MapView(table->tableView);
527 WMAddNotificationObserver(realizeTable, table, WMViewRealizedNotification,
528 table->tableView);
530 table->tableView->flags.dontCompressExpose = 1;
532 table->gridColor = WMCreateNamedColor(scr, "#cccccc", False);
533 /* table->gridColor = WMGrayColor(scr);*/
536 XGCValues gcv;
538 table->backColor = WMWhiteColor(scr);
540 gcv.foreground = WMColorPixel(table->gridColor);
541 gcv.dashes = 1;
542 gcv.line_style = LineOnOffDash;
543 table->gridGC = XCreateGC(WMScreenDisplay(scr), W_DRAWABLE(scr),
544 GCForeground, &gcv);
547 table->editingRow = -1;
548 table->clickedRow = -1;
550 table->drawsGrid = 1;
551 table->rowHeight = 16;
553 table->tableWidth = 1;
555 table->columns = WMCreateArray(4);
556 table->splitters = WMCreateArray(4);
558 table->selectedRows = WMCreateArray(16);
560 table->splitterCursor = XCreateFontCursor(WMScreenDisplay(scr),
561 XC_sb_h_double_arrow);
563 table->canSelectRow = 1;
565 WMCreateEventHandler(table->view, ExposureMask|StructureNotifyMask,
566 handleEvents, table);
568 WMCreateEventHandler(table->tableView, ExposureMask|ButtonPressMask|
569 ButtonReleaseMask|ButtonMotionMask,
570 handleTableEvents, table);
572 WMResizeWidget(table, 50, 50);
574 return table;
576 error:
577 if (table->tableView)
578 W_DestroyView(table->tableView);
579 if (table->view)
580 W_DestroyView(table->view);
581 wfree(table);
582 return NULL;
586 void WMAddTableViewColumn(WMTableView *table, WMTableColumn *column)
588 WMScreen *scr = WMWidgetScreen(table);
590 column->table = table;
592 WMAddToArray(table->columns, column);
594 if (!column->titleW) {
595 column->titleW = WMCreateLabel(table);
596 WMSetLabelRelief(column->titleW, WRRaised);
597 WMSetLabelFont(column->titleW, scr->boldFont);
598 WMSetLabelTextColor(column->titleW, scr->white);
599 WMSetWidgetBackgroundColor(column->titleW, scr->darkGray);
600 WMSetLabelText(column->titleW, column->title);
601 W_ReparentView(WMWidgetView(column->titleW),
602 WMWidgetView(table->header), 0, 0);
603 if (W_VIEW_REALIZED(table->view))
604 WMRealizeWidget(column->titleW);
605 WMMapWidget(column->titleW);
609 WMView *splitter = W_CreateView(WMWidgetView(table->header));
611 W_SetViewBackgroundColor(splitter, WMWhiteColor(scr));
613 if (W_VIEW_REALIZED(table->view))
614 W_RealizeView(splitter);
616 W_ResizeView(splitter, 2, table->headerHeight);
617 W_MapView(splitter);
619 W_SetViewCursor(splitter, table->splitterCursor);
620 WMCreateEventHandler(splitter, ButtonPressMask|ButtonReleaseMask,
621 splitterHandler, column);
623 WMAddToArray(table->splitters, splitter);
626 rearrangeHeader(table);
630 void WMSetTableViewHeaderHeight(WMTableView *table, unsigned height)
632 table->headerHeight = height;
634 handleResize(NULL, table->view);
638 void WMSetTableViewDelegate(WMTableView *table, WMTableViewDelegate *delegate)
640 table->delegate = delegate;
644 void WMSetTableViewAction(WMTableView *table, WMAction *action, void *clientData)
646 table->action = action;
648 table->clientData = clientData;
652 void *WMGetTableViewClickedColumn(WMTableView *table)
654 return table->clickedColumn;
658 int WMGetTableViewClickedRow(WMTableView *table)
660 return table->clickedRow;
664 WMArray *WMGetTableViewSelectedRows(WMTableView *table)
666 return table->selectedRows;
670 WMView *WMGetTableViewDocumentView(WMTableView *table)
672 return table->tableView;
676 void *WMTableViewDataForCell(WMTableView *table, WMTableColumn *column,
677 int row)
679 return (*table->delegate->valueForCell)(table->delegate, column, row);
683 void WMSetTableViewDataForCell(WMTableView *table, WMTableColumn *column,
684 int row, void *data)
686 (*table->delegate->setValueForCell)(table->delegate, column, row, data);
690 WMRect WMTableViewRectForCell(WMTableView *table, WMTableColumn *column,
691 int row)
693 WMRect rect;
694 int i;
696 rect.pos.x = 0;
697 rect.pos.y = row * table->rowHeight;
698 rect.size.height = table->rowHeight;
700 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
701 WMTableColumn *col;
702 col = WMGetFromArray(table->columns, i);
704 if (col == column) {
705 rect.size.width = col->width;
706 break;
709 rect.pos.x += col->width;
713 WMRect r = getVisibleRect(table);
715 rect.pos.y -= r.pos.y;
716 rect.pos.x -= r.pos.x;
719 return rect;
723 void WMSetTableViewDataSource(WMTableView *table, void *source)
725 table->dataSource = source;
729 void *WMGetTableViewDataSource(WMTableView *table)
731 return table->dataSource;
735 void WMSetTableViewBackgroundColor(WMTableView *table, WMColor *color)
737 W_SetViewBackgroundColor(table->tableView, color);
739 if (table->backColor)
740 WMReleaseColor(table->backColor);
742 table->backColor = WMRetainColor(color);
744 repaintTable(table);
748 void WMSetTableViewGridColor(WMTableView *table, WMColor *color)
750 WMReleaseColor(table->gridColor);
751 table->gridColor = WMRetainColor(color);
752 XSetForeground(WMScreenDisplay(WMWidgetScreen(table)), table->gridGC,
753 WMColorPixel(color));
754 repaintTable(table);
759 void WMSetTableViewRowHeight(WMTableView *table, int height)
761 table->rowHeight = height;
763 repaintTable(table);
767 void WMScrollTableViewRowToVisible(WMTableView *table, int row)
769 WMScroller *scroller;
770 WMRange range;
771 WMRect rect;
772 int newY, tmp;
774 rect = getVisibleRect(table);
775 range = rowsInRect(table, rect);
777 scroller = table->vscroll;
779 if (row < range.position) {
780 newY = row * table->rowHeight - rect.size.height / 2;
781 } else if (row >= range.position + range.count) {
782 newY = row * table->rowHeight - rect.size.height / 2;
783 } else {
784 return;
786 tmp = table->rows*table->rowHeight - rect.size.height;
787 newY = WMAX(0, WMIN(newY, tmp));
789 scrollToPoint(table, rect.pos.x, newY);
794 static void drawGrid(WMTableView *table, WMRect rect)
796 WMScreen *scr = WMWidgetScreen(table);
797 Display *dpy = WMScreenDisplay(scr);
798 int i;
799 int y1, y2;
800 int x1, x2;
801 int xx;
802 Drawable d = WMGetPixmapXID(table->viewBuffer);
803 GC gc = table->gridGC;
805 #if 0
806 char dashl[1] = {1};
808 XSetDashes(dpy, gc, 0, dashl, 1);
810 y1 = (rect.pos.y/table->rowHeight - 1)*table->rowHeight;
811 y2 = y1 + (rect.size.height/table->rowHeight+2)*table->rowHeight;
812 #endif
813 y1 = 0;
814 y2 = W_VIEW_HEIGHT(table->tableView);
816 xx = -rect.pos.x;
817 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
818 WMTableColumn *column;
820 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
822 column = WMGetFromArray(table->columns, i);
823 xx += column->width;
825 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
827 x1 = 0;
828 x2 = rect.size.width;
830 if (x2 <= x1)
831 return;
832 #if 0
833 XSetDashes(dpy, gc, (rect.pos.x&1), dashl, 1);
834 #endif
836 y1 = -rect.pos.y%table->rowHeight;
837 y2 = y1 + rect.size.height + table->rowHeight;
839 for (i = y1; i <= y2; i += table->rowHeight) {
840 XDrawLine(dpy, d, gc, x1, i, x2, i);
845 static WMRange columnsInRect(WMTableView *table, WMRect rect)
847 WMTableColumn *column;
848 int width;
849 int i , j;
850 int totalColumns = WMGetArrayItemCount(table->columns);
851 WMRange range;
853 j = 0;
854 width = 0;
855 for (i = 0; i < totalColumns; i++) {
856 column = WMGetFromArray(table->columns, i);
857 if (j == 0) {
858 if (width <= rect.pos.x && width + column->width > rect.pos.x) {
859 range.position = i;
860 j = 1;
862 } else {
863 range.count++;
864 if (width > rect.pos.x + rect.size.width) {
865 break;
868 width += column->width;
870 range.count = WMAX(1, WMIN(range.count, totalColumns - range.position));
871 return range;
875 static WMRange rowsInRect(WMTableView *table, WMRect rect)
877 WMRange range;
878 int rh = table->rowHeight;
879 int dif;
881 dif = rect.pos.y % rh;
883 range.position = WMAX(0, (rect.pos.y - dif) / rh);
884 range.count = WMAX(1, WMIN((rect.size.height + dif) / rh, table->rows));
886 return range;
890 static void drawRow(WMTableView *table, int row, WMRect clipRect)
892 int i;
893 WMRange cols = columnsInRect(table, clipRect);
894 WMTableColumn *column;
895 Drawable d = WMGetPixmapXID(table->viewBuffer);
897 for (i = cols.position; i < cols.position+cols.count; i++) {
898 column = WMGetFromArray(table->columns, i);
900 if (!column->delegate || !column->delegate->drawCell)
901 continue;
903 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
904 (*column->delegate->drawSelectedCell)(column->delegate, column, row, d);
905 else
906 (*column->delegate->drawCell)(column->delegate, column, row, d);
911 static void drawFullRow(WMTableView *table, int row)
913 int i;
914 WMTableColumn *column;
915 Drawable d = WMGetPixmapXID(table->viewBuffer);
917 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
918 column = WMGetFromArray(table->columns, i);
920 if (!column->delegate || !column->delegate->drawCell)
921 continue;
923 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound)
924 (*column->delegate->drawSelectedCell)(column->delegate, column, row, d);
925 else
926 (*column->delegate->drawCell)(column->delegate, column, row, d);
931 static void setRowSelected(WMTableView *table, unsigned row, Bool flag)
933 int repaint = 0;
935 if (WMFindInArray(table->selectedRows, NULL, (void*)row) != WANotFound) {
936 if (!flag) {
937 WMRemoveFromArray(table->selectedRows, (void*)row);
938 repaint = 1;
940 } else {
941 if (flag) {
942 WMAddToArray(table->selectedRows, (void*)row);
943 repaint = 1;
946 if (repaint && row < table->rows) {
947 //drawFullRow(table, row);
948 repaintTable(table);
953 static void repaintTable(WMTableView *table)
955 WMRect rect;
956 WMRange rows;
957 WMScreen *scr = WMWidgetScreen(table);
958 int i;
960 if (!table->delegate || !W_VIEW_REALIZED(table->view))
961 return;
963 wassertr(table->delegate->numberOfRows);
965 if (!table->viewBuffer) {
966 table->viewBuffer = WMCreatePixmap(scr,
967 W_VIEW_WIDTH(table->tableView),
968 W_VIEW_HEIGHT(table->tableView),
969 WMScreenDepth(scr), 0);
972 XFillRectangle(scr->display,
973 WMGetPixmapXID(table->viewBuffer),
974 WMColorGC(table->backColor), 0, 0,
975 W_VIEW_WIDTH(table->tableView),
976 W_VIEW_HEIGHT(table->tableView));
978 rect = getVisibleRect(table);
980 if (table->drawsGrid) {
981 drawGrid(table, rect);
984 rows = rowsInRect(table, rect);
985 for (i = rows.position;
986 i < WMIN(rows.position+rows.count + 1, table->rows);
987 i++) {
988 drawRow(table, i, rect);
991 XSetWindowBackgroundPixmap(scr->display, table->tableView->window,
992 WMGetPixmapXID(table->viewBuffer));
993 XClearWindow(scr->display, table->tableView->window);
997 static void stopRowEdit(WMTableView *table, int row)
999 int i;
1000 WMTableColumn *column;
1002 table->editingRow = -1;
1003 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1004 column = WMGetFromArray(table->columns, i);
1006 if (column->delegate && column->delegate->endCellEdit)
1007 (*column->delegate->endCellEdit)(column->delegate, column, row);
1013 void WMEditTableViewRow(WMTableView *table, int row)
1015 int i;
1016 WMTableColumn *column;
1018 if (table->editingRow >= 0) {
1019 stopRowEdit(table, table->editingRow);
1022 table->editingRow = row;
1024 if (row < 0)
1025 return;
1027 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1028 column = WMGetFromArray(table->columns, i);
1030 if (column->delegate && column->delegate->beginCellEdit)
1031 (*column->delegate->beginCellEdit)(column->delegate, column, row);
1036 void WMSelectTableViewRow(WMTableView *table, int row)
1038 if (table->clickedRow >= 0)
1039 setRowSelected(table, table->clickedRow, False);
1041 if (row >= table->rows) {
1042 return;
1045 setRowSelected(table, row, True);
1046 table->clickedRow = row;
1048 if (table->action)
1049 (*table->action)(table, table->clientData);
1050 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
1051 table, NULL);
1055 void WMReloadTableView(WMTableView *table)
1057 WMRect rect = getVisibleRect(table);
1059 if (table->editingRow >= 0)
1060 stopRowEdit(table, table->editingRow);
1062 /* when this is called, nothing in the table can be assumed to be
1063 * like the last time we accessed it (ie, rows might have disappeared) */
1065 WMEmptyArray(table->selectedRows);
1067 if (table->clickedRow >= 0) {
1068 table->clickedRow = -1;
1070 if (table->action)
1071 (*table->action)(table, table->clientData);
1072 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
1073 table, NULL);
1076 if (table->delegate && table->delegate->numberOfRows) {
1077 int rows;
1079 rows = (*table->delegate->numberOfRows)(table->delegate, table);
1081 if (rows != table->rows) {
1082 table->rows = rows;
1083 handleResize(table->view->delegate, table->view);
1084 } else {
1085 repaintTable(table);
1091 void WMNoteTableViewNumberOfRowsChanged(WMTableView *table)
1093 WMReloadTableView(table);
1097 static void handleTableEvents(XEvent *event, void *data)
1099 WMTableView *table = (WMTableView*)data;
1100 int row;
1102 switch (event->type) {
1103 case ButtonPress:
1104 if (event->xbutton.button == Button1) {
1105 WMRect rect = getVisibleRect(table);
1107 row = (event->xbutton.y + rect.pos.y)/table->rowHeight;
1108 if (row != table->clickedRow) {
1109 setRowSelected(table, table->clickedRow, False);
1110 setRowSelected(table, row, True);
1111 table->clickedRow = row;
1112 table->dragging = 1;
1113 } else {
1114 table->dragging = 1;
1117 break;
1119 case MotionNotify:
1120 if (table->dragging && event->xmotion.y >= 0) {
1121 WMRect rect = getVisibleRect(table);
1123 row = (event->xmotion.y + rect.pos.y)/table->rowHeight;
1124 if (table->clickedRow != row && row >= 0 && row < table->rows) {
1125 setRowSelected(table, table->clickedRow, False);
1126 setRowSelected(table, row, True);
1127 table->clickedRow = row;
1130 break;
1132 case ButtonRelease:
1133 if (event->xbutton.button == Button1) {
1134 if (table->action)
1135 (*table->action)(table, table->clientData);
1136 WMPostNotificationName(WMTableViewSelectionDidChangeNotification,
1137 table, NULL);
1138 table->dragging = 0;
1140 break;
1145 static void handleEvents(XEvent *event, void *data)
1147 WMTableView *table = (WMTableView*)data;
1148 WMScreen *scr = WMWidgetScreen(table);
1150 switch (event->type) {
1151 case Expose:
1152 W_DrawRelief(scr, W_VIEW_DRAWABLE(table->view), 0, 0,
1153 W_VIEW_WIDTH(table->view), W_VIEW_HEIGHT(table->view),
1154 WRSunken);
1155 break;
1160 static void handleResize(W_ViewDelegate *self, WMView *view)
1162 int width;
1163 int height;
1164 WMTableView *table = view->self;
1165 WMSize size = getTotalSize(table);
1166 WMScreen *scr = WMWidgetScreen(table);
1167 int vw, vh;
1169 width = W_VIEW_WIDTH(view) - 2;
1170 height = W_VIEW_HEIGHT(view) - 3;
1172 height -= table->headerHeight; /* table header */
1174 if (table->corner)
1175 WMResizeWidget(table->corner, 20, table->headerHeight);
1177 WMMoveWidget(table->vscroll, 1, table->headerHeight + 1);
1178 WMResizeWidget(table->vscroll, 20, height + 1);
1180 WMMoveWidget(table->hscroll, 20, W_VIEW_HEIGHT(view) - 20 - 1);
1181 WMResizeWidget(table->hscroll, width-20+1, 20);
1183 if (table->header)
1184 WMResizeWidget(table->header, width - 21, table->headerHeight);
1186 if (table->viewBuffer) {
1187 WMReleasePixmap(table->viewBuffer);
1188 table->viewBuffer = NULL;
1191 width -= 20;
1192 height -= 20;
1194 vw = WMIN(size.width, width);
1195 vh = WMIN(size.height, height);
1197 W_MoveView(table->tableView, 21, 1+table->headerHeight+1);
1198 W_ResizeView(table->tableView, WMAX(vw, 1), WMAX(vh, 1)+1);
1200 adjustScrollers(table);
1202 repaintTable(table);
1206 static void rearrangeHeader(WMTableView *table)
1208 int width;
1209 int count;
1210 int i;
1211 /*WMRect rect = WMGetScrollViewVisibleRect(table->scrollView);*/
1213 width = 0;
1215 count = WMGetArrayItemCount(table->columns);
1216 for (i = 0; i < count; i++) {
1217 WMTableColumn *column = WMGetFromArray(table->columns, i);
1218 WMView *splitter = WMGetFromArray(table->splitters, i);
1220 WMMoveWidget(column->titleW, width, 0);
1221 WMResizeWidget(column->titleW, column->width-1, table->headerHeight);
1223 width += column->width;
1224 W_MoveView(splitter, width-1, 0);
1227 wassertr(table->delegate && table->delegate->numberOfRows);
1229 table->rows = table->delegate->numberOfRows(table->delegate, table);
1231 table->tableWidth = width + 1;
1233 handleResize(table->view->delegate, table->view);