strncpy's third argument should be the length of the dest buffer, not the source.
[wmaker-crm.git] / WINGs / wsplitview.c
blob3d8072db8e3e41209a4a83b863a9f71440022636
2 #include "WINGsP.h"
4 /*
5 char *WMSplitViewDidResizeSubviewsNotification
6 = "WMSplitViewDidResizeSubviewsNotification";
7 char *WMSplitViewWillResizeSubviewsNotification
8 = "WMSplitViewWillResizeSubviewsNotification";
9 */
11 typedef struct W_SplitViewSubview {
12 WMView *view;
13 int minSize;
14 int maxSize;
15 int size;
16 int pos;
17 } W_SplitViewSubview;
19 typedef struct W_SplitView {
20 W_Class widgetClass;
21 W_View *view;
23 WMArray *subviews;
25 WMSplitViewConstrainProc *constrainProc;
27 struct {
28 unsigned int vertical:1;
29 unsigned int adjustOnPaint:1;
30 unsigned int subviewsWereManuallyMoved:1;
31 } flags;
33 /* WMSplitViewResizeSubviewsProc *resizeSubviewsProc; */
35 } W_SplitView;
37 #define DIVIDER_THICKNESS 8
38 #define MIN_SUBVIEW_SIZE 4
39 #define MAX_SUBVIEW_SIZE -1
41 /* TODO: rewrite --Dan */
42 #define _GetSubviewsCount() WMGetArrayItemCount(sPtr->subviews)
44 #define _GetPSubviewStructAt(i) \
45 ((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))
47 #define _GetSubviewAt(i) \
48 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->view)
50 #define _GetMinSizeAt(i) \
51 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->minSize)
53 #define _GetMaxSizeAt(i) \
54 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->maxSize)
56 #define _GetSizeAt(i) \
57 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->size)
59 #define _GetPosAt(i) \
60 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->pos)
62 #define _GetSplitViewSize() \
63 ((sPtr->flags.vertical) ? sPtr->view->size.width : sPtr->view->size.height)
65 static void destroySplitView(WMSplitView * sPtr);
66 static void paintSplitView(WMSplitView * sPtr);
68 static void handleEvents(XEvent * event, void *data);
69 static void handleActionEvents(XEvent * event, void *data);
71 static void getConstraints(WMSplitView * sPtr, int index, int *minSize, int *maxSize)
73 *minSize = MIN_SUBVIEW_SIZE;
74 *maxSize = MAX_SUBVIEW_SIZE;
76 if (sPtr->constrainProc)
77 (*sPtr->constrainProc) (sPtr, index, minSize, maxSize);
79 if (*minSize < MIN_SUBVIEW_SIZE)
80 *minSize = MIN_SUBVIEW_SIZE;
82 if (*maxSize < MIN_SUBVIEW_SIZE)
83 *maxSize = MAX_SUBVIEW_SIZE;
84 else if (*maxSize < *minSize)
85 *maxSize = *minSize;
88 static void updateConstraints(WMSplitView * sPtr)
90 W_SplitViewSubview *p;
91 int i, count;
93 count = _GetSubviewsCount();
94 for (i = 0; i < count; i++) {
95 p = _GetPSubviewStructAt(i);
96 getConstraints(sPtr, i, &(p->minSize), &(p->maxSize));
100 static void resizeView(WMSplitView * sPtr, WMView * view, int size)
102 int width, height;
104 if (sPtr->flags.vertical) {
105 width = size;
106 height = sPtr->view->size.height;
107 } else {
108 width = sPtr->view->size.width;
109 height = size;
112 if (view->self)
113 WMResizeWidget(view->self, width, height);
114 else
115 W_ResizeView(view, width, height);
118 static void reparentView(WMSplitView * sPtr, WMView * view, int pos)
120 int x, y;
122 if (sPtr->flags.vertical) {
123 x = pos;
124 y = 0;
125 } else {
126 x = 0;
127 y = pos;
130 W_ReparentView(view, sPtr->view, x, y);
133 static void moveView(WMSplitView * sPtr, WMView * view, int pos)
135 int x, y;
137 if (sPtr->flags.vertical) {
138 x = pos;
139 y = 0;
140 } else {
141 x = 0;
142 y = pos;
145 if (view->self)
146 WMMoveWidget(view->self, x, y);
147 else
148 W_MoveView(view, x, y);
151 static int checkSizes(WMSplitView * sPtr)
153 int i, count, offset;
154 W_SplitViewSubview *p;
156 count = _GetSubviewsCount();
157 offset = 0;
158 for (i = 0; i < count; i++) {
159 p = _GetPSubviewStructAt(i);
160 if (p->size < p->minSize) {
161 offset += p->minSize - p->size;
162 p->size = p->minSize;
163 } else if (p->maxSize != MAX_SUBVIEW_SIZE && p->size > p->maxSize) {
164 offset += p->maxSize - p->size;
165 p->size = p->maxSize;
169 return (offset);
172 static void checkPositions(WMSplitView * sPtr)
174 int i, count, pos;
175 W_SplitViewSubview *p;
177 count = _GetSubviewsCount();
178 pos = 0;
179 for (i = 0; i < count; i++) {
180 p = _GetPSubviewStructAt(i);
181 p->pos = pos;
182 pos += p->size + DIVIDER_THICKNESS;
186 static void updateSubviewsGeom(WMSplitView * sPtr)
188 int i, count;
189 W_SplitViewSubview *p;
191 count = _GetSubviewsCount();
192 for (i = 0; i < count; i++) {
193 p = _GetPSubviewStructAt(i);
194 resizeView(sPtr, p->view, p->size);
195 moveView(sPtr, p->view, p->pos);
199 static int getTotalSize(WMSplitView * sPtr)
201 int i, count, totSize;
203 count = _GetSubviewsCount();
204 if (!count)
205 return (0);
207 totSize = 0;
208 for (i = 0; i < count; i++)
209 totSize += _GetSizeAt(i) + DIVIDER_THICKNESS;
211 return (totSize - DIVIDER_THICKNESS);
214 static Bool distributeOffsetEqually(WMSplitView * sPtr, int offset)
216 W_SplitViewSubview *p;
217 int i, count, sizeChanged, forced;
219 if ((count = _GetSubviewsCount()) < 1)
220 return (True);
222 forced = False;
223 while (offset != 0) {
224 sizeChanged = 0;
225 for (i = 0; i < count && offset != 0; i++) {
226 p = _GetPSubviewStructAt(i);
227 if (offset < 0) {
228 if (p->size > p->minSize) {
229 offset++;
230 p->size--;
231 sizeChanged = 1;
233 } else if (p->maxSize == MAX_SUBVIEW_SIZE || p->size < p->maxSize) {
234 offset--;
235 p->size++;
236 sizeChanged = 1;
239 if (offset != 0 && !sizeChanged) {
240 p = _GetPSubviewStructAt(count - 1);
241 if (offset > 0) {
242 p->size += offset;
243 p->maxSize = MAX_SUBVIEW_SIZE;
245 offset = 0;
246 forced = True;
250 return (forced);
253 static Bool distributeOffsetFormEnd(WMSplitView * sPtr, int offset)
255 W_SplitViewSubview *p;
256 int i, count, sizeTmp;
258 if ((count = _GetSubviewsCount()) < 1)
259 return (True);
261 for (i = count - 1; i >= 0 && offset != 0; i--) {
262 p = _GetPSubviewStructAt(i);
263 sizeTmp = p->size;
264 if (offset > 0) {
265 if (p->maxSize == MAX_SUBVIEW_SIZE || p->size + offset < p->maxSize)
266 p->size += offset;
267 else
268 p->size = p->maxSize;
269 } else {
270 if (p->size + offset >= p->minSize)
271 p->size += offset;
272 else
273 p->size = p->minSize;
275 offset -= p->size - sizeTmp;
278 return (offset == 0);
281 static void adjustSplitViewSubviews(WMSplitView * sPtr)
283 W_SplitViewSubview *p;
284 int i, count, adjSize, adjPad;
286 CHECK_CLASS(sPtr, WC_SplitView);
288 #if 0
289 printf("---- (adjustSplitViewSubviews - 1) ----\n");
290 dumpSubviews(sPtr);
291 #endif
293 if ((count = _GetSubviewsCount()) < 1)
294 return;
296 adjSize = (_GetSplitViewSize() - ((count - 1) * DIVIDER_THICKNESS)) / count;
297 adjPad = (_GetSplitViewSize() - ((count - 1) * DIVIDER_THICKNESS)) % count;
298 for (i = 0; i < count; i++) {
299 p = _GetPSubviewStructAt(i);
300 p->size = adjSize;
303 distributeOffsetEqually(sPtr, adjPad - checkSizes(sPtr));
305 checkPositions(sPtr);
306 updateSubviewsGeom(sPtr);
308 sPtr->flags.subviewsWereManuallyMoved = 0;
310 #if 0
311 printf("---- (adjustSplitViewSubviews - 2) ----\n");
312 dumpSubviews(sPtr);
313 #endif
316 #if 0
317 static void handleSubviewResized(void *self, WMNotification * notif)
319 WMSplitView *sPtr = (WMSplitView *) self;
321 CHECK_CLASS(sPtr, WC_SplitView);
323 if (WMGetNotificationName(notif) == WMViewSizeDidChangeNotification) {
324 W_SplitViewSubview *p;
325 int i, count, done;
326 WMView *view = WMGetNotificationObject(notif);
328 count = _GetSubviewsCount();
329 done = 0;
330 for (i = 0; i < count; i++) {
331 p = _GetPSubviewStructAt(i);
332 if (p->view == view) {
333 done = 1;
334 break;
338 if (done) {
339 /* TODO !!! */
340 resizeView(sPtr, p->view, p->size);
341 moveView(sPtr, p->view, p->pos);
345 #endif
347 static void handleViewResized(void *self, WMNotification * notification)
349 WMSplitView *sPtr = (WMSplitView *) self;
351 /* Parameter not used, but tell the compiler that it is ok */
352 (void) notification;
354 #if 0
355 printf("---- (handleViewResized - 1) ----\n");
356 dumpSubviews(sPtr);
357 #endif
359 updateConstraints(sPtr);
360 checkSizes(sPtr);
362 if (sPtr->constrainProc || sPtr->flags.subviewsWereManuallyMoved) {
363 distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr));
364 checkPositions(sPtr);
365 updateSubviewsGeom(sPtr);
366 } else
367 adjustSplitViewSubviews(sPtr);
369 assert(checkSizes(sPtr) == 0);
371 #if 0
372 printf("---- (handleViewResized - 2) ----\n");
373 dumpSubviews(sPtr);
374 #endif
377 static void paintSplitView(WMSplitView * sPtr)
379 W_SplitViewSubview *p;
380 W_Screen *scr = sPtr->view->screen;
381 int x, y, i, count;
382 WMPixmap *dimple = scr->scrollerDimple;
384 #if 0
385 printf("---- (paintSplitView - 1) ----\n");
386 dumpSubviews(sPtr);
387 #endif
389 if (!sPtr->view->flags.mapped || !sPtr->view->flags.realized)
390 return;
392 XClearWindow(scr->display, sPtr->view->window);
394 count = _GetSubviewsCount();
395 if (count == 0)
396 return;
398 if (sPtr->flags.adjustOnPaint) {
399 handleViewResized(sPtr, NULL);
400 sPtr->flags.adjustOnPaint = 0;
403 XSetClipMask(scr->display, scr->clipGC, dimple->mask);
405 if (sPtr->flags.vertical) {
406 x = ((DIVIDER_THICKNESS - dimple->width) / 2);
407 y = (sPtr->view->size.height - dimple->height) / 2;
408 } else {
409 x = (sPtr->view->size.width - dimple->width) / 2;
410 y = ((DIVIDER_THICKNESS - dimple->height) / 2);
413 for (i = 0; i < count - 1; i++) {
414 p = _GetPSubviewStructAt(i);
416 if (sPtr->flags.vertical)
417 x += p->size;
418 else
419 y += p->size;
421 XSetClipOrigin(scr->display, scr->clipGC, x, y);
422 XCopyArea(scr->display, dimple->pixmap, sPtr->view->window,
423 scr->clipGC, 0, 0, dimple->width, dimple->height, x, y);
425 if (sPtr->flags.vertical)
426 x += DIVIDER_THICKNESS;
427 else
428 y += DIVIDER_THICKNESS;
431 #if 0
432 printf("---- (paintSplitView - 2) ----\n");
433 dumpSubviews(sPtr);
434 #endif
437 static void drawDragingRectangle(WMSplitView * sPtr, int pos)
439 int x, y, w, h;
441 if (sPtr->flags.vertical) {
442 x = pos;
443 y = 0;
444 w = DIVIDER_THICKNESS;
445 h = sPtr->view->size.height;
446 } else {
447 x = 0;
448 y = pos;
449 w = sPtr->view->size.width;
450 h = DIVIDER_THICKNESS;
453 XFillRectangle(sPtr->view->screen->display, sPtr->view->window, sPtr->view->screen->ixorGC, x, y, w, h);
456 static void getMinMaxDividerCoord(WMSplitView * sPtr, int divider, int *minC, int *maxC)
458 int relMinC, relMaxC;
459 int totSize = _GetSizeAt(divider) + _GetSizeAt(divider + 1);
461 relMinC = _GetMinSizeAt(divider);
462 if (_GetMaxSizeAt(divider + 1) != MAX_SUBVIEW_SIZE && relMinC < totSize - _GetMaxSizeAt(divider + 1))
463 relMinC = totSize - _GetMaxSizeAt(divider + 1);
465 relMaxC = totSize - _GetMinSizeAt(divider + 1);
466 if (_GetMaxSizeAt(divider) != MAX_SUBVIEW_SIZE && relMaxC > _GetMaxSizeAt(divider))
467 relMaxC = _GetMaxSizeAt(divider);
469 *minC = _GetPosAt(divider) + relMinC;
470 *maxC = _GetPosAt(divider) + relMaxC;
473 static void dragDivider(WMSplitView * sPtr, int clickX, int clickY)
475 int divider, pos, ofs, done, dragging;
476 int i, count;
477 XEvent ev;
478 WMScreen *scr;
479 int minCoord, maxCoord, coord;
481 if (sPtr->constrainProc) {
482 updateConstraints(sPtr);
483 checkSizes(sPtr);
484 distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr));
485 checkPositions(sPtr);
486 updateSubviewsGeom(sPtr);
489 scr = sPtr->view->screen;
490 divider = ofs = pos = done = 0;
491 coord = (sPtr->flags.vertical) ? clickX : clickY;
492 count = _GetSubviewsCount();
493 if (count < 2)
494 return;
496 for (i = 0; i < count - 1; i++) {
497 pos += _GetSizeAt(i) + DIVIDER_THICKNESS;
498 if (coord < pos) {
499 ofs = coord - pos + DIVIDER_THICKNESS;
500 done = 1;
501 break;
503 divider++;
506 if (!done)
507 return;
509 getMinMaxDividerCoord(sPtr, divider, &minCoord, &maxCoord);
511 done = 0;
512 dragging = 0;
513 while (!done) {
514 WMMaskEvent(scr->display, ButtonMotionMask | ButtonReleaseMask | ExposureMask, &ev);
516 coord = (sPtr->flags.vertical) ? ev.xmotion.x : ev.xmotion.y;
518 switch (ev.type) {
519 case ButtonRelease:
520 done = 1;
521 if (dragging)
522 drawDragingRectangle(sPtr, pos);
523 break;
525 case MotionNotify:
526 if (dragging)
527 drawDragingRectangle(sPtr, pos);
528 if (coord - ofs < minCoord)
529 pos = minCoord;
530 else if (coord - ofs > maxCoord)
531 pos = maxCoord;
532 else
533 pos = coord - ofs;
534 drawDragingRectangle(sPtr, pos);
535 dragging = 1;
536 break;
538 default:
539 WMHandleEvent(&ev);
540 break;
544 if (dragging) {
545 W_SplitViewSubview *p1, *p2;
546 int totSize;
548 p1 = _GetPSubviewStructAt(divider);
549 p2 = _GetPSubviewStructAt(divider + 1);
551 totSize = p1->size + DIVIDER_THICKNESS + p2->size;
553 p1->size = pos - p1->pos;
554 p2->size = totSize - p1->size - DIVIDER_THICKNESS;
555 p2->pos = p1->pos + p1->size + DIVIDER_THICKNESS;
557 resizeView(sPtr, p1->view, p1->size);
558 moveView(sPtr, p2->view, p2->pos);
559 resizeView(sPtr, p2->view, p2->size);
560 sPtr->flags.subviewsWereManuallyMoved = 1;
564 static void handleEvents(XEvent * event, void *data)
566 WMSplitView *sPtr = (WMSplitView *) data;
568 CHECK_CLASS(data, WC_SplitView);
570 switch (event->type) {
571 case Expose:
572 if (event->xexpose.count != 0)
573 break;
574 paintSplitView(sPtr);
575 break;
577 case DestroyNotify:
578 destroySplitView(sPtr);
579 break;
583 static void handleActionEvents(XEvent * event, void *data)
586 CHECK_CLASS(data, WC_SplitView);
588 switch (event->type) {
589 case ButtonPress:
590 if (event->xbutton.button == Button1)
591 dragDivider(data, event->xbutton.x, event->xbutton.y);
592 break;
596 static void destroySplitView(WMSplitView * sPtr)
598 WMFreeArray(sPtr->subviews);
600 WMRemoveNotificationObserver(sPtr);
602 wfree(sPtr);
605 WMSplitView *WMCreateSplitView(WMWidget * parent)
607 WMSplitView *sPtr;
609 sPtr = wmalloc(sizeof(WMSplitView));
610 sPtr->widgetClass = WC_SplitView;
612 sPtr->view = W_CreateView(W_VIEW(parent));
613 if (!sPtr->view) {
614 wfree(sPtr);
615 return NULL;
617 sPtr->view->self = sPtr;
619 WMSetViewNotifySizeChanges(sPtr->view, True);
621 WMCreateEventHandler(sPtr->view, ExposureMask | StructureNotifyMask
622 | ClientMessageMask, handleEvents, sPtr);
624 WMCreateEventHandler(sPtr->view, ButtonPressMask | ButtonReleaseMask
625 | EnterWindowMask | LeaveWindowMask, handleActionEvents, sPtr);
627 WMAddNotificationObserver(handleViewResized, sPtr, WMViewSizeDidChangeNotification, sPtr->view);
629 sPtr->subviews = WMCreateArrayWithDestructor(8, wfree);
631 return sPtr;
634 void WMAdjustSplitViewSubviews(WMSplitView * sPtr)
636 CHECK_CLASS(sPtr, WC_SplitView);
638 checkSizes(sPtr);
640 adjustSplitViewSubviews(sPtr);
642 assert(checkSizes(sPtr) == 0);
645 void WMAddSplitViewSubview(WMSplitView * sPtr, WMView * subview)
647 int wasMapped, count;
648 W_SplitViewSubview *p;
650 CHECK_CLASS(sPtr, WC_SplitView);
652 p = (W_SplitViewSubview *) wmalloc(sizeof(W_SplitViewSubview));
653 if (!p)
654 return;
656 wasMapped = subview->flags.mapped;
657 if (wasMapped)
658 W_UnmapView(subview);
660 count = _GetSubviewsCount();
661 p->view = subview;
662 getConstraints(sPtr, count, &(p->minSize), &(p->maxSize));
663 if (sPtr->flags.vertical)
664 p->size = subview->size.width;
665 else
666 p->size = subview->size.height;
668 WMAddToArray(sPtr->subviews, p);
669 reparentView(sPtr, subview, 0);
672 We should have something like that...
674 WMSetViewNotifySizeChanges(subview, True);
675 WMAddNotificationObserver(handleSubviewResized, sPtr,
676 WMViewSizeDidChangeNotification,
677 subview);
678 WMSetViewNotifyMoveChanges(subview, True);
679 WMAddNotificationObserver(handleSubviewResized, sPtr,
680 WMViewMoveDidChangeNotification,
681 subview);
683 if (wasMapped) {
684 W_MapView(subview);
686 sPtr->flags.adjustOnPaint = 1;
687 paintSplitView(sPtr);
688 } else {
689 handleViewResized(sPtr, NULL);
693 WMView *WMGetSplitViewSubviewAt(WMSplitView * sPtr, int index)
695 CHECK_CLASS(sPtr, WC_SplitView);
697 if (index >= 0 && index < _GetSubviewsCount())
698 return (_GetSubviewAt(index));
699 else
700 return (NULL);
703 void WMRemoveSplitViewSubview(WMSplitView * sPtr, WMView * view)
705 W_SplitViewSubview *p;
706 int i, count;
708 CHECK_CLASS(sPtr, WC_SplitView);
710 /* TODO: rewrite this. This code with macros is getting more complex than it worths */
711 count = _GetSubviewsCount();
712 for (i = 0; i < count; i++) {
713 p = _GetPSubviewStructAt(i);
714 if (p->view == view) {
715 WMDeleteFromArray(sPtr->subviews, i);
716 sPtr->flags.adjustOnPaint = 1;
717 paintSplitView(sPtr);
718 break;
723 void WMRemoveSplitViewSubviewAt(WMSplitView * sPtr, int index)
725 CHECK_CLASS(sPtr, WC_SplitView);
727 /* TODO: same about rewrite */
728 if (index >= 0 && index < _GetSubviewsCount()) {
729 WMDeleteFromArray(sPtr->subviews, index);
730 sPtr->flags.adjustOnPaint = 1;
731 paintSplitView(sPtr);
735 void WMSetSplitViewConstrainProc(WMSplitView * sPtr, WMSplitViewConstrainProc * proc)
737 CHECK_CLASS(sPtr, WC_SplitView);
739 sPtr->constrainProc = proc;
742 int WMGetSplitViewSubviewsCount(WMSplitView * sPtr)
744 CHECK_CLASS(sPtr, WC_SplitView);
746 return (_GetSubviewsCount());
749 Bool WMGetSplitViewVertical(WMSplitView * sPtr)
751 CHECK_CLASS(sPtr, WC_SplitView);
753 return (sPtr->flags.vertical == 1);
756 void WMSetSplitViewVertical(WMSplitView * sPtr, Bool flag)
758 int vertical;
760 CHECK_CLASS(sPtr, WC_SplitView);
762 vertical = ((flag == 0) ? 0 : 1);
763 if (sPtr->flags.vertical == vertical)
764 return;
766 sPtr->flags.vertical = vertical;
768 /* if (sPtr->view->flags.mapped && sPtr->view->flags.realized) */
769 handleViewResized(sPtr, NULL);
770 /* else
771 sPtr->flags.adjustOnPaint = 1;
775 int WMGetSplitViewDividerThickness(WMSplitView *sPtr)
777 CHECK_CLASS(sPtr, WC_SplitView);
779 (void) sPtr;
781 return (DIVIDER_THICKNESS);
784 #if 0
785 void WMSetSplitViewResizeSubviewsProc(WMSplitView * sPtr, WMSplitViewResizeSubviewsProc * proc)
787 CHECK_CLASS(sPtr, WC_SplitView);
789 sPtr->resizeSubviewsProc = proc;
791 #endif