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
);
36 W_ViewProcedureTable _SplitViewViewProcedures
= {
45 subviewCount(SplitView
*sPtr
)
50 for (view
=sPtr
->view
->childrenList
; view
!= NULL
; view
=view
->nextSister
)
58 handleViewResized(void *self
, WMNotification
*notification
)
60 SplitView
*sPtr
= (SplitView
*)self
;
62 WMView
*view
= sPtr
->view
;
63 int newWidth
= view
->size
.width
;
64 WMView
*upper
, *lower
;
66 if (!view
->childrenList
)
69 if (view
->childrenList
->nextSister
==NULL
) {
71 WMResizeWidget(view
->childrenList
->self
, newWidth
, view
->size
.height
);
73 W_ResizeView(view
->childrenList
, newWidth
, view
->size
.height
);
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
);
84 WMResizeWidget(upper
->self
, newWidth
, upper
->size
.height
);
86 W_ResizeView(upper
, newWidth
, upper
->size
.height
);
89 WMResizeWidget(lower
->self
, newWidth
,
90 view
->size
.height
-lower
->pos
.y
);
92 W_ResizeView(lower
, newWidth
, view
->size
.height
-lower
->pos
.y
);
101 WMCreateSplitView(WMWidget
*parent
)
105 sPtr
= wmalloc(sizeof(SplitView
));
106 memset(sPtr
, 0, sizeof(SplitView
));
108 sPtr
->widgetClass
= WC_SplitView
;
110 sPtr
->view
= W_CreateView(W_VIEW(parent
));
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
);
136 WMAddSplitViewSubview(WMSplitView
*sPtr
, WMView
*subview
)
140 assert(!sPtr
->flags
.splitViewIsFull
);
142 wasMapped
= subview
->flags
.mapped
;
144 W_UnmapView(subview
);
147 W_ReparentView(subview
, sPtr
->view
);
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 */
158 WMAdjustSplitViewSubviews(sPtr
);
160 if (subviewCount(sPtr
)==2)
161 sPtr
->flags
.splitViewIsFull
= 1;
170 WMSetSplitViewConstrainProc(WMSplitView
*sPtr
, WMSplitViewConstrainProc
*proc
)
172 sPtr
->constrainProc
= proc
;
177 WMAdjustSplitViewSubviews(WMSplitView
*sPtr
)
179 int theight
= sPtr
->view
->size
.height
;
180 int width
= sPtr
->view
->size
.width
;
185 count
= subviewCount(sPtr
);
187 height
= (theight
- (count
-1)*DIVIDER_THICKNESS
)/count
;
190 view
= sPtr
->view
->childrenList
;
192 WMMoveWidget(view
->self
, 0, 0);
193 WMResizeWidget(view
->self
, width
, height
);
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
) {
203 WMMoveWidget(view
->self
, 0, y
);
204 WMResizeWidget(view
->self
, width
, height
);
206 W_MoveView(view
, 0, y
);
207 W_ResizeView(view
, width
, height
);
209 y
+= height
+ DIVIDER_THICKNESS
;
215 WMSetSplitViewResizeSubviewsProc(WMSplitView
*sPtr
,
216 WMSplitViewResizeSubviewsProc
*proc
)
218 sPtr
->resizeSubviewsProc
= proc
;
224 WMGetSplitViewDividerThickness(WMSplitView
*sPtr
)
226 return DIVIDER_THICKNESS
;
231 paintSplitView(SplitView
*sPtr
)
233 W_Screen
*scr
= sPtr
->view
->screen
;
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
;
262 handleEvents(XEvent
*event
, void *data
)
264 SplitView
*sPtr
= (SplitView
*)data
;
266 CHECK_CLASS(data
, WC_SplitView
);
269 switch (event
->type
) {
271 if (event
->xexpose
.count
!=0)
273 paintSplitView(sPtr
);
277 destroySplitView(sPtr
);
284 dragDivider(SplitView
*sPtr
, int clickY
)
287 WMView
*view
, *view1
=NULL
, *view2
=NULL
;
295 WMScreen
*scr
= sPtr
->view
->screen
;
297 view
= sPtr
->view
->childrenList
;
303 y
+= view
->size
.height
+DIVIDER_THICKNESS
;
305 /* offset from point where use clicked and the top of the
307 ofsY
= clickY
- y
+ DIVIDER_THICKNESS
;
309 view2
= view
->nextSister
;
310 /* can't be NULL. It would mean the divider is at the bottom */
315 view
= view
->nextSister
;
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
);
329 WMMaskEvent(scr
->display
, ButtonMotionMask
|ButtonReleaseMask
336 XFillRectangle(scr
->display
, sPtr
->view
->window
, scr
->ixorGC
,
337 0, y
, sPtr
->view
->size
.width
,DIVIDER_THICKNESS
);
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
)
348 else if (ev
.xmotion
.y
-ofsY
> maxCoord
)
351 y
= ev
.xmotion
.y
-ofsY
;
352 XFillRectangle(scr
->display
, sPtr
->view
->window
, scr
->ixorGC
,
353 0, y
, sPtr
->view
->size
.width
, DIVIDER_THICKNESS
);
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
);
379 handleActionEvents(XEvent
*event
, void *data
)
382 CHECK_CLASS(data
, WC_SplitView
);
385 switch (event
->type
) {
387 if (event
->xbutton
.button
== Button1
)
388 dragDivider(data
, event
->xbutton
.y
);
396 destroySplitView(SplitView
*sPtr
)
398 WMRemoveNotificationObserver(sPtr
);