X-Git-Url: https://repo.or.cz/w/wmaker-crm.git/blobdiff_plain/59fc927dc9f183802621138534fa6eaafe5593ba..688a56e8ab67b56550e2874d9d7423f0d435bfd9:/WINGs/wsplitview.c diff --git a/WINGs/wsplitview.c b/WINGs/wsplitview.c dissimilarity index 91% index de9d0f50..e8cb383d 100644 --- a/WINGs/wsplitview.c +++ b/WINGs/wsplitview.c @@ -1,872 +1,793 @@ - - - - -#include "WINGsP.h" - -/* - char *WMSplitViewDidResizeSubviewsNotification - = "WMSplitViewDidResizeSubviewsNotification"; - char *WMSplitViewWillResizeSubviewsNotification - = "WMSplitViewWillResizeSubviewsNotification"; - */ - -typedef struct W_SplitViewSubview { - WMView *view; - int minSize; - int maxSize; - int size; - int pos; -} W_SplitViewSubview; - - -typedef struct W_SplitView { - W_Class widgetClass; - W_View *view; - - WMArray *subviews; - - WMSplitViewConstrainProc *constrainProc; - - struct { - unsigned int vertical:1; - unsigned int adjustOnPaint:1; - unsigned int subviewsWereManuallyMoved:1; - } flags; - - /* WMSplitViewResizeSubviewsProc *resizeSubviewsProc; */ - -} W_SplitView; - - -#define DIVIDER_THICKNESS 8 -#define MIN_SUBVIEW_SIZE 4 -#define MAX_SUBVIEW_SIZE -1 - - -/* TODO: rewrite --Dan */ -#define _GetSubviewsCount() WMGetArrayItemCount(sPtr->subviews) - -#define _AddPSubviewStruct(P) \ - (WMAddToArray(sPtr->subviews,((void*)P))) - -#define _GetPSubviewStructAt(i) \ - ((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i))) - -#define _GetSubviewAt(i) \ - (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->view) - -#define _GetMinSizeAt(i) \ - (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->minSize) - -#define _GetMaxSizeAt(i) \ - (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->maxSize) - -#define _GetSizeAt(i) \ - (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->size) - -#define _GetPosAt(i) \ - (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->pos) - -#define _GetSplitViewSize() \ - ((sPtr->flags.vertical) ? sPtr->view->size.width : sPtr->view->size.height) - -static void destroySplitView(WMSplitView *sPtr); -static void paintSplitView(WMSplitView *sPtr); - -static void handleEvents(XEvent *event, void *data); -static void handleActionEvents(XEvent *event, void *data); - - -static void -getConstraints(WMSplitView *sPtr, int index, int *minSize, int *maxSize) -{ - *minSize = MIN_SUBVIEW_SIZE; - *maxSize = MAX_SUBVIEW_SIZE; - - if (sPtr->constrainProc) - (*sPtr->constrainProc)(sPtr, index, minSize, maxSize); - - if (*minSize < MIN_SUBVIEW_SIZE) - *minSize = MIN_SUBVIEW_SIZE; - - if (*maxSize < MIN_SUBVIEW_SIZE) - *maxSize = MAX_SUBVIEW_SIZE; - else if (*maxSize < *minSize) - *maxSize = *minSize; -} - - -static void -updateConstraints(WMSplitView *sPtr) -{ - W_SplitViewSubview *p; - int i, count; - - count = _GetSubviewsCount(); - for (i = 0; i < count; i++) { - p = _GetPSubviewStructAt(i); - getConstraints(sPtr, i, &(p->minSize), &(p->maxSize)); - } -} - - -static void -resizeView(WMSplitView *sPtr, WMView *view, int size) -{ - int width, height; - - if (sPtr->flags.vertical) { - width = size; - height = sPtr->view->size.height; - } else { - width = sPtr->view->size.width; - height = size; - } - - if (view->self) - WMResizeWidget(view->self, width, height); - else - W_ResizeView(view, width, height); -} - - -static void -reparentView(WMSplitView *sPtr, WMView *view, int pos) -{ - int x, y; - - if (sPtr->flags.vertical) { - x = pos; - y = 0; - } else { - x = 0; - y = pos; - } - - W_ReparentView(view, sPtr->view, x, y); -} - - -static void -moveView(WMSplitView *sPtr, WMView *view, int pos) -{ - int x, y; - - if (sPtr->flags.vertical) { - x = pos; - y = 0; - } else { - x = 0; - y = pos; - } - - if (view->self) - WMMoveWidget(view->self, x, y); - else - W_MoveView(view, x, y); -} - - -static int -checkSizes(WMSplitView *sPtr) -{ - int i, count, offset; - W_SplitViewSubview *p; - - count = _GetSubviewsCount(); - offset = 0; - for (i = 0; i < count; i++) { - p = _GetPSubviewStructAt(i); - if (p->size < p->minSize) { - offset += p->minSize - p->size; - p->size = p->minSize; - } else if (p->maxSize != MAX_SUBVIEW_SIZE && p->size > p->maxSize) { - offset += p->maxSize - p->size; - p->size = p->maxSize; - } - } - - return (offset); -} - - -static void -checkPositions(WMSplitView *sPtr) -{ - int i, count, pos; - W_SplitViewSubview *p; - - count = _GetSubviewsCount(); - pos = 0; - for (i = 0; i < count; i++) { - p = _GetPSubviewStructAt(i); - p->pos = pos; - pos += p->size + DIVIDER_THICKNESS; - } -} - - -static void -updateSubviewsGeom(WMSplitView *sPtr) -{ - int i, count; - W_SplitViewSubview *p; - - count = _GetSubviewsCount(); - for (i = 0; i < count; i++) { - p = _GetPSubviewStructAt(i); - resizeView(sPtr, p->view, p->size); - moveView(sPtr, p->view, p->pos); - } -} - - -static int -getTotalSize(WMSplitView *sPtr) -{ - int i, count, totSize; - - count = _GetSubviewsCount(); - if (!count) - return (0); - - totSize = 0; - for (i = 0; i < count; i++) - totSize += _GetSizeAt(i) + DIVIDER_THICKNESS; - - return (totSize - DIVIDER_THICKNESS); -} - - -static Bool -distributeOffsetEqually(WMSplitView *sPtr, int offset) -{ - W_SplitViewSubview *p; - int i, count, sizeChanged, forced; - - if ((count = _GetSubviewsCount()) < 1) - return (True); - - forced = False; - while (offset != 0) { - sizeChanged = 0; - for (i = 0; i < count && offset != 0; i++) { - p = _GetPSubviewStructAt(i); - if (offset < 0) { - if (p->size > p->minSize) { - offset++; - p->size--; - sizeChanged = 1; - } - } else if (p->maxSize == MAX_SUBVIEW_SIZE || p->size < p->maxSize) { - offset--; - p->size++; - sizeChanged = 1; - } - } - if (offset != 0 && !sizeChanged) { - p = _GetPSubviewStructAt(count-1); - if (offset > 0) { - p->size += offset; - p->maxSize = MAX_SUBVIEW_SIZE; - } - offset = 0; - forced = True; - } - } - - return (forced); -} - - -static Bool -distributeOffsetFormEnd(WMSplitView *sPtr, int offset) -{ - W_SplitViewSubview *p; - int i, count, sizeTmp; - - if ((count = _GetSubviewsCount()) < 1) - return (True); - - for (i = count-1; i >= 0 && offset != 0; i--) { - p = _GetPSubviewStructAt(i); - sizeTmp = p->size; - if (offset > 0) { - if (p->maxSize == MAX_SUBVIEW_SIZE || p->size + offset < p->maxSize) - p->size += offset; - else - p->size = p->maxSize; - } else { - if (p->size + offset >= p->minSize) - p->size += offset; - else - p->size = p->minSize; - } - offset -= p->size - sizeTmp; - } - - return (offset == 0); -} - - -static void -adjustSplitViewSubviews(WMSplitView *sPtr) -{ - W_SplitViewSubview *p; - int i, count, adjSize, adjPad; - - CHECK_CLASS(sPtr, WC_SplitView); - -#if 0 - printf("---- (adjustSplitViewSubviews - 1) ----\n"); - dumpSubviews(sPtr); -#endif - - if ((count = _GetSubviewsCount()) < 1) - return; - - adjSize = (_GetSplitViewSize() - ((count-1) * DIVIDER_THICKNESS)) / count; - adjPad = (_GetSplitViewSize() - ((count-1) * DIVIDER_THICKNESS)) % count; - for (i = 0; i < count; i++) { - p = _GetPSubviewStructAt(i); - p->size = adjSize; - } - - distributeOffsetEqually(sPtr, adjPad - checkSizes(sPtr)); - - checkPositions(sPtr); - updateSubviewsGeom(sPtr); - - sPtr->flags.subviewsWereManuallyMoved = 0; - -#if 0 - printf("---- (adjustSplitViewSubviews - 2) ----\n"); - dumpSubviews(sPtr); -#endif -} - -#if 0 -static void -handleSubviewResized(void *self, WMNotification *notif) -{ - WMSplitView *sPtr = (WMSplitView*)self; - - CHECK_CLASS(sPtr, WC_SplitView); - - if (WMGetNotificationName(notif) == WMViewSizeDidChangeNotification) { - W_SplitViewSubview *p; - int i, count, done; - WMView *view = WMGetNotificationObject(notif); - - count = _GetSubviewsCount(); - done = 0; - for (i = 0; i < count; i++) { - p = _GetPSubviewStructAt(i); - if (p->view == view) { - done = 1; - break; - } - } - - if (done) { - /* TODO !!! */ - resizeView(sPtr, p->view, p->size); - moveView(sPtr, p->view, p->pos); - } - } -} -#endif - -static void -handleViewResized(void *self, WMNotification *notification) -{ - WMSplitView *sPtr = (WMSplitView*)self; - -#if 0 - printf("---- (handleViewResized - 1) ----\n"); - dumpSubviews(sPtr); -#endif - - updateConstraints(sPtr); - checkSizes(sPtr); - - if (sPtr->constrainProc || sPtr->flags.subviewsWereManuallyMoved) { - distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr)); - checkPositions(sPtr); - updateSubviewsGeom(sPtr); - } else - adjustSplitViewSubviews(sPtr); - - assert(checkSizes(sPtr) == 0); - -#if 0 - printf("---- (handleViewResized - 2) ----\n"); - dumpSubviews(sPtr); -#endif -} - - -static void -paintSplitView(WMSplitView *sPtr) -{ - W_SplitViewSubview *p; - W_Screen *scr = sPtr->view->screen; - int x, y, i, count; - WMPixmap *dimple = scr->scrollerDimple; - -#if 0 - printf("---- (paintSplitView - 1) ----\n"); - dumpSubviews(sPtr); -#endif - - if (!sPtr->view->flags.mapped || !sPtr->view->flags.realized) - return; - - XClearWindow(scr->display, sPtr->view->window); - - count = _GetSubviewsCount(); - if (count == 0) - return; - - if (sPtr->flags.adjustOnPaint) { - handleViewResized(sPtr, NULL); - sPtr->flags.adjustOnPaint = 0; - } - - XSetClipMask(scr->display, scr->clipGC, dimple->mask); - - if (sPtr->flags.vertical) { - x = ((DIVIDER_THICKNESS - dimple->width) / 2); - y = (sPtr->view->size.height - dimple->height)/2; - } else { - x = (sPtr->view->size.width - dimple->width)/2; - y = ((DIVIDER_THICKNESS - dimple->height) / 2); - } - - for (i = 0; i < count-1; i++) { - p = _GetPSubviewStructAt(i); - - if (sPtr->flags.vertical) - x += p->size; - else - y += p->size; - - XSetClipOrigin(scr->display, scr->clipGC, x, y); - XCopyArea(scr->display, dimple->pixmap, sPtr->view->window, - scr->clipGC, 0, 0, dimple->width, dimple->height, x, y); - - if (sPtr->flags.vertical) - x += DIVIDER_THICKNESS; - else - y += DIVIDER_THICKNESS; - } - -#if 0 - printf("---- (paintSplitView - 2) ----\n"); - dumpSubviews(sPtr); -#endif -} - - -static void -drawDragingRectangle(WMSplitView *sPtr, int pos) -{ - int x, y, w, h; - - if (sPtr->flags.vertical) { - x = pos; - y = 0; - w = DIVIDER_THICKNESS; - h = sPtr->view->size.height; - } else { - x = 0; - y = pos; - w = sPtr->view->size.width; - h = DIVIDER_THICKNESS; - } - - XFillRectangle(sPtr->view->screen->display, sPtr->view->window, - sPtr->view->screen->ixorGC, x, y, w, h); -} - - -static void -getMinMaxDividerCoord(WMSplitView *sPtr, int divider, int *minC, int *maxC) -{ - int relMinC, relMaxC; - int totSize = _GetSizeAt(divider) + _GetSizeAt(divider+1); - - relMinC = _GetMinSizeAt(divider); - if (_GetMaxSizeAt(divider+1) != MAX_SUBVIEW_SIZE - && relMinC < totSize - _GetMaxSizeAt(divider+1)) - relMinC = totSize - _GetMaxSizeAt(divider+1); - - relMaxC = totSize - _GetMinSizeAt(divider+1); - if (_GetMaxSizeAt(divider) != MAX_SUBVIEW_SIZE - && relMaxC > _GetMaxSizeAt(divider)) - relMaxC = _GetMaxSizeAt(divider); - - *minC = _GetPosAt(divider) + relMinC; - *maxC = _GetPosAt(divider) + relMaxC; -} - - -static void -dragDivider(WMSplitView *sPtr, int clickX, int clickY) -{ - int divider, pos, ofs, done, dragging; - int i, count; - XEvent ev; - WMScreen *scr; - int minCoord, maxCoord, coord; - - if (sPtr->constrainProc) { - updateConstraints(sPtr); - checkSizes(sPtr); - distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr)); - checkPositions(sPtr); - updateSubviewsGeom(sPtr); - } - - scr = sPtr->view->screen; - divider = ofs = pos = done = 0; - coord = (sPtr->flags.vertical) ? clickX : clickY; - count = _GetSubviewsCount(); - if (count < 2) - return; - - for (i = 0; i < count-1; i++) { - pos += _GetSizeAt(i) + DIVIDER_THICKNESS; - if (coord < pos) { - ofs = coord - pos + DIVIDER_THICKNESS; - done = 1; - break; - } - divider++; - } - - if (!done) - return; - - getMinMaxDividerCoord(sPtr, divider, &minCoord, &maxCoord); - - done = 0; - dragging = 0; - while (!done) { - WMMaskEvent(scr->display, ButtonMotionMask|ButtonReleaseMask - |ExposureMask, &ev); - - coord = (sPtr->flags.vertical) ? ev.xmotion.x : ev.xmotion.y; - - switch (ev.type) { - case ButtonRelease: - done = 1; - if (dragging) - drawDragingRectangle(sPtr, pos); - break; - - case MotionNotify: - if (dragging) - drawDragingRectangle(sPtr, pos); - if (coord - ofs < minCoord) - pos = minCoord; - else if (coord - ofs > maxCoord) - pos = maxCoord; - else - pos = coord - ofs; - drawDragingRectangle(sPtr, pos); - dragging = 1; - break; - - default: - WMHandleEvent(&ev); - break; - } - } - - if (dragging) { - W_SplitViewSubview *p1, *p2; - int totSize; - - p1 = _GetPSubviewStructAt(divider); - p2 = _GetPSubviewStructAt(divider+1); - - totSize = p1->size + DIVIDER_THICKNESS + p2->size; - - p1->size = pos - p1->pos; - p2->size = totSize - p1->size - DIVIDER_THICKNESS; - p2->pos = p1->pos + p1->size + DIVIDER_THICKNESS; - - resizeView(sPtr, p1->view, p1->size); - moveView(sPtr, p2->view, p2->pos); - resizeView(sPtr, p2->view, p2->size); - sPtr->flags.subviewsWereManuallyMoved = 1; - } -} - - -static void -handleEvents(XEvent *event, void *data) -{ - WMSplitView *sPtr = (WMSplitView*)data; - - CHECK_CLASS(data, WC_SplitView); - - - switch (event->type) { - case Expose: - if (event->xexpose.count!=0) - break; - paintSplitView(sPtr); - break; - - case DestroyNotify: - destroySplitView(sPtr); - break; - } -} - - -static void -handleActionEvents(XEvent *event, void *data) -{ - - CHECK_CLASS(data, WC_SplitView); - - switch (event->type) { - case ButtonPress: - if (event->xbutton.button == Button1) - dragDivider(data, event->xbutton.x, event->xbutton.y); - break; - } -} - - - -static void -destroySplitView(WMSplitView *sPtr) -{ - WMFreeArray(sPtr->subviews); - - WMRemoveNotificationObserver(sPtr); - - wfree(sPtr); -} - -WMSplitView* -WMCreateSplitView(WMWidget *parent) -{ - WMSplitView *sPtr; - - sPtr = wmalloc(sizeof(WMSplitView)); - memset(sPtr, 0, sizeof(WMSplitView)); - - sPtr->widgetClass = WC_SplitView; - - sPtr->view = W_CreateView(W_VIEW(parent)); - if (!sPtr->view) { - wfree(sPtr); - return NULL; - } - sPtr->view->self = sPtr; - - WMSetViewNotifySizeChanges(sPtr->view, True); - - WMCreateEventHandler(sPtr->view, ExposureMask|StructureNotifyMask - |ClientMessageMask, handleEvents, sPtr); - - - WMCreateEventHandler(sPtr->view, ButtonPressMask|ButtonReleaseMask - |EnterWindowMask|LeaveWindowMask, - handleActionEvents, sPtr); - - - WMAddNotificationObserver(handleViewResized, sPtr, - WMViewSizeDidChangeNotification, sPtr->view); - - sPtr->subviews = WMCreateArrayWithDestructor(8, wfree); - - return sPtr; -} - - -void -WMAdjustSplitViewSubviews(WMSplitView *sPtr) -{ - CHECK_CLASS(sPtr, WC_SplitView); - - checkSizes(sPtr); - - adjustSplitViewSubviews(sPtr); - - assert(checkSizes(sPtr) == 0); -} - - -void -WMAddSplitViewSubview(WMSplitView *sPtr, WMView *subview) -{ - int wasMapped, count; - W_SplitViewSubview *p; - - CHECK_CLASS(sPtr, WC_SplitView); - - if (!(p = (W_SplitViewSubview*)wmalloc(sizeof(W_SplitViewSubview)))) - return; - - wasMapped = subview->flags.mapped; - if (wasMapped) - W_UnmapView(subview); - - count = _GetSubviewsCount(); - p->view = subview; - getConstraints(sPtr, count, &(p->minSize), &(p->maxSize)); - if (sPtr->flags.vertical) - p->size = subview->size.width; - else - p->size = subview->size.height; - - WMAddToArray(sPtr->subviews, p); - reparentView(sPtr, subview, 0); - - /* - We should have something like that... - - WMSetViewNotifySizeChanges(subview, True); - WMAddNotificationObserver(handleSubviewResized, sPtr, - WMViewSizeDidChangeNotification, - subview); - WMSetViewNotifyMoveChanges(subview, True); - WMAddNotificationObserver(handleSubviewResized, sPtr, - WMViewMoveDidChangeNotification, - subview); - */ - if (wasMapped) { - W_MapView(subview); - - sPtr->flags.adjustOnPaint = 1; - paintSplitView(sPtr); - } else { - handleViewResized(sPtr, NULL); - } -} - - -WMView* -WMGetSplitViewSubviewAt(WMSplitView *sPtr, int index) -{ - CHECK_CLASS(sPtr, WC_SplitView); - - if (index >= 0 && index < _GetSubviewsCount()) - return (_GetSubviewAt(index)); - else - return (NULL); -} - - -void -WMRemoveSplitViewSubview(WMSplitView *sPtr, WMView *view) -{ - W_SplitViewSubview *p; - int i, count; - - CHECK_CLASS(sPtr, WC_SplitView); - - /* TODO: rewrite this. This code with macros is getting more complex than it worths */ - count = _GetSubviewsCount(); - for (i = 0; i < count; i++) { - p = _GetPSubviewStructAt(i); - if (p->view == view) { - WMDeleteFromArray(sPtr->subviews, i); - sPtr->flags.adjustOnPaint = 1; - paintSplitView(sPtr); - break; - } - } -} - - -void -WMRemoveSplitViewSubviewAt(WMSplitView *sPtr, int index) -{ - W_SplitViewSubview *p; - - CHECK_CLASS(sPtr, WC_SplitView); - - /* TODO: same about rewrite */ - if (index >= 0 && index < _GetSubviewsCount()) { - p = _GetPSubviewStructAt(index); - WMDeleteFromArray(sPtr->subviews, index); - sPtr->flags.adjustOnPaint = 1; - paintSplitView(sPtr); - } -} - - -void -WMSetSplitViewConstrainProc(WMSplitView *sPtr, WMSplitViewConstrainProc *proc) -{ - CHECK_CLASS(sPtr, WC_SplitView); - - sPtr->constrainProc = proc; -} - - -int -WMGetSplitViewSubviewsCount(WMSplitView *sPtr) -{ - CHECK_CLASS(sPtr, WC_SplitView); - - return (_GetSubviewsCount()); -} - - -Bool -WMGetSplitViewVertical(WMSplitView *sPtr) -{ - CHECK_CLASS(sPtr, WC_SplitView); - - return (sPtr->flags.vertical == 1); -} - -void -WMSetSplitViewVertical(WMSplitView *sPtr, Bool flag) -{ - int vertical; - - CHECK_CLASS(sPtr, WC_SplitView); - - vertical = ((flag==0) ? 0 : 1); - if (sPtr->flags.vertical == vertical) - return; - - sPtr->flags.vertical = vertical; - - /* if (sPtr->view->flags.mapped && sPtr->view->flags.realized)*/ - handleViewResized(sPtr, NULL); - /* else - sPtr->flags.adjustOnPaint = 1; - */ -} - - -int -WMGetSplitViewDividerThickness(WMSplitView *sPtr) -{ - CHECK_CLASS(sPtr, WC_SplitView); - - return (DIVIDER_THICKNESS); -} - -#if 0 -void -WMSetSplitViewResizeSubviewsProc(WMSplitView *sPtr, - WMSplitViewResizeSubviewsProc *proc) -{ - CHECK_CLASS(sPtr, WC_SplitView); - - sPtr->resizeSubviewsProc = proc; -} -#endif - + +#include "WINGsP.h" + +/* + char *WMSplitViewDidResizeSubviewsNotification + = "WMSplitViewDidResizeSubviewsNotification"; + char *WMSplitViewWillResizeSubviewsNotification + = "WMSplitViewWillResizeSubviewsNotification"; + */ + +typedef struct W_SplitViewSubview { + WMView *view; + int minSize; + int maxSize; + int size; + int pos; +} W_SplitViewSubview; + +typedef struct W_SplitView { + W_Class widgetClass; + W_View *view; + + WMArray *subviews; + + WMSplitViewConstrainProc *constrainProc; + + struct { + unsigned int vertical:1; + unsigned int adjustOnPaint:1; + unsigned int subviewsWereManuallyMoved:1; + } flags; + + /* WMSplitViewResizeSubviewsProc *resizeSubviewsProc; */ + +} W_SplitView; + +#define DIVIDER_THICKNESS 8 +#define MIN_SUBVIEW_SIZE 4 +#define MAX_SUBVIEW_SIZE -1 + +/* TODO: rewrite --Dan */ +#define _GetSubviewsCount() WMGetArrayItemCount(sPtr->subviews) + +#define _AddPSubviewStruct(P) \ + (WMAddToArray(sPtr->subviews,((void*)P))) + +#define _GetPSubviewStructAt(i) \ + ((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i))) + +#define _GetSubviewAt(i) \ + (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->view) + +#define _GetMinSizeAt(i) \ + (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->minSize) + +#define _GetMaxSizeAt(i) \ + (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->maxSize) + +#define _GetSizeAt(i) \ + (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->size) + +#define _GetPosAt(i) \ + (((W_SplitViewSubview*)WMGetFromArray(sPtr->subviews,(i)))->pos) + +#define _GetSplitViewSize() \ + ((sPtr->flags.vertical) ? sPtr->view->size.width : sPtr->view->size.height) + +static void destroySplitView(WMSplitView * sPtr); +static void paintSplitView(WMSplitView * sPtr); + +static void handleEvents(XEvent * event, void *data); +static void handleActionEvents(XEvent * event, void *data); + +static void getConstraints(WMSplitView * sPtr, int index, int *minSize, int *maxSize) +{ + *minSize = MIN_SUBVIEW_SIZE; + *maxSize = MAX_SUBVIEW_SIZE; + + if (sPtr->constrainProc) + (*sPtr->constrainProc) (sPtr, index, minSize, maxSize); + + if (*minSize < MIN_SUBVIEW_SIZE) + *minSize = MIN_SUBVIEW_SIZE; + + if (*maxSize < MIN_SUBVIEW_SIZE) + *maxSize = MAX_SUBVIEW_SIZE; + else if (*maxSize < *minSize) + *maxSize = *minSize; +} + +static void updateConstraints(WMSplitView * sPtr) +{ + W_SplitViewSubview *p; + int i, count; + + count = _GetSubviewsCount(); + for (i = 0; i < count; i++) { + p = _GetPSubviewStructAt(i); + getConstraints(sPtr, i, &(p->minSize), &(p->maxSize)); + } +} + +static void resizeView(WMSplitView * sPtr, WMView * view, int size) +{ + int width, height; + + if (sPtr->flags.vertical) { + width = size; + height = sPtr->view->size.height; + } else { + width = sPtr->view->size.width; + height = size; + } + + if (view->self) + WMResizeWidget(view->self, width, height); + else + W_ResizeView(view, width, height); +} + +static void reparentView(WMSplitView * sPtr, WMView * view, int pos) +{ + int x, y; + + if (sPtr->flags.vertical) { + x = pos; + y = 0; + } else { + x = 0; + y = pos; + } + + W_ReparentView(view, sPtr->view, x, y); +} + +static void moveView(WMSplitView * sPtr, WMView * view, int pos) +{ + int x, y; + + if (sPtr->flags.vertical) { + x = pos; + y = 0; + } else { + x = 0; + y = pos; + } + + if (view->self) + WMMoveWidget(view->self, x, y); + else + W_MoveView(view, x, y); +} + +static int checkSizes(WMSplitView * sPtr) +{ + int i, count, offset; + W_SplitViewSubview *p; + + count = _GetSubviewsCount(); + offset = 0; + for (i = 0; i < count; i++) { + p = _GetPSubviewStructAt(i); + if (p->size < p->minSize) { + offset += p->minSize - p->size; + p->size = p->minSize; + } else if (p->maxSize != MAX_SUBVIEW_SIZE && p->size > p->maxSize) { + offset += p->maxSize - p->size; + p->size = p->maxSize; + } + } + + return (offset); +} + +static void checkPositions(WMSplitView * sPtr) +{ + int i, count, pos; + W_SplitViewSubview *p; + + count = _GetSubviewsCount(); + pos = 0; + for (i = 0; i < count; i++) { + p = _GetPSubviewStructAt(i); + p->pos = pos; + pos += p->size + DIVIDER_THICKNESS; + } +} + +static void updateSubviewsGeom(WMSplitView * sPtr) +{ + int i, count; + W_SplitViewSubview *p; + + count = _GetSubviewsCount(); + for (i = 0; i < count; i++) { + p = _GetPSubviewStructAt(i); + resizeView(sPtr, p->view, p->size); + moveView(sPtr, p->view, p->pos); + } +} + +static int getTotalSize(WMSplitView * sPtr) +{ + int i, count, totSize; + + count = _GetSubviewsCount(); + if (!count) + return (0); + + totSize = 0; + for (i = 0; i < count; i++) + totSize += _GetSizeAt(i) + DIVIDER_THICKNESS; + + return (totSize - DIVIDER_THICKNESS); +} + +static Bool distributeOffsetEqually(WMSplitView * sPtr, int offset) +{ + W_SplitViewSubview *p; + int i, count, sizeChanged, forced; + + if ((count = _GetSubviewsCount()) < 1) + return (True); + + forced = False; + while (offset != 0) { + sizeChanged = 0; + for (i = 0; i < count && offset != 0; i++) { + p = _GetPSubviewStructAt(i); + if (offset < 0) { + if (p->size > p->minSize) { + offset++; + p->size--; + sizeChanged = 1; + } + } else if (p->maxSize == MAX_SUBVIEW_SIZE || p->size < p->maxSize) { + offset--; + p->size++; + sizeChanged = 1; + } + } + if (offset != 0 && !sizeChanged) { + p = _GetPSubviewStructAt(count - 1); + if (offset > 0) { + p->size += offset; + p->maxSize = MAX_SUBVIEW_SIZE; + } + offset = 0; + forced = True; + } + } + + return (forced); +} + +static Bool distributeOffsetFormEnd(WMSplitView * sPtr, int offset) +{ + W_SplitViewSubview *p; + int i, count, sizeTmp; + + if ((count = _GetSubviewsCount()) < 1) + return (True); + + for (i = count - 1; i >= 0 && offset != 0; i--) { + p = _GetPSubviewStructAt(i); + sizeTmp = p->size; + if (offset > 0) { + if (p->maxSize == MAX_SUBVIEW_SIZE || p->size + offset < p->maxSize) + p->size += offset; + else + p->size = p->maxSize; + } else { + if (p->size + offset >= p->minSize) + p->size += offset; + else + p->size = p->minSize; + } + offset -= p->size - sizeTmp; + } + + return (offset == 0); +} + +static void adjustSplitViewSubviews(WMSplitView * sPtr) +{ + W_SplitViewSubview *p; + int i, count, adjSize, adjPad; + + CHECK_CLASS(sPtr, WC_SplitView); + +#if 0 + printf("---- (adjustSplitViewSubviews - 1) ----\n"); + dumpSubviews(sPtr); +#endif + + if ((count = _GetSubviewsCount()) < 1) + return; + + adjSize = (_GetSplitViewSize() - ((count - 1) * DIVIDER_THICKNESS)) / count; + adjPad = (_GetSplitViewSize() - ((count - 1) * DIVIDER_THICKNESS)) % count; + for (i = 0; i < count; i++) { + p = _GetPSubviewStructAt(i); + p->size = adjSize; + } + + distributeOffsetEqually(sPtr, adjPad - checkSizes(sPtr)); + + checkPositions(sPtr); + updateSubviewsGeom(sPtr); + + sPtr->flags.subviewsWereManuallyMoved = 0; + +#if 0 + printf("---- (adjustSplitViewSubviews - 2) ----\n"); + dumpSubviews(sPtr); +#endif +} + +#if 0 +static void handleSubviewResized(void *self, WMNotification * notif) +{ + WMSplitView *sPtr = (WMSplitView *) self; + + CHECK_CLASS(sPtr, WC_SplitView); + + if (WMGetNotificationName(notif) == WMViewSizeDidChangeNotification) { + W_SplitViewSubview *p; + int i, count, done; + WMView *view = WMGetNotificationObject(notif); + + count = _GetSubviewsCount(); + done = 0; + for (i = 0; i < count; i++) { + p = _GetPSubviewStructAt(i); + if (p->view == view) { + done = 1; + break; + } + } + + if (done) { + /* TODO !!! */ + resizeView(sPtr, p->view, p->size); + moveView(sPtr, p->view, p->pos); + } + } +} +#endif + +static void handleViewResized(void *self, WMNotification * notification) +{ + WMSplitView *sPtr = (WMSplitView *) self; + +#if 0 + printf("---- (handleViewResized - 1) ----\n"); + dumpSubviews(sPtr); +#endif + + updateConstraints(sPtr); + checkSizes(sPtr); + + if (sPtr->constrainProc || sPtr->flags.subviewsWereManuallyMoved) { + distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr)); + checkPositions(sPtr); + updateSubviewsGeom(sPtr); + } else + adjustSplitViewSubviews(sPtr); + + assert(checkSizes(sPtr) == 0); + +#if 0 + printf("---- (handleViewResized - 2) ----\n"); + dumpSubviews(sPtr); +#endif +} + +static void paintSplitView(WMSplitView * sPtr) +{ + W_SplitViewSubview *p; + W_Screen *scr = sPtr->view->screen; + int x, y, i, count; + WMPixmap *dimple = scr->scrollerDimple; + +#if 0 + printf("---- (paintSplitView - 1) ----\n"); + dumpSubviews(sPtr); +#endif + + if (!sPtr->view->flags.mapped || !sPtr->view->flags.realized) + return; + + XClearWindow(scr->display, sPtr->view->window); + + count = _GetSubviewsCount(); + if (count == 0) + return; + + if (sPtr->flags.adjustOnPaint) { + handleViewResized(sPtr, NULL); + sPtr->flags.adjustOnPaint = 0; + } + + XSetClipMask(scr->display, scr->clipGC, dimple->mask); + + if (sPtr->flags.vertical) { + x = ((DIVIDER_THICKNESS - dimple->width) / 2); + y = (sPtr->view->size.height - dimple->height) / 2; + } else { + x = (sPtr->view->size.width - dimple->width) / 2; + y = ((DIVIDER_THICKNESS - dimple->height) / 2); + } + + for (i = 0; i < count - 1; i++) { + p = _GetPSubviewStructAt(i); + + if (sPtr->flags.vertical) + x += p->size; + else + y += p->size; + + XSetClipOrigin(scr->display, scr->clipGC, x, y); + XCopyArea(scr->display, dimple->pixmap, sPtr->view->window, + scr->clipGC, 0, 0, dimple->width, dimple->height, x, y); + + if (sPtr->flags.vertical) + x += DIVIDER_THICKNESS; + else + y += DIVIDER_THICKNESS; + } + +#if 0 + printf("---- (paintSplitView - 2) ----\n"); + dumpSubviews(sPtr); +#endif +} + +static void drawDragingRectangle(WMSplitView * sPtr, int pos) +{ + int x, y, w, h; + + if (sPtr->flags.vertical) { + x = pos; + y = 0; + w = DIVIDER_THICKNESS; + h = sPtr->view->size.height; + } else { + x = 0; + y = pos; + w = sPtr->view->size.width; + h = DIVIDER_THICKNESS; + } + + XFillRectangle(sPtr->view->screen->display, sPtr->view->window, sPtr->view->screen->ixorGC, x, y, w, h); +} + +static void getMinMaxDividerCoord(WMSplitView * sPtr, int divider, int *minC, int *maxC) +{ + int relMinC, relMaxC; + int totSize = _GetSizeAt(divider) + _GetSizeAt(divider + 1); + + relMinC = _GetMinSizeAt(divider); + if (_GetMaxSizeAt(divider + 1) != MAX_SUBVIEW_SIZE && relMinC < totSize - _GetMaxSizeAt(divider + 1)) + relMinC = totSize - _GetMaxSizeAt(divider + 1); + + relMaxC = totSize - _GetMinSizeAt(divider + 1); + if (_GetMaxSizeAt(divider) != MAX_SUBVIEW_SIZE && relMaxC > _GetMaxSizeAt(divider)) + relMaxC = _GetMaxSizeAt(divider); + + *minC = _GetPosAt(divider) + relMinC; + *maxC = _GetPosAt(divider) + relMaxC; +} + +static void dragDivider(WMSplitView * sPtr, int clickX, int clickY) +{ + int divider, pos, ofs, done, dragging; + int i, count; + XEvent ev; + WMScreen *scr; + int minCoord, maxCoord, coord; + + if (sPtr->constrainProc) { + updateConstraints(sPtr); + checkSizes(sPtr); + distributeOffsetFormEnd(sPtr, _GetSplitViewSize() - getTotalSize(sPtr)); + checkPositions(sPtr); + updateSubviewsGeom(sPtr); + } + + scr = sPtr->view->screen; + divider = ofs = pos = done = 0; + coord = (sPtr->flags.vertical) ? clickX : clickY; + count = _GetSubviewsCount(); + if (count < 2) + return; + + for (i = 0; i < count - 1; i++) { + pos += _GetSizeAt(i) + DIVIDER_THICKNESS; + if (coord < pos) { + ofs = coord - pos + DIVIDER_THICKNESS; + done = 1; + break; + } + divider++; + } + + if (!done) + return; + + getMinMaxDividerCoord(sPtr, divider, &minCoord, &maxCoord); + + done = 0; + dragging = 0; + while (!done) { + WMMaskEvent(scr->display, ButtonMotionMask | ButtonReleaseMask | ExposureMask, &ev); + + coord = (sPtr->flags.vertical) ? ev.xmotion.x : ev.xmotion.y; + + switch (ev.type) { + case ButtonRelease: + done = 1; + if (dragging) + drawDragingRectangle(sPtr, pos); + break; + + case MotionNotify: + if (dragging) + drawDragingRectangle(sPtr, pos); + if (coord - ofs < minCoord) + pos = minCoord; + else if (coord - ofs > maxCoord) + pos = maxCoord; + else + pos = coord - ofs; + drawDragingRectangle(sPtr, pos); + dragging = 1; + break; + + default: + WMHandleEvent(&ev); + break; + } + } + + if (dragging) { + W_SplitViewSubview *p1, *p2; + int totSize; + + p1 = _GetPSubviewStructAt(divider); + p2 = _GetPSubviewStructAt(divider + 1); + + totSize = p1->size + DIVIDER_THICKNESS + p2->size; + + p1->size = pos - p1->pos; + p2->size = totSize - p1->size - DIVIDER_THICKNESS; + p2->pos = p1->pos + p1->size + DIVIDER_THICKNESS; + + resizeView(sPtr, p1->view, p1->size); + moveView(sPtr, p2->view, p2->pos); + resizeView(sPtr, p2->view, p2->size); + sPtr->flags.subviewsWereManuallyMoved = 1; + } +} + +static void handleEvents(XEvent * event, void *data) +{ + WMSplitView *sPtr = (WMSplitView *) data; + + CHECK_CLASS(data, WC_SplitView); + + switch (event->type) { + case Expose: + if (event->xexpose.count != 0) + break; + paintSplitView(sPtr); + break; + + case DestroyNotify: + destroySplitView(sPtr); + break; + } +} + +static void handleActionEvents(XEvent * event, void *data) +{ + + CHECK_CLASS(data, WC_SplitView); + + switch (event->type) { + case ButtonPress: + if (event->xbutton.button == Button1) + dragDivider(data, event->xbutton.x, event->xbutton.y); + break; + } +} + +static void destroySplitView(WMSplitView * sPtr) +{ + WMFreeArray(sPtr->subviews); + + WMRemoveNotificationObserver(sPtr); + + wfree(sPtr); +} + +WMSplitView *WMCreateSplitView(WMWidget * parent) +{ + WMSplitView *sPtr; + + sPtr = wmalloc(sizeof(WMSplitView)); + memset(sPtr, 0, sizeof(WMSplitView)); + + sPtr->widgetClass = WC_SplitView; + + sPtr->view = W_CreateView(W_VIEW(parent)); + if (!sPtr->view) { + wfree(sPtr); + return NULL; + } + sPtr->view->self = sPtr; + + WMSetViewNotifySizeChanges(sPtr->view, True); + + WMCreateEventHandler(sPtr->view, ExposureMask | StructureNotifyMask + | ClientMessageMask, handleEvents, sPtr); + + WMCreateEventHandler(sPtr->view, ButtonPressMask | ButtonReleaseMask + | EnterWindowMask | LeaveWindowMask, handleActionEvents, sPtr); + + WMAddNotificationObserver(handleViewResized, sPtr, WMViewSizeDidChangeNotification, sPtr->view); + + sPtr->subviews = WMCreateArrayWithDestructor(8, wfree); + + return sPtr; +} + +void WMAdjustSplitViewSubviews(WMSplitView * sPtr) +{ + CHECK_CLASS(sPtr, WC_SplitView); + + checkSizes(sPtr); + + adjustSplitViewSubviews(sPtr); + + assert(checkSizes(sPtr) == 0); +} + +void WMAddSplitViewSubview(WMSplitView * sPtr, WMView * subview) +{ + int wasMapped, count; + W_SplitViewSubview *p; + + CHECK_CLASS(sPtr, WC_SplitView); + + if (!(p = (W_SplitViewSubview *) wmalloc(sizeof(W_SplitViewSubview)))) + return; + + wasMapped = subview->flags.mapped; + if (wasMapped) + W_UnmapView(subview); + + count = _GetSubviewsCount(); + p->view = subview; + getConstraints(sPtr, count, &(p->minSize), &(p->maxSize)); + if (sPtr->flags.vertical) + p->size = subview->size.width; + else + p->size = subview->size.height; + + WMAddToArray(sPtr->subviews, p); + reparentView(sPtr, subview, 0); + + /* + We should have something like that... + + WMSetViewNotifySizeChanges(subview, True); + WMAddNotificationObserver(handleSubviewResized, sPtr, + WMViewSizeDidChangeNotification, + subview); + WMSetViewNotifyMoveChanges(subview, True); + WMAddNotificationObserver(handleSubviewResized, sPtr, + WMViewMoveDidChangeNotification, + subview); + */ + if (wasMapped) { + W_MapView(subview); + + sPtr->flags.adjustOnPaint = 1; + paintSplitView(sPtr); + } else { + handleViewResized(sPtr, NULL); + } +} + +WMView *WMGetSplitViewSubviewAt(WMSplitView * sPtr, int index) +{ + CHECK_CLASS(sPtr, WC_SplitView); + + if (index >= 0 && index < _GetSubviewsCount()) + return (_GetSubviewAt(index)); + else + return (NULL); +} + +void WMRemoveSplitViewSubview(WMSplitView * sPtr, WMView * view) +{ + W_SplitViewSubview *p; + int i, count; + + CHECK_CLASS(sPtr, WC_SplitView); + + /* TODO: rewrite this. This code with macros is getting more complex than it worths */ + count = _GetSubviewsCount(); + for (i = 0; i < count; i++) { + p = _GetPSubviewStructAt(i); + if (p->view == view) { + WMDeleteFromArray(sPtr->subviews, i); + sPtr->flags.adjustOnPaint = 1; + paintSplitView(sPtr); + break; + } + } +} + +void WMRemoveSplitViewSubviewAt(WMSplitView * sPtr, int index) +{ + W_SplitViewSubview *p; + + CHECK_CLASS(sPtr, WC_SplitView); + + /* TODO: same about rewrite */ + if (index >= 0 && index < _GetSubviewsCount()) { + p = _GetPSubviewStructAt(index); + WMDeleteFromArray(sPtr->subviews, index); + sPtr->flags.adjustOnPaint = 1; + paintSplitView(sPtr); + } +} + +void WMSetSplitViewConstrainProc(WMSplitView * sPtr, WMSplitViewConstrainProc * proc) +{ + CHECK_CLASS(sPtr, WC_SplitView); + + sPtr->constrainProc = proc; +} + +int WMGetSplitViewSubviewsCount(WMSplitView * sPtr) +{ + CHECK_CLASS(sPtr, WC_SplitView); + + return (_GetSubviewsCount()); +} + +Bool WMGetSplitViewVertical(WMSplitView * sPtr) +{ + CHECK_CLASS(sPtr, WC_SplitView); + + return (sPtr->flags.vertical == 1); +} + +void WMSetSplitViewVertical(WMSplitView * sPtr, Bool flag) +{ + int vertical; + + CHECK_CLASS(sPtr, WC_SplitView); + + vertical = ((flag == 0) ? 0 : 1); + if (sPtr->flags.vertical == vertical) + return; + + sPtr->flags.vertical = vertical; + + /* if (sPtr->view->flags.mapped && sPtr->view->flags.realized) */ + handleViewResized(sPtr, NULL); + /* else + sPtr->flags.adjustOnPaint = 1; + */ +} + +int WMGetSplitViewDividerThickness(WMSplitView * sPtr) +{ + CHECK_CLASS(sPtr, WC_SplitView); + + return (DIVIDER_THICKNESS); +} + +#if 0 +void WMSetSplitViewResizeSubviewsProc(WMSplitView * sPtr, WMSplitViewResizeSubviewsProc * proc) +{ + CHECK_CLASS(sPtr, WC_SplitView); + + sPtr->resizeSubviewsProc = proc; +} +#endif