Initial revision
[wmaker-crm.git] / WINGs / wsplitview.c
blobe79b9fcec3465f0624e32225577d3e483c74fa55
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 W_ViewProcedureTable _SplitViewViewProcedures = {
37 NULL,
38 NULL,
39 NULL
44 static int
45 subviewCount(SplitView *sPtr)
47 int count = 0;
48 WMView *view;
50 for (view=sPtr->view->childrenList; view != NULL; view=view->nextSister)
51 count++;
53 return count;
57 static void
58 handleViewResized(void *self, WMNotification *notification)
60 SplitView *sPtr = (SplitView*)self;
61 int oldHeight;
62 WMView *view = sPtr->view;
63 int newWidth = view->size.width;
64 WMView *upper, *lower;
66 if (!view->childrenList)
67 return;
69 if (view->childrenList->nextSister==NULL) {
70 if (view->self)
71 WMResizeWidget(view->childrenList->self, newWidth, view->size.height);
72 else
73 W_ResizeView(view->childrenList, newWidth, view->size.height);
74 } else {
75 upper = view->childrenList;
76 lower = upper->nextSister;
77 oldHeight = upper->size.height+DIVIDER_THICKNESS+lower->size.height;
79 if (oldHeight > view->size.height
80 && upper->size.height+DIVIDER_THICKNESS >= view->size.height) {
81 WMAdjustSplitViewSubviews(sPtr);
82 } else {
83 if (upper->self) {
84 WMResizeWidget(upper->self, newWidth, upper->size.height);
85 } else {
86 W_ResizeView(upper, newWidth, upper->size.height);
88 if (lower->self) {
89 WMResizeWidget(lower->self, newWidth,
90 view->size.height-lower->pos.y);
91 } else {
92 W_ResizeView(lower, newWidth, view->size.height-lower->pos.y);
100 WMSplitView*
101 WMCreateSplitView(WMWidget *parent)
103 SplitView *sPtr;
105 sPtr = wmalloc(sizeof(SplitView));
106 memset(sPtr, 0, sizeof(SplitView));
108 sPtr->widgetClass = WC_SplitView;
110 sPtr->view = W_CreateView(W_VIEW(parent));
111 if (!sPtr->view) {
112 free(sPtr);
113 return NULL;
115 sPtr->view->self = sPtr;
117 WMSetViewNotifySizeChanges(sPtr->view, True);
119 WMCreateEventHandler(sPtr->view, ExposureMask|StructureNotifyMask
120 |ClientMessageMask, handleEvents, sPtr);
123 WMCreateEventHandler(sPtr->view, ButtonPressMask|ButtonReleaseMask
124 |EnterWindowMask|LeaveWindowMask,
125 handleActionEvents, sPtr);
128 WMAddNotificationObserver(handleViewResized, sPtr,
129 WMViewSizeDidChangeNotification, sPtr->view);
131 return sPtr;
135 void
136 WMAddSplitViewSubview(WMSplitView *sPtr, WMView *subview)
138 int wasMapped;
140 assert(!sPtr->flags.splitViewIsFull);
142 wasMapped = subview->flags.mapped;
143 if (wasMapped) {
144 W_UnmapView(subview);
147 W_ReparentView(subview, sPtr->view);
149 #if 0
150 if (sPtr->resizeSubviewsProc && subviewCount(sPtr)>1) {
151 (*sPtr->resizeSubviewsProc)(sPtr, sPtr->view->size.width,
152 sPtr->view->size.height);
153 /* check if there is free space for the new subview and
154 * put the subview in it */
155 } else {
157 #endif
158 WMAdjustSplitViewSubviews(sPtr);
160 if (subviewCount(sPtr)==2)
161 sPtr->flags.splitViewIsFull = 1;
163 if (wasMapped) {
164 W_MapView(subview);
169 void
170 WMSetSplitViewConstrainProc(WMSplitView *sPtr, WMSplitViewConstrainProc *proc)
172 sPtr->constrainProc = proc;
176 void
177 WMAdjustSplitViewSubviews(WMSplitView *sPtr)
179 int theight = sPtr->view->size.height;
180 int width = sPtr->view->size.width;
181 int height;
182 int y, count;
183 W_View *view;
185 count = subviewCount(sPtr);
187 height = (theight - (count-1)*DIVIDER_THICKNESS)/count;
190 view = sPtr->view->childrenList;
191 if (view->self) {
192 WMMoveWidget(view->self, 0, 0);
193 WMResizeWidget(view->self, width, height);
194 } else {
195 W_MoveView(view, 0, 0);
196 W_ResizeView(view, width, height);
199 y = height + DIVIDER_THICKNESS;
201 for (view = view->nextSister; view != NULL; view = view->nextSister) {
202 if (view->self) {
203 WMMoveWidget(view->self, 0, y);
204 WMResizeWidget(view->self, width, height);
205 } else {
206 W_MoveView(view, 0, y);
207 W_ResizeView(view, width, height);
209 y += height + DIVIDER_THICKNESS;
213 #if 0
214 void
215 WMSetSplitViewResizeSubviewsProc(WMSplitView *sPtr,
216 WMSplitViewResizeSubviewsProc *proc)
218 sPtr->resizeSubviewsProc = proc;
220 #endif
224 WMGetSplitViewDividerThickness(WMSplitView *sPtr)
226 return DIVIDER_THICKNESS;
230 static void
231 paintSplitView(SplitView *sPtr)
233 W_Screen *scr = sPtr->view->screen;
234 int y, x;
235 W_View *ptr;
236 WMPixmap *dimple = scr->scrollerDimple;
238 XClearWindow(scr->display, sPtr->view->window);
240 x = (sPtr->view->size.width - dimple->width)/2;
242 ptr = sPtr->view->childrenList;
244 y = ptr->size.height;
245 XSetClipMask(scr->display, scr->clipGC, dimple->mask);
246 while (ptr->nextSister) {
247 y += (DIVIDER_THICKNESS - dimple->width)/2;
249 XSetClipOrigin(scr->display, scr->clipGC, x, y);
250 XCopyArea(scr->display, dimple->pixmap, sPtr->view->window,
251 scr->clipGC, 0, 0, dimple->width, dimple->height, x, y);
253 y += ptr->size.height;
255 ptr = ptr->nextSister;
261 static void
262 handleEvents(XEvent *event, void *data)
264 SplitView *sPtr = (SplitView*)data;
266 CHECK_CLASS(data, WC_SplitView);
269 switch (event->type) {
270 case Expose:
271 if (event->xexpose.count!=0)
272 break;
273 paintSplitView(sPtr);
274 break;
276 case DestroyNotify:
277 destroySplitView(sPtr);
278 break;
283 static void
284 dragDivider(SplitView *sPtr, int clickY)
286 int divider;
287 WMView *view, *view1=NULL, *view2=NULL;
288 int y;
289 int ofsY;
290 int done;
291 int dragging;
292 int minCoord;
293 int maxCoord;
294 XEvent ev;
295 WMScreen *scr = sPtr->view->screen;
297 view = sPtr->view->childrenList;
298 divider = 0;
299 ofsY = 0;
300 y = 0;
301 done = 0;
302 while (view) {
303 y += view->size.height+DIVIDER_THICKNESS;
304 if (clickY < y) {
305 /* offset from point where use clicked and the top of the
306 * divider */
307 ofsY = clickY - y + DIVIDER_THICKNESS;
308 view1 = view;
309 view2 = view->nextSister;
310 /* can't be NULL. It would mean the divider is at the bottom */
311 assert(view2!=NULL);
312 done = 1;
313 break;
315 view = view->nextSister;
316 divider++;
318 assert(done);
320 minCoord = view1->pos.y;
321 maxCoord = view2->pos.y+view2->size.height-DIVIDER_THICKNESS;
323 if (sPtr->constrainProc)
324 (*sPtr->constrainProc)(sPtr, divider, &minCoord, &maxCoord);
326 done = 0;
327 dragging = 0;
328 while (!done) {
329 WMMaskEvent(scr->display, ButtonMotionMask|ButtonReleaseMask
330 |ExposureMask, &ev);
332 switch (ev.type) {
333 case ButtonRelease:
334 done = 1;
335 if (dragging) {
336 XFillRectangle(scr->display, sPtr->view->window, scr->ixorGC,
337 0, y, sPtr->view->size.width,DIVIDER_THICKNESS);
339 break;
341 case MotionNotify:
342 if (dragging) {
343 XFillRectangle(scr->display, sPtr->view->window, scr->ixorGC,
344 0, y, sPtr->view->size.width,DIVIDER_THICKNESS);
346 if (ev.xmotion.y-ofsY < minCoord)
347 y = minCoord;
348 else if (ev.xmotion.y-ofsY > maxCoord)
349 y = maxCoord;
350 else
351 y = ev.xmotion.y-ofsY;
352 XFillRectangle(scr->display, sPtr->view->window, scr->ixorGC,
353 0, y, sPtr->view->size.width, DIVIDER_THICKNESS);
354 dragging = 1;
355 break;
357 default:
358 WMHandleEvent(&ev);
359 break;
363 if (dragging) {
364 int theight;
366 theight = view1->size.height + view2->size.height + DIVIDER_THICKNESS;
368 WMResizeWidget(view1->self, sPtr->view->size.width, y - view1->pos.y);
370 WMResizeWidget(view2->self, sPtr->view->size.width,
371 theight - view1->size.height - DIVIDER_THICKNESS);
372 WMMoveWidget(view2->self, 0,
373 view1->pos.y+view1->size.height+DIVIDER_THICKNESS);
378 static void
379 handleActionEvents(XEvent *event, void *data)
382 CHECK_CLASS(data, WC_SplitView);
385 switch (event->type) {
386 case ButtonPress:
387 if (event->xbutton.button == Button1)
388 dragDivider(data, event->xbutton.y);
389 break;
395 static void
396 destroySplitView(SplitView *sPtr)
398 WMRemoveNotificationObserver(sPtr);
400 free(sPtr);