replaced free() with wfree() everywhere
[wmaker-crm.git] / WINGs / wsplitview.c
blob6e34f64a8c93523cf5e7c96d34a8343bcb56213a
5 #include "WINGsP.h"
10 typedef struct W_SplitView {
11 W_Class widgetClass;
12 W_View *view;
14 /* WMSplitViewResizeSubviewsProc *resizeSubviewsProc;
16 WMSplitViewConstrainProc *constrainProc;
18 struct {
19 unsigned int splitViewIsFull:1; /* already added 2 subviews */
20 } flags;
21 } SplitView;
24 #define DIVIDER_THICKNESS 8
28 static void destroySplitView(SplitView *sPtr);
29 static void paintSplitView(SplitView *sPtr);
32 static void handleEvents(XEvent *event, void *data);
33 static void handleActionEvents(XEvent *event, void *data);
36 static int
37 subviewCount(SplitView *sPtr)
39 int count = 0;
40 WMView *view;
42 for (view=sPtr->view->childrenList; view != NULL; view=view->nextSister)
43 count++;
45 return count;
49 static void
50 handleViewResized(void *self, WMNotification *notification)
52 SplitView *sPtr = (SplitView*)self;
53 int oldHeight;
54 WMView *view = sPtr->view;
55 int newWidth = view->size.width;
56 WMView *upper, *lower;
58 if (!view->childrenList)
59 return;
61 if (view->childrenList->nextSister==NULL) {
62 if (view->self)
63 WMResizeWidget(view->childrenList->self, newWidth, view->size.height);
64 else
65 W_ResizeView(view->childrenList, newWidth, view->size.height);
66 } else {
67 upper = view->childrenList;
68 lower = upper->nextSister;
69 oldHeight = upper->size.height+DIVIDER_THICKNESS+lower->size.height;
71 if (oldHeight > view->size.height
72 && upper->size.height+DIVIDER_THICKNESS >= view->size.height) {
73 WMAdjustSplitViewSubviews(sPtr);
74 } else {
75 if (upper->self) {
76 WMResizeWidget(upper->self, newWidth, upper->size.height);
77 } else {
78 W_ResizeView(upper, newWidth, upper->size.height);
80 if (lower->self) {
81 WMResizeWidget(lower->self, newWidth,
82 view->size.height-lower->pos.y);
83 } else {
84 W_ResizeView(lower, newWidth, view->size.height-lower->pos.y);
92 WMSplitView*
93 WMCreateSplitView(WMWidget *parent)
95 SplitView *sPtr;
97 sPtr = wmalloc(sizeof(SplitView));
98 memset(sPtr, 0, sizeof(SplitView));
100 sPtr->widgetClass = WC_SplitView;
102 sPtr->view = W_CreateView(W_VIEW(parent));
103 if (!sPtr->view) {
104 wfree(sPtr);
105 return NULL;
107 sPtr->view->self = sPtr;
109 WMSetViewNotifySizeChanges(sPtr->view, True);
111 WMCreateEventHandler(sPtr->view, ExposureMask|StructureNotifyMask
112 |ClientMessageMask, handleEvents, sPtr);
115 WMCreateEventHandler(sPtr->view, ButtonPressMask|ButtonReleaseMask
116 |EnterWindowMask|LeaveWindowMask,
117 handleActionEvents, sPtr);
120 WMAddNotificationObserver(handleViewResized, sPtr,
121 WMViewSizeDidChangeNotification, sPtr->view);
123 return sPtr;
127 void
128 WMAddSplitViewSubview(WMSplitView *sPtr, WMView *subview)
130 int wasMapped;
132 assert(!sPtr->flags.splitViewIsFull);
134 wasMapped = subview->flags.mapped;
135 if (wasMapped) {
136 W_UnmapView(subview);
139 W_ReparentView(subview, sPtr->view, 0, 0);
141 #if 0
142 if (sPtr->resizeSubviewsProc && subviewCount(sPtr)>1) {
143 (*sPtr->resizeSubviewsProc)(sPtr, sPtr->view->size.width,
144 sPtr->view->size.height);
145 /* check if there is free space for the new subview and
146 * put the subview in it */
147 } else {
149 #endif
150 WMAdjustSplitViewSubviews(sPtr);
152 if (subviewCount(sPtr)==2)
153 sPtr->flags.splitViewIsFull = 1;
155 if (wasMapped) {
156 W_MapView(subview);
161 void
162 WMSetSplitViewConstrainProc(WMSplitView *sPtr, WMSplitViewConstrainProc *proc)
164 sPtr->constrainProc = proc;
168 void
169 WMAdjustSplitViewSubviews(WMSplitView *sPtr)
171 int theight = sPtr->view->size.height;
172 int width = sPtr->view->size.width;
173 int height;
174 int y, count;
175 W_View *view;
177 count = subviewCount(sPtr);
179 height = (theight - (count-1)*DIVIDER_THICKNESS)/count;
182 view = sPtr->view->childrenList;
183 if (view->self) {
184 WMMoveWidget(view->self, 0, 0);
185 WMResizeWidget(view->self, width, height);
186 } else {
187 W_MoveView(view, 0, 0);
188 W_ResizeView(view, width, height);
191 y = height + DIVIDER_THICKNESS;
193 for (view = view->nextSister; view != NULL; view = view->nextSister) {
194 if (view->self) {
195 WMMoveWidget(view->self, 0, y);
196 WMResizeWidget(view->self, width, height);
197 } else {
198 W_MoveView(view, 0, y);
199 W_ResizeView(view, width, height);
201 y += height + DIVIDER_THICKNESS;
205 #if 0
206 void
207 WMSetSplitViewResizeSubviewsProc(WMSplitView *sPtr,
208 WMSplitViewResizeSubviewsProc *proc)
210 sPtr->resizeSubviewsProc = proc;
212 #endif
216 WMGetSplitViewDividerThickness(WMSplitView *sPtr)
218 return DIVIDER_THICKNESS;
222 static void
223 paintSplitView(SplitView *sPtr)
225 W_Screen *scr = sPtr->view->screen;
226 int y, x;
227 W_View *ptr;
228 WMPixmap *dimple = scr->scrollerDimple;
230 XClearWindow(scr->display, sPtr->view->window);
232 x = (sPtr->view->size.width - dimple->width)/2;
234 ptr = sPtr->view->childrenList;
236 y = ptr->size.height;
237 XSetClipMask(scr->display, scr->clipGC, dimple->mask);
238 while (ptr->nextSister) {
239 y += (DIVIDER_THICKNESS - dimple->width)/2;
241 XSetClipOrigin(scr->display, scr->clipGC, x, y);
242 XCopyArea(scr->display, dimple->pixmap, sPtr->view->window,
243 scr->clipGC, 0, 0, dimple->width, dimple->height, x, y);
245 y += ptr->size.height;
247 ptr = ptr->nextSister;
253 static void
254 handleEvents(XEvent *event, void *data)
256 SplitView *sPtr = (SplitView*)data;
258 CHECK_CLASS(data, WC_SplitView);
261 switch (event->type) {
262 case Expose:
263 if (event->xexpose.count!=0)
264 break;
265 paintSplitView(sPtr);
266 break;
268 case DestroyNotify:
269 destroySplitView(sPtr);
270 break;
275 static void
276 dragDivider(SplitView *sPtr, int clickY)
278 int divider;
279 WMView *view, *view1=NULL, *view2=NULL;
280 int y;
281 int ofsY;
282 int done;
283 int dragging;
284 int minCoord;
285 int maxCoord;
286 XEvent ev;
287 WMScreen *scr = sPtr->view->screen;
289 view = sPtr->view->childrenList;
290 divider = 0;
291 ofsY = 0;
292 y = 0;
293 done = 0;
294 while (view) {
295 y += view->size.height+DIVIDER_THICKNESS;
296 if (clickY < y) {
297 /* offset from point where use clicked and the top of the
298 * divider */
299 ofsY = clickY - y + DIVIDER_THICKNESS;
300 view1 = view;
301 view2 = view->nextSister;
302 /* can't be NULL. It would mean the divider is at the bottom */
303 assert(view2!=NULL);
304 done = 1;
305 break;
307 view = view->nextSister;
308 divider++;
310 assert(done);
312 minCoord = view1->pos.y;
313 maxCoord = view2->pos.y+view2->size.height-DIVIDER_THICKNESS;
315 if (sPtr->constrainProc)
316 (*sPtr->constrainProc)(sPtr, divider, &minCoord, &maxCoord);
318 done = 0;
319 dragging = 0;
320 while (!done) {
321 WMMaskEvent(scr->display, ButtonMotionMask|ButtonReleaseMask
322 |ExposureMask, &ev);
324 switch (ev.type) {
325 case ButtonRelease:
326 done = 1;
327 if (dragging) {
328 XFillRectangle(scr->display, sPtr->view->window, scr->ixorGC,
329 0, y, sPtr->view->size.width,DIVIDER_THICKNESS);
331 break;
333 case MotionNotify:
334 if (dragging) {
335 XFillRectangle(scr->display, sPtr->view->window, scr->ixorGC,
336 0, y, sPtr->view->size.width,DIVIDER_THICKNESS);
338 if (ev.xmotion.y-ofsY < minCoord)
339 y = minCoord;
340 else if (ev.xmotion.y-ofsY > maxCoord)
341 y = maxCoord;
342 else
343 y = ev.xmotion.y-ofsY;
344 XFillRectangle(scr->display, sPtr->view->window, scr->ixorGC,
345 0, y, sPtr->view->size.width, DIVIDER_THICKNESS);
346 dragging = 1;
347 break;
349 default:
350 WMHandleEvent(&ev);
351 break;
355 if (dragging) {
356 int theight;
358 theight = view1->size.height + view2->size.height + DIVIDER_THICKNESS;
360 WMResizeWidget(view1->self, sPtr->view->size.width, y - view1->pos.y);
362 WMResizeWidget(view2->self, sPtr->view->size.width,
363 theight - view1->size.height - DIVIDER_THICKNESS);
364 WMMoveWidget(view2->self, 0,
365 view1->pos.y+view1->size.height+DIVIDER_THICKNESS);
370 static void
371 handleActionEvents(XEvent *event, void *data)
374 CHECK_CLASS(data, WC_SplitView);
377 switch (event->type) {
378 case ButtonPress:
379 if (event->xbutton.button == Button1)
380 dragDivider(data, event->xbutton.y);
381 break;
387 static void
388 destroySplitView(SplitView *sPtr)
390 WMRemoveNotificationObserver(sPtr);
392 wfree(sPtr);