Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / wscrollview.c
1
2 #include "WINGsP.h"
3
4 typedef struct W_ScrollView {
5 W_Class widgetClass;
6 WMView *view;
7
8 WMView *contentView;
9 WMView *viewport;
10
11 WMScroller *vScroller;
12 WMScroller *hScroller;
13
14 short lineScroll;
15 short pageScroll;
16
17 struct {
18 WMReliefType relief:3;
19 unsigned int hasVScroller:1;
20 unsigned int hasHScroller:1;
21
22 } flags;
23
24 } ScrollView;
25
26 static void destroyScrollView(ScrollView * sPtr);
27
28 static void paintScrollView(ScrollView * sPtr);
29 static void handleEvents(XEvent * event, void *data);
30 static void handleViewportEvents(XEvent * event, void *data);
31 static void resizeScrollView();
32 static void updateScrollerProportion();
33
34 W_ViewDelegate _ScrollViewViewDelegate = {
35 NULL,
36 NULL,
37 resizeScrollView,
38 NULL,
39 NULL
40 };
41
42 WMScrollView *WMCreateScrollView(WMWidget * parent)
43 {
44 ScrollView *sPtr;
45
46 sPtr = wmalloc(sizeof(ScrollView));
47 memset(sPtr, 0, sizeof(ScrollView));
48
49 sPtr->widgetClass = WC_ScrollView;
50
51 sPtr->view = W_CreateView(W_VIEW(parent));
52 if (!sPtr->view) {
53 wfree(sPtr);
54 return NULL;
55 }
56 sPtr->viewport = W_CreateView(sPtr->view);
57 if (!sPtr->viewport) {
58 W_DestroyView(sPtr->view);
59 wfree(sPtr);
60 return NULL;
61 }
62 sPtr->view->self = sPtr;
63 sPtr->viewport->self = sPtr;
64
65 sPtr->view->delegate = &_ScrollViewViewDelegate;
66
67 sPtr->viewport->flags.mapWhenRealized = 1;
68
69 WMCreateEventHandler(sPtr->view, StructureNotifyMask | ExposureMask, handleEvents, sPtr);
70 WMCreateEventHandler(sPtr->viewport, SubstructureNotifyMask, handleViewportEvents, sPtr);
71
72 sPtr->lineScroll = 4;
73
74 sPtr->pageScroll = 0;
75
76 return sPtr;
77 }
78
79 static void applyScrollerValues(WMScrollView * sPtr)
80 {
81 int x, y;
82
83 if (sPtr->contentView == NULL)
84 return;
85
86 if (sPtr->flags.hasHScroller) {
87 float v = WMGetScrollerValue(sPtr->hScroller);
88 int size;
89
90 size = sPtr->contentView->size.width - sPtr->viewport->size.width;
91
92 x = v * size;
93 } else {
94 x = 0;
95 }
96
97 if (sPtr->flags.hasVScroller) {
98 float v = WMGetScrollerValue(sPtr->vScroller);
99
100 int size;
101
102 size = sPtr->contentView->size.height - sPtr->viewport->size.height;
103
104 y = v * size;
105 } else {
106 y = 0;
107 }
108
109 x = WMAX(0, x);
110 y = WMAX(0, y);
111
112 W_MoveView(sPtr->contentView, -x, -y);
113
114 W_RaiseView(sPtr->viewport);
115 }
116
117 static void reorganizeInterior(WMScrollView * sPtr)
118 {
119 int hx, hy, hw;
120 int vx, vy, vh;
121 int cx, cy, cw, ch;
122
123 cw = hw = sPtr->view->size.width;
124 vh = ch = sPtr->view->size.height;
125
126 if (sPtr->flags.relief == WRSimple) {
127 cw -= 2;
128 ch -= 2;
129 cx = 1;
130 cy = 1;
131 } else if (sPtr->flags.relief != WRFlat) {
132 cw -= 3;
133 ch -= 3;
134 cx = 2;
135 cy = 2;
136 } else {
137 cx = 0;
138 cy = 0;
139 }
140
141 if (sPtr->flags.hasHScroller) {
142 int h = 20;
143
144 ch -= h;
145
146 if (sPtr->flags.relief == WRSimple) {
147 hx = 0;
148 hy = sPtr->view->size.height - h;
149 } else if (sPtr->flags.relief != WRFlat) {
150 hx = 1;
151 hy = sPtr->view->size.height - h - 1;
152 hw -= 2;
153 } else {
154 hx = 0;
155 hy = sPtr->view->size.height - h;
156 }
157 } else {
158 /* make compiler shutup */
159 hx = 0;
160 hy = 0;
161 }
162
163 if (sPtr->flags.hasVScroller) {
164 int w = 20;
165 cw -= w;
166 cx += w;
167 hx += w - 1;
168 hw -= w - 1;
169
170 if (sPtr->flags.relief == WRSimple) {
171 vx = 0;
172 vy = 0;
173 } else if (sPtr->flags.relief != WRFlat) {
174 vx = 1;
175 vy = 1;
176 vh -= 2;
177 } else {
178 vx = 0;
179 vy = 0;
180 }
181 } else {
182 /* make compiler shutup */
183 vx = 0;
184 vy = 0;
185 }
186
187 W_ResizeView(sPtr->viewport, cw, ch);
188 W_MoveView(sPtr->viewport, cx, cy);
189
190 if (sPtr->flags.hasHScroller) {
191 WMResizeWidget(sPtr->hScroller, hw, 20);
192 WMMoveWidget(sPtr->hScroller, hx, hy);
193 }
194 if (sPtr->flags.hasVScroller) {
195 WMResizeWidget(sPtr->vScroller, 20, vh);
196 WMMoveWidget(sPtr->vScroller, vx, vy);
197 }
198
199 applyScrollerValues(sPtr);
200 }
201
202 static void resizeScrollView(W_ViewDelegate * self, WMView * view)
203 {
204 reorganizeInterior(view->self);
205 updateScrollerProportion(view->self);
206 }
207
208 void WMResizeScrollViewContent(WMScrollView * sPtr, unsigned int width, unsigned int height)
209 {
210 int w, h, x;
211
212 w = width;
213 h = height;
214
215 x = 0;
216 if (sPtr->flags.relief == WRSimple) {
217 w += 2;
218 h += 2;
219 } else if (sPtr->flags.relief != WRFlat) {
220 w += 4;
221 h += 4;
222 x = 1;
223 }
224
225 if (sPtr->flags.hasVScroller) {
226 WMResizeWidget(sPtr->vScroller, 20, h);
227 width -= W_VIEW(sPtr->vScroller)->size.width;
228 }
229
230 if (sPtr->flags.hasHScroller) {
231 WMResizeWidget(sPtr->hScroller, w, 20);
232 WMMoveWidget(sPtr->hScroller, x, h);
233 height -= W_VIEW(sPtr->hScroller)->size.height;
234 }
235
236 W_ResizeView(sPtr->view, w, h);
237
238 W_ResizeView(sPtr->viewport, width, height);
239 }
240
241 void WMSetScrollViewLineScroll(WMScrollView * sPtr, int amount)
242 {
243 assert(amount > 0);
244
245 sPtr->lineScroll = amount;
246 }
247
248 void WMSetScrollViewPageScroll(WMScrollView * sPtr, int amount)
249 {
250 assert(amount >= 0);
251
252 sPtr->pageScroll = amount;
253 }
254
255 WMRect WMGetScrollViewVisibleRect(WMScrollView * sPtr)
256 {
257 WMRect rect;
258
259 rect.pos.x = -sPtr->contentView->pos.x;
260 rect.pos.y = -sPtr->contentView->pos.y;
261 rect.size = sPtr->viewport->size;
262
263 return rect;
264 }
265
266 void WMScrollViewScrollPoint(WMScrollView * sPtr, WMPoint point)
267 {
268 float xsize, ysize;
269 float xpos, ypos;
270
271 xsize = sPtr->contentView->size.width - sPtr->viewport->size.width;
272 ysize = sPtr->contentView->size.height - sPtr->viewport->size.height;
273
274 xpos = point.x / xsize;
275 ypos = point.y / ysize;
276
277 if (sPtr->hScroller)
278 WMSetScrollerParameters(sPtr->hScroller, xpos, WMGetScrollerKnobProportion(sPtr->hScroller));
279 if (sPtr->vScroller)
280 WMSetScrollerParameters(sPtr->vScroller, ypos, WMGetScrollerKnobProportion(sPtr->vScroller));
281
282 W_MoveView(sPtr->contentView, -point.x, -point.y);
283 }
284
285 static void doScrolling(WMWidget * self, void *data)
286 {
287 ScrollView *sPtr = (ScrollView *) data;
288 float value;
289 int pos;
290 int vpsize;
291 float size;
292
293 if (sPtr->hScroller == (WMScroller *) self) {
294 pos = -sPtr->contentView->pos.x;
295 size = sPtr->contentView->size.width - sPtr->viewport->size.width;
296 vpsize = sPtr->viewport->size.width - sPtr->pageScroll;
297 } else {
298 pos = -sPtr->contentView->pos.y;
299 size = sPtr->contentView->size.height - sPtr->viewport->size.height;
300 vpsize = sPtr->viewport->size.height - sPtr->pageScroll;
301 }
302 if (vpsize <= 0)
303 vpsize = 1;
304
305 switch (WMGetScrollerHitPart(self)) {
306 case WSDecrementLine:
307 if (pos > 0) {
308 pos -= sPtr->lineScroll;
309 if (pos < 0)
310 pos = 0;
311 value = (float)pos / size;
312 WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
313 }
314 break;
315 case WSIncrementLine:
316 if (pos < size) {
317 pos += sPtr->lineScroll;
318 if (pos > size)
319 pos = size;
320 value = (float)pos / size;
321 WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
322 }
323 break;
324
325 case WSKnob:
326 value = WMGetScrollerValue(self);
327 pos = value * size;
328 break;
329
330 case WSDecrementPage:
331 if (pos > 0) {
332 pos -= vpsize;
333 if (pos < 0)
334 pos = 0;
335 value = (float)pos / size;
336 WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
337 }
338 break;
339
340 case WSDecrementWheel:
341 if (pos > 0) {
342 pos -= vpsize / 3;
343 if (pos < 0)
344 pos = 0;
345 value = (float)pos / size;
346 WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
347 }
348 break;
349
350 case WSIncrementPage:
351 if (pos < size) {
352 pos += vpsize;
353 if (pos > size)
354 pos = size;
355 value = (float)pos / size;
356 WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
357 }
358 break;
359
360 case WSIncrementWheel:
361 if (pos < size) {
362 pos += vpsize / 3;
363 if (pos > size)
364 pos = size;
365 value = (float)pos / size;
366 WMSetScrollerParameters(self, value, WMGetScrollerKnobProportion(self));
367 }
368 break;
369
370 case WSNoPart:
371 case WSKnobSlot:
372 break;
373 }
374
375 if (sPtr->hScroller == (WMScroller *) self) {
376 W_MoveView(sPtr->contentView, -pos, sPtr->contentView->pos.y);
377 } else {
378 W_MoveView(sPtr->contentView, sPtr->contentView->pos.x, -pos);
379 }
380 }
381
382 WMScroller *WMGetScrollViewHorizontalScroller(WMScrollView * sPtr)
383 {
384 return sPtr->hScroller;
385 }
386
387 WMScroller *WMGetScrollViewVerticalScroller(WMScrollView * sPtr)
388 {
389 return sPtr->vScroller;
390 }
391
392 void WMSetScrollViewHasHorizontalScroller(WMScrollView * sPtr, Bool flag)
393 {
394 if (flag) {
395 if (sPtr->flags.hasHScroller)
396 return;
397 sPtr->flags.hasHScroller = 1;
398
399 sPtr->hScroller = WMCreateScroller(sPtr);
400 WMSetScrollerAction(sPtr->hScroller, doScrolling, sPtr);
401 /* make it a horiz. scroller */
402 WMResizeWidget(sPtr->hScroller, 2, 1);
403
404 if (W_VIEW_REALIZED(sPtr->view)) {
405 WMRealizeWidget(sPtr->hScroller);
406 }
407
408 reorganizeInterior(sPtr);
409
410 WMMapWidget(sPtr->hScroller);
411 } else {
412 if (!sPtr->flags.hasHScroller)
413 return;
414
415 WMUnmapWidget(sPtr->hScroller);
416 WMDestroyWidget(sPtr->hScroller);
417 sPtr->hScroller = NULL;
418 sPtr->flags.hasHScroller = 0;
419
420 reorganizeInterior(sPtr);
421 }
422 }
423
424 void WMSetScrollViewHasVerticalScroller(WMScrollView * sPtr, Bool flag)
425 {
426 if (flag) {
427 if (sPtr->flags.hasVScroller)
428 return;
429 sPtr->flags.hasVScroller = 1;
430
431 sPtr->vScroller = WMCreateScroller(sPtr);
432 WMSetScrollerAction(sPtr->vScroller, doScrolling, sPtr);
433 WMSetScrollerArrowsPosition(sPtr->vScroller, WSAMaxEnd);
434 /* make it a vert. scroller */
435 WMResizeWidget(sPtr->vScroller, 1, 2);
436
437 if (W_VIEW_REALIZED(sPtr->view)) {
438 WMRealizeWidget(sPtr->vScroller);
439 }
440
441 reorganizeInterior(sPtr);
442
443 WMMapWidget(sPtr->vScroller);
444 } else {
445 if (!sPtr->flags.hasVScroller)
446 return;
447 sPtr->flags.hasVScroller = 0;
448
449 WMUnmapWidget(sPtr->vScroller);
450 WMDestroyWidget(sPtr->vScroller);
451 sPtr->vScroller = NULL;
452
453 reorganizeInterior(sPtr);
454 }
455 }
456
457 void WMSetScrollViewContentView(WMScrollView * sPtr, WMView * view)
458 {
459 assert(sPtr->contentView == NULL);
460
461 sPtr->contentView = view;
462
463 W_ReparentView(sPtr->contentView, sPtr->viewport, 0, 0);
464
465 if (sPtr->flags.hasHScroller) {
466 float prop;
467
468 prop = (float)sPtr->viewport->size.width / sPtr->contentView->size.width;
469 WMSetScrollerParameters(sPtr->hScroller, 0, prop);
470 }
471 if (sPtr->flags.hasVScroller) {
472 float prop;
473
474 prop = (float)sPtr->viewport->size.height / sPtr->contentView->size.height;
475
476 WMSetScrollerParameters(sPtr->vScroller, 0, prop);
477 }
478 }
479
480 void WMSetScrollViewRelief(WMScrollView * sPtr, WMReliefType type)
481 {
482 sPtr->flags.relief = type;
483
484 reorganizeInterior(sPtr);
485
486 if (sPtr->view->flags.mapped)
487 paintScrollView(sPtr);
488
489 }
490
491 static void paintScrollView(ScrollView * sPtr)
492 {
493 W_DrawRelief(sPtr->view->screen, sPtr->view->window, 0, 0,
494 sPtr->view->size.width, sPtr->view->size.height, sPtr->flags.relief);
495 }
496
497 static void updateScrollerProportion(ScrollView * sPtr)
498 {
499 float prop, value;
500 float oldV, oldP;
501
502 if (sPtr->flags.hasHScroller) {
503 oldV = WMGetScrollerValue(sPtr->hScroller);
504 oldP = WMGetScrollerKnobProportion(sPtr->hScroller);
505
506 prop = (float)sPtr->viewport->size.width / (float)sPtr->contentView->size.width;
507
508 if (oldP == 1.0)
509 value = 0;
510 else
511 value = (prop * oldV) / oldP;
512 WMSetScrollerParameters(sPtr->hScroller, value, prop);
513 }
514 if (sPtr->flags.hasVScroller) {
515 oldV = WMGetScrollerValue(sPtr->vScroller);
516 oldP = WMGetScrollerKnobProportion(sPtr->vScroller);
517
518 prop = (float)sPtr->viewport->size.height / (float)sPtr->contentView->size.height;
519
520 if (oldP == 1.0)
521 value = 0;
522 else
523 value = (prop * oldV) / oldP;
524 WMSetScrollerParameters(sPtr->vScroller, value, prop);
525 }
526 applyScrollerValues(sPtr);
527 }
528
529 static void handleViewportEvents(XEvent * event, void *data)
530 {
531 ScrollView *sPtr = (ScrollView *) data;
532
533 if (sPtr->contentView && event->xconfigure.window == sPtr->contentView->window)
534 updateScrollerProportion(sPtr);
535 }
536
537 static void handleEvents(XEvent * event, void *data)
538 {
539 ScrollView *sPtr = (ScrollView *) data;
540
541 CHECK_CLASS(data, WC_ScrollView);
542
543 switch (event->type) {
544 case Expose:
545 if (event->xexpose.count != 0)
546 break;
547 if (event->xexpose.serial == 0) /* means it's artificial */
548 W_RedisplayView(sPtr->contentView);
549 else
550 paintScrollView(sPtr);
551 break;
552
553 case DestroyNotify:
554 destroyScrollView(sPtr);
555 break;
556
557 }
558 }
559
560 static void destroyScrollView(ScrollView * sPtr)
561 {
562 wfree(sPtr);
563 }