Update Serbian translation from master branch
[wmaker-crm.git] / WINGs / Extras / wtableview.c
blob967198491b8c01146a2e3a0fc3671d3c13348515
2 #include <WINGs/WINGsP.h>
3 #include <X11/cursorfont.h>
4 #include <stdint.h>
5 #include <math.h>
6 #include <float.h>
8 #include "wtableview.h"
10 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;
28 static void handleResize(W_ViewDelegate * self, WMView * view);
30 static void rearrangeHeader(WMTableView * table);
32 static WMRange rowsInRect(WMTableView * table, WMRect rect);
34 WMTableColumn *WMCreateTableColumn(char *title)
36 WMTableColumn *col = wmalloc(sizeof(WMTableColumn));
38 col->table = NULL;
39 col->titleW = NULL;
40 col->width = 50;
41 col->minWidth = 5;
42 col->maxWidth = 0;
44 col->id = NULL;
46 col->title = wstrdup(title);
48 col->delegate = NULL;
50 col->resizable = 1;
51 col->editable = 0;
53 return col;
56 void WMSetTableColumnId(WMTableColumn * column, void *id)
58 column->id = id;
61 void *WMGetTableColumnId(WMTableColumn * column)
63 return column->id;
66 void WMSetTableColumnWidth(WMTableColumn * column, unsigned width)
68 if (column->maxWidth == 0)
69 column->width = WMAX(column->minWidth, width);
70 else
71 column->width = WMAX(column->minWidth, WMIN(column->maxWidth, width));
73 if (column->table) {
74 rearrangeHeader(column->table);
78 void WMSetTableColumnDelegate(WMTableColumn * column, WMTableColumnDelegate * delegate)
80 column->delegate = delegate;
83 void WMSetTableColumnConstraints(WMTableColumn * column, unsigned minWidth, unsigned maxWidth)
85 wassertr(maxWidth == 0 || minWidth <= maxWidth);
87 column->minWidth = minWidth;
88 column->maxWidth = maxWidth;
90 if (column->width < column->minWidth)
91 WMSetTableColumnWidth(column, column->minWidth);
92 else if (column->width > column->maxWidth && column->maxWidth != 0)
93 WMSetTableColumnWidth(column, column->maxWidth);
96 void WMSetTableColumnEditable(WMTableColumn * column, Bool flag)
98 column->editable = ((flag == 0) ? 0 : 1);
101 WMTableView *WMGetTableColumnTableView(WMTableColumn * column)
103 return column->table;
106 struct W_TableView {
107 W_Class widgetClass;
108 WMView *view;
110 WMFrame *header;
112 WMLabel *corner;
114 WMScroller *hscroll;
115 WMScroller *vscroll;
116 WMView *tableView;
118 WMPixmap *viewBuffer;
120 WMArray *columns;
121 WMArray *splitters;
123 WMArray *selectedRows;
125 int tableWidth;
127 int rows;
129 WMColor *backColor;
131 GC gridGC;
132 WMColor *gridColor;
134 Cursor splitterCursor;
136 void *dataSource;
138 WMTableViewDelegate *delegate;
140 WMAction *action;
141 void *clientData;
143 void *clickedColumn;
144 int clickedRow;
146 int editingRow;
148 unsigned headerHeight;
150 unsigned rowHeight;
152 unsigned dragging:1;
153 unsigned drawsGrid:1;
154 unsigned canSelectRow:1;
155 unsigned canSelectMultiRows:1;
156 unsigned canDeselectRow:1;
158 unsigned int hasVScroller:1;
159 unsigned int hasHScroller:1;
162 static W_Class tableClass = 0;
164 static W_ViewDelegate viewDelegate = {
165 NULL,
166 NULL,
167 handleResize,
168 NULL,
169 NULL
172 static void reorganizeInterior(WMTableView * table);
174 static void handleEvents(XEvent * event, void *data);
175 static void handleTableEvents(XEvent * event, void *data);
176 static void repaintTable(WMTableView * table);
178 static WMSize getTotalSize(WMTableView * table)
180 WMSize size;
181 int i;
183 /* get width from columns */
184 size.width = 0;
185 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
186 WMTableColumn *column;
188 column = WMGetFromArray(table->columns, i);
190 size.width += column->width;
193 /* get height from rows */
194 size.height = table->rows * table->rowHeight;
196 return size;
199 static WMRect getVisibleRect(WMTableView * table)
201 WMSize size = getTotalSize(table);
202 WMRect rect;
204 if (table->vscroll) {
205 rect.size.height = size.height * WMGetScrollerKnobProportion(table->vscroll);
206 rect.pos.y = (size.height - rect.size.height) * WMGetScrollerValue(table->vscroll);
207 } else {
208 rect.size.height = size.height;
209 rect.pos.y = 0;
212 if (table->hscroll) {
213 rect.size.width = size.width * WMGetScrollerKnobProportion(table->hscroll);
214 rect.pos.x = (size.width - rect.size.width) * WMGetScrollerValue(table->hscroll);
215 } else {
216 rect.size.width = size.width;
217 rect.pos.x = 0;
220 return rect;
223 static void scrollToPoint(WMTableView * table, int x, int y)
225 WMSize size = getTotalSize(table);
226 int i;
227 float value, prop;
229 if (table->hscroll) {
230 if (size.width > W_VIEW_WIDTH(table->tableView)) {
231 prop = (float)W_VIEW_WIDTH(table->tableView) / (float)size.width;
232 value = (float)x / (float)(size.width - W_VIEW_WIDTH(table->tableView));
233 } else {
234 prop = 1.0;
235 value = 0.0;
237 WMSetScrollerParameters(table->hscroll, value, prop);
240 if (table->vscroll) {
241 if (size.height > W_VIEW_HEIGHT(table->tableView)) {
242 prop = (float)W_VIEW_HEIGHT(table->tableView) / (float)size.height;
243 value = (float)y / (float)(size.height - W_VIEW_HEIGHT(table->tableView));
244 } else {
245 prop = 1.0;
246 value = 0.0;
249 WMSetScrollerParameters(table->vscroll, value, prop);
252 if (table->editingRow >= 0) {
253 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
254 WMTableColumn *column;
256 column = WMGetFromArray(table->columns, i);
258 if (column->delegate && column->delegate->beginCellEdit)
259 (*column->delegate->beginCellEdit) (column->delegate, column, table->editingRow);
263 repaintTable(table);
266 static void adjustScrollers(WMTableView * table)
268 WMSize size = getTotalSize(table);
269 WMSize vsize = WMGetViewSize(table->tableView);
270 float prop, value;
271 float oprop, ovalue;
273 if (table->hscroll) {
274 if (size.width <= vsize.width) {
275 value = 0.0;
276 prop = 1.0;
277 } else {
278 oprop = WMGetScrollerKnobProportion(table->hscroll);
279 if (fabs(oprop) <= DBL_EPSILON)
280 oprop = 1.0;
281 ovalue = WMGetScrollerValue(table->hscroll);
283 prop = (float)vsize.width / (float)size.width;
284 value = prop * ovalue / oprop;
286 WMSetScrollerParameters(table->hscroll, value, prop);
289 if (table->vscroll) {
290 if (size.height <= vsize.height) {
291 value = 0.0;
292 prop = 1.0;
293 } else {
294 oprop = WMGetScrollerKnobProportion(table->vscroll);
295 if (fabs(oprop) <= DBL_EPSILON)
296 oprop = 1.0;
297 ovalue = WMGetScrollerValue(table->vscroll);
299 prop = (float)vsize.height / (float)size.height;
300 value = prop * ovalue / oprop;
302 WMSetScrollerParameters(table->vscroll, value, prop);
306 static void doScroll(WMWidget * self, void *data)
308 WMTableView *table = (WMTableView *) data;
309 float value;
310 float vpsize;
311 float size;
312 WMSize ts = getTotalSize(table);
314 value = WMGetScrollerValue(self);
316 if (table->hscroll == (WMScroller *) self) {
317 vpsize = W_VIEW_WIDTH(table->tableView);
318 size = ts.width;
319 } else {
320 vpsize = W_VIEW_HEIGHT(table->tableView);
321 size = ts.height;
324 switch (WMGetScrollerHitPart(self)) {
325 case WSDecrementWheel:
326 case WSDecrementLine:
327 value -= (float)table->rowHeight / size;
328 if (value < 0)
329 value = 0.0;
330 WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
331 repaintTable(table);
332 break;
334 case WSIncrementWheel:
335 case WSIncrementLine:
336 value += (float)table->rowHeight / size;
337 if (value > (float)1.0)
338 value = 1.0;
339 WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
340 repaintTable(table);
341 break;
343 case WSKnob:
344 repaintTable(table);
345 break;
347 case WSDecrementPage:
348 value -= vpsize / size;
349 if (value < (float)0.0)
350 value = 0.0;
351 WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
352 repaintTable(table);
353 break;
355 case WSIncrementPage:
356 value += vpsize / size;
357 if (value > (float)1.0)
358 value = 1.0;
359 WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
360 repaintTable(table);
361 break;
363 case WSNoPart:
364 case WSKnobSlot:
365 break;
368 if (table->editingRow >= 0) {
369 int i;
370 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
371 WMTableColumn *column;
373 column = WMGetFromArray(table->columns, i);
375 if (column->delegate && column->delegate->beginCellEdit)
376 (*column->delegate->beginCellEdit) (column->delegate, column, table->editingRow);
380 if (table->hscroll == self) {
381 int x = 0;
382 int i;
383 WMRect rect = getVisibleRect(table);
385 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
386 WMTableColumn *column;
387 WMView *splitter;
389 column = WMGetFromArray(table->columns, i);
391 WMMoveWidget(column->titleW, x - rect.pos.x, 0);
393 x += W_VIEW_WIDTH(WMWidgetView(column->titleW)) + 1;
395 splitter = WMGetFromArray(table->splitters, i);
396 W_MoveView(splitter, x - rect.pos.x - 1, 0);
401 static void splitterHandler(XEvent * event, void *data)
403 (void) event;
404 WMTableColumn *column = (WMTableColumn *) data;
405 WMTableView *table = column->table;
406 int done = 0;
407 int cx, ox, offsX;
408 WMPoint pos;
409 WMScreen *scr = WMWidgetScreen(table);
410 GC gc = scr->ixorGC;
411 Display *dpy = WMScreenDisplay(scr);
412 int h = WMWidgetHeight(table) - 22;
413 Window w = WMViewXID(table->view);
415 pos = WMGetViewPosition(WMWidgetView(column->titleW));
417 offsX = pos.x + column->width;
419 ox = cx = offsX;
421 XDrawLine(dpy, w, gc, cx + 20, 0, cx + 20, h);
423 while (!done) {
424 XEvent ev;
426 WMMaskEvent(dpy, ButtonMotionMask | ButtonReleaseMask, &ev);
428 switch (ev.type) {
429 case MotionNotify:
430 ox = cx;
432 if (column->width + ev.xmotion.x < column->minWidth)
433 cx = pos.x + column->minWidth;
434 else if (column->maxWidth > 0 && column->width + ev.xmotion.x > column->maxWidth)
435 cx = pos.x + column->maxWidth;
436 else
437 cx = offsX + ev.xmotion.x;
439 XDrawLine(dpy, w, gc, ox + 20, 0, ox + 20, h);
440 XDrawLine(dpy, w, gc, cx + 20, 0, cx + 20, h);
441 break;
443 case ButtonRelease:
444 column->width = cx - pos.x;
445 done = 1;
446 break;
450 XDrawLine(dpy, w, gc, cx + 20, 0, cx + 20, h);
451 rearrangeHeader(table);
452 repaintTable(table);
455 static void realizeTable(void *data, WMNotification * notif)
457 (void) notif;
459 repaintTable(data);
462 WMTableView *WMCreateTableView(WMWidget * parent)
464 WMTableView *table = wmalloc(sizeof(WMTableView));
465 WMScreen *scr = WMWidgetScreen(parent);
467 memset(table, 0, sizeof(WMTableView));
469 if (!tableClass) {
470 tableClass = W_RegisterUserWidget();
472 table->widgetClass = tableClass;
474 table->view = W_CreateView(W_VIEW(parent));
475 if (!table->view)
476 goto error;
477 table->view->self = table;
479 table->view->delegate = &viewDelegate;
481 table->headerHeight = 20;
483 table->hscroll = WMCreateScroller(table);
484 WMSetScrollerAction(table->hscroll, doScroll, table);
485 WMMoveWidget(table->hscroll, 1, 2 + table->headerHeight);
486 WMMapWidget(table->hscroll);
488 table->hasHScroller = 1;
490 table->vscroll = WMCreateScroller(table);
491 WMSetScrollerArrowsPosition(table->vscroll, WSAMaxEnd);
492 WMSetScrollerAction(table->vscroll, doScroll, table);
493 WMMoveWidget(table->vscroll, 1, 2 + table->headerHeight);
494 WMMapWidget(table->vscroll);
496 table->hasVScroller = 1;
498 table->header = WMCreateFrame(table);
499 WMMoveWidget(table->header, 22, 2);
500 WMMapWidget(table->header);
501 WMSetFrameRelief(table->header, WRFlat);
503 table->corner = WMCreateLabel(table);
504 WMResizeWidget(table->corner, 20, table->headerHeight);
505 WMMoveWidget(table->corner, 2, 2);
506 WMMapWidget(table->corner);
507 WMSetLabelRelief(table->corner, WRRaised);
508 WMSetWidgetBackgroundColor(table->corner, scr->darkGray);
510 table->tableView = W_CreateView(table->view);
511 if (!table->tableView)
512 goto error;
513 table->tableView->self = table;
514 W_MapView(table->tableView);
516 WMAddNotificationObserver(realizeTable, table, WMViewRealizedNotification, table->tableView);
518 table->tableView->flags.dontCompressExpose = 1;
520 table->gridColor = WMCreateNamedColor(scr, "#cccccc", False);
521 /* table->gridColor = WMGrayColor(scr); */
524 XGCValues gcv;
526 table->backColor = WMWhiteColor(scr);
528 gcv.foreground = WMColorPixel(table->gridColor);
529 gcv.dashes = 1;
530 gcv.line_style = LineOnOffDash;
531 table->gridGC = XCreateGC(WMScreenDisplay(scr), W_DRAWABLE(scr), GCForeground, &gcv);
534 table->editingRow = -1;
535 table->clickedRow = -1;
537 table->drawsGrid = 1;
538 table->rowHeight = 16;
540 table->tableWidth = 1;
542 table->columns = WMCreateArray(4);
543 table->splitters = WMCreateArray(4);
545 table->selectedRows = WMCreateArray(16);
547 table->splitterCursor = XCreateFontCursor(WMScreenDisplay(scr), XC_sb_h_double_arrow);
549 table->canSelectRow = 1;
551 WMCreateEventHandler(table->view, ExposureMask | StructureNotifyMask, handleEvents, table);
553 WMCreateEventHandler(table->tableView, ExposureMask | ButtonPressMask |
554 ButtonReleaseMask | ButtonMotionMask, handleTableEvents, table);
556 WMResizeWidget(table, 50, 50);
558 return table;
560 error:
561 if (table->tableView)
562 W_DestroyView(table->tableView);
563 if (table->view)
564 W_DestroyView(table->view);
565 wfree(table);
566 return NULL;
569 void WMAddTableViewColumn(WMTableView * table, WMTableColumn * column)
571 WMScreen *scr = WMWidgetScreen(table);
573 column->table = table;
575 WMAddToArray(table->columns, column);
577 if (!column->titleW) {
578 column->titleW = WMCreateLabel(table);
579 WMSetLabelRelief(column->titleW, WRRaised);
580 WMSetLabelFont(column->titleW, scr->boldFont);
581 WMSetLabelTextColor(column->titleW, scr->white);
582 WMSetWidgetBackgroundColor(column->titleW, scr->darkGray);
583 WMSetLabelText(column->titleW, column->title);
584 W_ReparentView(WMWidgetView(column->titleW), WMWidgetView(table->header), 0, 0);
585 if (W_VIEW_REALIZED(table->view))
586 WMRealizeWidget(column->titleW);
587 WMMapWidget(column->titleW);
591 WMView *splitter = W_CreateView(WMWidgetView(table->header));
593 W_SetViewBackgroundColor(splitter, WMWhiteColor(scr));
595 if (W_VIEW_REALIZED(table->view))
596 W_RealizeView(splitter);
598 W_ResizeView(splitter, 2, table->headerHeight);
599 W_MapView(splitter);
601 W_SetViewCursor(splitter, table->splitterCursor);
602 WMCreateEventHandler(splitter, ButtonPressMask | ButtonReleaseMask, splitterHandler, column);
604 WMAddToArray(table->splitters, splitter);
607 rearrangeHeader(table);
610 void WMSetTableViewHeaderHeight(WMTableView * table, unsigned height)
612 table->headerHeight = height;
614 handleResize(NULL, table->view);
617 void WMSetTableViewDelegate(WMTableView * table, WMTableViewDelegate * delegate)
619 table->delegate = delegate;
622 void WMSetTableViewAction(WMTableView * table, WMAction * action, void *clientData)
624 table->action = action;
626 table->clientData = clientData;
629 void *WMGetTableViewClickedColumn(WMTableView * table)
631 return table->clickedColumn;
634 int WMGetTableViewClickedRow(WMTableView * table)
636 return table->clickedRow;
639 WMArray *WMGetTableViewSelectedRows(WMTableView * table)
641 return table->selectedRows;
644 WMView *WMGetTableViewDocumentView(WMTableView * table)
646 return table->tableView;
649 void *WMTableViewDataForCell(WMTableView * table, WMTableColumn * column, int row)
651 return (*table->delegate->valueForCell) (table->delegate, column, row);
654 void WMSetTableViewDataForCell(WMTableView * table, WMTableColumn * column, int row, void *data)
656 (*table->delegate->setValueForCell) (table->delegate, column, row, data);
659 WMRect WMTableViewRectForCell(WMTableView * table, WMTableColumn * column, int row)
661 WMRect rect;
662 int i;
664 rect.pos.x = 0;
665 rect.pos.y = row * table->rowHeight;
666 rect.size.height = table->rowHeight;
668 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
669 WMTableColumn *col;
670 col = WMGetFromArray(table->columns, i);
672 if (col == column) {
673 rect.size.width = col->width;
674 break;
677 rect.pos.x += col->width;
681 WMRect r = getVisibleRect(table);
683 rect.pos.y -= r.pos.y;
684 rect.pos.x -= r.pos.x;
687 return rect;
690 void WMSetTableViewDataSource(WMTableView * table, void *source)
692 table->dataSource = source;
695 void *WMGetTableViewDataSource(WMTableView * table)
697 return table->dataSource;
700 void WMSetTableViewHasHorizontalScroller(WMTableView * tPtr, Bool flag)
702 if (flag) {
703 if (tPtr->hasHScroller)
704 return;
705 tPtr->hasHScroller = 1;
707 tPtr->hscroll = WMCreateScroller(tPtr);
708 WMSetScrollerAction(tPtr->hscroll, doScroll, tPtr);
709 WMSetScrollerArrowsPosition(tPtr->hscroll, WSAMaxEnd);
710 /* make it a horiz. scroller */
711 WMResizeWidget(tPtr->hscroll, 1, 2);
713 if (W_VIEW_REALIZED(tPtr->view)) {
714 WMRealizeWidget(tPtr->hscroll);
717 reorganizeInterior(tPtr);
719 WMMapWidget(tPtr->hscroll);
720 } else {
721 if (!tPtr->hasHScroller)
722 return;
723 tPtr->hasHScroller = 0;
725 WMUnmapWidget(tPtr->hscroll);
726 WMDestroyWidget(tPtr->hscroll);
727 tPtr->hscroll = NULL;
729 reorganizeInterior(tPtr);
733 #if 0
734 /* not supported by now */
735 void WMSetTableViewHasVerticalScroller(WMTableView * tPtr, Bool flag)
737 if (flag) {
738 if (tPtr->hasVScroller)
739 return;
740 tPtr->hasVScroller = 1;
742 tPtr->vscroll = WMCreateScroller(tPtr);
743 WMSetScrollerAction(tPtr->vscroll, doScroll, tPtr);
744 WMSetScrollerArrowsPosition(tPtr->vscroll, WSAMaxEnd);
745 /* make it a vert. scroller */
746 WMResizeWidget(tPtr->vscroll, 1, 2);
748 if (W_VIEW_REALIZED(tPtr->view)) {
749 WMRealizeWidget(tPtr->vscroll);
752 reorganizeInterior(tPtr);
754 WMMapWidget(tPtr->vscroll);
755 } else {
756 if (!tPtr->hasVScroller)
757 return;
758 tPtr->hasVScroller = 0;
760 WMUnmapWidget(tPtr->vscroll);
761 WMDestroyWidget(tPtr->vscroll);
762 tPtr->vscroll = NULL;
764 reorganizeInterior(tPtr);
767 #endif
769 void WMSetTableViewBackgroundColor(WMTableView * table, WMColor * color)
771 W_SetViewBackgroundColor(table->tableView, color);
773 if (table->backColor)
774 WMReleaseColor(table->backColor);
776 table->backColor = WMRetainColor(color);
778 repaintTable(table);
781 void WMSetTableViewGridColor(WMTableView * table, WMColor * color)
783 WMReleaseColor(table->gridColor);
784 table->gridColor = WMRetainColor(color);
785 XSetForeground(WMScreenDisplay(WMWidgetScreen(table)), table->gridGC, WMColorPixel(color));
786 repaintTable(table);
789 void WMSetTableViewRowHeight(WMTableView * table, int height)
791 table->rowHeight = height;
793 repaintTable(table);
796 void WMScrollTableViewRowToVisible(WMTableView * table, int row)
798 WMRange range;
799 WMRect rect;
800 int newY, tmp;
802 rect = getVisibleRect(table);
803 range = rowsInRect(table, rect);
806 if (row < range.position) {
807 newY = row * table->rowHeight - rect.size.height / 2;
808 } else if (row >= range.position + range.count) {
809 newY = row * table->rowHeight - rect.size.height / 2;
810 } else {
811 return;
813 tmp = table->rows * table->rowHeight - rect.size.height;
814 newY = WMAX(0, WMIN(newY, tmp));
816 scrollToPoint(table, rect.pos.x, newY);
819 static void drawGrid(WMTableView * table, WMRect rect)
821 WMScreen *scr = WMWidgetScreen(table);
822 Display *dpy = WMScreenDisplay(scr);
823 int i;
824 int y1, y2;
825 int x1, x2;
826 int xx;
827 Drawable d = WMGetPixmapXID(table->viewBuffer);
828 GC gc = table->gridGC;
830 #if 0
831 char dashl[1] = { 1 };
833 XSetDashes(dpy, gc, 0, dashl, 1);
835 y1 = (rect.pos.y / table->rowHeight - 1) * table->rowHeight;
836 y2 = y1 + (rect.size.height / table->rowHeight + 2) * table->rowHeight;
837 #endif
838 y1 = 0;
839 y2 = W_VIEW_HEIGHT(table->tableView);
841 xx = -rect.pos.x;
842 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
843 WMTableColumn *column;
845 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
847 column = WMGetFromArray(table->columns, i);
848 xx += column->width;
850 XDrawLine(dpy, d, gc, xx, y1, xx, y2);
852 x1 = 0;
853 x2 = rect.size.width;
855 if (x2 <= x1)
856 return;
857 #if 0
858 XSetDashes(dpy, gc, (rect.pos.x & 1), dashl, 1);
859 #endif
861 y1 = -rect.pos.y % table->rowHeight;
862 y2 = y1 + rect.size.height + table->rowHeight;
864 for (i = y1; i <= y2; i += table->rowHeight) {
865 XDrawLine(dpy, d, gc, x1, i, x2, i);
869 static WMRange columnsInRect(WMTableView * table, WMRect rect)
871 WMTableColumn *column;
872 int pos;
873 int i, found;
874 int totalColumns = WMGetArrayItemCount(table->columns);
875 WMRange range;
877 pos = 0;
878 found = 0;
879 for (i = 0; i < totalColumns; i++) {
880 column = WMGetFromArray(table->columns, i);
881 if (!found) {
882 if (rect.pos.x >= pos && rect.pos.x < pos + column->width) {
883 range.position = i;
884 range.count = 1;
885 found = 1;
887 } else {
888 if (pos > rect.pos.x + rect.size.width) {
889 break;
891 range.count++;
893 pos += column->width;
895 range.count = WMAX(1, WMIN(range.count, totalColumns - range.position));
896 return range;
899 static WMRange rowsInRect(WMTableView * table, WMRect rect)
901 WMRange range;
902 int rh = table->rowHeight;
903 int dif;
905 dif = rect.pos.y % rh;
907 range.position = WMAX(0, (rect.pos.y - dif) / rh);
908 range.count = WMAX(1, WMIN((rect.size.height + dif) / rh, table->rows));
910 return range;
913 static void drawRow(WMTableView * table, int row, WMRect clipRect)
915 int i;
916 WMRange cols = columnsInRect(table, clipRect);
917 WMTableColumn *column;
918 Drawable d = WMGetPixmapXID(table->viewBuffer);
920 for (i = cols.position; i < cols.position + cols.count; i++) {
921 column = WMGetFromArray(table->columns, i);
923 if (!column->delegate || !column->delegate->drawCell)
924 continue;
926 if (WMFindInArray(table->selectedRows, NULL, (void *)(uintptr_t) row) != WANotFound)
927 (*column->delegate->drawSelectedCell) (column->delegate, column, row, d);
928 else
929 (*column->delegate->drawCell) (column->delegate, column, row, d);
933 #if 0
934 static void drawFullRow(WMTableView * table, int row)
936 int i;
937 WMTableColumn *column;
938 Drawable d = WMGetPixmapXID(table->viewBuffer);
940 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
941 column = WMGetFromArray(table->columns, i);
943 if (!column->delegate || !column->delegate->drawCell)
944 continue;
946 if (WMFindInArray(table->selectedRows, NULL, (void *)row) != WANotFound)
947 (*column->delegate->drawSelectedCell) (column->delegate, column, row, d);
948 else
949 (*column->delegate->drawCell) (column->delegate, column, row, d);
952 #endif
954 static void setRowSelected(WMTableView * table, unsigned row, Bool flag)
956 int repaint = 0;
958 if (WMFindInArray(table->selectedRows, NULL, (void *)(uintptr_t) row) != WANotFound) {
959 if (!flag) {
960 WMRemoveFromArray(table->selectedRows, (void *)(uintptr_t) row);
961 repaint = 1;
963 } else {
964 if (flag) {
965 WMAddToArray(table->selectedRows, (void *)(uintptr_t) row);
966 repaint = 1;
969 if (repaint && row < table->rows) {
970 /*drawFullRow(table, row); */
971 repaintTable(table);
975 static void repaintTable(WMTableView * table)
977 WMRect rect;
978 WMRange rows;
979 WMScreen *scr = WMWidgetScreen(table);
980 int i;
982 if (!table->delegate || !W_VIEW_REALIZED(table->view))
983 return;
985 wassertr(table->delegate->numberOfRows);
987 if (!table->viewBuffer) {
988 table->viewBuffer = WMCreatePixmap(scr,
989 W_VIEW_WIDTH(table->tableView),
990 W_VIEW_HEIGHT(table->tableView), WMScreenDepth(scr), 0);
993 XFillRectangle(scr->display,
994 WMGetPixmapXID(table->viewBuffer),
995 WMColorGC(table->backColor), 0, 0,
996 W_VIEW_WIDTH(table->tableView), W_VIEW_HEIGHT(table->tableView));
998 rect = getVisibleRect(table);
1000 if (table->drawsGrid) {
1001 drawGrid(table, rect);
1004 rows = rowsInRect(table, rect);
1005 for (i = rows.position; i < WMIN(rows.position + rows.count + 1, table->rows); i++) {
1006 drawRow(table, i, rect);
1009 XSetWindowBackgroundPixmap(scr->display, table->tableView->window, WMGetPixmapXID(table->viewBuffer));
1010 XClearWindow(scr->display, table->tableView->window);
1013 static void stopRowEdit(WMTableView * table, int row)
1015 int i;
1016 WMTableColumn *column;
1018 table->editingRow = -1;
1019 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1020 column = WMGetFromArray(table->columns, i);
1022 if (column->delegate && column->delegate->endCellEdit)
1023 (*column->delegate->endCellEdit) (column->delegate, column, row);
1027 void WMEditTableViewRow(WMTableView * table, int row)
1029 int i;
1030 WMTableColumn *column;
1032 if (table->editingRow >= 0) {
1033 stopRowEdit(table, table->editingRow);
1036 table->editingRow = row;
1038 if (row < 0)
1039 return;
1041 for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1042 column = WMGetFromArray(table->columns, i);
1044 if (column->delegate && column->delegate->beginCellEdit)
1045 (*column->delegate->beginCellEdit) (column->delegate, column, row);
1049 void WMSelectTableViewRow(WMTableView * table, int row)
1051 if (table->clickedRow >= 0)
1052 setRowSelected(table, table->clickedRow, False);
1054 if (row >= table->rows) {
1055 return;
1058 setRowSelected(table, row, True);
1059 table->clickedRow = row;
1061 if (table->action)
1062 (*table->action) (table, table->clientData);
1063 WMPostNotificationName(WMTableViewSelectionDidChangeNotification, table, NULL);
1066 void WMReloadTableView(WMTableView * table)
1068 if (table->editingRow >= 0)
1069 stopRowEdit(table, table->editingRow);
1071 /* when this is called, nothing in the table can be assumed to be
1072 * like the last time we accessed it (ie, rows might have disappeared) */
1074 WMEmptyArray(table->selectedRows);
1076 if (table->clickedRow >= 0) {
1077 if (table->action)
1078 (*table->action) (table, table->clientData);
1079 WMPostNotificationName(WMTableViewSelectionDidChangeNotification, table, NULL);
1080 table->clickedRow = -1;
1083 if (table->delegate && table->delegate->numberOfRows) {
1084 int rows;
1086 rows = (*table->delegate->numberOfRows) (table->delegate, table);
1088 if (rows != table->rows) {
1089 table->rows = rows;
1090 handleResize(table->view->delegate, table->view);
1091 } else {
1092 repaintTable(table);
1097 void WMNoteTableViewNumberOfRowsChanged(WMTableView * table)
1099 WMReloadTableView(table);
1102 static void handleTableEvents(XEvent * event, void *data)
1104 WMTableView *table = (WMTableView *) data;
1105 int row;
1107 switch (event->type) {
1108 case ButtonPress:
1109 if (event->xbutton.button == Button1) {
1110 WMRect rect = getVisibleRect(table);
1112 row = (event->xbutton.y + rect.pos.y) / table->rowHeight;
1113 if (row != table->clickedRow) {
1114 setRowSelected(table, table->clickedRow, False);
1115 setRowSelected(table, row, True);
1116 table->clickedRow = row;
1117 table->dragging = 1;
1118 } else {
1119 table->dragging = 1;
1122 break;
1124 case MotionNotify:
1125 if (table->dragging && event->xmotion.y >= 0) {
1126 WMRect rect = getVisibleRect(table);
1128 row = (event->xmotion.y + rect.pos.y) / table->rowHeight;
1129 if (table->clickedRow != row && row >= 0 && row < table->rows) {
1130 setRowSelected(table, table->clickedRow, False);
1131 setRowSelected(table, row, True);
1132 table->clickedRow = row;
1135 break;
1137 case ButtonRelease:
1138 if (event->xbutton.button == Button1) {
1139 if (table->action)
1140 (*table->action) (table, table->clientData);
1141 WMPostNotificationName(WMTableViewSelectionDidChangeNotification, table, NULL);
1142 table->dragging = 0;
1144 break;
1148 static void handleEvents(XEvent * event, void *data)
1150 WMTableView *table = (WMTableView *) data;
1151 WMScreen *scr = WMWidgetScreen(table);
1153 switch (event->type) {
1154 case Expose:
1155 W_DrawRelief(scr, W_VIEW_DRAWABLE(table->view), 0, 0,
1156 W_VIEW_WIDTH(table->view), W_VIEW_HEIGHT(table->view), WRSunken);
1157 break;
1161 static void handleResize(W_ViewDelegate * self, WMView * view)
1163 (void) self;
1165 reorganizeInterior(view->self);
1168 static void reorganizeInterior(WMTableView * table)
1170 int width;
1171 int height;
1172 WMSize size = getTotalSize(table);
1173 WMView *view = table->view;
1174 int vw, vh;
1175 int hsThickness = 0;
1176 int vsThickness = 0;
1178 if (table->vscroll)
1179 vsThickness = WMWidgetWidth(table->vscroll);
1180 if (table->hscroll)
1181 hsThickness = WMWidgetHeight(table->hscroll);
1183 width = W_VIEW_WIDTH(view) - 2;
1184 height = W_VIEW_HEIGHT(view) - 3;
1186 height -= table->headerHeight; /* table header */
1188 if (table->corner)
1189 WMResizeWidget(table->corner, 20, table->headerHeight);
1191 WMMoveWidget(table->vscroll, 1, table->headerHeight + 1);
1192 WMResizeWidget(table->vscroll, 20, height + 1);
1194 if (table->hscroll) {
1195 WMMoveWidget(table->hscroll, vsThickness, W_VIEW_HEIGHT(view) - hsThickness - 1);
1196 WMResizeWidget(table->hscroll, width - (vsThickness + 1), hsThickness);
1199 if (table->header)
1200 WMResizeWidget(table->header, width - (vsThickness + 1), table->headerHeight);
1202 if (table->viewBuffer) {
1203 WMReleasePixmap(table->viewBuffer);
1204 table->viewBuffer = NULL;
1207 width -= vsThickness;
1208 height -= hsThickness;
1210 vw = WMIN(size.width, width);
1211 vh = WMIN(size.height, height);
1213 W_MoveView(table->tableView, vsThickness + 1, 1 + table->headerHeight + 1);
1214 W_ResizeView(table->tableView, WMAX(vw, 1), WMAX(vh, 1) + 1);
1216 adjustScrollers(table);
1218 repaintTable(table);
1221 static void rearrangeHeader(WMTableView * table)
1223 int width;
1224 int count;
1225 int i;
1226 /*WMRect rect = WMGetScrollViewVisibleRect(table->scrollView); */
1228 width = 0;
1230 count = WMGetArrayItemCount(table->columns);
1231 for (i = 0; i < count; i++) {
1232 WMTableColumn *column = WMGetFromArray(table->columns, i);
1233 WMView *splitter = WMGetFromArray(table->splitters, i);
1235 WMMoveWidget(column->titleW, width, 0);
1236 WMResizeWidget(column->titleW, column->width - 1, table->headerHeight);
1238 width += column->width;
1239 W_MoveView(splitter, width - 1, 0);
1242 wassertr(table->delegate && table->delegate->numberOfRows);
1244 table->rows = table->delegate->numberOfRows(table->delegate, table);
1246 table->tableWidth = width + 1;
1248 handleResize(table->view->delegate, table->view);