Many changes, bugfixes to user vsnprintf and to escape code handling
[tpg/acess2.git] / Usermode / Applications / axwin2_src / WM / wm.c
1 /*
2  * Acess GUI (AxWin) Version 2
3  * By John Hodge (thePowersGang)
4  * 
5  * Window Manager and Widget Control
6  */
7 #include "common.h"
8 #include <stdlib.h>
9 #include <strings.h>
10 #include "wm.h"
11
12 // === IMPORTS ===
13 extern void     Decorator_RenderWidget(tElement *Element);
14
15 // === PROTOTYPES ===
16 tElement        *WM_CreateElement(tElement *Parent, int Type, int Flags);
17 void    WM_SetFlags(tElement *Element, int Flags);
18 void    WM_SetSize(tElement *Element, int Size);
19 void    WM_SetText(tElement *Element, char *Text);
20 void    WM_UpdateDimensions(tElement *Element, int Pass);
21 void    WM_UpdatePosition(tElement *Element);
22 void    WM_RenderWidget(tElement *Element);
23 void    WM_Update(void);
24
25 // === GLOBALS ===
26 tElement        gWM_RootElement;
27 struct {
28         void    (*Init)(tElement *This);
29         void    (*UpdateFlags)(tElement *This);
30         void    (*UpdateSize)(tElement *This);
31         void    (*UpdateText)(tElement *This);
32 }       gaWM_WidgetTypes[MAX_ELETYPES] = {
33         {NULL, NULL, NULL, NULL},       // NULL
34         {NULL, NULL, NULL, NULL}        // Box
35 };
36
37 // === CODE ===
38 // --- Widget Creation and Control ---
39 tElement *WM_CreateElement(tElement *Parent, int Type, int Flags)
40 {
41         tElement        *ret;
42         
43         if(Type < 0 || Type > NUM_ELETYPES)     return NULL;
44         
45         ret = calloc(sizeof(tElement), 1);
46         if(!ret)        return NULL;
47         
48         // Prepare
49         ret->Type = Type;
50         if(Parent == NULL)      Parent = &gWM_RootElement;
51         ret->Parent = Parent;
52         
53         // Append to parent's list
54         ret->NextSibling = Parent->LastChild;
55         Parent->LastChild = ret;
56         if(!Parent->FirstChild) Parent->FirstChild = ret;
57         
58         ret->PaddingL = 2;
59         ret->PaddingR = 2;
60         ret->PaddingT = 2;
61         ret->PaddingB = 2;
62         
63         return ret;
64 }
65
66 void WM_SetFlags(tElement *Element, int Flags)
67 {
68         // Permissions are handled in the message handler
69         if(!Element) {
70                 gWM_RootElement.Flags = Flags;
71                 return ;
72         }
73         
74         Element->Flags = Flags;
75         return ;
76 }
77
78 void WM_SetSize(tElement *Element, int Size)
79 {
80         if(!Element)    return ;
81         Element->Size = Size;
82         return ;
83 }
84
85 void WM_SetText(tElement *Element, char *Text)
86 {
87         if(!Element)    return ;
88         if(Element->Text)       free(Element->Text);
89         Element->Text = strdup(Text);
90         
91         switch(Element->Type)
92         {
93         case ELETYPE_IMAGE:
94                 if(Element->Data)       free(Element->Data);
95                 Element->Data = LoadImage( Element->Text );
96                 if(!Element->Data) {
97                         Element->Flags &= ~ELEFLAG_FIXEDSIZE;
98                         return ;
99                 }
100                 
101                 Element->Flags |= ELEFLAG_FIXEDSIZE;
102                 Element->CachedW = ((tImage*)Element->Data)->Width;
103                 Element->CachedH = ((tImage*)Element->Data)->Height;
104                 
105                 if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL)
106                         Element->Size = Element->CachedH;
107                 else
108                         Element->Size = Element->CachedW;
109                 break;
110         }
111         
112         return ;
113 }
114
115 // --- Pre-Rendering ---
116 #if 0
117 void WM_UpdateDimensions(tElement *Element, int Pass)
118 {
119         // Pass zero intialises
120         if( Pass == 0 )
121         {
122                 // If not a fixed size element, initialise the sizes
123                 if( !(Element->Flags & ELEFLAG_FIXEDSIZE) )
124                 {
125                         Element->CachedH = 0;
126                         Element->CachedW = 0;
127                         if( Element->Size )
128                         {
129                                 if( Element->Parent->Flags & ELEFLAG_VERTICAL )
130                                         Element->CachedH = Element->Size;
131                                 else
132                                         Element->CachedW = Element->Size;
133                         }
134                 }
135         }
136
137          int    fixedSize = 0, maxCross = 0;
138          int    nFixed = 0, nChildren = 0;
139         for( child = Element->FirstChild; child; child = child->NextSibling )
140         {
141                 WM_UpdateDimensions( child, 0 );
142                 
143                 if( Element->Flags & ELEFLAG_VERTICAL )
144                 {
145                         if( child->CachedH ) {
146                                 fixedSize += child->CachedH;
147                                 nFixed ++;
148                         }
149                         if( maxCross < child->CachedW )
150                                 maxCross = child->CachedW;
151                 }
152                 else
153                 {
154                         if( child->CachedW ) {
155                                 fixedSize += child->CachedW;
156                                 nFixed ++;
157                         }
158                         if( maxCross < child->CachedH )
159                                 maxCross = child->CachedH;
160                 }
161                 nChildren ++;
162         }
163
164
165         // If we don't have our dimensions, get the child dimensions
166         if( Element->CachedW == 0 || Element->CachedH == 0 )
167         {       
168                 if( Element->Flags & ELEFLAG_VERTICAL )
169                 {
170                         if( Element->CachedW == 0 && maxCross )
171                                 Element->CachedW = Element->PaddingL
172                                         + Element->PaddingR + maxCross;
173                         
174                         if( Element->CachedH == 0 && nFixed == nChildren )
175                                 Element->CachedH = Element->PaddingT
176                                         + Element->PaddingB + fixedSize
177                                         + nChildren * Element->GapSize;
178                 }
179                 else
180                 {
181                         if( maxCross )
182                                 Element->CachedH = Element->PaddingT
183                                         + Element->PaddingB + maxCross;
184                         
185                         if( Element->CachedW == 0 && nFixed == nChildren )
186                                 Element->CachedW = Element->PaddingL
187                                         + Element->PaddingR + fixedSize
188                                         + nChildren * Element->GapSize;
189                 }
190         }
191         
192         // Now, if we have the "length" of the widget, we can size the children
193         if( (Element->Flags & ELEFLAG_VERTICAL && Element->CachedH > 0)
194          || (!(Element->Flags & ELEFLAG_VERTICAL) && Element->CachedW > 0) )
195         {
196                  int    dynSize;
197                 
198                 // Calculate the size of dynamically sized elements
199                 if( Element->Flags & ELEFLAG_VERTICAL )
200                         dynSize = Element->CachedH - Element->PaddingT
201                                  - Element->PaddingB - fixedSize;
202                 else
203                         dynSize = Element->CachedW - Element->PaddingL
204                                  - Element->PaddingR - fixedSize;
205                 dynSize /= nChildren - nFixed;
206                 
207                 // Itterate children again
208                 for( child = Element->FirstChild; child; child = child->NextSibling )
209                 {
210                          int    tmp;
211                         
212                         // Get the size of them
213                         if(child->Size)
214                                 tmp = child->Size;
215                         else if(dynSize < Element->MinSize)
216                                 tmp = child->MinSize;
217                         else
218                                 tmp = dynSize;
219                         
220                         if( Element->Flags & ELEFLAG_VERTICAL )
221                                 child->CachedH = tmp;
222                         else
223                                 child->CachedW = tmp;
224                         
225                         WM_UpdateDimensions(child, 1);
226                 }
227         }
228 }
229
230 #endif
231
232 /**
233  * \brief Updates the dimensions of an element
234  * 
235  * The dimensions of an element are calculated from the parent's
236  * cross dimension (the side at right angles to the alignment) sans some
237  * padding.
238  */
239 void WM_UpdateDimensions(tElement *Element, int Pass)
240 {
241         tElement        *child;
242          int    fixedChildSize = 0;
243          int    dynamicSize;
244          int    nChildren = 0;
245          int    nFixed = 0;
246         
247         _SysDebug("WM_UpdateDimensions: (Element=%p{Flags:0x%x}, Pass=%i)",
248                 Element, Element->Flags,
249                 Pass);
250         
251         if( Pass == 0 )
252         {
253                 if( Element->Flags & ELEFLAG_NORENDER ) return ;
254                 
255                 if( !(Element->Flags & ELEFLAG_ABSOLUTEPOS) ) {
256                         Element->CachedX = 0;
257                         Element->CachedY = 0;
258                 }
259                 if( !(Element->Flags & ELEFLAG_FIXEDSIZE) ) {
260                         Element->CachedW = 0;
261                         Element->CachedH = 0;
262                 }
263         }
264         
265         if( !(Element->Flags & ELEFLAG_FIXEDSIZE) ) {
266                 // If the element is sized, fix its dimension(s)
267                 if(Element->Size)
268                 {
269                         if(Element->Flags & ELEFLAG_NOEXPAND)
270                         {
271                                 Element->CachedW = Element->Size;
272                                 Element->CachedH = Element->Size;
273                         }
274                         else {
275                                 if( Element->Parent->Flags & ELEFLAG_VERTICAL ) {
276                                         Element->CachedH = Element->Size;
277                                         Element->CachedW = Element->Parent->CachedW;
278                                         if(Element->CachedW)
279                                                 Element->CachedW -= (Element->Parent->PaddingL + Element->Parent->PaddingR);
280                                 }
281                                 else {
282                                         Element->CachedW = Element->Size;
283                                         Element->CachedH = Element->Parent->CachedH;
284                                         if(Element->CachedH)
285                                                 Element->CachedH -= (Element->Parent->PaddingT + Element->Parent->PaddingB);
286                                 }
287                         }
288                 }
289                 else {
290                         // Ok, so now we need to calculate the size of all child elements
291                         // However, if ELEFLAG_NOEXPAND is not set, we can still set one
292                         // dimension
293                         if( !(Element->Flags & ELEFLAG_NOEXPAND) ) {
294                                 if( Element->Parent->Flags & ELEFLAG_VERTICAL ) {
295                                         Element->CachedW = Element->Parent->CachedW;
296                                         if(Element->CachedW)
297                                                 Element->CachedW -= (Element->Parent->PaddingL + Element->Parent->PaddingR);
298                                 }
299                                 else {
300                                         Element->CachedH = Element->Parent->CachedH;
301                                         if(Element->CachedH)
302                                                 Element->CachedH -= (Element->Parent->PaddingT + Element->Parent->PaddingB);
303                                 }
304                         }
305                 }
306         }
307         
308         // Process Children (first pass)
309         for( child = Element->FirstChild; child; child = child->NextSibling )
310         {
311                 if( child->Flags & ELEFLAG_NORENDER )   continue;
312                 WM_UpdateDimensions(child, 0);
313                 
314                 // Children that don't inherit positions are ignored
315                 if( child->Flags & ELEFLAG_ABSOLUTEPOS )        continue;
316                 
317                 fixedChildSize += child->Size;
318                 if(child->Size > 0)
319                         nFixed ++;
320                 nChildren ++;
321                 
322                 // If we are wrapping the children, get the largest cross size
323                 if( !(Element->Flags & ELEFLAG_FIXEDSIZE)
324                  && Element->Flags & ELEFLAG_NOEXPAND
325                  && Element->Size == 0 )
326                 {
327                         if( Element->Flags & ELEFLAG_VERTICAL ) {
328                                 if( Element->CachedW < child->CachedW )
329                                         Element->CachedW = child->CachedW;
330                         }
331                         else {
332                                 if( Element->CachedH < child->CachedH )
333                                         Element->CachedH = child->CachedH;
334                         }
335                 }
336         }
337         
338         // Let's avoid a #DIV0 shall we?
339         if( nChildren > 0 )
340         {
341                 // Calculate the size of dynamically sized children
342                 if( Element->Flags & ELEFLAG_VERTICAL ) {
343                         if( Element->CachedH == 0 ) {
344                                 if( nFixed == nChildren )
345                                         Element->CachedH = fixedChildSize;
346                                 else
347                                         return ;
348                         }
349                         dynamicSize = (Element->CachedH - (Element->PaddingT + Element->PaddingB)  - fixedChildSize) / nChildren;
350                 }
351                 else {
352                         if( Element->CachedW == 0 ) {
353                                 if( nFixed == nChildren )
354                                         Element->CachedW = fixedChildSize;
355                                 else
356                                         return ;
357                         }
358                         dynamicSize = (Element->CachedW - (Element->PaddingL + Element->PaddingR) - fixedChildSize) / nChildren;
359                 }
360                 
361                 // Process Children (second pass)
362                 for( child = Element->FirstChild; child; child = child->NextSibling )
363                 {
364                         if( child->Flags & ELEFLAG_NORENDER )   continue;
365                         // Children that don't inherit positions are ignored
366                         if( child->Flags & ELEFLAG_ABSOLUTEPOS )        continue;
367                         
368                         if(!child->Size) {
369                                 if(child->Flags & ELEFLAG_VERTICAL)
370                                         child->CachedH = dynamicSize;
371                                 else
372                                         child->CachedW = dynamicSize;
373                         }
374                         
375                         WM_UpdateDimensions(child, 1);
376                         
377                         // If we are wrapping the children, get the largest cross size
378                         if( Element->Flags & ELEFLAG_NOEXPAND ) {
379                                 if( Element->Flags & ELEFLAG_VERTICAL ) {
380                                         if( Element->CachedW < child->CachedW )
381                                                 Element->CachedW = child->CachedW;
382                                 }
383                                 else {
384                                         if( Element->CachedH < child->CachedH )
385                                                 Element->CachedH = child->CachedH;
386                                 }
387                         }
388                 }
389         }
390         
391         // Add the padding
392         //Element->CachedW += Element->PaddingL + Element->PaddingR;
393         //Element->CachedH += Element->PaddingT + Element->PaddingB;
394         
395         _SysDebug("Pass %i, Element %p %ix%i",
396                 Pass, Element, Element->CachedW, Element->CachedH
397                 );
398         
399         // We should be done
400         // Next function will do the coordinates
401 }
402
403
404 /**
405  * \brief Updates the position of an element
406  * 
407  * The parent element sets the positions of its children
408  */
409 void WM_UpdatePosition(tElement *Element)
410 {
411         tElement        *child;
412          int    x, y;
413         
414         if( Element->Flags & ELEFLAG_NORENDER ) return ;
415         
416         _SysDebug("Element=%p{PaddingL:%i, PaddingT:%i}",
417                 Element, Element->PaddingL, Element->PaddingT);
418         
419         // Initialise
420         x = Element->CachedX + Element->PaddingL;
421         y = Element->CachedY + Element->PaddingT;
422         
423         // Update each child
424         for(child = Element->FirstChild; child; child = child->NextSibling)
425         {
426                 child->CachedX = x;
427                 child->CachedY = y;
428                 
429                 // Set Alignment
430                 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
431                         if(Element->Flags & ELEFLAG_VERTICAL )
432                                 child->CachedX += Element->CachedW/2 - child->CachedW/2;
433                         else
434                                 child->CachedY += Element->CachedH/2 - child->CachedH/2;
435                 }
436                 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
437                         if(Element->Flags & ELEFLAG_VERTICAL )
438                                 child->CachedX += Element->CachedW - child->CachedW;
439                         else
440                                 child->CachedY += Element->CachedH - child->CachedH;
441                 }
442                 
443                 // Update child's children positions
444                 WM_UpdatePosition(child);
445                 
446                 // Increment
447                 if(Element->Flags & ELEFLAG_VERTICAL ) {
448                         y += child->CachedH + Element->GapSize;
449                 }
450                 else {
451                         x += child->CachedW + Element->GapSize;
452                 }
453         }
454         
455         _SysDebug("Element %p (%i,%i)",
456                  Element, Element->CachedX, Element->CachedY
457                 );
458 }
459
460 // --- Render ---
461 void WM_RenderWidget(tElement *Element)
462 {
463         tElement        *child;
464         
465         if( Element->Flags & ELEFLAG_NORENDER ) return ;
466         if( Element->Flags & ELEFLAG_INVISIBLE )        return ;
467         
468         Decorator_RenderWidget(Element);
469         
470         for(child = Element->FirstChild; child; child = child->NextSibling)
471         {
472                 WM_RenderWidget(child);
473         }
474 }
475
476 void WM_Update(void)
477 {
478         gWM_RootElement.CachedX = 0;    gWM_RootElement.CachedY = 0;
479         gWM_RootElement.CachedW = giScreenWidth;
480         gWM_RootElement.CachedH = giScreenHeight;
481         gWM_RootElement.Flags |= ELEFLAG_NOEXPAND|ELEFLAG_ABSOLUTEPOS|ELEFLAG_FIXEDSIZE;
482         
483         WM_UpdateDimensions( &gWM_RootElement, 0 );
484         WM_UpdatePosition( &gWM_RootElement );
485         WM_RenderWidget( &gWM_RootElement );
486         
487         Video_Update();
488 }

UCC git Repository :: git.ucc.asn.au