10 typedef struct W_SplitView
{
14 /* WMSplitViewResizeSubviewsProc *resizeSubviewsProc;
16 WMSplitViewConstrainProc
*constrainProc
;
19 unsigned int splitViewIsFull
:1; /* already added 2 subviews */
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
);
37 subviewCount(SplitView
*sPtr
)
42 for (view
=sPtr
->view
->childrenList
; view
!= NULL
; view
=view
->nextSister
)
50 handleViewResized(void *self
, WMNotification
*notification
)
52 SplitView
*sPtr
= (SplitView
*)self
;
54 WMView
*view
= sPtr
->view
;
55 int newWidth
= view
->size
.width
;
56 WMView
*upper
, *lower
;
58 if (!view
->childrenList
)
61 if (view
->childrenList
->nextSister
==NULL
) {
63 WMResizeWidget(view
->childrenList
->self
, newWidth
, view
->size
.height
);
65 W_ResizeView(view
->childrenList
, newWidth
, view
->size
.height
);
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
);
76 WMResizeWidget(upper
->self
, newWidth
, upper
->size
.height
);
78 W_ResizeView(upper
, newWidth
, upper
->size
.height
);
81 WMResizeWidget(lower
->self
, newWidth
,
82 view
->size
.height
-lower
->pos
.y
);
84 W_ResizeView(lower
, newWidth
, view
->size
.height
-lower
->pos
.y
);
93 WMCreateSplitView(WMWidget
*parent
)
97 sPtr
= wmalloc(sizeof(SplitView
));
98 memset(sPtr
, 0, sizeof(SplitView
));
100 sPtr
->widgetClass
= WC_SplitView
;
102 sPtr
->view
= W_CreateView(W_VIEW(parent
));
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
);
128 WMAddSplitViewSubview(WMSplitView
*sPtr
, WMView
*subview
)
132 assert(!sPtr
->flags
.splitViewIsFull
);
134 wasMapped
= subview
->flags
.mapped
;
136 W_UnmapView(subview
);
139 W_ReparentView(subview
, sPtr
->view
, 0, 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 */
150 WMAdjustSplitViewSubviews(sPtr
);
152 if (subviewCount(sPtr
)==2)
153 sPtr
->flags
.splitViewIsFull
= 1;
162 WMSetSplitViewConstrainProc(WMSplitView
*sPtr
, WMSplitViewConstrainProc
*proc
)
164 sPtr
->constrainProc
= proc
;
169 WMAdjustSplitViewSubviews(WMSplitView
*sPtr
)
171 int theight
= sPtr
->view
->size
.height
;
172 int width
= sPtr
->view
->size
.width
;
177 count
= subviewCount(sPtr
);
179 height
= (theight
- (count
-1)*DIVIDER_THICKNESS
)/count
;
182 view
= sPtr
->view
->childrenList
;
184 WMMoveWidget(view
->self
, 0, 0);
185 WMResizeWidget(view
->self
, width
, height
);
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
) {
195 WMMoveWidget(view
->self
, 0, y
);
196 WMResizeWidget(view
->self
, width
, height
);
198 W_MoveView(view
, 0, y
);
199 W_ResizeView(view
, width
, height
);
201 y
+= height
+ DIVIDER_THICKNESS
;
207 WMSetSplitViewResizeSubviewsProc(WMSplitView
*sPtr
,
208 WMSplitViewResizeSubviewsProc
*proc
)
210 sPtr
->resizeSubviewsProc
= proc
;
216 WMGetSplitViewDividerThickness(WMSplitView
*sPtr
)
218 return DIVIDER_THICKNESS
;
223 paintSplitView(SplitView
*sPtr
)
225 W_Screen
*scr
= sPtr
->view
->screen
;
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
;
254 handleEvents(XEvent
*event
, void *data
)
256 SplitView
*sPtr
= (SplitView
*)data
;
258 CHECK_CLASS(data
, WC_SplitView
);
261 switch (event
->type
) {
263 if (event
->xexpose
.count
!=0)
265 paintSplitView(sPtr
);
269 destroySplitView(sPtr
);
276 dragDivider(SplitView
*sPtr
, int clickY
)
279 WMView
*view
, *view1
=NULL
, *view2
=NULL
;
287 WMScreen
*scr
= sPtr
->view
->screen
;
289 view
= sPtr
->view
->childrenList
;
295 y
+= view
->size
.height
+DIVIDER_THICKNESS
;
297 /* offset from point where use clicked and the top of the
299 ofsY
= clickY
- y
+ DIVIDER_THICKNESS
;
301 view2
= view
->nextSister
;
302 /* can't be NULL. It would mean the divider is at the bottom */
307 view
= view
->nextSister
;
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
);
321 WMMaskEvent(scr
->display
, ButtonMotionMask
|ButtonReleaseMask
328 XFillRectangle(scr
->display
, sPtr
->view
->window
, scr
->ixorGC
,
329 0, y
, sPtr
->view
->size
.width
,DIVIDER_THICKNESS
);
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
)
340 else if (ev
.xmotion
.y
-ofsY
> maxCoord
)
343 y
= ev
.xmotion
.y
-ofsY
;
344 XFillRectangle(scr
->display
, sPtr
->view
->window
, scr
->ixorGC
,
345 0, y
, sPtr
->view
->size
.width
, DIVIDER_THICKNESS
);
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
);
371 handleActionEvents(XEvent
*event
, void *data
)
374 CHECK_CLASS(data
, WC_SplitView
);
377 switch (event
->type
) {
379 if (event
->xbutton
.button
== Button1
)
380 dragDivider(data
, event
->xbutton
.y
);
388 destroySplitView(SplitView
*sPtr
)
390 WMRemoveNotificationObserver(sPtr
);