various table widget updates
[wmaker-crm.git] / WINGs / wsplitview.c
blobca3fe11bba8c718dbcfde36fcf392e3ecd588016
5 #include "WINGsP.h"
7 /*
8 char *WMSplitViewDidResizeSubviewsNotification
9 = "WMSplitViewDidResizeSubviewsNotification";
10 char *WMSplitViewWillResizeSubviewsNotification
11 = "WMSplitViewWillResizeSubviewsNotification";
14 typedef struct _T_SplitViewSubview {
15 WMView *view;
16 int minSize;
17 int maxSize;
18 int size;
19 int pos;
20 } T_SplitViewSubview;
23 typedef struct W_SplitView {
24 W_Class widgetClass;
25 W_View *view;
27 WMBag *subviewsBag;
29 WMSplitViewConstrainProc *constrainProc;
31 struct {
32 unsigned int vertical:1;
33 unsigned int adjustOnPaint:1;
34 unsigned int subviewsWereManuallyMoved:1;
35 } flags;
37 /* WMSplitViewResizeSubviewsProc *resizeSubviewsProc; */
39 } SplitView;
42 #define DIVIDER_THICKNESS 8
43 #define MIN_SUBVIEW_SIZE 4
44 #define MAX_SUBVIEW_SIZE -1
47 #define _GetSubviewsCount() WMGetBagItemCount(sPtr->subviewsBag)
49 #define _AddPSubviewStruct(P) \
50 (WMPutInBag(sPtr->subviewsBag,((void*)P)))
52 #define _GetPSubviewStructAt(i) \
53 ((T_SplitViewSubview*)WMGetFromBag(sPtr->subviewsBag,(i)))
55 #define _GetSubviewAt(i) \
56 (((T_SplitViewSubview*)WMGetFromBag(sPtr->subviewsBag,(i)))->view)
58 #define _GetMinSizeAt(i) \
59 (((T_SplitViewSubview*)WMGetFromBag(sPtr->subviewsBag,(i)))->minSize)
61 #define _GetMaxSizeAt(i) \
62 (((T_SplitViewSubview*)WMGetFromBag(sPtr->subviewsBag,(i)))->maxSize)
64 #define _GetSizeAt(i) \
65 (((T_SplitViewSubview*)WMGetFromBag(sPtr->subviewsBag,(i)))->size)
67 #define _GetPosAt(i) \
68 (((T_SplitViewSubview*)WMGetFromBag(sPtr->subviewsBag,(i)))->pos)
70 #define _GetSplitViewSize() \
71 ((sPtr->flags.vertical) ? sPtr->view->size.width : sPtr->view->size.height)
73 static void destroySplitView(SplitView *sPtr);
74 static void paintSplitView(SplitView *sPtr);
76 static void handleEvents(XEvent *event, void *data);
77 static void handleActionEvents(XEvent *event, void *data);
80 static void
81 getConstraints(SplitView *sPtr, int index, int *minSize, int *maxSize)
83 *minSize = MIN_SUBVIEW_SIZE;
84 *maxSize = MAX_SUBVIEW_SIZE;
86 if (sPtr->constrainProc)
87 (*sPtr->constrainProc)(sPtr, index, minSize, maxSize);
89 if (*minSize < MIN_SUBVIEW_SIZE)
90 *minSize = MIN_SUBVIEW_SIZE;
92 if (*maxSize < MIN_SUBVIEW_SIZE)
93 *maxSize = MAX_SUBVIEW_SIZE;
94 else if (*maxSize < *minSize)
95 *maxSize = *minSize;
99 static void
100 updateConstraints(SplitView *sPtr)
102 T_SplitViewSubview *p;
103 int i, count;
105 count = _GetSubviewsCount();
106 for (i = 0; i < count; i++) {
107 p = _GetPSubviewStructAt(i);
108 getConstraints(sPtr, i, &(p->minSize), &(p->maxSize));
113 static void
114 resizeView(SplitView *sPtr, WMView *view, int size)
116 int width, height;
118 if (sPtr->flags.vertical) {
119 width = size;
120 height = sPtr->view->size.height;
121 } else {
122 width = sPtr->view->size.width;
123 height = size;
126 if (view->self)
127 WMResizeWidget(view->self, width, height);
128 else
129 W_ResizeView(view, width, height);
133 static void
134 reparentView(SplitView *sPtr, WMView *view, int pos)
136 int x, y;
138 if (sPtr->flags.vertical) {
139 x = pos;
140 y = 0;
141 } else {
142 x = 0;
143 y = pos;
146 W_ReparentView(view, sPtr->view, x, y);
150 static void
151 moveView(SplitView *sPtr, WMView *view, int pos)
153 int x, y;
155 if (sPtr->flags.vertical) {
156 x = pos;
157 y = 0;
158 } else {
159 x = 0;
160 y = pos;
163 if (view->self)
164 WMMoveWidget(view->self, x, y);
165 else
166 W_MoveView(view, x, y);
170 static int
171 checkSizes(SplitView *sPtr)
173 int i, count, offset;
174 T_SplitViewSubview *p;
176 count = _GetSubviewsCount();
177 offset = 0;
178 for (i = 0; i < count; i++) {
179 p = _GetPSubviewStructAt(i);
180 if (p->size < p->minSize) {
181 offset += p->minSize - p->size;
182 p->size = p->minSize;
183 } else if (p->maxSize != MAX_SUBVIEW_SIZE && p->size > p->maxSize) {
184 offset += p->maxSize - p->size;
185 p->size = p->maxSize;
189 return (offset);
193 static void
194 checkPositions(SplitView *sPtr)
196 int i, count, pos;
197 T_SplitViewSubview *p;
199 count = _GetSubviewsCount();
200 pos = 0;
201 for (i = 0; i < count; i++) {
202 p = _GetPSubviewStructAt(i);
203 p->pos = pos;
204 pos += p->size + DIVIDER_THICKNESS;
209 static void
210 updateSubviewsGeom(SplitView *sPtr)
212 int i, count;
213 T_SplitViewSubview *p;
215 count = _GetSubviewsCount();
216 for (i = 0; i < count; i++) {
217 p = _GetPSubviewStructAt(i);
218 resizeView(sPtr, p->view, p->size);
219 moveView(sPtr, p->view, p->pos);
224 static int
225 getTotalSize(SplitView *sPtr)
227 int i, count, totSize;
229 count = _GetSubviewsCount();
230 if (!count)
231 return (0);
233 totSize = 0;
234 for (i = 0; i < count; i++)
235 totSize += _GetSizeAt(i) + DIVIDER_THICKNESS;
237 return (totSize - DIVIDER_THICKNESS);
241 static Bool
242 distributeOffsetEqually(SplitView *sPtr, int offset)
244 T_SplitViewSubview *p;
245 int i, count, sizeChanged, forced;
247 if ((count = _GetSubviewsCount()) < 1)
248 return (True);
250 forced = False;
251 while (offset != 0) {
252 sizeChanged = 0;
253 for (i = 0; i < count && offset != 0; i++) {
254 p = _GetPSubviewStructAt(i);
255 if (offset < 0) {
256 if (p->size > p->minSize) {
257 offset++;
258 p->size--;
259 sizeChanged = 1;
261 } else if (p->maxSize == MAX_SUBVIEW_SIZE || p->size < p->maxSize) {
262 offset--;
263 p->size++;
264 sizeChanged = 1;
267 if (offset != 0 && !sizeChanged) {
268 p = _GetPSubviewStructAt(count-1);
269 if (offset > 0) {
270 p->size += offset;
271 p->maxSize = MAX_SUBVIEW_SIZE;
273 offset = 0;
274 forced = True;
278 return (forced);
282 static Bool
283 distributeOffsetFormEnd(SplitView *sPtr, int offset)
285 T_SplitViewSubview *p;
286 int i, count, sizeTmp;
288 if ((count = _GetSubviewsCount()) < 1)
289 return (True);
291 for (i = count-1; i >= 0 && offset != 0; i--) {
292 p = _GetPSubviewStructAt(i);
293 sizeTmp = p->size;
294 if (offset > 0) {
295 if (p->maxSize == MAX_SUBVIEW_SIZE || p->size + offset < p->maxSize)
296 p->size += offset;
297 else
298 p->size = p->maxSize;
299 } else {
300 if (p->size + offset >= p->minSize)
301 p->size += offset;
302 else
303 p->size = p->minSize;
305 offset -= p->size - sizeTmp;
308 return (offset == 0);
312 static void
313 adjustSplitViewSubviews(WMSplitView *sPtr)
315 T_SplitViewSubview *p;
316 int i, count, adjSize, adjPad;
318 CHECK_CLASS(sPtr, WC_SplitView);
320 #if 0
321 printf("---- (adjustSplitViewSubviews - 1) ----\n");
322 dumpSubviews(sPtr);
323 #endif
325 if ((count = _GetSubviewsCount()) < 1)
326 return;
328 adjSize = (_GetSplitViewSize() - ((count-1) * DIVIDER_THICKNESS)) / count;
329 adjPad = (_GetSplitViewSize() - ((count-1) * DIVIDER_THICKNESS)) % count;
330 for (i = 0; i < count; i++) {
331 p = _GetPSubviewStructAt(i);
332 p->size = adjSize;
335 distributeOffsetEqually(sPtr, adjPad - checkSizes(sPtr));
337 checkPositions(sPtr);
338 updateSubviewsGeom(sPtr);
340 sPtr->flags.subviewsWereManuallyMoved = 0;
342 #if 0
343 printf("---- (adjustSplitViewSubviews - 2) ----\n");
344 dumpSubviews(sPtr);
345 #endif
348 #if 0
349 static void
350 handleSubviewResized(void *self, WMNotification *notif)
352 SplitView *sPtr = (SplitView*)self;
354 CHECK_CLASS(sPtr, WC_SplitView);
356 if (WMGetNotificationName(notif) == WMViewSizeDidChangeNotification) {
357 T_SplitViewSubview *p;
358 int i, count, done;
359 WMView *view = WMGetNotificationObject(notif);
361 count = _GetSubviewsCount();
362 done = 0;
363 for (i = 0; i < count; i++) {
364 p = _GetPSubviewStructAt(i);
365 if (p->view == view) {
366 done = 1;
367 break;
371 if (done) {
372 /* TODO !!! */
373 resizeView(sPtr, p->view, p->size);
374 moveView(sPtr, p->view, p->pos);
378 #endif
380 static void
381 handleViewResized(void *self, WMNotification *notification)
383 SplitView *sPtr = (SplitView*)self;
385 #if 0
386 printf("---- (handleViewResized - 1) ----\n");
387 dumpSubviews(sPtr);
388 #endif
390 updateConstraints(sPtr);
391 checkSizes(sPtr);
393 if (sPtr->constrainProc || sPtr->flags.subviewsWereManuallyMoved) {
394 distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr));
395 checkPositions(sPtr);
396 updateSubviewsGeom(sPtr);
397 } else
398 adjustSplitViewSubviews(sPtr);
400 assert(checkSizes(sPtr) == 0);
402 #if 0
403 printf("---- (handleViewResized - 2) ----\n");
404 dumpSubviews(sPtr);
405 #endif
409 static void
410 paintSplitView(SplitView *sPtr)
412 T_SplitViewSubview *p;
413 W_Screen *scr = sPtr->view->screen;
414 int x, y, i, count;
415 WMPixmap *dimple = scr->scrollerDimple;
417 #if 0
418 printf("---- (paintSplitView - 1) ----\n");
419 dumpSubviews(sPtr);
420 #endif
422 if (!sPtr->view->flags.mapped || !sPtr->view->flags.realized)
423 return;
425 XClearWindow(scr->display, sPtr->view->window);
427 count = _GetSubviewsCount();
428 if (count == 0)
429 return;
431 if (sPtr->flags.adjustOnPaint) {
432 handleViewResized(sPtr, NULL);
433 sPtr->flags.adjustOnPaint = 0;
436 XSetClipMask(scr->display, scr->clipGC, dimple->mask);
438 if (sPtr->flags.vertical) {
439 x = ((DIVIDER_THICKNESS - dimple->width) / 2);
440 y = (sPtr->view->size.height - dimple->height)/2;
441 } else {
442 x = (sPtr->view->size.width - dimple->width)/2;
443 y = ((DIVIDER_THICKNESS - dimple->height) / 2);
446 for (i = 0; i < count-1; i++) {
447 p = _GetPSubviewStructAt(i);
449 if (sPtr->flags.vertical)
450 x += p->size;
451 else
452 y += p->size;
454 XSetClipOrigin(scr->display, scr->clipGC, x, y);
455 XCopyArea(scr->display, dimple->pixmap, sPtr->view->window,
456 scr->clipGC, 0, 0, dimple->width, dimple->height, x, y);
458 if (sPtr->flags.vertical)
459 x += DIVIDER_THICKNESS;
460 else
461 y += DIVIDER_THICKNESS;
464 #if 0
465 printf("---- (paintSplitView - 2) ----\n");
466 dumpSubviews(sPtr);
467 #endif
471 static void
472 drawDragingRectangle(SplitView *sPtr, int pos)
474 int x, y, w, h;
476 if (sPtr->flags.vertical) {
477 x = pos;
478 y = 0;
479 w = DIVIDER_THICKNESS;
480 h = sPtr->view->size.height;
481 } else {
482 x = 0;
483 y = pos;
484 w = sPtr->view->size.width;
485 h = DIVIDER_THICKNESS;
488 XFillRectangle(sPtr->view->screen->display, sPtr->view->window,
489 sPtr->view->screen->ixorGC, x, y, w, h);
493 static void
494 getMinMaxDividerCoord(SplitView *sPtr, int divider, int *minC, int *maxC)
496 int relMinC, relMaxC;
497 int totSize = _GetSizeAt(divider) + _GetSizeAt(divider+1);
499 relMinC = _GetMinSizeAt(divider);
500 if (_GetMaxSizeAt(divider+1) != MAX_SUBVIEW_SIZE
501 && relMinC < totSize - _GetMaxSizeAt(divider+1))
502 relMinC = totSize - _GetMaxSizeAt(divider+1);
504 relMaxC = totSize - _GetMinSizeAt(divider+1);
505 if (_GetMaxSizeAt(divider) != MAX_SUBVIEW_SIZE
506 && relMaxC > _GetMaxSizeAt(divider))
507 relMaxC = _GetMaxSizeAt(divider);
509 *minC = _GetPosAt(divider) + relMinC;
510 *maxC = _GetPosAt(divider) + relMaxC;
514 static void
515 dragDivider(SplitView *sPtr, int clickX, int clickY)
517 int divider, pos, ofs, done, dragging;
518 int i, count;
519 XEvent ev;
520 WMScreen *scr;
521 int minCoord, maxCoord, coord;
523 if (sPtr->constrainProc) {
524 updateConstraints(sPtr);
525 checkSizes(sPtr);
526 distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr));
527 checkPositions(sPtr);
528 updateSubviewsGeom(sPtr);
531 scr = sPtr->view->screen;
532 divider = ofs = pos = done = 0;
533 coord = (sPtr->flags.vertical) ? clickX : clickY;
534 count = _GetSubviewsCount();
535 if (count < 2)
536 return;
538 for (i = 0; i < count-1; i++) {
539 pos += _GetSizeAt(i) + DIVIDER_THICKNESS;
540 if (coord < pos) {
541 ofs = coord - pos + DIVIDER_THICKNESS;
542 done = 1;
543 break;
545 divider++;
548 if (!done)
549 return;
551 getMinMaxDividerCoord(sPtr, divider, &minCoord, &maxCoord);
553 done = 0;
554 dragging = 0;
555 while (!done) {
556 WMMaskEvent(scr->display, ButtonMotionMask|ButtonReleaseMask
557 |ExposureMask, &ev);
559 coord = (sPtr->flags.vertical) ? ev.xmotion.x : ev.xmotion.y;
561 switch (ev.type) {
562 case ButtonRelease:
563 done = 1;
564 if (dragging)
565 drawDragingRectangle(sPtr, pos);
566 break;
568 case MotionNotify:
569 if (dragging)
570 drawDragingRectangle(sPtr, pos);
571 if (coord - ofs < minCoord)
572 pos = minCoord;
573 else if (coord - ofs > maxCoord)
574 pos = maxCoord;
575 else
576 pos = coord - ofs;
577 drawDragingRectangle(sPtr, pos);
578 dragging = 1;
579 break;
581 default:
582 WMHandleEvent(&ev);
583 break;
587 if (dragging) {
588 T_SplitViewSubview *p1, *p2;
589 int totSize;
591 p1 = _GetPSubviewStructAt(divider);
592 p2 = _GetPSubviewStructAt(divider+1);
594 totSize = p1->size + DIVIDER_THICKNESS + p2->size;
596 p1->size = pos - p1->pos;
597 p2->size = totSize - p1->size - DIVIDER_THICKNESS;
598 p2->pos = p1->pos + p1->size + DIVIDER_THICKNESS;
600 resizeView(sPtr, p1->view, p1->size);
601 moveView(sPtr, p2->view, p2->pos);
602 resizeView(sPtr, p2->view, p2->size);
603 sPtr->flags.subviewsWereManuallyMoved = 1;
608 static void
609 handleEvents(XEvent *event, void *data)
611 SplitView *sPtr = (SplitView*)data;
613 CHECK_CLASS(data, WC_SplitView);
616 switch (event->type) {
617 case Expose:
618 if (event->xexpose.count!=0)
619 break;
620 paintSplitView(sPtr);
621 break;
623 case DestroyNotify:
624 destroySplitView(sPtr);
625 break;
630 static void
631 handleActionEvents(XEvent *event, void *data)
634 CHECK_CLASS(data, WC_SplitView);
636 switch (event->type) {
637 case ButtonPress:
638 if (event->xbutton.button == Button1)
639 dragDivider(data, event->xbutton.x, event->xbutton.y);
640 break;
646 static void
647 destroySplitView(SplitView *sPtr)
649 int i, count;
651 count = _GetSubviewsCount();
652 for (i = 0; i < count; i++)
653 wfree(WMGetFromBag(sPtr->subviewsBag, i));
654 WMFreeBag(sPtr->subviewsBag);
656 WMRemoveNotificationObserver(sPtr);
658 wfree(sPtr);
661 WMSplitView*
662 WMCreateSplitView(WMWidget *parent)
664 SplitView *sPtr;
666 sPtr = wmalloc(sizeof(SplitView));
667 memset(sPtr, 0, sizeof(SplitView));
669 sPtr->widgetClass = WC_SplitView;
671 sPtr->view = W_CreateView(W_VIEW(parent));
672 if (!sPtr->view) {
673 wfree(sPtr);
674 return NULL;
676 sPtr->view->self = sPtr;
678 WMSetViewNotifySizeChanges(sPtr->view, True);
680 WMCreateEventHandler(sPtr->view, ExposureMask|StructureNotifyMask
681 |ClientMessageMask, handleEvents, sPtr);
684 WMCreateEventHandler(sPtr->view, ButtonPressMask|ButtonReleaseMask
685 |EnterWindowMask|LeaveWindowMask,
686 handleActionEvents, sPtr);
689 WMAddNotificationObserver(handleViewResized, sPtr,
690 WMViewSizeDidChangeNotification, sPtr->view);
692 sPtr->subviewsBag = WMCreateBag(8);
694 return sPtr;
698 void
699 WMAdjustSplitViewSubviews(WMSplitView *sPtr)
701 CHECK_CLASS(sPtr, WC_SplitView);
703 checkSizes(sPtr);
705 adjustSplitViewSubviews(sPtr);
707 assert(checkSizes(sPtr) == 0);
711 void
712 WMAddSplitViewSubview(WMSplitView *sPtr, WMView *subview)
714 int wasMapped, count;
715 T_SplitViewSubview *p;
717 CHECK_CLASS(sPtr, WC_SplitView);
719 if (!(p = (T_SplitViewSubview*)wmalloc(sizeof(T_SplitViewSubview))))
720 return;
722 wasMapped = subview->flags.mapped;
723 if (wasMapped)
724 W_UnmapView(subview);
726 count = _GetSubviewsCount();
727 p->view = subview;
728 getConstraints(sPtr, count, &(p->minSize), &(p->maxSize));
729 if (sPtr->flags.vertical)
730 p->size = subview->size.width;
731 else
732 p->size = subview->size.height;
734 WMPutInBag(sPtr->subviewsBag,(void*)p);
735 reparentView(sPtr, subview, 0);
738 We should have something like that...
740 WMSetViewNotifySizeChanges(subview, True);
741 WMAddNotificationObserver(handleSubviewResized, sPtr,
742 WMViewSizeDidChangeNotification,
743 subview);
744 WMSetViewNotifyMoveChanges(subview, True);
745 WMAddNotificationObserver(handleSubviewResized, sPtr,
746 WMViewMoveDidChangeNotification,
747 subview);
749 if (wasMapped) {
750 W_MapView(subview);
752 sPtr->flags.adjustOnPaint = 1;
753 paintSplitView(sPtr);
754 } else {
755 handleViewResized(sPtr, NULL);
760 WMView*
761 WMGetSplitViewSubviewAt(WMSplitView *sPtr, int index)
763 CHECK_CLASS(sPtr, WC_SplitView);
765 if (index > 0 && index < _GetSubviewsCount())
766 return (_GetSubviewAt(index));
767 else
768 return (NULL);
772 void
773 WMRemoveSplitViewSubview(WMSplitView *sPtr, WMView *view)
775 T_SplitViewSubview *p;
776 int i, count;
778 CHECK_CLASS(sPtr, WC_SplitView);
780 count = _GetSubviewsCount();
781 for (i = 0; i < count; i++) {
782 p = _GetPSubviewStructAt(i);
783 if (p->view == view) {
784 wfree(p);
785 WMDeleteFromBag(sPtr->subviewsBag, i);
786 sPtr->flags.adjustOnPaint = 1;
787 paintSplitView(sPtr);
788 break;
794 void
795 WMRemoveSplitViewSubviewAt(WMSplitView *sPtr, int index)
797 T_SplitViewSubview *p;
799 CHECK_CLASS(sPtr, WC_SplitView);
801 if (index > 0 && index < _GetSubviewsCount()) {
802 p = _GetPSubviewStructAt(index);
803 wfree(p);
804 WMDeleteFromBag(sPtr->subviewsBag, index);
805 sPtr->flags.adjustOnPaint = 1;
806 paintSplitView(sPtr);
811 void
812 WMSetSplitViewConstrainProc(WMSplitView *sPtr, WMSplitViewConstrainProc *proc)
814 CHECK_CLASS(sPtr, WC_SplitView);
816 sPtr->constrainProc = proc;
821 WMGetSplitViewSubviewsCount(WMSplitView *sPtr)
823 CHECK_CLASS(sPtr, WC_SplitView);
825 return (_GetSubviewsCount());
829 Bool
830 WMGetSplitViewVertical(WMSplitView *sPtr)
832 CHECK_CLASS(sPtr, WC_SplitView);
834 return (sPtr->flags.vertical == 1);
837 void
838 WMSetSplitViewVertical(WMSplitView *sPtr, Bool flag)
840 int vertical;
842 CHECK_CLASS(sPtr, WC_SplitView);
844 vertical = (flag) ? 1 : 0;
845 if (sPtr->flags.vertical == vertical)
846 return;
848 sPtr->flags.vertical = vertical;
850 /* if (sPtr->view->flags.mapped && sPtr->view->flags.realized)*/
851 handleViewResized(sPtr, NULL);
852 /* else
853 sPtr->flags.adjustOnPaint = 1;
859 WMGetSplitViewDividerThickness(WMSplitView *sPtr)
861 CHECK_CLASS(sPtr, WC_SplitView);
863 return (DIVIDER_THICKNESS);
866 #if 0
867 void
868 WMSetSplitViewResizeSubviewsProc(WMSplitView *sPtr,
869 WMSplitViewResizeSubviewsProc *proc)
871 CHECK_CLASS(sPtr, WC_SplitView);
873 sPtr->resizeSubviewsProc = proc;
875 #endif