4 #include <X11/cursorfont.h>
6 #include "wtableview.h"
18 WMTableColumnDelegate
*delegate
;
26 static void handleResize(W_ViewDelegate
*self
, WMView
*view
);
29 WMTableColumn
*WMCreateTableColumn(char *title
)
31 WMTableColumn
*col
= wmalloc(sizeof(WMTableColumn
));
41 col
->title
= wstrdup(title
);
52 void WMSetTableColumnId(WMTableColumn
*column
, void *id
)
58 void *WMGetTableColumnId(WMTableColumn
*column
)
64 void WMSetTableColumnWidth(WMTableColumn
*column
, unsigned width
)
66 if (column
->maxWidth
== 0)
67 column
->width
= WMAX(column
->minWidth
, width
);
69 column
->width
= WMAX(column
->minWidth
, WMIN(column
->maxWidth
, width
));
72 handleResize(NULL
, W_VIEW(column
->table
));
77 void WMSetTableColumnDelegate(WMTableColumn
*column
,
78 WMTableColumnDelegate
*delegate
)
80 column
->delegate
= delegate
;
84 void WMSetTableColumnConstraints(WMTableColumn
*column
,
85 unsigned minWidth
, unsigned maxWidth
)
87 wassertr(minWidth
<= maxWidth
);
89 column
->minWidth
= minWidth
;
90 column
->maxWidth
= maxWidth
;
92 if (column
->width
< column
->minWidth
)
93 WMSetTableColumnWidth(column
, column
->minWidth
);
94 else if (column
->width
> column
->maxWidth
|| column
->maxWidth
== 0)
95 WMSetTableColumnWidth(column
, column
->maxWidth
);
99 void WMSetTableColumnEditable(WMTableColumn
*column
, Bool flag
)
101 column
->editable
= flag
;
105 WMTableView
*WMGetTableColumnTableView(WMTableColumn
*column
)
107 return column
->table
;
120 WMScrollView
*scrollView
;
133 Cursor splitterCursor
;
137 WMTableViewDelegate
*delegate
;
139 unsigned headerHeight
;
143 unsigned drawsGrid
:1;
146 static W_Class tableClass
= 0;
149 static W_ViewDelegate viewDelegate
= {
161 static void handleEvents(XEvent
*event
, void *data
);
162 static void handleTableEvents(XEvent
*event
, void *data
);
165 static void scrollObserver(void *self
, WMNotification
*notif
)
167 WMTableView
*table
= (WMTableView
*)self
;
171 rect
= WMGetScrollViewVisibleRect(table
->scrollView
);
174 for (i
= 0; i
< WMGetArrayItemCount(table
->columns
); i
++) {
175 WMTableColumn
*column
;
177 column
= WMGetFromArray(table
->columns
, i
);
179 WMMoveWidget(column
->titleW
, x
+ rect
.pos
.x
, 0);
184 splitter
= WMGetFromArray(table
->splitters
, i
-1);
185 W_MoveView(splitter
, x
+ rect
.pos
.x
- 1, 0);
188 x
+= W_VIEW_WIDTH(WMWidgetView(column
->titleW
)) + 1;
193 static void splitterHandler(XEvent
*event
, void *data
)
195 WMTableView
*table
= (WMTableView
*)data
;
202 WMTableView
*WMCreateTableView(WMWidget
*parent
)
204 WMTableView
*table
= wmalloc(sizeof(WMTableView
));
205 WMScreen
*scr
= WMWidgetScreen(parent
);
207 memset(table
, 0, sizeof(WMTableView
));
210 tableClass
= W_RegisterUserWidget();
212 table
->widgetClass
= tableClass
;
214 table
->view
= W_CreateView(W_VIEW(parent
));
217 table
->view
->self
= table
;
219 table
->view
->delegate
= &viewDelegate
;
221 table
->headerHeight
= 20;
223 table
->scrollView
= WMCreateScrollView(table
);
224 if (!table
->scrollView
)
226 WMResizeWidget(table
->scrollView
, 10, 10);
227 WMSetScrollViewHasVerticalScroller(table
->scrollView
, True
);
228 WMSetScrollViewHasHorizontalScroller(table
->scrollView
, True
);
230 WMScroller
*scroller
;
231 scroller
= WMGetScrollViewHorizontalScroller(table
->scrollView
);
232 WMAddNotificationObserver(scrollObserver
, table
,
233 WMScrollerDidScrollNotification
,
236 WMMoveWidget(table
->scrollView
, 1, 2+table
->headerHeight
);
237 WMMapWidget(table
->scrollView
);
239 table
->header
= WMCreateFrame(table
);
240 WMMoveWidget(table
->header
, 22, 2);
241 WMMapWidget(table
->header
);
242 WMSetFrameRelief(table
->header
, WRFlat
);
243 table
->corner
= WMCreateLabel(table
);
244 WMResizeWidget(table
->corner
, 20, table
->headerHeight
);
245 WMMoveWidget(table
->corner
, 2, 2);
246 WMMapWidget(table
->corner
);
247 WMSetLabelRelief(table
->corner
, WRRaised
);
248 WMSetWidgetBackgroundColor(table
->corner
, scr
->darkGray
);
251 table
->tableView
= W_CreateView(W_VIEW(parent
));
252 if (!table
->tableView
)
254 W_ResizeView(table
->tableView
, 100, 1000);
255 W_MapView(table
->tableView
);
257 WMSetScrollViewContentView(table
->scrollView
, table
->tableView
);
259 table
->tableView
->flags
.dontCompressExpose
= 1;
261 table
->gridColor
= WMDarkGrayColor(scr
);
266 gcv
.foreground
= WMColorPixel(table
->gridColor
);
268 gcv
.line_style
= LineOnOffDash
;
269 table
->gridGC
= XCreateGC(WMScreenDisplay(scr
), W_DRAWABLE(scr
),
273 table
->drawsGrid
= 1;
274 table
->rowHeight
= 16;
276 table
->tableWidth
= 1;
278 table
->columns
= WMCreateArray(4);
279 table
->splitters
= WMCreateArray(4);
281 table
->splitterCursor
= XCreateFontCursor(WMScreenDisplay(scr
),
282 XC_sb_h_double_arrow
);
284 WMCreateEventHandler(table
->view
, ExposureMask
|StructureNotifyMask
,
285 handleEvents
, table
);
287 WMCreateEventHandler(table
->tableView
, ExposureMask
,
288 handleTableEvents
, table
);
293 if (table
->scrollView
)
294 WMDestroyWidget(table
->scrollView
);
295 if (table
->tableView
)
296 W_DestroyView(table
->tableView
);
298 W_DestroyView(table
->view
);
304 void WMAddTableViewColumn(WMTableView
*table
, WMTableColumn
*column
)
308 WMScreen
*scr
= WMWidgetScreen(table
);
311 column
->table
= table
;
313 WMAddToArray(table
->columns
, column
);
315 if (!column
->titleW
) {
316 column
->titleW
= WMCreateLabel(table
);
317 WMSetLabelRelief(column
->titleW
, WRRaised
);
318 WMSetLabelFont(column
->titleW
, scr
->boldFont
);
319 WMSetLabelTextColor(column
->titleW
, scr
->white
);
320 WMSetWidgetBackgroundColor(column
->titleW
, scr
->darkGray
);
321 WMSetLabelText(column
->titleW
, column
->title
);
322 W_ReparentView(WMWidgetView(column
->titleW
),
323 WMWidgetView(table
->header
), 0, 0);
324 if (W_VIEW_REALIZED(table
->view
))
325 WMRealizeWidget(column
->titleW
);
326 WMMapWidget(column
->titleW
);
329 if (WMGetArrayItemCount(table
->columns
) > 1) {
330 WMView
*splitter
= W_CreateView(WMWidgetView(table
->header
));
332 W_SetViewBackgroundColor(splitter
, WMWhiteColor(scr
));
334 if (W_VIEW_REALIZED(table
->view
))
335 W_RealizeView(splitter
);
337 W_ResizeView(splitter
, 2, table
->headerHeight
-1);
340 W_SetViewCursor(splitter
, table
->splitterCursor
);
341 WMCreateEventHandler(splitter
, ButtonPressMask
|ButtonReleaseMask
342 |PointerMotionMask
, splitterHandler
, table
);
344 WMAddToArray(table
->splitters
, splitter
);
347 count
= WMGetArrayItemCount(table
->columns
);
348 for (i
= 0, width
= 0; i
< count
; i
++) {
349 WMTableColumn
*column
= WMGetFromArray(table
->columns
, i
);
351 WMMoveWidget(column
->titleW
, width
, 0);
352 WMResizeWidget(column
->titleW
, column
->width
-1, table
->headerHeight
);
355 WMView
*splitter
= WMGetFromArray(table
->splitters
, i
-1);
357 W_MoveView(splitter
, width
-1, 0);
359 width
+= column
->width
;
362 wassertr(table
->delegate
&& table
->delegate
->numberOfRows
);
364 table
->rows
= table
->delegate
->numberOfRows(table
->delegate
, table
);
366 W_ResizeView(table
->tableView
, width
+1,
367 table
->rows
* table
->rowHeight
+ 1);
369 table
->tableWidth
= width
+ 1;
373 void WMSetTableViewHeaderHeight(WMTableView
*table
, unsigned height
)
375 table
->headerHeight
= height
;
377 handleResize(NULL
, table
->view
);
381 void WMSetTableViewDelegate(WMTableView
*table
, WMTableViewDelegate
*delegate
)
383 table
->delegate
= delegate
;
387 WMView
*WMGetTableViewDocumentView(WMTableView
*table
)
389 return table
->tableView
;
393 void *WMTableViewDataForCell(WMTableView
*table
, WMTableColumn
*column
,
396 return (*table
->delegate
->valueForCell
)(table
->delegate
, column
, row
);
400 WMRect
WMTableViewRectForCell(WMTableView
*table
, WMTableColumn
*column
,
407 rect
.pos
.y
= row
* table
->rowHeight
;
408 rect
.size
.height
= table
->rowHeight
;
410 for (i
= 0; i
< WMGetArrayItemCount(table
->columns
); i
++) {
412 col
= WMGetFromArray(table
->columns
, i
);
415 rect
.size
.width
= col
->width
;
419 rect
.pos
.x
+= col
->width
;
425 void WMSetTableViewDataSource(WMTableView
*table
, void *source
)
427 table
->dataSource
= source
;
431 void *WMGetTableViewDataSource(WMTableView
*table
)
433 return table
->dataSource
;
437 void WMSetTableViewBackgroundColor(WMTableView
*table
, WMColor
*color
)
439 W_SetViewBackgroundColor(table
->tableView
, color
);
443 void WMSetTableViewGridColor(WMTableView
*table
, WMColor
*color
)
445 WMReleaseColor(table
->gridColor
);
446 table
->gridColor
= WMRetainColor(color
);
447 XSetForeground(WMScreenDisplay(WMWidgetScreen(table
)), table
->gridGC
, WMColorPixel(color
));
451 static void drawGrid(WMTableView
*table
, WMRect rect
)
453 WMScreen
*scr
= WMWidgetScreen(table
);
454 Display
*dpy
= WMScreenDisplay(scr
);
459 Drawable d
= W_VIEW_DRAWABLE(table
->tableView
);
460 GC gc
= table
->gridGC
;
465 XSetDashes(dpy
, gc
, 0, dashl
, 1);
467 y1
= (rect
.pos
.y
/table
->rowHeight
- 1)*table
->rowHeight
;
468 y2
= y1
+ (rect
.size
.height
/table
->rowHeight
+2)*table
->rowHeight
;
471 y2
= W_VIEW_HEIGHT(table
->tableView
);
474 for (i
= 0; i
< WMGetArrayItemCount(table
->columns
); i
++) {
475 WMTableColumn
*column
;
477 if (xx
>= rect
.pos
.x
&& xx
<= rect
.pos
.x
+rect
.size
.width
) {
478 XDrawLine(dpy
, d
, gc
, xx
, y1
, xx
, y2
);
480 column
= WMGetFromArray(table
->columns
, i
);
483 XDrawLine(dpy
, d
, gc
, xx
, y1
, xx
, y2
);
487 x2
= WMIN(x1
+ rect
.size
.width
, xx
);
492 XSetDashes(dpy
, gc
, (rect
.pos
.x
&1), dashl
, 1);
495 y1
= rect
.pos
.y
- rect
.pos
.y
%table
->rowHeight
;
496 y2
= y1
+ rect
.size
.height
+ table
->rowHeight
;
498 for (i
= y1
; i
<= y2
; i
+= table
->rowHeight
) {
499 XDrawLine(dpy
, d
, gc
, x1
, i
, x2
, i
);
504 static WMRange
columnsInRect(WMTableView
*table
, WMRect rect
)
506 WMTableColumn
*column
;
509 int totalColumns
= WMGetArrayItemCount(table
->columns
);
514 for (i
= 0; i
< totalColumns
; i
++) {
515 column
= WMGetFromArray(table
->columns
, i
);
517 if (width
<= rect
.pos
.x
&& width
+ column
->width
> rect
.pos
.x
) {
522 if (width
> rect
.pos
.x
+ rect
.size
.width
) {
523 range
.count
= i
- range
.position
;
527 width
+= column
->width
;
530 range
.count
= WMAX(1, WMIN(range
.count
, totalColumns
- range
.position
));
536 static WMRange
rowsInRect(WMTableView
*table
, WMRect rect
)
539 int rh
= table
->rowHeight
;
541 range
.position
= WMAX(0, rect
.pos
.y
/ rh
- 1);
542 range
.count
= WMAX(1, WMIN(rect
.size
.height
/ rh
+ 3, table
->rows
));
548 static void drawRow(WMTableView
*table
, int row
, WMRect clipRect
)
551 WMRange cols
= columnsInRect(table
, clipRect
);
552 WMTableColumn
*column
;
554 for (i
= cols
.position
; i
< cols
.position
+cols
.count
; i
++) {
555 column
= WMGetFromArray(table
->columns
, i
);
557 wassertr(column
->delegate
&& column
->delegate
->drawCell
);
559 (*column
->delegate
->drawCell
)(column
->delegate
, column
, row
);
564 static void repaintTable(WMTableView
*table
, int x
, int y
,
565 int width
, int height
)
571 wassertr(table
->delegate
&& table
->delegate
->numberOfRows
);
572 i
= (*table
->delegate
->numberOfRows
)(table
->delegate
, table
);
574 if (i
!= table
->rows
) {
576 W_ResizeView(table
->tableView
, table
->tableWidth
,
577 table
->rows
* table
->rowHeight
+ 1);
581 rect
.pos
= wmkpoint(x
,y
);
582 rect
.size
= wmksize(width
, height
);
584 if (table
->drawsGrid
) {
585 drawGrid(table
, rect
);
588 rows
= rowsInRect(table
, rect
);
589 for (i
= rows
.position
;
590 i
< WMIN(rows
.position
+rows
.count
, table
->rows
);
592 drawRow(table
, i
, rect
);
597 static void handleTableEvents(XEvent
*event
, void *data
)
599 WMTableView
*table
= (WMTableView
*)data
;
601 switch (event
->type
) {
603 repaintTable(table
, event
->xexpose
.x
, event
->xexpose
.y
,
604 event
->xexpose
.width
, event
->xexpose
.height
);
610 static void handleEvents(XEvent
*event
, void *data
)
612 WMTableView
*table
= (WMTableView
*)data
;
613 WMScreen
*scr
= WMWidgetScreen(table
);
615 switch (event
->type
) {
617 W_DrawRelief(scr
, W_VIEW_DRAWABLE(table
->view
), 0, 0,
618 W_VIEW_WIDTH(table
->view
), W_VIEW_HEIGHT(table
->view
),
625 static void handleResize(W_ViewDelegate
*self
, WMView
*view
)
629 WMTableView
*table
= view
->self
;
631 width
= W_VIEW_WIDTH(view
) - 2;
632 height
= W_VIEW_HEIGHT(view
) - 3;
634 height
-= table
->headerHeight
; /* table header */
637 WMResizeWidget(table
->corner
, 20, table
->headerHeight
);
639 if (table
->scrollView
) {
640 WMMoveWidget(table
->scrollView
, 1, table
->headerHeight
+ 2);
641 WMResizeWidget(table
->scrollView
, width
, height
);
644 WMResizeWidget(table
->header
, width
- 21, table
->headerHeight
);