New Czech locales from Jiri Hnidek <Jiri.Hnidek@vslib.cz>
[wmaker-crm.git] / WINGs / wsplitview.c
blob4dfd8b04fdf69de11251da73a6c5180822310989
5 #include "WINGsP.h"
7 /*
8 char *WMSplitViewDidResizeSubviewsNotification
9 = "WMSplitViewDidResizeSubviewsNotification";
10 char *WMSplitViewWillResizeSubviewsNotification
11 = "WMSplitViewWillResizeSubviewsNotification";
14 typedef struct W_SplitViewSubview {
15 WMView *view;
16 int minSize;
17 int maxSize;
18 int size;
19 int pos;
20 } W_SplitViewSubview;
23 typedef struct W_SplitView {
24 W_Class widgetClass;
25 W_View *view;
27 WMArray *subviews;
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 } W_SplitView;
42 #define DIVIDER_THICKNESS 8
43 #define MIN_SUBVIEW_SIZE 4
44 #define MAX_SUBVIEW_SIZE -1
47 /* TODO: rewrite --Dan */
48 #define _GetSubviewsCount() WMGetArrayItemCount(sPtr->subviews)
50 #define _AddPSubviewStruct(P) \
51 (WMAddToArray(sPtr->subviews,((void*)P)))
53 #define _GetPSubviewStructAt(i) \
54 ((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))
56 #define _GetSubviewAt(i) \
57 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->view)
59 #define _GetMinSizeAt(i) \
60 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->minSize)
62 #define _GetMaxSizeAt(i) \
63 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->maxSize)
65 #define _GetSizeAt(i) \
66 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->size)
68 #define _GetPosAt(i) \
69 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->pos)
71 #define _GetSplitViewSize() \
72 ((sPtr->flags.vertical) ? sPtr->view->size.width : sPtr->view->size.height)
74 static void destroySplitView(WMSplitView *sPtr);
75 static void paintSplitView(WMSplitView *sPtr);
77 static void handleEvents(XEvent *event, void *data);
78 static void handleActionEvents(XEvent *event, void *data);
81 static void
82 getConstraints(WMSplitView *sPtr, int index, int *minSize, int *maxSize)
84 *minSize = MIN_SUBVIEW_SIZE;
85 *maxSize = MAX_SUBVIEW_SIZE;
87 if (sPtr->constrainProc)
88 (*sPtr->constrainProc)(sPtr, index, minSize, maxSize);
90 if (*minSize < MIN_SUBVIEW_SIZE)
91 *minSize = MIN_SUBVIEW_SIZE;
93 if (*maxSize < MIN_SUBVIEW_SIZE)
94 *maxSize = MAX_SUBVIEW_SIZE;
95 else if (*maxSize < *minSize)
96 *maxSize = *minSize;
100 static void
101 updateConstraints(WMSplitView *sPtr)
103 W_SplitViewSubview *p;
104 int i, count;
106 count = _GetSubviewsCount();
107 for (i = 0; i < count; i++) {
108 p = _GetPSubviewStructAt(i);
109 getConstraints(sPtr, i, &(p->minSize), &(p->maxSize));
114 static void
115 resizeView(WMSplitView *sPtr, WMView *view, int size)
117 int width, height;
119 if (sPtr->flags.vertical) {
120 width = size;
121 height = sPtr->view->size.height;
122 } else {
123 width = sPtr->view->size.width;
124 height = size;
127 if (view->self)
128 WMResizeWidget(view->self, width, height);
129 else
130 W_ResizeView(view, width, height);
134 static void
135 reparentView(WMSplitView *sPtr, WMView *view, int pos)
137 int x, y;
139 if (sPtr->flags.vertical) {
140 x = pos;
141 y = 0;
142 } else {
143 x = 0;
144 y = pos;
147 W_ReparentView(view, sPtr->view, x, y);
151 static void
152 moveView(WMSplitView *sPtr, WMView *view, int pos)
154 int x, y;
156 if (sPtr->flags.vertical) {
157 x = pos;
158 y = 0;
159 } else {
160 x = 0;
161 y = pos;
164 if (view->self)
165 WMMoveWidget(view->self, x, y);
166 else
167 W_MoveView(view, x, y);
171 static int
172 checkSizes(WMSplitView *sPtr)
174 int i, count, offset;
175 W_SplitViewSubview *p;
177 count = _GetSubviewsCount();
178 offset = 0;
179 for (i = 0; i < count; i++) {
180 p = _GetPSubviewStructAt(i);
181 if (p->size < p->minSize) {
182 offset += p->minSize - p->size;
183 p->size = p->minSize;
184 } else if (p->maxSize != MAX_SUBVIEW_SIZE && p->size > p->maxSize) {
185 offset += p->maxSize - p->size;
186 p->size = p->maxSize;
190 return (offset);
194 static void
195 checkPositions(WMSplitView *sPtr)
197 int i, count, pos;
198 W_SplitViewSubview *p;
200 count = _GetSubviewsCount();
201 pos = 0;
202 for (i = 0; i < count; i++) {
203 p = _GetPSubviewStructAt(i);
204 p->pos = pos;
205 pos += p->size + DIVIDER_THICKNESS;
210 static void
211 updateSubviewsGeom(WMSplitView *sPtr)
213 int i, count;
214 W_SplitViewSubview *p;
216 count = _GetSubviewsCount();
217 for (i = 0; i < count; i++) {
218 p = _GetPSubviewStructAt(i);
219 resizeView(sPtr, p->view, p->size);
220 moveView(sPtr, p->view, p->pos);
225 static int
226 getTotalSize(WMSplitView *sPtr)
228 int i, count, totSize;
230 count = _GetSubviewsCount();
231 if (!count)
232 return (0);
234 totSize = 0;
235 for (i = 0; i < count; i++)
236 totSize += _GetSizeAt(i) + DIVIDER_THICKNESS;
238 return (totSize - DIVIDER_THICKNESS);
242 static Bool
243 distributeOffsetEqually(WMSplitView *sPtr, int offset)
245 W_SplitViewSubview *p;
246 int i, count, sizeChanged, forced;
248 if ((count = _GetSubviewsCount()) < 1)
249 return (True);
251 forced = False;
252 while (offset != 0) {
253 sizeChanged = 0;
254 for (i = 0; i < count && offset != 0; i++) {
255 p = _GetPSubviewStructAt(i);
256 if (offset < 0) {
257 if (p->size > p->minSize) {
258 offset++;
259 p->size--;
260 sizeChanged = 1;
262 } else if (p->maxSize == MAX_SUBVIEW_SIZE || p->size < p->maxSize) {
263 offset--;
264 p->size++;
265 sizeChanged = 1;
268 if (offset != 0 && !sizeChanged) {
269 p = _GetPSubviewStructAt(count-1);
270 if (offset > 0) {
271 p->size += offset;
272 p->maxSize = MAX_SUBVIEW_SIZE;
274 offset = 0;
275 forced = True;
279 return (forced);
283 static Bool
284 distributeOffsetFormEnd(WMSplitView *sPtr, int offset)
286 W_SplitViewSubview *p;
287 int i, count, sizeTmp;
289 if ((count = _GetSubviewsCount()) < 1)
290 return (True);
292 for (i = count-1; i >= 0 && offset != 0; i--) {
293 p = _GetPSubviewStructAt(i);
294 sizeTmp = p->size;
295 if (offset > 0) {
296 if (p->maxSize == MAX_SUBVIEW_SIZE || p->size + offset < p->maxSize)
297 p->size += offset;
298 else
299 p->size = p->maxSize;
300 } else {
301 if (p->size + offset >= p->minSize)
302 p->size += offset;
303 else
304 p->size = p->minSize;
306 offset -= p->size - sizeTmp;
309 return (offset == 0);
313 static void
314 adjustSplitViewSubviews(WMSplitView *sPtr)
316 W_SplitViewSubview *p;
317 int i, count, adjSize, adjPad;
319 CHECK_CLASS(sPtr, WC_SplitView);
321 #if 0
322 printf("---- (adjustSplitViewSubviews - 1) ----\n");
323 dumpSubviews(sPtr);
324 #endif
326 if ((count = _GetSubviewsCount()) < 1)
327 return;
329 adjSize = (_GetSplitViewSize() - ((count-1) * DIVIDER_THICKNESS)) / count;
330 adjPad = (_GetSplitViewSize() - ((count-1) * DIVIDER_THICKNESS)) % count;
331 for (i = 0; i < count; i++) {
332 p = _GetPSubviewStructAt(i);
333 p->size = adjSize;
336 distributeOffsetEqually(sPtr, adjPad - checkSizes(sPtr));
338 checkPositions(sPtr);
339 updateSubviewsGeom(sPtr);
341 sPtr->flags.subviewsWereManuallyMoved = 0;
343 #if 0
344 printf("---- (adjustSplitViewSubviews - 2) ----\n");
345 dumpSubviews(sPtr);
346 #endif
349 #if 0
350 static void
351 handleSubviewResized(void *self, WMNotification *notif)
353 WMSplitView *sPtr = (WMSplitView*)self;
355 CHECK_CLASS(sPtr, WC_SplitView);
357 if (WMGetNotificationName(notif) == WMViewSizeDidChangeNotification) {
358 W_SplitViewSubview *p;
359 int i, count, done;
360 WMView *view = WMGetNotificationObject(notif);
362 count = _GetSubviewsCount();
363 done = 0;
364 for (i = 0; i < count; i++) {
365 p = _GetPSubviewStructAt(i);
366 if (p->view == view) {
367 done = 1;
368 break;
372 if (done) {
373 /* TODO !!! */
374 resizeView(sPtr, p->view, p->size);
375 moveView(sPtr, p->view, p->pos);
379 #endif
381 static void
382 handleViewResized(void *self, WMNotification *notification)
384 WMSplitView *sPtr = (WMSplitView*)self;
386 #if 0
387 printf("---- (handleViewResized - 1) ----\n");
388 dumpSubviews(sPtr);
389 #endif
391 updateConstraints(sPtr);
392 checkSizes(sPtr);
394 if (sPtr->constrainProc || sPtr->flags.subviewsWereManuallyMoved) {
395 distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr));
396 checkPositions(sPtr);
397 updateSubviewsGeom(sPtr);
398 } else
399 adjustSplitViewSubviews(sPtr);
401 assert(checkSizes(sPtr) == 0);
403 #if 0
404 printf("---- (handleViewResized - 2) ----\n");
405 dumpSubviews(sPtr);
406 #endif
410 static void
411 paintSplitView(WMSplitView *sPtr)
413 W_SplitViewSubview *p;
414 W_Screen *scr = sPtr->view->screen;
415 int x, y, i, count;
416 WMPixmap *dimple = scr->scrollerDimple;
418 #if 0
419 printf("---- (paintSplitView - 1) ----\n");
420 dumpSubviews(sPtr);
421 #endif
423 if (!sPtr->view->flags.mapped || !sPtr->view->flags.realized)
424 return;
426 XClearWindow(scr->display, sPtr->view->window);
428 count = _GetSubviewsCount();
429 if (count == 0)
430 return;
432 if (sPtr->flags.adjustOnPaint) {
433 handleViewResized(sPtr, NULL);
434 sPtr->flags.adjustOnPaint = 0;
437 XSetClipMask(scr->display, scr->clipGC, dimple->mask);
439 if (sPtr->flags.vertical) {
440 x = ((DIVIDER_THICKNESS - dimple->width) / 2);
441 y = (sPtr->view->size.height - dimple->height)/2;
442 } else {
443 x = (sPtr->view->size.width - dimple->width)/2;
444 y = ((DIVIDER_THICKNESS - dimple->height) / 2);
447 for (i = 0; i < count-1; i++) {
448 p = _GetPSubviewStructAt(i);
450 if (sPtr->flags.vertical)
451 x += p->size;
452 else
453 y += p->size;
455 XSetClipOrigin(scr->display, scr->clipGC, x, y);
456 XCopyArea(scr->display, dimple->pixmap, sPtr->view->window,
457 scr->clipGC, 0, 0, dimple->width, dimple->height, x, y);
459 if (sPtr->flags.vertical)
460 x += DIVIDER_THICKNESS;
461 else
462 y += DIVIDER_THICKNESS;
465 #if 0
466 printf("---- (paintSplitView - 2) ----\n");
467 dumpSubviews(sPtr);
468 #endif
472 static void
473 drawDragingRectangle(WMSplitView *sPtr, int pos)
475 int x, y, w, h;
477 if (sPtr->flags.vertical) {
478 x = pos;
479 y = 0;
480 w = DIVIDER_THICKNESS;
481 h = sPtr->view->size.height;
482 } else {
483 x = 0;
484 y = pos;
485 w = sPtr->view->size.width;
486 h = DIVIDER_THICKNESS;
489 XFillRectangle(sPtr->view->screen->display, sPtr->view->window,
490 sPtr->view->screen->ixorGC, x, y, w, h);
494 static void
495 getMinMaxDividerCoord(WMSplitView *sPtr, int divider, int *minC, int *maxC)
497 int relMinC, relMaxC;
498 int totSize = _GetSizeAt(divider) + _GetSizeAt(divider+1);
500 relMinC = _GetMinSizeAt(divider);
501 if (_GetMaxSizeAt(divider+1) != MAX_SUBVIEW_SIZE
502 && relMinC < totSize - _GetMaxSizeAt(divider+1))
503 relMinC = totSize - _GetMaxSizeAt(divider+1);
505 relMaxC = totSize - _GetMinSizeAt(divider+1);
506 if (_GetMaxSizeAt(divider) != MAX_SUBVIEW_SIZE
507 && relMaxC > _GetMaxSizeAt(divider))
508 relMaxC = _GetMaxSizeAt(divider);
510 *minC = _GetPosAt(divider) + relMinC;
511 *maxC = _GetPosAt(divider) + relMaxC;
515 static void
516 dragDivider(WMSplitView *sPtr, int clickX, int clickY)
518 int divider, pos, ofs, done, dragging;
519 int i, count;
520 XEvent ev;
521 WMScreen *scr;
522 int minCoord, maxCoord, coord;
524 if (sPtr->constrainProc) {
525 updateConstraints(sPtr);
526 checkSizes(sPtr);
527 distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr));
528 checkPositions(sPtr);
529 updateSubviewsGeom(sPtr);
532 scr = sPtr->view->screen;
533 divider = ofs = pos = done = 0;
534 coord = (sPtr->flags.vertical) ? clickX : clickY;
535 count = _GetSubviewsCount();
536 if (count < 2)
537 return;
539 for (i = 0; i < count-1; i++) {
540 pos += _GetSizeAt(i) + DIVIDER_THICKNESS;
541 if (coord < pos) {
542 ofs = coord - pos + DIVIDER_THICKNESS;
543 done = 1;
544 break;
546 divider++;
549 if (!done)
550 return;
552 getMinMaxDividerCoord(sPtr, divider, &minCoord, &maxCoord);
554 done = 0;
555 dragging = 0;
556 while (!done) {
557 WMMaskEvent(scr->display, ButtonMotionMask|ButtonReleaseMask
558 |ExposureMask, &ev);
560 coord = (sPtr->flags.vertical) ? ev.xmotion.x : ev.xmotion.y;
562 switch (ev.type) {
563 case ButtonRelease:
564 done = 1;
565 if (dragging)
566 drawDragingRectangle(sPtr, pos);
567 break;
569 case MotionNotify:
570 if (dragging)
571 drawDragingRectangle(sPtr, pos);
572 if (coord - ofs < minCoord)
573 pos = minCoord;
574 else if (coord - ofs > maxCoord)
575 pos = maxCoord;
576 else
577 pos = coord - ofs;
578 drawDragingRectangle(sPtr, pos);
579 dragging = 1;
580 break;
582 default:
583 WMHandleEvent(&ev);
584 break;
588 if (dragging) {
589 W_SplitViewSubview *p1, *p2;
590 int totSize;
592 p1 = _GetPSubviewStructAt(divider);
593 p2 = _GetPSubviewStructAt(divider+1);
595 totSize = p1->size + DIVIDER_THICKNESS + p2->size;
597 p1->size = pos - p1->pos;
598 p2->size = totSize - p1->size - DIVIDER_THICKNESS;
599 p2->pos = p1->pos + p1->size + DIVIDER_THICKNESS;
601 resizeView(sPtr, p1->view, p1->size);
602 moveView(sPtr, p2->view, p2->pos);
603 resizeView(sPtr, p2->view, p2->size);
604 sPtr->flags.subviewsWereManuallyMoved = 1;
609 static void
610 handleEvents(XEvent *event, void *data)
612 WMSplitView *sPtr = (WMSplitView*)data;
614 CHECK_CLASS(data, WC_SplitView);
617 switch (event->type) {
618 case Expose:
619 if (event->xexpose.count!=0)
620 break;
621 paintSplitView(sPtr);
622 break;
624 case DestroyNotify:
625 destroySplitView(sPtr);
626 break;
631 static void
632 handleActionEvents(XEvent *event, void *data)
635 CHECK_CLASS(data, WC_SplitView);
637 switch (event->type) {
638 case ButtonPress:
639 if (event->xbutton.button == Button1)
640 dragDivider(data, event->xbutton.x, event->xbutton.y);
641 break;
647 static void
648 destroySplitView(WMSplitView *sPtr)
650 WMFreeArray(sPtr->subviews);
652 WMRemoveNotificationObserver(sPtr);
654 wfree(sPtr);
657 WMSplitView*
658 WMCreateSplitView(WMWidget *parent)
660 WMSplitView *sPtr;
662 sPtr = wmalloc(sizeof(WMSplitView));
663 memset(sPtr, 0, sizeof(WMSplitView));
665 sPtr->widgetClass = WC_SplitView;
667 sPtr->view = W_CreateView(W_VIEW(parent));
668 if (!sPtr->view) {
669 wfree(sPtr);
670 return NULL;
672 sPtr->view->self = sPtr;
674 WMSetViewNotifySizeChanges(sPtr->view, True);
676 WMCreateEventHandler(sPtr->view, ExposureMask|StructureNotifyMask
677 |ClientMessageMask, handleEvents, sPtr);
680 WMCreateEventHandler(sPtr->view, ButtonPressMask|ButtonReleaseMask
681 |EnterWindowMask|LeaveWindowMask,
682 handleActionEvents, sPtr);
685 WMAddNotificationObserver(handleViewResized, sPtr,
686 WMViewSizeDidChangeNotification, sPtr->view);
688 sPtr->subviews = WMCreateArrayWithDestructor(8, wfree);
690 return sPtr;
694 void
695 WMAdjustSplitViewSubviews(WMSplitView *sPtr)
697 CHECK_CLASS(sPtr, WC_SplitView);
699 checkSizes(sPtr);
701 adjustSplitViewSubviews(sPtr);
703 assert(checkSizes(sPtr) == 0);
707 void
708 WMAddSplitViewSubview(WMSplitView *sPtr, WMView *subview)
710 int wasMapped, count;
711 W_SplitViewSubview *p;
713 CHECK_CLASS(sPtr, WC_SplitView);
715 if (!(p = (W_SplitViewSubview*)wmalloc(sizeof(W_SplitViewSubview))))
716 return;
718 wasMapped = subview->flags.mapped;
719 if (wasMapped)
720 W_UnmapView(subview);
722 count = _GetSubviewsCount();
723 p->view = subview;
724 getConstraints(sPtr, count, &(p->minSize), &(p->maxSize));
725 if (sPtr->flags.vertical)
726 p->size = subview->size.width;
727 else
728 p->size = subview->size.height;
730 WMAddToArray(sPtr->subviews, p);
731 reparentView(sPtr, subview, 0);
734 We should have something like that...
736 WMSetViewNotifySizeChanges(subview, True);
737 WMAddNotificationObserver(handleSubviewResized, sPtr,
738 WMViewSizeDidChangeNotification,
739 subview);
740 WMSetViewNotifyMoveChanges(subview, True);
741 WMAddNotificationObserver(handleSubviewResized, sPtr,
742 WMViewMoveDidChangeNotification,
743 subview);
745 if (wasMapped) {
746 W_MapView(subview);
748 sPtr->flags.adjustOnPaint = 1;
749 paintSplitView(sPtr);
750 } else {
751 handleViewResized(sPtr, NULL);
756 WMView*
757 WMGetSplitViewSubviewAt(WMSplitView *sPtr, int index)
759 CHECK_CLASS(sPtr, WC_SplitView);
761 if (index >= 0 && index < _GetSubviewsCount())
762 return (_GetSubviewAt(index));
763 else
764 return (NULL);
768 void
769 WMRemoveSplitViewSubview(WMSplitView *sPtr, WMView *view)
771 W_SplitViewSubview *p;
772 int i, count;
774 CHECK_CLASS(sPtr, WC_SplitView);
776 /* TODO: rewrite this. This code with macros is getting more complex than it worths */
777 count = _GetSubviewsCount();
778 for (i = 0; i < count; i++) {
779 p = _GetPSubviewStructAt(i);
780 if (p->view == view) {
781 WMDeleteFromArray(sPtr->subviews, i);
782 sPtr->flags.adjustOnPaint = 1;
783 paintSplitView(sPtr);
784 break;
790 void
791 WMRemoveSplitViewSubviewAt(WMSplitView *sPtr, int index)
793 W_SplitViewSubview *p;
795 CHECK_CLASS(sPtr, WC_SplitView);
797 /* TODO: same about rewrite */
798 if (index >= 0 && index < _GetSubviewsCount()) {
799 p = _GetPSubviewStructAt(index);
800 WMDeleteFromArray(sPtr->subviews, index);
801 sPtr->flags.adjustOnPaint = 1;
802 paintSplitView(sPtr);
807 void
808 WMSetSplitViewConstrainProc(WMSplitView *sPtr, WMSplitViewConstrainProc *proc)
810 CHECK_CLASS(sPtr, WC_SplitView);
812 sPtr->constrainProc = proc;
817 WMGetSplitViewSubviewsCount(WMSplitView *sPtr)
819 CHECK_CLASS(sPtr, WC_SplitView);
821 return (_GetSubviewsCount());
825 Bool
826 WMGetSplitViewVertical(WMSplitView *sPtr)
828 CHECK_CLASS(sPtr, WC_SplitView);
830 return (sPtr->flags.vertical == 1);
833 void
834 WMSetSplitViewVertical(WMSplitView *sPtr, Bool flag)
836 int vertical;
838 CHECK_CLASS(sPtr, WC_SplitView);
840 vertical = (flag) ? 1 : 0;
841 if (sPtr->flags.vertical == vertical)
842 return;
844 sPtr->flags.vertical = vertical;
846 /* if (sPtr->view->flags.mapped && sPtr->view->flags.realized)*/
847 handleViewResized(sPtr, NULL);
848 /* else
849 sPtr->flags.adjustOnPaint = 1;
855 WMGetSplitViewDividerThickness(WMSplitView *sPtr)
857 CHECK_CLASS(sPtr, WC_SplitView);
859 return (DIVIDER_THICKNESS);
862 #if 0
863 void
864 WMSetSplitViewResizeSubviewsProc(WMSplitView *sPtr,
865 WMSplitViewResizeSubviewsProc *proc)
867 CHECK_CLASS(sPtr, WC_SplitView);
869 sPtr->resizeSubviewsProc = proc;
871 #endif