1 
2 #include <WINGs/WINGsP.h>
3 #include <X11/cursorfont.h>
4 #include <stdint.h>
5 
6 #include "wtableview.h"
7 
8 const char *WMTableViewSelectionDidChangeNotification = "WMTableViewSelectionDidChangeNotification";
9 
10 struct W_TableColumn {
11 	WMTableView *table;
12 	WMWidget *titleW;
13 	char *title;
14 	int width;
15 	int minWidth;
16 	int maxWidth;
17 
18 	void *id;
19 
20 	WMTableColumnDelegate *delegate;
21 
22 	unsigned resizable:1;
23 	unsigned editable:1;
24 };
25 
26 static void handleResize(W_ViewDelegate * self, WMView * view);
27 
28 static void rearrangeHeader(WMTableView * table);
29 
30 static WMRange rowsInRect(WMTableView * table, WMRect rect);
31 
WMCreateTableColumn(char * title)32 WMTableColumn *WMCreateTableColumn(char *title)
33 {
34 	WMTableColumn *col = wmalloc(sizeof(WMTableColumn));
35 
36 	col->table = NULL;
37 	col->titleW = NULL;
38 	col->width = 50;
39 	col->minWidth = 5;
40 	col->maxWidth = 0;
41 
42 	col->id = NULL;
43 
44 	col->title = wstrdup(title);
45 
46 	col->delegate = NULL;
47 
48 	col->resizable = 1;
49 	col->editable = 0;
50 
51 	return col;
52 }
53 
WMSetTableColumnId(WMTableColumn * column,void * id)54 void WMSetTableColumnId(WMTableColumn * column, void *id)
55 {
56 	column->id = id;
57 }
58 
WMGetTableColumnId(WMTableColumn * column)59 void *WMGetTableColumnId(WMTableColumn * column)
60 {
61 	return column->id;
62 }
63 
WMSetTableColumnWidth(WMTableColumn * column,unsigned width)64 void WMSetTableColumnWidth(WMTableColumn * column, unsigned width)
65 {
66 	if (column->maxWidth == 0)
67 		column->width = WMAX(column->minWidth, width);
68 	else
69 		column->width = WMAX(column->minWidth, WMIN(column->maxWidth, width));
70 
71 	if (column->table) {
72 		rearrangeHeader(column->table);
73 	}
74 }
75 
WMSetTableColumnDelegate(WMTableColumn * column,WMTableColumnDelegate * delegate)76 void WMSetTableColumnDelegate(WMTableColumn * column, WMTableColumnDelegate * delegate)
77 {
78 	column->delegate = delegate;
79 }
80 
WMSetTableColumnConstraints(WMTableColumn * column,unsigned minWidth,unsigned maxWidth)81 void WMSetTableColumnConstraints(WMTableColumn * column, unsigned minWidth, unsigned maxWidth)
82 {
83 	wassertr(maxWidth == 0 || minWidth <= maxWidth);
84 
85 	column->minWidth = minWidth;
86 	column->maxWidth = maxWidth;
87 
88 	if (column->width < column->minWidth)
89 		WMSetTableColumnWidth(column, column->minWidth);
90 	else if (column->width > column->maxWidth && column->maxWidth != 0)
91 		WMSetTableColumnWidth(column, column->maxWidth);
92 }
93 
WMSetTableColumnEditable(WMTableColumn * column,Bool flag)94 void WMSetTableColumnEditable(WMTableColumn * column, Bool flag)
95 {
96 	column->editable = ((flag == 0) ? 0 : 1);
97 }
98 
WMGetTableColumnTableView(WMTableColumn * column)99 WMTableView *WMGetTableColumnTableView(WMTableColumn * column)
100 {
101 	return column->table;
102 }
103 
104 struct W_TableView {
105 	W_Class widgetClass;
106 	WMView *view;
107 
108 	WMFrame *header;
109 
110 	WMLabel *corner;
111 
112 	WMScroller *hscroll;
113 	WMScroller *vscroll;
114 	WMView *tableView;
115 
116 	WMPixmap *viewBuffer;
117 
118 	WMArray *columns;
119 	WMArray *splitters;
120 
121 	WMArray *selectedRows;
122 
123 	int tableWidth;
124 
125 	int rows;
126 
127 	WMColor *backColor;
128 
129 	GC gridGC;
130 	WMColor *gridColor;
131 
132 	Cursor splitterCursor;
133 
134 	void *dataSource;
135 
136 	WMTableViewDelegate *delegate;
137 
138 	WMAction *action;
139 	void *clientData;
140 
141 	void *clickedColumn;
142 	int clickedRow;
143 
144 	int editingRow;
145 
146 	unsigned headerHeight;
147 
148 	unsigned rowHeight;
149 
150 	unsigned dragging:1;
151 	unsigned drawsGrid:1;
152 	unsigned canSelectRow:1;
153 	unsigned canSelectMultiRows:1;
154 	unsigned canDeselectRow:1;
155 
156 	unsigned int hasVScroller:1;
157 	unsigned int hasHScroller:1;
158 };
159 
160 static W_Class tableClass = 0;
161 
162 static W_ViewDelegate viewDelegate = {
163 	NULL,
164 	NULL,
165 	handleResize,
166 	NULL,
167 	NULL
168 };
169 
170 static void reorganizeInterior(WMTableView * table);
171 
172 static void handleEvents(XEvent * event, void *data);
173 static void handleTableEvents(XEvent * event, void *data);
174 static void repaintTable(WMTableView * table);
175 
getTotalSize(WMTableView * table)176 static WMSize getTotalSize(WMTableView * table)
177 {
178 	WMSize size;
179 	int i;
180 
181 	/* get width from columns */
182 	size.width = 0;
183 	for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
184 		WMTableColumn *column;
185 
186 		column = WMGetFromArray(table->columns, i);
187 
188 		size.width += column->width;
189 	}
190 
191 	/* get height from rows */
192 	size.height = table->rows * table->rowHeight;
193 
194 	return size;
195 }
196 
getVisibleRect(WMTableView * table)197 static WMRect getVisibleRect(WMTableView * table)
198 {
199 	WMSize size = getTotalSize(table);
200 	WMRect rect;
201 
202 	if (table->vscroll) {
203 		rect.size.height = size.height * WMGetScrollerKnobProportion(table->vscroll);
204 		rect.pos.y = (size.height - rect.size.height) * WMGetScrollerValue(table->vscroll);
205 	} else {
206 		rect.size.height = size.height;
207 		rect.pos.y = 0;
208 	}
209 
210 	if (table->hscroll) {
211 		rect.size.width = size.width * WMGetScrollerKnobProportion(table->hscroll);
212 		rect.pos.x = (size.width - rect.size.width) * WMGetScrollerValue(table->hscroll);
213 	} else {
214 		rect.size.width = size.width;
215 		rect.pos.x = 0;
216 	}
217 
218 	return rect;
219 }
220 
scrollToPoint(WMTableView * table,int x,int y)221 static void scrollToPoint(WMTableView * table, int x, int y)
222 {
223 	WMSize size = getTotalSize(table);
224 	int i;
225 	float value, prop;
226 
227 	if (table->hscroll) {
228 		if (size.width > W_VIEW_WIDTH(table->tableView)) {
229 			prop = (float)W_VIEW_WIDTH(table->tableView) / (float)size.width;
230 			value = (float)x / (float)(size.width - W_VIEW_WIDTH(table->tableView));
231 		} else {
232 			prop = 1.0;
233 			value = 0.0;
234 		}
235 		WMSetScrollerParameters(table->hscroll, value, prop);
236 	}
237 
238 	if (table->vscroll) {
239 		if (size.height > W_VIEW_HEIGHT(table->tableView)) {
240 			prop = (float)W_VIEW_HEIGHT(table->tableView) / (float)size.height;
241 			value = (float)y / (float)(size.height - W_VIEW_HEIGHT(table->tableView));
242 		} else {
243 			prop = 1.0;
244 			value = 0.0;
245 		}
246 
247 		WMSetScrollerParameters(table->vscroll, value, prop);
248 	}
249 
250 	if (table->editingRow >= 0) {
251 		for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
252 			WMTableColumn *column;
253 
254 			column = WMGetFromArray(table->columns, i);
255 
256 			if (column->delegate && column->delegate->beginCellEdit)
257 				(*column->delegate->beginCellEdit) (column->delegate, column, table->editingRow);
258 		}
259 	}
260 
261 	repaintTable(table);
262 }
263 
adjustScrollers(WMTableView * table)264 static void adjustScrollers(WMTableView * table)
265 {
266 	WMSize size = getTotalSize(table);
267 	WMSize vsize = WMGetViewSize(table->tableView);
268 	float prop, value;
269 	float oprop, ovalue;
270 
271 	if (table->hscroll) {
272 		if (size.width <= vsize.width) {
273 			value = 0.0;
274 			prop = 1.0;
275 		} else {
276 			oprop = WMGetScrollerKnobProportion(table->hscroll);
277 			if (oprop == 0.0)
278 				oprop = 1.0;
279 			ovalue = WMGetScrollerValue(table->hscroll);
280 
281 			prop = (float)vsize.width / (float)size.width;
282 			value = prop * ovalue / oprop;
283 		}
284 		WMSetScrollerParameters(table->hscroll, value, prop);
285 	}
286 
287 	if (table->vscroll) {
288 		if (size.height <= vsize.height) {
289 			value = 0.0;
290 			prop = 1.0;
291 		} else {
292 			oprop = WMGetScrollerKnobProportion(table->vscroll);
293 			if (oprop == 0.0)
294 				oprop = 1.0;
295 			ovalue = WMGetScrollerValue(table->vscroll);
296 
297 			prop = (float)vsize.height / (float)size.height;
298 			value = prop * ovalue / oprop;
299 		}
300 		WMSetScrollerParameters(table->vscroll, value, prop);
301 	}
302 }
303 
doScroll(WMWidget * self,void * data)304 static void doScroll(WMWidget * self, void *data)
305 {
306 	WMTableView *table = (WMTableView *) data;
307 	float value;
308 	float vpsize;
309 	float size;
310 	WMSize ts = getTotalSize(table);
311 
312 	value = WMGetScrollerValue(self);
313 
314 	if (table->hscroll == (WMScroller *) self) {
315 		vpsize = W_VIEW_WIDTH(table->tableView);
316 		size = ts.width;
317 	} else {
318 		vpsize = W_VIEW_HEIGHT(table->tableView);
319 		size = ts.height;
320 	}
321 
322 	switch (WMGetScrollerHitPart(self)) {
323 	case WSDecrementWheel:
324 	case WSDecrementLine:
325 		value -= (float)table->rowHeight / size;
326 		if (value < 0)
327 			value = 0.0;
328 		WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
329 		repaintTable(table);
330 		break;
331 
332 	case WSIncrementWheel:
333 	case WSIncrementLine:
334 		value += (float)table->rowHeight / size;
335 		if (value > 1.0)
336 			value = 1.0;
337 		WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
338 		repaintTable(table);
339 		break;
340 
341 	case WSKnob:
342 		repaintTable(table);
343 		break;
344 
345 	case WSDecrementPage:
346 		value -= vpsize / size;
347 		if (value < 0.0)
348 			value = 0.0;
349 		WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
350 		repaintTable(table);
351 		break;
352 
353 	case WSIncrementPage:
354 		value += vpsize / size;
355 		if (value > 1.0)
356 			value = 1.0;
357 		WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
358 		repaintTable(table);
359 		break;
360 
361 	case WSNoPart:
362 	case WSKnobSlot:
363 		break;
364 	}
365 
366 	if (table->editingRow >= 0) {
367 		int i;
368 		for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
369 			WMTableColumn *column;
370 
371 			column = WMGetFromArray(table->columns, i);
372 
373 			if (column->delegate && column->delegate->beginCellEdit)
374 				(*column->delegate->beginCellEdit) (column->delegate, column, table->editingRow);
375 		}
376 	}
377 
378 	if (table->hscroll == self) {
379 		int x = 0;
380 		int i;
381 		WMRect rect = getVisibleRect(table);
382 
383 		for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
384 			WMTableColumn *column;
385 			WMView *splitter;
386 
387 			column = WMGetFromArray(table->columns, i);
388 
389 			WMMoveWidget(column->titleW, x - rect.pos.x, 0);
390 
391 			x += W_VIEW_WIDTH(WMWidgetView(column->titleW)) + 1;
392 
393 			splitter = WMGetFromArray(table->splitters, i);
394 			W_MoveView(splitter, x - rect.pos.x - 1, 0);
395 		}
396 	}
397 }
398 
splitterHandler(XEvent * event,void * data)399 static void splitterHandler(XEvent * event, void *data)
400 {
401 	WMTableColumn *column = (WMTableColumn *) data;
402 	WMTableView *table = column->table;
403 	int done = 0;
404 	int cx, ox, offsX;
405 	WMPoint pos;
406 	WMScreen *scr = WMWidgetScreen(table);
407 	GC gc = scr->ixorGC;
408 	Display *dpy = WMScreenDisplay(scr);
409 	int h = WMWidgetHeight(table) - 22;
410 	Window w = WMViewXID(table->view);
411 
412 	pos = WMGetViewPosition(WMWidgetView(column->titleW));
413 
414 	offsX = pos.x + column->width;
415 
416 	ox = cx = offsX;
417 
418 	XDrawLine(dpy, w, gc, cx + 20, 0, cx + 20, h);
419 
420 	while (!done) {
421 		XEvent ev;
422 
423 		WMMaskEvent(dpy, ButtonMotionMask | ButtonReleaseMask, &ev);
424 
425 		switch (ev.type) {
426 		case MotionNotify:
427 			ox = cx;
428 
429 			if (column->width + ev.xmotion.x < column->minWidth)
430 				cx = pos.x + column->minWidth;
431 			else if (column->maxWidth > 0 && column->width + ev.xmotion.x > column->maxWidth)
432 				cx = pos.x + column->maxWidth;
433 			else
434 				cx = offsX + ev.xmotion.x;
435 
436 			XDrawLine(dpy, w, gc, ox + 20, 0, ox + 20, h);
437 			XDrawLine(dpy, w, gc, cx + 20, 0, cx + 20, h);
438 			break;
439 
440 		case ButtonRelease:
441 			column->width = cx - pos.x;
442 			done = 1;
443 			break;
444 		}
445 	}
446 
447 	XDrawLine(dpy, w, gc, cx + 20, 0, cx + 20, h);
448 	rearrangeHeader(table);
449 	repaintTable(table);
450 }
451 
realizeTable(void * data,WMNotification * notif)452 static void realizeTable(void *data, WMNotification * notif)
453 {
454 	repaintTable(data);
455 }
456 
WMCreateTableView(WMWidget * parent)457 WMTableView *WMCreateTableView(WMWidget * parent)
458 {
459 	WMTableView *table = wmalloc(sizeof(WMTableView));
460 	WMScreen *scr = WMWidgetScreen(parent);
461 
462 	memset(table, 0, sizeof(WMTableView));
463 
464 	if (!tableClass) {
465 		tableClass = W_RegisterUserWidget();
466 	}
467 	table->widgetClass = tableClass;
468 
469 	table->view = W_CreateView(W_VIEW(parent));
470 	if (!table->view)
471 		goto error;
472 	table->view->self = table;
473 
474 	table->view->delegate = &viewDelegate;
475 
476 	table->headerHeight = 20;
477 
478 	table->hscroll = WMCreateScroller(table);
479 	WMSetScrollerAction(table->hscroll, doScroll, table);
480 	WMMoveWidget(table->hscroll, 1, 2 + table->headerHeight);
481 	WMMapWidget(table->hscroll);
482 
483 	table->hasHScroller = 1;
484 
485 	table->vscroll = WMCreateScroller(table);
486 	WMSetScrollerArrowsPosition(table->vscroll, WSAMaxEnd);
487 	WMSetScrollerAction(table->vscroll, doScroll, table);
488 	WMMoveWidget(table->vscroll, 1, 2 + table->headerHeight);
489 	WMMapWidget(table->vscroll);
490 
491 	table->hasVScroller = 1;
492 
493 	table->header = WMCreateFrame(table);
494 	WMMoveWidget(table->header, 22, 2);
495 	WMMapWidget(table->header);
496 	WMSetFrameRelief(table->header, WRFlat);
497 
498 	table->corner = WMCreateLabel(table);
499 	WMResizeWidget(table->corner, 20, table->headerHeight);
500 	WMMoveWidget(table->corner, 2, 2);
501 	WMMapWidget(table->corner);
502 	WMSetLabelRelief(table->corner, WRRaised);
503 	WMSetWidgetBackgroundColor(table->corner, scr->darkGray);
504 
505 	table->tableView = W_CreateView(table->view);
506 	if (!table->tableView)
507 		goto error;
508 	table->tableView->self = table;
509 	W_MapView(table->tableView);
510 
511 	WMAddNotificationObserver(realizeTable, table, WMViewRealizedNotification, table->tableView);
512 
513 	table->tableView->flags.dontCompressExpose = 1;
514 
515 	table->gridColor = WMCreateNamedColor(scr, "#cccccc", False);
516 	/*   table->gridColor = WMGrayColor(scr); */
517 
518 	{
519 		XGCValues gcv;
520 
521 		table->backColor = WMWhiteColor(scr);
522 
523 		gcv.foreground = WMColorPixel(table->gridColor);
524 		gcv.dashes = 1;
525 		gcv.line_style = LineOnOffDash;
526 		table->gridGC = XCreateGC(WMScreenDisplay(scr), W_DRAWABLE(scr), GCForeground, &gcv);
527 	}
528 
529 	table->editingRow = -1;
530 	table->clickedRow = -1;
531 
532 	table->drawsGrid = 1;
533 	table->rowHeight = 16;
534 
535 	table->tableWidth = 1;
536 
537 	table->columns = WMCreateArray(4);
538 	table->splitters = WMCreateArray(4);
539 
540 	table->selectedRows = WMCreateArray(16);
541 
542 	table->splitterCursor = XCreateFontCursor(WMScreenDisplay(scr), XC_sb_h_double_arrow);
543 
544 	table->canSelectRow = 1;
545 
546 	WMCreateEventHandler(table->view, ExposureMask | StructureNotifyMask, handleEvents, table);
547 
548 	WMCreateEventHandler(table->tableView, ExposureMask | ButtonPressMask |
549 			     ButtonReleaseMask | ButtonMotionMask, handleTableEvents, table);
550 
551 	WMResizeWidget(table, 50, 50);
552 
553 	return table;
554 
555  error:
556 	if (table->tableView)
557 		W_DestroyView(table->tableView);
558 	if (table->view)
559 		W_DestroyView(table->view);
560 	wfree(table);
561 	return NULL;
562 }
563 
WMAddTableViewColumn(WMTableView * table,WMTableColumn * column)564 void WMAddTableViewColumn(WMTableView * table, WMTableColumn * column)
565 {
566 	WMScreen *scr = WMWidgetScreen(table);
567 
568 	column->table = table;
569 
570 	WMAddToArray(table->columns, column);
571 
572 	if (!column->titleW) {
573 		column->titleW = WMCreateLabel(table);
574 		WMSetLabelRelief(column->titleW, WRRaised);
575 		WMSetLabelFont(column->titleW, scr->boldFont);
576 		WMSetLabelTextColor(column->titleW, scr->white);
577 		WMSetWidgetBackgroundColor(column->titleW, scr->darkGray);
578 		WMSetLabelText(column->titleW, column->title);
579 		W_ReparentView(WMWidgetView(column->titleW), WMWidgetView(table->header), 0, 0);
580 		if (W_VIEW_REALIZED(table->view))
581 			WMRealizeWidget(column->titleW);
582 		WMMapWidget(column->titleW);
583 	}
584 
585 	{
586 		WMView *splitter = W_CreateView(WMWidgetView(table->header));
587 
588 		W_SetViewBackgroundColor(splitter, WMWhiteColor(scr));
589 
590 		if (W_VIEW_REALIZED(table->view))
591 			W_RealizeView(splitter);
592 
593 		W_ResizeView(splitter, 2, table->headerHeight);
594 		W_MapView(splitter);
595 
596 		W_SetViewCursor(splitter, table->splitterCursor);
597 		WMCreateEventHandler(splitter, ButtonPressMask | ButtonReleaseMask, splitterHandler, column);
598 
599 		WMAddToArray(table->splitters, splitter);
600 	}
601 
602 	rearrangeHeader(table);
603 }
604 
WMSetTableViewHeaderHeight(WMTableView * table,unsigned height)605 void WMSetTableViewHeaderHeight(WMTableView * table, unsigned height)
606 {
607 	table->headerHeight = height;
608 
609 	handleResize(NULL, table->view);
610 }
611 
WMSetTableViewDelegate(WMTableView * table,WMTableViewDelegate * delegate)612 void WMSetTableViewDelegate(WMTableView * table, WMTableViewDelegate * delegate)
613 {
614 	table->delegate = delegate;
615 }
616 
WMSetTableViewAction(WMTableView * table,WMAction * action,void * clientData)617 void WMSetTableViewAction(WMTableView * table, WMAction * action, void *clientData)
618 {
619 	table->action = action;
620 
621 	table->clientData = clientData;
622 }
623 
WMGetTableViewClickedColumn(WMTableView * table)624 void *WMGetTableViewClickedColumn(WMTableView * table)
625 {
626 	return table->clickedColumn;
627 }
628 
WMGetTableViewClickedRow(WMTableView * table)629 int WMGetTableViewClickedRow(WMTableView * table)
630 {
631 	return table->clickedRow;
632 }
633 
WMGetTableViewSelectedRows(WMTableView * table)634 WMArray *WMGetTableViewSelectedRows(WMTableView * table)
635 {
636 	return table->selectedRows;
637 }
638 
WMGetTableViewDocumentView(WMTableView * table)639 WMView *WMGetTableViewDocumentView(WMTableView * table)
640 {
641 	return table->tableView;
642 }
643 
WMTableViewDataForCell(WMTableView * table,WMTableColumn * column,int row)644 void *WMTableViewDataForCell(WMTableView * table, WMTableColumn * column, int row)
645 {
646 	return (*table->delegate->valueForCell) (table->delegate, column, row);
647 }
648 
WMSetTableViewDataForCell(WMTableView * table,WMTableColumn * column,int row,void * data)649 void WMSetTableViewDataForCell(WMTableView * table, WMTableColumn * column, int row, void *data)
650 {
651 	(*table->delegate->setValueForCell) (table->delegate, column, row, data);
652 }
653 
WMTableViewRectForCell(WMTableView * table,WMTableColumn * column,int row)654 WMRect WMTableViewRectForCell(WMTableView * table, WMTableColumn * column, int row)
655 {
656 	WMRect rect;
657 	int i;
658 
659 	rect.pos.x = 0;
660 	rect.pos.y = row * table->rowHeight;
661 	rect.size.height = table->rowHeight;
662 
663 	for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
664 		WMTableColumn *col;
665 		col = WMGetFromArray(table->columns, i);
666 
667 		if (col == column) {
668 			rect.size.width = col->width;
669 			break;
670 		}
671 
672 		rect.pos.x += col->width;
673 	}
674 
675 	{
676 		WMRect r = getVisibleRect(table);
677 
678 		rect.pos.y -= r.pos.y;
679 		rect.pos.x -= r.pos.x;
680 	}
681 
682 	return rect;
683 }
684 
WMSetTableViewDataSource(WMTableView * table,void * source)685 void WMSetTableViewDataSource(WMTableView * table, void *source)
686 {
687 	table->dataSource = source;
688 }
689 
WMGetTableViewDataSource(WMTableView * table)690 void *WMGetTableViewDataSource(WMTableView * table)
691 {
692 	return table->dataSource;
693 }
694 
WMSetTableViewHasHorizontalScroller(WMTableView * tPtr,Bool flag)695 void WMSetTableViewHasHorizontalScroller(WMTableView * tPtr, Bool flag)
696 {
697 	if (flag) {
698 		if (tPtr->hasHScroller)
699 			return;
700 		tPtr->hasHScroller = 1;
701 
702 		tPtr->hscroll = WMCreateScroller(tPtr);
703 		WMSetScrollerAction(tPtr->hscroll, doScroll, tPtr);
704 		WMSetScrollerArrowsPosition(tPtr->hscroll, WSAMaxEnd);
705 		/* make it a horiz. scroller */
706 		WMResizeWidget(tPtr->hscroll, 1, 2);
707 
708 		if (W_VIEW_REALIZED(tPtr->view)) {
709 			WMRealizeWidget(tPtr->hscroll);
710 		}
711 
712 		reorganizeInterior(tPtr);
713 
714 		WMMapWidget(tPtr->hscroll);
715 	} else {
716 		if (!tPtr->hasHScroller)
717 			return;
718 		tPtr->hasHScroller = 0;
719 
720 		WMUnmapWidget(tPtr->hscroll);
721 		WMDestroyWidget(tPtr->hscroll);
722 		tPtr->hscroll = NULL;
723 
724 		reorganizeInterior(tPtr);
725 	}
726 }
727 
728 #if 0
729 /* not supported by now */
730 void WMSetTableViewHasVerticalScroller(WMTableView * tPtr, Bool flag)
731 {
732 	if (flag) {
733 		if (tPtr->hasVScroller)
734 			return;
735 		tPtr->hasVScroller = 1;
736 
737 		tPtr->vscroll = WMCreateScroller(tPtr);
738 		WMSetScrollerAction(tPtr->vscroll, doScroll, tPtr);
739 		WMSetScrollerArrowsPosition(tPtr->vscroll, WSAMaxEnd);
740 		/* make it a vert. scroller */
741 		WMResizeWidget(tPtr->vscroll, 1, 2);
742 
743 		if (W_VIEW_REALIZED(tPtr->view)) {
744 			WMRealizeWidget(tPtr->vscroll);
745 		}
746 
747 		reorganizeInterior(tPtr);
748 
749 		WMMapWidget(tPtr->vscroll);
750 	} else {
751 		if (!tPtr->hasVScroller)
752 			return;
753 		tPtr->hasVScroller = 0;
754 
755 		WMUnmapWidget(tPtr->vscroll);
756 		WMDestroyWidget(tPtr->vscroll);
757 		tPtr->vscroll = NULL;
758 
759 		reorganizeInterior(tPtr);
760 	}
761 }
762 #endif
763 
WMSetTableViewBackgroundColor(WMTableView * table,WMColor * color)764 void WMSetTableViewBackgroundColor(WMTableView * table, WMColor * color)
765 {
766 	W_SetViewBackgroundColor(table->tableView, color);
767 
768 	if (table->backColor)
769 		WMReleaseColor(table->backColor);
770 
771 	table->backColor = WMRetainColor(color);
772 
773 	repaintTable(table);
774 }
775 
WMSetTableViewGridColor(WMTableView * table,WMColor * color)776 void WMSetTableViewGridColor(WMTableView * table, WMColor * color)
777 {
778 	WMReleaseColor(table->gridColor);
779 	table->gridColor = WMRetainColor(color);
780 	XSetForeground(WMScreenDisplay(WMWidgetScreen(table)), table->gridGC, WMColorPixel(color));
781 	repaintTable(table);
782 }
783 
WMSetTableViewRowHeight(WMTableView * table,int height)784 void WMSetTableViewRowHeight(WMTableView * table, int height)
785 {
786 	table->rowHeight = height;
787 
788 	repaintTable(table);
789 }
790 
WMScrollTableViewRowToVisible(WMTableView * table,int row)791 void WMScrollTableViewRowToVisible(WMTableView * table, int row)
792 {
793 	WMScroller *scroller;
794 	WMRange range;
795 	WMRect rect;
796 	int newY, tmp;
797 
798 	rect = getVisibleRect(table);
799 	range = rowsInRect(table, rect);
800 
801 	scroller = table->vscroll;
802 
803 	if (row < range.position) {
804 		newY = row * table->rowHeight - rect.size.height / 2;
805 	} else if (row >= range.position + range.count) {
806 		newY = row * table->rowHeight - rect.size.height / 2;
807 	} else {
808 		return;
809 	}
810 	tmp = table->rows * table->rowHeight - rect.size.height;
811 	newY = WMAX(0, WMIN(newY, tmp));
812 
813 	scrollToPoint(table, rect.pos.x, newY);
814 }
815 
drawGrid(WMTableView * table,WMRect rect)816 static void drawGrid(WMTableView * table, WMRect rect)
817 {
818 	WMScreen *scr = WMWidgetScreen(table);
819 	Display *dpy = WMScreenDisplay(scr);
820 	int i;
821 	int y1, y2;
822 	int x1, x2;
823 	int xx;
824 	Drawable d = WMGetPixmapXID(table->viewBuffer);
825 	GC gc = table->gridGC;
826 
827 #if 0
828 	char dashl[1] = { 1 };
829 
830 	XSetDashes(dpy, gc, 0, dashl, 1);
831 
832 	y1 = (rect.pos.y / table->rowHeight - 1) * table->rowHeight;
833 	y2 = y1 + (rect.size.height / table->rowHeight + 2) * table->rowHeight;
834 #endif
835 	y1 = 0;
836 	y2 = W_VIEW_HEIGHT(table->tableView);
837 
838 	xx = -rect.pos.x;
839 	for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
840 		WMTableColumn *column;
841 
842 		XDrawLine(dpy, d, gc, xx, y1, xx, y2);
843 
844 		column = WMGetFromArray(table->columns, i);
845 		xx += column->width;
846 	}
847 	XDrawLine(dpy, d, gc, xx, y1, xx, y2);
848 
849 	x1 = 0;
850 	x2 = rect.size.width;
851 
852 	if (x2 <= x1)
853 		return;
854 #if 0
855 	XSetDashes(dpy, gc, (rect.pos.x & 1), dashl, 1);
856 #endif
857 
858 	y1 = -rect.pos.y % table->rowHeight;
859 	y2 = y1 + rect.size.height + table->rowHeight;
860 
861 	for (i = y1; i <= y2; i += table->rowHeight) {
862 		XDrawLine(dpy, d, gc, x1, i, x2, i);
863 	}
864 }
865 
columnsInRect(WMTableView * table,WMRect rect)866 static WMRange columnsInRect(WMTableView * table, WMRect rect)
867 {
868 	WMTableColumn *column;
869 	int pos;
870 	int i, found;
871 	int totalColumns = WMGetArrayItemCount(table->columns);
872 	WMRange range;
873 
874 	pos = 0;
875 	found = 0;
876 	for (i = 0; i < totalColumns; i++) {
877 		column = WMGetFromArray(table->columns, i);
878 		if (!found) {
879 			if (rect.pos.x >= pos && rect.pos.x < pos + column->width) {
880 				range.position = i;
881 				range.count = 1;
882 				found = 1;
883 			}
884 		} else {
885 			if (pos > rect.pos.x + rect.size.width) {
886 				break;
887 			}
888 			range.count++;
889 		}
890 		pos += column->width;
891 	}
892 	range.count = WMAX(1, WMIN(range.count, totalColumns - range.position));
893 	return range;
894 }
895 
rowsInRect(WMTableView * table,WMRect rect)896 static WMRange rowsInRect(WMTableView * table, WMRect rect)
897 {
898 	WMRange range;
899 	int rh = table->rowHeight;
900 	int dif;
901 
902 	dif = rect.pos.y % rh;
903 
904 	range.position = WMAX(0, (rect.pos.y - dif) / rh);
905 	range.count = WMAX(1, WMIN((rect.size.height + dif) / rh, table->rows));
906 
907 	return range;
908 }
909 
drawRow(WMTableView * table,int row,WMRect clipRect)910 static void drawRow(WMTableView * table, int row, WMRect clipRect)
911 {
912 	int i;
913 	WMRange cols = columnsInRect(table, clipRect);
914 	WMTableColumn *column;
915 	Drawable d = WMGetPixmapXID(table->viewBuffer);
916 
917 	for (i = cols.position; i < cols.position + cols.count; i++) {
918 		column = WMGetFromArray(table->columns, i);
919 
920 		if (!column->delegate || !column->delegate->drawCell)
921 			continue;
922 
923 		if (WMFindInArray(table->selectedRows, NULL, (void *)(uintptr_t) row) != WANotFound)
924 			(*column->delegate->drawSelectedCell) (column->delegate, column, row, d);
925 		else
926 			(*column->delegate->drawCell) (column->delegate, column, row, d);
927 	}
928 }
929 
930 #if 0
931 static void drawFullRow(WMTableView * table, int row)
932 {
933 	int i;
934 	WMTableColumn *column;
935 	Drawable d = WMGetPixmapXID(table->viewBuffer);
936 
937 	for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
938 		column = WMGetFromArray(table->columns, i);
939 
940 		if (!column->delegate || !column->delegate->drawCell)
941 			continue;
942 
943 		if (WMFindInArray(table->selectedRows, NULL, (void *)row) != WANotFound)
944 			(*column->delegate->drawSelectedCell) (column->delegate, column, row, d);
945 		else
946 			(*column->delegate->drawCell) (column->delegate, column, row, d);
947 	}
948 }
949 #endif
950 
setRowSelected(WMTableView * table,unsigned row,Bool flag)951 static void setRowSelected(WMTableView * table, unsigned row, Bool flag)
952 {
953 	int repaint = 0;
954 
955 	if (WMFindInArray(table->selectedRows, NULL, (void *)(uintptr_t) row) != WANotFound) {
956 		if (!flag) {
957 			WMRemoveFromArray(table->selectedRows, (void *)(uintptr_t) row);
958 			repaint = 1;
959 		}
960 	} else {
961 		if (flag) {
962 			WMAddToArray(table->selectedRows, (void *)(uintptr_t) row);
963 			repaint = 1;
964 		}
965 	}
966 	if (repaint && row < table->rows) {
967 		/*drawFullRow(table, row); */
968 		repaintTable(table);
969 	}
970 }
971 
repaintTable(WMTableView * table)972 static void repaintTable(WMTableView * table)
973 {
974 	WMRect rect;
975 	WMRange rows;
976 	WMScreen *scr = WMWidgetScreen(table);
977 	int i;
978 
979 	if (!table->delegate || !W_VIEW_REALIZED(table->view))
980 		return;
981 
982 	wassertr(table->delegate->numberOfRows);
983 
984 	if (!table->viewBuffer) {
985 		table->viewBuffer = WMCreatePixmap(scr,
986 						   W_VIEW_WIDTH(table->tableView),
987 						   W_VIEW_HEIGHT(table->tableView), WMScreenDepth(scr), 0);
988 	}
989 
990 	XFillRectangle(scr->display,
991 		       WMGetPixmapXID(table->viewBuffer),
992 		       WMColorGC(table->backColor), 0, 0,
993 		       W_VIEW_WIDTH(table->tableView), W_VIEW_HEIGHT(table->tableView));
994 
995 	rect = getVisibleRect(table);
996 
997 	if (table->drawsGrid) {
998 		drawGrid(table, rect);
999 	}
1000 
1001 	rows = rowsInRect(table, rect);
1002 	for (i = rows.position; i < WMIN(rows.position + rows.count + 1, table->rows); i++) {
1003 		drawRow(table, i, rect);
1004 	}
1005 
1006 	XSetWindowBackgroundPixmap(scr->display, table->tableView->window, WMGetPixmapXID(table->viewBuffer));
1007 	XClearWindow(scr->display, table->tableView->window);
1008 }
1009 
stopRowEdit(WMTableView * table,int row)1010 static void stopRowEdit(WMTableView * table, int row)
1011 {
1012 	int i;
1013 	WMTableColumn *column;
1014 
1015 	table->editingRow = -1;
1016 	for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1017 		column = WMGetFromArray(table->columns, i);
1018 
1019 		if (column->delegate && column->delegate->endCellEdit)
1020 			(*column->delegate->endCellEdit) (column->delegate, column, row);
1021 	}
1022 }
1023 
WMEditTableViewRow(WMTableView * table,int row)1024 void WMEditTableViewRow(WMTableView * table, int row)
1025 {
1026 	int i;
1027 	WMTableColumn *column;
1028 
1029 	if (table->editingRow >= 0) {
1030 		stopRowEdit(table, table->editingRow);
1031 	}
1032 
1033 	table->editingRow = row;
1034 
1035 	if (row < 0)
1036 		return;
1037 
1038 	for (i = 0; i < WMGetArrayItemCount(table->columns); i++) {
1039 		column = WMGetFromArray(table->columns, i);
1040 
1041 		if (column->delegate && column->delegate->beginCellEdit)
1042 			(*column->delegate->beginCellEdit) (column->delegate, column, row);
1043 	}
1044 }
1045 
WMSelectTableViewRow(WMTableView * table,int row)1046 void WMSelectTableViewRow(WMTableView * table, int row)
1047 {
1048 	if (table->clickedRow >= 0)
1049 		setRowSelected(table, table->clickedRow, False);
1050 
1051 	if (row >= table->rows) {
1052 		return;
1053 	}
1054 
1055 	setRowSelected(table, row, True);
1056 	table->clickedRow = row;
1057 
1058 	if (table->action)
1059 		(*table->action) (table, table->clientData);
1060 	WMPostNotificationName(WMTableViewSelectionDidChangeNotification, table, NULL);
1061 }
1062 
WMReloadTableView(WMTableView * table)1063 void WMReloadTableView(WMTableView * table)
1064 {
1065 	if (table->editingRow >= 0)
1066 		stopRowEdit(table, table->editingRow);
1067 
1068 	/* when this is called, nothing in the table can be assumed to be
1069 	 * like the last time we accessed it (ie, rows might have disappeared) */
1070 
1071 	WMEmptyArray(table->selectedRows);
1072 
1073 	if (table->clickedRow >= 0) {
1074 		if (table->action)
1075 			(*table->action) (table, table->clientData);
1076 		WMPostNotificationName(WMTableViewSelectionDidChangeNotification, table, NULL);
1077 		table->clickedRow = -1;
1078 	}
1079 
1080 	if (table->delegate && table->delegate->numberOfRows) {
1081 		int rows;
1082 
1083 		rows = (*table->delegate->numberOfRows) (table->delegate, table);
1084 
1085 		if (rows != table->rows) {
1086 			table->rows = rows;
1087 			handleResize(table->view->delegate, table->view);
1088 		} else {
1089 			repaintTable(table);
1090 		}
1091 	}
1092 }
1093 
WMNoteTableViewNumberOfRowsChanged(WMTableView * table)1094 void WMNoteTableViewNumberOfRowsChanged(WMTableView * table)
1095 {
1096 	WMReloadTableView(table);
1097 }
1098 
handleTableEvents(XEvent * event,void * data)1099 static void handleTableEvents(XEvent * event, void *data)
1100 {
1101 	WMTableView *table = (WMTableView *) data;
1102 	int row;
1103 
1104 	switch (event->type) {
1105 	case ButtonPress:
1106 		if (event->xbutton.button == Button1) {
1107 			WMRect rect = getVisibleRect(table);
1108 
1109 			row = (event->xbutton.y + rect.pos.y) / table->rowHeight;
1110 			if (row != table->clickedRow) {
1111 				setRowSelected(table, table->clickedRow, False);
1112 				setRowSelected(table, row, True);
1113 				table->clickedRow = row;
1114 				table->dragging = 1;
1115 			} else {
1116 				table->dragging = 1;
1117 			}
1118 		}
1119 		break;
1120 
1121 	case MotionNotify:
1122 		if (table->dragging && event->xmotion.y >= 0) {
1123 			WMRect rect = getVisibleRect(table);
1124 
1125 			row = (event->xmotion.y + rect.pos.y) / table->rowHeight;
1126 			if (table->clickedRow != row && row >= 0 && row < table->rows) {
1127 				setRowSelected(table, table->clickedRow, False);
1128 				setRowSelected(table, row, True);
1129 				table->clickedRow = row;
1130 			}
1131 		}
1132 		break;
1133 
1134 	case ButtonRelease:
1135 		if (event->xbutton.button == Button1) {
1136 			if (table->action)
1137 				(*table->action) (table, table->clientData);
1138 			WMPostNotificationName(WMTableViewSelectionDidChangeNotification, table, NULL);
1139 			table->dragging = 0;
1140 		}
1141 		break;
1142 	}
1143 }
1144 
handleEvents(XEvent * event,void * data)1145 static void handleEvents(XEvent * event, void *data)
1146 {
1147 	WMTableView *table = (WMTableView *) data;
1148 	WMScreen *scr = WMWidgetScreen(table);
1149 
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), WRSunken);
1154 		break;
1155 	}
1156 }
1157 
handleResize(W_ViewDelegate * self,WMView * view)1158 static void handleResize(W_ViewDelegate * self, WMView * view)
1159 {
1160 	reorganizeInterior(view->self);
1161 }
1162 
reorganizeInterior(WMTableView * table)1163 static void reorganizeInterior(WMTableView * table)
1164 {
1165 	int width;
1166 	int height;
1167 	WMSize size = getTotalSize(table);
1168 	WMView *view = table->view;
1169 	int vw, vh;
1170 	int hsThickness, vsThickness;
1171 
1172 	if (table->vscroll)
1173 		vsThickness = WMWidgetWidth(table->vscroll);
1174 	if (table->hscroll)
1175 		hsThickness = WMWidgetHeight(table->hscroll);
1176 
1177 	width = W_VIEW_WIDTH(view) - 2;
1178 	height = W_VIEW_HEIGHT(view) - 3;
1179 
1180 	height -= table->headerHeight;	/* table header */
1181 
1182 	if (table->corner)
1183 		WMResizeWidget(table->corner, 20, table->headerHeight);
1184 
1185 	WMMoveWidget(table->vscroll, 1, table->headerHeight + 1);
1186 	WMResizeWidget(table->vscroll, 20, height + 1);
1187 
1188 	if (table->hscroll) {
1189 		WMMoveWidget(table->hscroll, vsThickness, W_VIEW_HEIGHT(view) - hsThickness - 1);
1190 		WMResizeWidget(table->hscroll, width - (vsThickness + 1), hsThickness);
1191 	}
1192 
1193 	if (table->header)
1194 		WMResizeWidget(table->header, width - (vsThickness + 1), table->headerHeight);
1195 
1196 	if (table->viewBuffer) {
1197 		WMReleasePixmap(table->viewBuffer);
1198 		table->viewBuffer = NULL;
1199 	}
1200 
1201 	width -= vsThickness;
1202 	height -= hsThickness;
1203 
1204 	vw = WMIN(size.width, width);
1205 	vh = WMIN(size.height, height);
1206 
1207 	W_MoveView(table->tableView, vsThickness + 1, 1 + table->headerHeight + 1);
1208 	W_ResizeView(table->tableView, WMAX(vw, 1), WMAX(vh, 1) + 1);
1209 
1210 	adjustScrollers(table);
1211 
1212 	repaintTable(table);
1213 }
1214 
rearrangeHeader(WMTableView * table)1215 static void rearrangeHeader(WMTableView * table)
1216 {
1217 	int width;
1218 	int count;
1219 	int i;
1220 	/*WMRect rect = WMGetScrollViewVisibleRect(table->scrollView); */
1221 
1222 	width = 0;
1223 
1224 	count = WMGetArrayItemCount(table->columns);
1225 	for (i = 0; i < count; i++) {
1226 		WMTableColumn *column = WMGetFromArray(table->columns, i);
1227 		WMView *splitter = WMGetFromArray(table->splitters, i);
1228 
1229 		WMMoveWidget(column->titleW, width, 0);
1230 		WMResizeWidget(column->titleW, column->width - 1, table->headerHeight);
1231 
1232 		width += column->width;
1233 		W_MoveView(splitter, width - 1, 0);
1234 	}
1235 
1236 	wassertr(table->delegate && table->delegate->numberOfRows);
1237 
1238 	table->rows = table->delegate->numberOfRows(table->delegate, table);
1239 
1240 	table->tableWidth = width + 1;
1241 
1242 	handleResize(table->view->delegate, table->view);
1243 }
1244