WPrefs: set default to 1st color in gradient texture
[wmaker-crm.git] / WINGs / wsplitview.c
blob60d86015e8b96c2be3b6fc7617f63b5d5af9c962
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 _AddPSubviewStruct(P) \
45 (WMAddToArray(sPtr->subviews,((void*)P)))
47 #define _GetPSubviewStructAt(i) \
48 ((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))
50 #define _GetSubviewAt(i) \
51 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->view)
53 #define _GetMinSizeAt(i) \
54 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->minSize)
56 #define _GetMaxSizeAt(i) \
57 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->maxSize)
59 #define _GetSizeAt(i) \
60 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->size)
62 #define _GetPosAt(i) \
63 (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->pos)
65 #define _GetSplitViewSize() \
66 ((sPtr->flags.vertical) ? sPtr->view->size.width : sPtr->view->size.height)
68 static void destroySplitView(WMSplitView * sPtr);
69 static void paintSplitView(WMSplitView * sPtr);
71 static void handleEvents(XEvent * event, void *data);
72 static void handleActionEvents(XEvent * event, void *data);
74 static void getConstraints(WMSplitView * sPtr, int index, int *minSize, int *maxSize)
76 *minSize = MIN_SUBVIEW_SIZE;
77 *maxSize = MAX_SUBVIEW_SIZE;
79 if (sPtr->constrainProc)
80 (*sPtr->constrainProc) (sPtr, index, minSize, maxSize);
82 if (*minSize < MIN_SUBVIEW_SIZE)
83 *minSize = MIN_SUBVIEW_SIZE;
85 if (*maxSize < MIN_SUBVIEW_SIZE)
86 *maxSize = MAX_SUBVIEW_SIZE;
87 else if (*maxSize < *minSize)
88 *maxSize = *minSize;
91 static void updateConstraints(WMSplitView * sPtr)
93 W_SplitViewSubview *p;
94 int i, count;
96 count = _GetSubviewsCount();
97 for (i = 0; i < count; i++) {
98 p = _GetPSubviewStructAt(i);
99 getConstraints(sPtr, i, &(p->minSize), &(p->maxSize));
103 static void resizeView(WMSplitView * sPtr, WMView * view, int size)
105 int width, height;
107 if (sPtr->flags.vertical) {
108 width = size;
109 height = sPtr->view->size.height;
110 } else {
111 width = sPtr->view->size.width;
112 height = size;
115 if (view->self)
116 WMResizeWidget(view->self, width, height);
117 else
118 W_ResizeView(view, width, height);
121 static void reparentView(WMSplitView * sPtr, WMView * view, int pos)
123 int x, y;
125 if (sPtr->flags.vertical) {
126 x = pos;
127 y = 0;
128 } else {
129 x = 0;
130 y = pos;
133 W_ReparentView(view, sPtr->view, x, y);
136 static void moveView(WMSplitView * sPtr, WMView * view, int pos)
138 int x, y;
140 if (sPtr->flags.vertical) {
141 x = pos;
142 y = 0;
143 } else {
144 x = 0;
145 y = pos;
148 if (view->self)
149 WMMoveWidget(view->self, x, y);
150 else
151 W_MoveView(view, x, y);
154 static int checkSizes(WMSplitView * sPtr)
156 int i, count, offset;
157 W_SplitViewSubview *p;
159 count = _GetSubviewsCount();
160 offset = 0;
161 for (i = 0; i < count; i++) {
162 p = _GetPSubviewStructAt(i);
163 if (p->size < p->minSize) {
164 offset += p->minSize - p->size;
165 p->size = p->minSize;
166 } else if (p->maxSize != MAX_SUBVIEW_SIZE && p->size > p->maxSize) {
167 offset += p->maxSize - p->size;
168 p->size = p->maxSize;
172 return (offset);
175 static void checkPositions(WMSplitView * sPtr)
177 int i, count, pos;
178 W_SplitViewSubview *p;
180 count = _GetSubviewsCount();
181 pos = 0;
182 for (i = 0; i < count; i++) {
183 p = _GetPSubviewStructAt(i);
184 p->pos = pos;
185 pos += p->size + DIVIDER_THICKNESS;
189 static void updateSubviewsGeom(WMSplitView * sPtr)
191 int i, count;
192 W_SplitViewSubview *p;
194 count = _GetSubviewsCount();
195 for (i = 0; i < count; i++) {
196 p = _GetPSubviewStructAt(i);
197 resizeView(sPtr, p->view, p->size);
198 moveView(sPtr, p->view, p->pos);
202 static int getTotalSize(WMSplitView * sPtr)
204 int i, count, totSize;
206 count = _GetSubviewsCount();
207 if (!count)
208 return (0);
210 totSize = 0;
211 for (i = 0; i < count; i++)
212 totSize += _GetSizeAt(i) + DIVIDER_THICKNESS;
214 return (totSize - DIVIDER_THICKNESS);
217 static Bool distributeOffsetEqually(WMSplitView * sPtr, int offset)
219 W_SplitViewSubview *p;
220 int i, count, sizeChanged, forced;
222 if ((count = _GetSubviewsCount()) < 1)
223 return (True);
225 forced = False;
226 while (offset != 0) {
227 sizeChanged = 0;
228 for (i = 0; i < count && offset != 0; i++) {
229 p = _GetPSubviewStructAt(i);
230 if (offset < 0) {
231 if (p->size > p->minSize) {
232 offset++;
233 p->size--;
234 sizeChanged = 1;
236 } else if (p->maxSize == MAX_SUBVIEW_SIZE || p->size < p->maxSize) {
237 offset--;
238 p->size++;
239 sizeChanged = 1;
242 if (offset != 0 && !sizeChanged) {
243 p = _GetPSubviewStructAt(count - 1);
244 if (offset > 0) {
245 p->size += offset;
246 p->maxSize = MAX_SUBVIEW_SIZE;
248 offset = 0;
249 forced = True;
253 return (forced);
256 static Bool distributeOffsetFormEnd(WMSplitView * sPtr, int offset)
258 W_SplitViewSubview *p;
259 int i, count, sizeTmp;
261 if ((count = _GetSubviewsCount()) < 1)
262 return (True);
264 for (i = count - 1; i >= 0 && offset != 0; i--) {
265 p = _GetPSubviewStructAt(i);
266 sizeTmp = p->size;
267 if (offset > 0) {
268 if (p->maxSize == MAX_SUBVIEW_SIZE || p->size + offset < p->maxSize)
269 p->size += offset;
270 else
271 p->size = p->maxSize;
272 } else {
273 if (p->size + offset >= p->minSize)
274 p->size += offset;
275 else
276 p->size = p->minSize;
278 offset -= p->size - sizeTmp;
281 return (offset == 0);
284 static void adjustSplitViewSubviews(WMSplitView * sPtr)
286 W_SplitViewSubview *p;
287 int i, count, adjSize, adjPad;
289 CHECK_CLASS(sPtr, WC_SplitView);
291 #if 0
292 printf("---- (adjustSplitViewSubviews - 1) ----\n");
293 dumpSubviews(sPtr);
294 #endif
296 if ((count = _GetSubviewsCount()) < 1)
297 return;
299 adjSize = (_GetSplitViewSize() - ((count - 1) * DIVIDER_THICKNESS)) / count;
300 adjPad = (_GetSplitViewSize() - ((count - 1) * DIVIDER_THICKNESS)) % count;
301 for (i = 0; i < count; i++) {
302 p = _GetPSubviewStructAt(i);
303 p->size = adjSize;
306 distributeOffsetEqually(sPtr, adjPad - checkSizes(sPtr));
308 checkPositions(sPtr);
309 updateSubviewsGeom(sPtr);
311 sPtr->flags.subviewsWereManuallyMoved = 0;
313 #if 0
314 printf("---- (adjustSplitViewSubviews - 2) ----\n");
315 dumpSubviews(sPtr);
316 #endif
319 #if 0
320 static void handleSubviewResized(void *self, WMNotification * notif)
322 WMSplitView *sPtr = (WMSplitView *) self;
324 CHECK_CLASS(sPtr, WC_SplitView);
326 if (WMGetNotificationName(notif) == WMViewSizeDidChangeNotification) {
327 W_SplitViewSubview *p;
328 int i, count, done;
329 WMView *view = WMGetNotificationObject(notif);
331 count = _GetSubviewsCount();
332 done = 0;
333 for (i = 0; i < count; i++) {
334 p = _GetPSubviewStructAt(i);
335 if (p->view == view) {
336 done = 1;
337 break;
341 if (done) {
342 /* TODO !!! */
343 resizeView(sPtr, p->view, p->size);
344 moveView(sPtr, p->view, p->pos);
348 #endif
350 static void handleViewResized(void *self, WMNotification * notification)
352 WMSplitView *sPtr = (WMSplitView *) self;
354 /* Parameter not used, but tell the compiler that it is ok */
355 (void) notification;
357 #if 0
358 printf("---- (handleViewResized - 1) ----\n");
359 dumpSubviews(sPtr);
360 #endif
362 updateConstraints(sPtr);
363 checkSizes(sPtr);
365 if (sPtr->constrainProc || sPtr->flags.subviewsWereManuallyMoved) {
366 distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr));
367 checkPositions(sPtr);
368 updateSubviewsGeom(sPtr);
369 } else
370 adjustSplitViewSubviews(sPtr);
372 assert(checkSizes(sPtr) == 0);
374 #if 0
375 printf("---- (handleViewResized - 2) ----\n");
376 dumpSubviews(sPtr);
377 #endif
380 static void paintSplitView(WMSplitView * sPtr)
382 W_SplitViewSubview *p;
383 W_Screen *scr = sPtr->view->screen;
384 int x, y, i, count;
385 WMPixmap *dimple = scr->scrollerDimple;
387 #if 0
388 printf("---- (paintSplitView - 1) ----\n");
389 dumpSubviews(sPtr);
390 #endif
392 if (!sPtr->view->flags.mapped || !sPtr->view->flags.realized)
393 return;
395 XClearWindow(scr->display, sPtr->view->window);
397 count = _GetSubviewsCount();
398 if (count == 0)
399 return;
401 if (sPtr->flags.adjustOnPaint) {
402 handleViewResized(sPtr, NULL);
403 sPtr->flags.adjustOnPaint = 0;
406 XSetClipMask(scr->display, scr->clipGC, dimple->mask);
408 if (sPtr->flags.vertical) {
409 x = ((DIVIDER_THICKNESS - dimple->width) / 2);
410 y = (sPtr->view->size.height - dimple->height) / 2;
411 } else {
412 x = (sPtr->view->size.width - dimple->width) / 2;
413 y = ((DIVIDER_THICKNESS - dimple->height) / 2);
416 for (i = 0; i < count - 1; i++) {
417 p = _GetPSubviewStructAt(i);
419 if (sPtr->flags.vertical)
420 x += p->size;
421 else
422 y += p->size;
424 XSetClipOrigin(scr->display, scr->clipGC, x, y);
425 XCopyArea(scr->display, dimple->pixmap, sPtr->view->window,
426 scr->clipGC, 0, 0, dimple->width, dimple->height, x, y);
428 if (sPtr->flags.vertical)
429 x += DIVIDER_THICKNESS;
430 else
431 y += DIVIDER_THICKNESS;
434 #if 0
435 printf("---- (paintSplitView - 2) ----\n");
436 dumpSubviews(sPtr);
437 #endif
440 static void drawDragingRectangle(WMSplitView * sPtr, int pos)
442 int x, y, w, h;
444 if (sPtr->flags.vertical) {
445 x = pos;
446 y = 0;
447 w = DIVIDER_THICKNESS;
448 h = sPtr->view->size.height;
449 } else {
450 x = 0;
451 y = pos;
452 w = sPtr->view->size.width;
453 h = DIVIDER_THICKNESS;
456 XFillRectangle(sPtr->view->screen->display, sPtr->view->window, sPtr->view->screen->ixorGC, x, y, w, h);
459 static void getMinMaxDividerCoord(WMSplitView * sPtr, int divider, int *minC, int *maxC)
461 int relMinC, relMaxC;
462 int totSize = _GetSizeAt(divider) + _GetSizeAt(divider + 1);
464 relMinC = _GetMinSizeAt(divider);
465 if (_GetMaxSizeAt(divider + 1) != MAX_SUBVIEW_SIZE && relMinC < totSize - _GetMaxSizeAt(divider + 1))
466 relMinC = totSize - _GetMaxSizeAt(divider + 1);
468 relMaxC = totSize - _GetMinSizeAt(divider + 1);
469 if (_GetMaxSizeAt(divider) != MAX_SUBVIEW_SIZE && relMaxC > _GetMaxSizeAt(divider))
470 relMaxC = _GetMaxSizeAt(divider);
472 *minC = _GetPosAt(divider) + relMinC;
473 *maxC = _GetPosAt(divider) + relMaxC;
476 static void dragDivider(WMSplitView * sPtr, int clickX, int clickY)
478 int divider, pos, ofs, done, dragging;
479 int i, count;
480 XEvent ev;
481 WMScreen *scr;
482 int minCoord, maxCoord, coord;
484 if (sPtr->constrainProc) {
485 updateConstraints(sPtr);
486 checkSizes(sPtr);
487 distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr));
488 checkPositions(sPtr);
489 updateSubviewsGeom(sPtr);
492 scr = sPtr->view->screen;
493 divider = ofs = pos = done = 0;
494 coord = (sPtr->flags.vertical) ? clickX : clickY;
495 count = _GetSubviewsCount();
496 if (count < 2)
497 return;
499 for (i = 0; i < count - 1; i++) {
500 pos += _GetSizeAt(i) + DIVIDER_THICKNESS;
501 if (coord < pos) {
502 ofs = coord - pos + DIVIDER_THICKNESS;
503 done = 1;
504 break;
506 divider++;
509 if (!done)
510 return;
512 getMinMaxDividerCoord(sPtr, divider, &minCoord, &maxCoord);
514 done = 0;
515 dragging = 0;
516 while (!done) {
517 WMMaskEvent(scr->display, ButtonMotionMask | ButtonReleaseMask | ExposureMask, &ev);
519 coord = (sPtr->flags.vertical) ? ev.xmotion.x : ev.xmotion.y;
521 switch (ev.type) {
522 case ButtonRelease:
523 done = 1;
524 if (dragging)
525 drawDragingRectangle(sPtr, pos);
526 break;
528 case MotionNotify:
529 if (dragging)
530 drawDragingRectangle(sPtr, pos);
531 if (coord - ofs < minCoord)
532 pos = minCoord;
533 else if (coord - ofs > maxCoord)
534 pos = maxCoord;
535 else
536 pos = coord - ofs;
537 drawDragingRectangle(sPtr, pos);
538 dragging = 1;
539 break;
541 default:
542 WMHandleEvent(&ev);
543 break;
547 if (dragging) {
548 W_SplitViewSubview *p1, *p2;
549 int totSize;
551 p1 = _GetPSubviewStructAt(divider);
552 p2 = _GetPSubviewStructAt(divider + 1);
554 totSize = p1->size + DIVIDER_THICKNESS + p2->size;
556 p1->size = pos - p1->pos;
557 p2->size = totSize - p1->size - DIVIDER_THICKNESS;
558 p2->pos = p1->pos + p1->size + DIVIDER_THICKNESS;
560 resizeView(sPtr, p1->view, p1->size);
561 moveView(sPtr, p2->view, p2->pos);
562 resizeView(sPtr, p2->view, p2->size);
563 sPtr->flags.subviewsWereManuallyMoved = 1;
567 static void handleEvents(XEvent * event, void *data)
569 WMSplitView *sPtr = (WMSplitView *) data;
571 CHECK_CLASS(data, WC_SplitView);
573 switch (event->type) {
574 case Expose:
575 if (event->xexpose.count != 0)
576 break;
577 paintSplitView(sPtr);
578 break;
580 case DestroyNotify:
581 destroySplitView(sPtr);
582 break;
586 static void handleActionEvents(XEvent * event, void *data)
589 CHECK_CLASS(data, WC_SplitView);
591 switch (event->type) {
592 case ButtonPress:
593 if (event->xbutton.button == Button1)
594 dragDivider(data, event->xbutton.x, event->xbutton.y);
595 break;
599 static void destroySplitView(WMSplitView * sPtr)
601 WMFreeArray(sPtr->subviews);
603 WMRemoveNotificationObserver(sPtr);
605 wfree(sPtr);
608 WMSplitView *WMCreateSplitView(WMWidget * parent)
610 WMSplitView *sPtr;
612 sPtr = wmalloc(sizeof(WMSplitView));
613 sPtr->widgetClass = WC_SplitView;
615 sPtr->view = W_CreateView(W_VIEW(parent));
616 if (!sPtr->view) {
617 wfree(sPtr);
618 return NULL;
620 sPtr->view->self = sPtr;
622 WMSetViewNotifySizeChanges(sPtr->view, True);
624 WMCreateEventHandler(sPtr->view, ExposureMask | StructureNotifyMask
625 | ClientMessageMask, handleEvents, sPtr);
627 WMCreateEventHandler(sPtr->view, ButtonPressMask | ButtonReleaseMask
628 | EnterWindowMask | LeaveWindowMask, handleActionEvents, sPtr);
630 WMAddNotificationObserver(handleViewResized, sPtr, WMViewSizeDidChangeNotification, sPtr->view);
632 sPtr->subviews = WMCreateArrayWithDestructor(8, wfree);
634 return sPtr;
637 void WMAdjustSplitViewSubviews(WMSplitView * sPtr)
639 CHECK_CLASS(sPtr, WC_SplitView);
641 checkSizes(sPtr);
643 adjustSplitViewSubviews(sPtr);
645 assert(checkSizes(sPtr) == 0);
648 void WMAddSplitViewSubview(WMSplitView * sPtr, WMView * subview)
650 int wasMapped, count;
651 W_SplitViewSubview *p;
653 CHECK_CLASS(sPtr, WC_SplitView);
655 p = (W_SplitViewSubview *) wmalloc(sizeof(W_SplitViewSubview));
656 if (!p)
657 return;
659 wasMapped = subview->flags.mapped;
660 if (wasMapped)
661 W_UnmapView(subview);
663 count = _GetSubviewsCount();
664 p->view = subview;
665 getConstraints(sPtr, count, &(p->minSize), &(p->maxSize));
666 if (sPtr->flags.vertical)
667 p->size = subview->size.width;
668 else
669 p->size = subview->size.height;
671 WMAddToArray(sPtr->subviews, p);
672 reparentView(sPtr, subview, 0);
675 We should have something like that...
677 WMSetViewNotifySizeChanges(subview, True);
678 WMAddNotificationObserver(handleSubviewResized, sPtr,
679 WMViewSizeDidChangeNotification,
680 subview);
681 WMSetViewNotifyMoveChanges(subview, True);
682 WMAddNotificationObserver(handleSubviewResized, sPtr,
683 WMViewMoveDidChangeNotification,
684 subview);
686 if (wasMapped) {
687 W_MapView(subview);
689 sPtr->flags.adjustOnPaint = 1;
690 paintSplitView(sPtr);
691 } else {
692 handleViewResized(sPtr, NULL);
696 WMView *WMGetSplitViewSubviewAt(WMSplitView * sPtr, int index)
698 CHECK_CLASS(sPtr, WC_SplitView);
700 if (index >= 0 && index < _GetSubviewsCount())
701 return (_GetSubviewAt(index));
702 else
703 return (NULL);
706 void WMRemoveSplitViewSubview(WMSplitView * sPtr, WMView * view)
708 W_SplitViewSubview *p;
709 int i, count;
711 CHECK_CLASS(sPtr, WC_SplitView);
713 /* TODO: rewrite this. This code with macros is getting more complex than it worths */
714 count = _GetSubviewsCount();
715 for (i = 0; i < count; i++) {
716 p = _GetPSubviewStructAt(i);
717 if (p->view == view) {
718 WMDeleteFromArray(sPtr->subviews, i);
719 sPtr->flags.adjustOnPaint = 1;
720 paintSplitView(sPtr);
721 break;
726 void WMRemoveSplitViewSubviewAt(WMSplitView * sPtr, int index)
728 CHECK_CLASS(sPtr, WC_SplitView);
730 /* TODO: same about rewrite */
731 if (index >= 0 && index < _GetSubviewsCount()) {
732 WMDeleteFromArray(sPtr->subviews, index);
733 sPtr->flags.adjustOnPaint = 1;
734 paintSplitView(sPtr);
738 void WMSetSplitViewConstrainProc(WMSplitView * sPtr, WMSplitViewConstrainProc * proc)
740 CHECK_CLASS(sPtr, WC_SplitView);
742 sPtr->constrainProc = proc;
745 int WMGetSplitViewSubviewsCount(WMSplitView * sPtr)
747 CHECK_CLASS(sPtr, WC_SplitView);
749 return (_GetSubviewsCount());
752 Bool WMGetSplitViewVertical(WMSplitView * sPtr)
754 CHECK_CLASS(sPtr, WC_SplitView);
756 return (sPtr->flags.vertical == 1);
759 void WMSetSplitViewVertical(WMSplitView * sPtr, Bool flag)
761 int vertical;
763 CHECK_CLASS(sPtr, WC_SplitView);
765 vertical = ((flag == 0) ? 0 : 1);
766 if (sPtr->flags.vertical == vertical)
767 return;
769 sPtr->flags.vertical = vertical;
771 /* if (sPtr->view->flags.mapped && sPtr->view->flags.realized) */
772 handleViewResized(sPtr, NULL);
773 /* else
774 sPtr->flags.adjustOnPaint = 1;
778 int WMGetSplitViewDividerThickness(WMSplitView *sPtr)
780 CHECK_CLASS(sPtr, WC_SplitView);
782 (void) sPtr;
784 return (DIVIDER_THICKNESS);
787 #if 0
788 void WMSetSplitViewResizeSubviewsProc(WMSplitView * sPtr, WMSplitViewResizeSubviewsProc * proc)
790 CHECK_CLASS(sPtr, WC_SplitView);
792 sPtr->resizeSubviewsProc = proc;
794 #endif