4 #include <X11/cursorfont.h>
6 #include "wtableview.h"
9 const char *WMTableViewRowWasSelectedNotification
= "WMTableViewRowWasSelectedNotification";
10 const char *WMTableViewRowWasUnselectedNotification
= "WMTableViewRowWasUnselectedNotification";
13 struct W_TableColumn
{
23 WMTableColumnDelegate
*delegate
;
31 static void handleResize(W_ViewDelegate
*self
, WMView
*view
);
34 WMTableColumn
*WMCreateTableColumn(char *title
)
36 WMTableColumn
*col
= wmalloc(sizeof(WMTableColumn
));
46 col
->title
= wstrdup(title
);
57 void WMSetTableColumnId(WMTableColumn
*column
, void *id
)
63 void *WMGetTableColumnId(WMTableColumn
*column
)
69 void WMSetTableColumnWidth(WMTableColumn
*column
, unsigned width
)
71 if (column
->maxWidth
== 0)
72 column
->width
= WMAX(column
->minWidth
, width
);
74 column
->width
= WMAX(column
->minWidth
, WMIN(column
->maxWidth
, width
));
77 handleResize(NULL
, W_VIEW(column
->table
));
82 void WMSetTableColumnDelegate(WMTableColumn
*column
,
83 WMTableColumnDelegate
*delegate
)
85 column
->delegate
= delegate
;
89 void WMSetTableColumnConstraints(WMTableColumn
*column
,
90 unsigned minWidth
, unsigned maxWidth
)
92 wassertr(minWidth
<= maxWidth
);
94 column
->minWidth
= minWidth
;
95 column
->maxWidth
= maxWidth
;
97 if (column
->width
< column
->minWidth
)
98 WMSetTableColumnWidth(column
, column
->minWidth
);
99 else if (column
->width
> column
->maxWidth
|| column
->maxWidth
== 0)
100 WMSetTableColumnWidth(column
, column
->maxWidth
);
104 void WMSetTableColumnEditable(WMTableColumn
*column
, Bool flag
)
106 column
->editable
= flag
;
110 WMTableView
*WMGetTableColumnTableView(WMTableColumn
*column
)
112 return column
->table
;
125 WMScrollView
*scrollView
;
131 WMArray
*selectedRows
;
140 Cursor splitterCursor
;
144 WMTableViewDelegate
*delegate
;
148 unsigned headerHeight
;
152 unsigned drawsGrid
:1;
153 unsigned canSelectRow
:1;
154 unsigned canSelectMultiRows
:1;
155 unsigned canDeselectRow
:1;
158 static W_Class tableClass
= 0;
161 static W_ViewDelegate viewDelegate
= {
173 static void handleEvents(XEvent
*event
, void *data
);
174 static void handleTableEvents(XEvent
*event
, void *data
);
177 static void scrollObserver(void *self
, WMNotification
*notif
)
179 WMTableView
*table
= (WMTableView
*)self
;
183 rect
= WMGetScrollViewVisibleRect(table
->scrollView
);
186 for (i
= 0; i
< WMGetArrayItemCount(table
->columns
); i
++) {
187 WMTableColumn
*column
;
189 column
= WMGetFromArray(table
->columns
, i
);
191 WMMoveWidget(column
->titleW
, x
+ rect
.pos
.x
, 0);
196 splitter
= WMGetFromArray(table
->splitters
, i
-1);
197 W_MoveView(splitter
, x
+ rect
.pos
.x
- 1, 0);
200 x
+= W_VIEW_WIDTH(WMWidgetView(column
->titleW
)) + 1;
205 static void splitterHandler(XEvent
*event
, void *data
)
207 WMTableView
*table
= (WMTableView
*)data
;
213 WMMaskEvent(event
->xany
.display
, ButtonMotionMask
|ButtonReleaseMask
,
216 switch (event
->type
) {
218 printf("%i\n", event
->xmotion
.x
);
230 WMTableView
*WMCreateTableView(WMWidget
*parent
)
232 WMTableView
*table
= wmalloc(sizeof(WMTableView
));
233 WMScreen
*scr
= WMWidgetScreen(parent
);
235 memset(table
, 0, sizeof(WMTableView
));
238 tableClass
= W_RegisterUserWidget();
240 table
->widgetClass
= tableClass
;
242 table
->view
= W_CreateView(W_VIEW(parent
));
245 table
->view
->self
= table
;
247 table
->view
->delegate
= &viewDelegate
;
249 table
->headerHeight
= 20;
251 table
->scrollView
= WMCreateScrollView(table
);
252 if (!table
->scrollView
)
254 WMResizeWidget(table
->scrollView
, 10, 10);
255 WMSetScrollViewHasVerticalScroller(table
->scrollView
, True
);
256 WMSetScrollViewHasHorizontalScroller(table
->scrollView
, True
);
258 WMScroller
*scroller
;
259 scroller
= WMGetScrollViewHorizontalScroller(table
->scrollView
);
260 WMAddNotificationObserver(scrollObserver
, table
,
261 WMScrollerDidScrollNotification
,
264 WMMoveWidget(table
->scrollView
, 1, 2+table
->headerHeight
);
265 WMMapWidget(table
->scrollView
);
267 table
->header
= WMCreateFrame(table
);
268 WMMoveWidget(table
->header
, 22, 2);
269 WMMapWidget(table
->header
);
270 WMSetFrameRelief(table
->header
, WRFlat
);
271 table
->corner
= WMCreateLabel(table
);
272 WMResizeWidget(table
->corner
, 20, table
->headerHeight
);
273 WMMoveWidget(table
->corner
, 2, 2);
274 WMMapWidget(table
->corner
);
275 WMSetLabelRelief(table
->corner
, WRRaised
);
276 WMSetWidgetBackgroundColor(table
->corner
, scr
->darkGray
);
279 table
->tableView
= W_CreateView(W_VIEW(parent
));
280 if (!table
->tableView
)
282 table
->tableView
->self
= table
;
283 W_ResizeView(table
->tableView
, 100, 1000);
284 W_MapView(table
->tableView
);
286 WMSetScrollViewContentView(table
->scrollView
, table
->tableView
);
288 table
->tableView
->flags
.dontCompressExpose
= 1;
290 table
->gridColor
= WMDarkGrayColor(scr
);
295 gcv
.foreground
= WMColorPixel(table
->gridColor
);
297 gcv
.line_style
= LineOnOffDash
;
298 table
->gridGC
= XCreateGC(WMScreenDisplay(scr
), W_DRAWABLE(scr
),
302 table
->editingRow
= -1;
304 table
->drawsGrid
= 1;
305 table
->rowHeight
= 16;
307 table
->tableWidth
= 1;
309 table
->columns
= WMCreateArray(4);
310 table
->splitters
= WMCreateArray(4);
312 table
->selectedRows
= WMCreateArray(16);
314 table
->splitterCursor
= XCreateFontCursor(WMScreenDisplay(scr
),
315 XC_sb_h_double_arrow
);
317 table
->canSelectRow
= 1;
319 WMCreateEventHandler(table
->view
, ExposureMask
|StructureNotifyMask
,
320 handleEvents
, table
);
322 WMCreateEventHandler(table
->tableView
, ExposureMask
|ButtonPressMask
,
323 handleTableEvents
, table
);
328 if (table
->scrollView
)
329 WMDestroyWidget(table
->scrollView
);
330 if (table
->tableView
)
331 W_DestroyView(table
->tableView
);
333 W_DestroyView(table
->view
);
339 void WMAddTableViewColumn(WMTableView
*table
, WMTableColumn
*column
)
343 WMScreen
*scr
= WMWidgetScreen(table
);
346 column
->table
= table
;
348 WMAddToArray(table
->columns
, column
);
350 if (!column
->titleW
) {
351 column
->titleW
= WMCreateLabel(table
);
352 WMSetLabelRelief(column
->titleW
, WRRaised
);
353 WMSetLabelFont(column
->titleW
, scr
->boldFont
);
354 WMSetLabelTextColor(column
->titleW
, scr
->white
);
355 WMSetWidgetBackgroundColor(column
->titleW
, scr
->darkGray
);
356 WMSetLabelText(column
->titleW
, column
->title
);
357 W_ReparentView(WMWidgetView(column
->titleW
),
358 WMWidgetView(table
->header
), 0, 0);
359 if (W_VIEW_REALIZED(table
->view
))
360 WMRealizeWidget(column
->titleW
);
361 WMMapWidget(column
->titleW
);
364 if (WMGetArrayItemCount(table
->columns
) > 1) {
365 WMView
*splitter
= W_CreateView(WMWidgetView(table
->header
));
367 W_SetViewBackgroundColor(splitter
, WMWhiteColor(scr
));
369 if (W_VIEW_REALIZED(table
->view
))
370 W_RealizeView(splitter
);
372 W_ResizeView(splitter
, 2, table
->headerHeight
-1);
375 W_SetViewCursor(splitter
, table
->splitterCursor
);
376 WMCreateEventHandler(splitter
, ButtonPressMask
,
377 splitterHandler
, table
);
379 WMAddToArray(table
->splitters
, splitter
);
382 count
= WMGetArrayItemCount(table
->columns
);
383 for (i
= 0, width
= 0; i
< count
; i
++) {
384 WMTableColumn
*column
= WMGetFromArray(table
->columns
, i
);
386 WMMoveWidget(column
->titleW
, width
, 0);
387 WMResizeWidget(column
->titleW
, column
->width
-1, table
->headerHeight
);
390 WMView
*splitter
= WMGetFromArray(table
->splitters
, i
-1);
392 W_MoveView(splitter
, width
-1, 0);
394 width
+= column
->width
;
397 wassertr(table
->delegate
&& table
->delegate
->numberOfRows
);
399 table
->rows
= table
->delegate
->numberOfRows(table
->delegate
, table
);
401 W_ResizeView(table
->tableView
, width
+1,
402 table
->rows
* table
->rowHeight
+ 1);
404 table
->tableWidth
= width
+ 1;
408 void WMSetTableViewHeaderHeight(WMTableView
*table
, unsigned height
)
410 table
->headerHeight
= height
;
412 handleResize(NULL
, table
->view
);
416 void WMSetTableViewDelegate(WMTableView
*table
, WMTableViewDelegate
*delegate
)
418 table
->delegate
= delegate
;
422 WMView
*WMGetTableViewDocumentView(WMTableView
*table
)
424 return table
->tableView
;
428 void *WMTableViewDataForCell(WMTableView
*table
, WMTableColumn
*column
,
431 return (*table
->delegate
->valueForCell
)(table
->delegate
, column
, row
);
435 void WMSetTableViewDataForCell(WMTableView
*table
, WMTableColumn
*column
,
438 (*table
->delegate
->setValueForCell
)(table
->delegate
, column
, row
, data
);
442 WMRect
WMTableViewRectForCell(WMTableView
*table
, WMTableColumn
*column
,
449 rect
.pos
.y
= row
* table
->rowHeight
;
450 rect
.size
.height
= table
->rowHeight
;
452 for (i
= 0; i
< WMGetArrayItemCount(table
->columns
); i
++) {
454 col
= WMGetFromArray(table
->columns
, i
);
457 rect
.size
.width
= col
->width
;
461 rect
.pos
.x
+= col
->width
;
467 void WMSetTableViewDataSource(WMTableView
*table
, void *source
)
469 table
->dataSource
= source
;
473 void *WMGetTableViewDataSource(WMTableView
*table
)
475 return table
->dataSource
;
479 void WMSetTableViewBackgroundColor(WMTableView
*table
, WMColor
*color
)
481 W_SetViewBackgroundColor(table
->tableView
, color
);
485 void WMSetTableViewGridColor(WMTableView
*table
, WMColor
*color
)
487 WMReleaseColor(table
->gridColor
);
488 table
->gridColor
= WMRetainColor(color
);
489 XSetForeground(WMScreenDisplay(WMWidgetScreen(table
)), table
->gridGC
, WMColorPixel(color
));
494 static void drawGrid(WMTableView
*table
, WMRect rect
)
496 WMScreen
*scr
= WMWidgetScreen(table
);
497 Display
*dpy
= WMScreenDisplay(scr
);
502 Drawable d
= W_VIEW_DRAWABLE(table
->tableView
);
503 GC gc
= table
->gridGC
;
508 XSetDashes(dpy
, gc
, 0, dashl
, 1);
510 y1
= (rect
.pos
.y
/table
->rowHeight
- 1)*table
->rowHeight
;
511 y2
= y1
+ (rect
.size
.height
/table
->rowHeight
+2)*table
->rowHeight
;
514 y2
= W_VIEW_HEIGHT(table
->tableView
);
517 for (i
= 0; i
< WMGetArrayItemCount(table
->columns
); i
++) {
518 WMTableColumn
*column
;
520 if (xx
>= rect
.pos
.x
&& xx
<= rect
.pos
.x
+rect
.size
.width
) {
521 XDrawLine(dpy
, d
, gc
, xx
, y1
, xx
, y2
);
523 column
= WMGetFromArray(table
->columns
, i
);
526 XDrawLine(dpy
, d
, gc
, xx
, y1
, xx
, y2
);
530 x2
= WMIN(x1
+ rect
.size
.width
, xx
);
535 XSetDashes(dpy
, gc
, (rect
.pos
.x
&1), dashl
, 1);
538 y1
= rect
.pos
.y
- rect
.pos
.y
%table
->rowHeight
;
539 y2
= y1
+ rect
.size
.height
+ table
->rowHeight
;
541 for (i
= y1
; i
<= y2
; i
+= table
->rowHeight
) {
542 XDrawLine(dpy
, d
, gc
, x1
, i
, x2
, i
);
547 static WMRange
columnsInRect(WMTableView
*table
, WMRect rect
)
549 WMTableColumn
*column
;
552 int totalColumns
= WMGetArrayItemCount(table
->columns
);
557 for (i
= 0; i
< totalColumns
; i
++) {
558 column
= WMGetFromArray(table
->columns
, i
);
560 if (width
<= rect
.pos
.x
&& width
+ column
->width
> rect
.pos
.x
) {
565 if (width
> rect
.pos
.x
+ rect
.size
.width
) {
566 range
.count
= i
- range
.position
;
570 width
+= column
->width
;
573 range
.count
= WMAX(1, WMIN(range
.count
, totalColumns
- range
.position
));
579 static WMRange
rowsInRect(WMTableView
*table
, WMRect rect
)
582 int rh
= table
->rowHeight
;
584 range
.position
= WMAX(0, rect
.pos
.y
/ rh
- 1);
585 range
.count
= WMAX(1, WMIN(rect
.size
.height
/ rh
+ 3, table
->rows
));
591 static void drawRow(WMTableView
*table
, int row
, WMRect clipRect
)
594 WMRange cols
= columnsInRect(table
, clipRect
);
595 WMTableColumn
*column
;
597 for (i
= cols
.position
; i
< cols
.position
+cols
.count
; i
++) {
598 column
= WMGetFromArray(table
->columns
, i
);
600 wassertr(column
->delegate
&& column
->delegate
->drawCell
);
602 if (WMFindInArray(table
->selectedRows
, NULL
, (void*)row
) != WANotFound
)
603 (*column
->delegate
->drawSelectedCell
)(column
->delegate
, column
, row
);
605 (*column
->delegate
->drawCell
)(column
->delegate
, column
, row
);
610 static void drawFullRow(WMTableView
*table
, int row
)
613 WMTableColumn
*column
;
615 for (i
= 0; i
< WMGetArrayItemCount(table
->columns
); i
++) {
616 column
= WMGetFromArray(table
->columns
, i
);
618 wassertr(column
->delegate
&& column
->delegate
->drawCell
);
620 if (WMFindInArray(table
->selectedRows
, NULL
, (void*)row
) != WANotFound
)
621 (*column
->delegate
->drawSelectedCell
)(column
->delegate
, column
, row
);
623 (*column
->delegate
->drawCell
)(column
->delegate
, column
, row
);
628 static void setRowSelected(WMTableView
*table
, unsigned row
, Bool flag
)
633 if (WMGetArrayItemCount(table
->selectedRows
) > 0
634 && !table
->canSelectMultiRows
) {
635 int r
= (int)WMGetFromArray(table
->selectedRows
, 0);
637 WMDeleteFromArray(table
->selectedRows
, 0);
639 drawFullRow(table
, r
);
641 WMPostNotificationName(WMTableViewRowWasUnselectedNotification
,
645 if (WMFindInArray(table
->selectedRows
, NULL
, (void*)row
) != WANotFound
) {
647 WMRemoveFromArray(table
->selectedRows
, (void*)row
);
648 WMPostNotificationName(WMTableViewRowWasUnselectedNotification
,
654 WMAddToArray(table
->selectedRows
, (void*)row
);
655 WMPostNotificationName(WMTableViewRowWasSelectedNotification
,
662 drawFullRow(table
, row
);
667 static void repaintTable(WMTableView
*table
, int x
, int y
,
668 int width
, int height
)
674 wassertr(table
->delegate
&& table
->delegate
->numberOfRows
);
675 i
= (*table
->delegate
->numberOfRows
)(table
->delegate
, table
);
677 if (i
!= table
->rows
) {
679 W_ResizeView(table
->tableView
, table
->tableWidth
,
680 table
->rows
* table
->rowHeight
+ 1);
684 rect
.pos
= wmkpoint(x
,y
);
685 rect
.size
= wmksize(width
, height
);
687 if (table
->drawsGrid
) {
688 drawGrid(table
, rect
);
691 rows
= rowsInRect(table
, rect
);
692 for (i
= rows
.position
;
693 i
< WMIN(rows
.position
+rows
.count
, table
->rows
);
695 drawRow(table
, i
, rect
);
700 static void stopRowEdit(WMTableView
*table
, int row
)
703 WMTableColumn
*column
;
705 table
->editingRow
= -1;
706 for (i
= 0; i
< WMGetArrayItemCount(table
->columns
); i
++) {
707 column
= WMGetFromArray(table
->columns
, i
);
709 wassertr(column
->delegate
&& column
->delegate
->endCellEdit
);
711 (*column
->delegate
->endCellEdit
)(column
->delegate
, column
, row
);
717 void WMEditTableViewRow(WMTableView
*table
, int row
)
720 WMTableColumn
*column
;
722 if (table
->editingRow
>= 0) {
723 stopRowEdit(table
, table
->editingRow
);
726 table
->editingRow
= row
;
727 for (i
= 0; i
< WMGetArrayItemCount(table
->columns
); i
++) {
728 column
= WMGetFromArray(table
->columns
, i
);
730 wassertr(column
->delegate
&& column
->delegate
->beginCellEdit
);
732 (*column
->delegate
->beginCellEdit
)(column
->delegate
, column
, row
);
737 static void handleTableEvents(XEvent
*event
, void *data
)
739 WMTableView
*table
= (WMTableView
*)data
;
741 switch (event
->type
) {
743 setRowSelected(table
, event
->xbutton
.y
/table
->rowHeight
, True
);
747 repaintTable(table
, event
->xexpose
.x
, event
->xexpose
.y
,
748 event
->xexpose
.width
, event
->xexpose
.height
);
754 static void handleEvents(XEvent
*event
, void *data
)
756 WMTableView
*table
= (WMTableView
*)data
;
757 WMScreen
*scr
= WMWidgetScreen(table
);
759 switch (event
->type
) {
761 W_DrawRelief(scr
, W_VIEW_DRAWABLE(table
->view
), 0, 0,
762 W_VIEW_WIDTH(table
->view
), W_VIEW_HEIGHT(table
->view
),
769 static void handleResize(W_ViewDelegate
*self
, WMView
*view
)
773 WMTableView
*table
= view
->self
;
775 width
= W_VIEW_WIDTH(view
) - 2;
776 height
= W_VIEW_HEIGHT(view
) - 3;
778 height
-= table
->headerHeight
; /* table header */
781 WMResizeWidget(table
->corner
, 20, table
->headerHeight
);
783 if (table
->scrollView
) {
784 WMMoveWidget(table
->scrollView
, 1, table
->headerHeight
+ 2);
785 WMResizeWidget(table
->scrollView
, width
, height
);
788 WMResizeWidget(table
->header
, width
- 21, table
->headerHeight
);