Fixing bugs exposed by scan-build
[tpg/acess2.git] / Usermode / Applications / axwin3_src / WM / renderers / widget.c
index 1bd437d..7668cbd 100644 (file)
@@ -14,6 +14,7 @@
 #include "widget/common.h"
 
 #define DEFAULT_ELETABLE_SIZE  64
+#define BORDER_EVERYTHING      0
 
 // === PROTOTYPES ===
  int   Renderer_Widget_Init(void);
@@ -42,12 +43,21 @@ tWMRenderer gRenderer_Widget = {
 // --- Element callbacks
 tWidgetDef     *gaWM_WidgetTypes[NUM_ELETYPES];
 const int      ciWM_NumWidgetTypes = sizeof(gaWM_WidgetTypes)/sizeof(gaWM_WidgetTypes[0]);
+tWidgetDef     gWidget_NullWidgetDef;
 
 // === CODE ===
 int Renderer_Widget_Init(void)
 {
+        int    i;
        WM_RegisterRenderer(&gRenderer_Widget); 
 
+       for(i = 0; i < ciWM_NumWidgetTypes; i ++)
+       {
+               if(gaWM_WidgetTypes[i] != NULL) continue;
+               
+               gaWM_WidgetTypes[i] = &gWidget_NullWidgetDef;
+       }
+
        return 0;
 }
 
@@ -60,7 +70,8 @@ void Widget_int_SetTypeDef(int Type, tWidgetDef *Ptr)
                return ;
        }
        
-       if( gaWM_WidgetTypes[Type] ) {
+       if( gaWM_WidgetTypes[Type] && gaWM_WidgetTypes[Type] != &gWidget_NullWidgetDef )
+       {
                _SysDebug("ERROR - Widget ID %i redefined by %p",
                        Type, __builtin_return_address(0)
                        );
@@ -68,7 +79,6 @@ void Widget_int_SetTypeDef(int Type, tWidgetDef *Ptr)
        }
        
        gaWM_WidgetTypes[Type] = Ptr;
-       _SysDebug("Registered type %i to %p", Type, Ptr);
 }
 
 tWindow        *Renderer_Widget_Create(int Flags)
@@ -87,6 +97,7 @@ tWindow       *Renderer_Widget_Create(int Flags)
        info = ret->RendererInfo;
        
        info->TableSize = eletable_size;
+       info->FocusedElement = &info->RootElement;
        info->RootElement.Window = ret;
        info->RootElement.ID = -1;
        info->RootElement.BackgroundColour = 0xCCCCCC;
@@ -102,7 +113,7 @@ tWindow     *Renderer_Widget_Create(int Flags)
 void Renderer_Widget_Redraw(tWindow *Window)
 {
        tWidgetWin      *info = Window->RendererInfo;
-       WM_Render_FillRect(Window, 0, 0, 0xFFF, 0xFFF, info->RootElement.BackgroundColour);
+       WM_Render_FillRect(Window, 0, 0, Window->W, Window->H, info->RootElement.BackgroundColour);
 
        Widget_UpdateDimensions(&info->RootElement);
        Widget_UpdatePosition(&info->RootElement);
@@ -118,17 +129,19 @@ void Widget_RenderWidget(tWindow *Window, tElement *Element)
        if( Element->Flags & ELEFLAG_NORENDER ) return ;
        if( Element->Flags & ELEFLAG_INVISIBLE )        return ;
 
-       if( Element->Type < ciWM_NumWidgetTypes
-        && gaWM_WidgetTypes[Element->Type]
-        && gaWM_WidgetTypes[Element->Type]->Render
-           )
+       #if BORDER_EVERYTHING
+       WM_Render_DrawRect(
+               Window,
+               Element->CachedX, Element->CachedY,
+               Element->CachedW, Element->CachedH,
+               0
+               );
+       #endif  
+
+       if(gaWM_WidgetTypes[Element->Type]->Render)
        {
                gaWM_WidgetTypes[Element->Type]->Render(Window, Element);
        }
-       else
-       {
-               Widget_Decorator_RenderWidget(Window, Element);
-       }
        
        for(child = Element->FirstChild; child; child = child->NextSibling)
        {
@@ -143,12 +156,20 @@ void Widget_UpdateDimensions(tElement *Element)
         int    nFixed = 0;
         int    maxCross = 0;
         int    fixedSize = 0;
-        int    fullCross, dynWith;
+        int    fullCross, dynWith = 0;
+        int    bVertical = Element->Flags & ELEFLAG_VERTICAL;
+
+       // Check if this element can have children
+       if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
+               return ;
        
        // Pass 1
        // - Get the fixed and minimum sizes of the element
        for( child = Element->FirstChild; child; child = child->NextSibling )
        {
+                int    minWith  = bVertical ? child->MinH : child->MinW;
+                int    minCross = bVertical ? child->MinW : child->MinH;
+
                // Ignore elements that will not be rendered
                if( child->Flags & ELEFLAG_NORENDER )   continue ;
                
@@ -164,81 +185,80 @@ void Widget_UpdateDimensions(tElement *Element)
                else if( child->Flags & ELEFLAG_NOSTRETCH )
                {
                        nFixed ++;
-                       fixedSize += child->MinWith;
+                       fixedSize += minWith;
                }
                
-               if( child->FixedCross && maxCross < child->FixedCross )
-                       maxCross = child->FixedCross;
-               if( child->MinCross && maxCross < child->MinCross )
-                       maxCross = child->MinCross;
+               if( maxCross < child->FixedCross )      maxCross = child->FixedCross;
+               if( maxCross < minCross )       maxCross = minCross;
                nChildren ++;
        }
 
        // Get the dynamic with size from the unused space in the element
        if( nChildren > nFixed ) {
-               if( Element->Flags & ELEFLAG_VERTICAL )
+               if( bVertical )
                        dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
                else
                        dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
                dynWith -= fixedSize;
+               dynWith -= Element->GapSize * (nChildren-1);
                if( dynWith < 0 )       return ;
                dynWith /= nChildren - nFixed;
        }
+       else {
+               dynWith = 0;
+       }
        
-//     _SysDebug("%i - nChildren = %i, nFixed = %i, dynWith = %i, fixedSize = %i",
-//             Element->ID, nChildren, nFixed, dynWith, fixedSize);
-
        // Get the cross size
-       if( Element->Flags & ELEFLAG_VERTICAL )
+       if( bVertical )
                fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
        else
                fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
+
+       _SysDebug("%i (p=%i) - WxH=%ix%i",
+               Element->ID, (Element->Parent ? Element->Parent->ID : -1),
+               Element->CachedW, Element->CachedH
+               );
+       _SysDebug("  %s dynWith = %i, fullCross = %i",
+               (Element->Flags & ELEFLAG_VERTICAL ? "Vert" : "Horiz"),
+               dynWith, fullCross
+               );
        
        // Pass 2 - Set sizes and recurse
        for( child = Element->FirstChild; child; child = child->NextSibling )
        {
-                int    cross, with;
+                int    w, h;
 
                // Ignore elements that will not be rendered
                if( child->Flags & ELEFLAG_NORENDER )   continue ;
+               // Don't resize floating elements
+               if( child->Flags & ELEFLAG_ABSOLUTEPOS )        continue ;
                
-               // --- Cross Size ---
-               // TODO: Expand to fill?
-               // TODO: Extra flag so options are (Expand, Equal, Wrap)
-               if( child->FixedCross )
-                       cross = child->FixedCross;
-               else if( child->Flags & ELEFLAG_NOEXPAND )
-                       cross = child->MinCross;
-               else
-                       cross = fullCross;
-               
-               // --- With Size ---
-               if( child->FixedWith )
-                       with = child->FixedWith;
-               else if( child->Flags & ELEFLAG_NOSTRETCH )
-                       with = child->MinWith;
+               // --- Width ---
+               if( child->Flags & ELEFLAG_NOEXPAND )
+                       w = child->MinW;
+               else if( bVertical )
+                       w = child->FixedCross ? child->FixedCross : fullCross;
                else
-                       with = dynWith;
+                       w = child->FixedWith ? child->FixedWith : dynWith;
        
+               // --- Height ---
+               if( child->Flags & ELEFLAG_NOSTRETCH )
+                       h = child->MinH;
+               else if( bVertical )
+                       h = child->FixedWith ? child->FixedWith : dynWith;
+               else
+                       h = child->FixedCross ? child->FixedCross : fullCross;
 
-               if(with < child->MinWith)       with = child->MinWith;
-               if(cross < child->MinCross)     cross = child->MinCross;
-               
+               if(w < child->MinW)     w = child->MinW;
+               if(h < child->MinH)     h = child->MinH;
+       
+//             _SysDebug("Child %ix%i (min %ix%i)", w, h, child->MinW, child->MinH);
+       
                // Update the dimensions if they have changed
-               if( Element->Flags & ELEFLAG_VERTICAL ) {
-                       // If no change, don't recurse
-                       if( child->CachedW == cross && child->CachedH == with )
-                               continue ;
-                       child->CachedW = cross;
-                       child->CachedH = with;
-               }
-               else {
-                       // If no change, don't recurse
-                       if( child->CachedW == with && child->CachedH == cross )
-                               continue ;
-                       child->CachedW = with;
-                       child->CachedH = cross;
-               }
+               if( child->CachedW == w && child->CachedH == h )
+                       continue ;
+               child->CachedW = w;
+               child->CachedH = h;
                
                // Force the positions of child elements to be recalculated
                child->CachedX = -1;
@@ -259,6 +279,10 @@ void Widget_UpdatePosition(tElement *Element)
        
        if( Element->Flags & ELEFLAG_NORENDER ) return ;
 
+       // Check if this element can have children
+       if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
+               return ;
+
 //     _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
 //             Element, Element->ID, Element->Type, Element->Flags);
        
@@ -275,7 +299,7 @@ void Widget_UpdatePosition(tElement *Element)
 
                newX = x; newY = y;
                
-               // Handle alignment
+               // Handle alignment (across parent)
                if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
                        if(Element->Flags & ELEFLAG_VERTICAL)
                                newX += Element->CachedW/2 - child->CachedW/2;
@@ -319,40 +343,46 @@ void Widget_UpdatePosition(tElement *Element)
 void Widget_UpdateMinDims(tElement *Element)
 {
        tElement        *child;
+        int    minW, minH;
+        int    nChildren;
        
        if(!Element)    return;
        
-       Element->MinCross = 0;
-       Element->MinWith = 0;
+       minW = 0;
+       minH = 0;
+       nChildren = 0;
        
        for(child = Element->FirstChild; child; child = child->NextSibling)
        {
-               if( Element->Parent &&
-                       (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
-                       )
+                int    cross;
+               
+               if(Element->Flags & ELEFLAG_NORENDER)   continue ;
+               
+               if( (Element->Flags & ELEFLAG_VERTICAL) )
                {
-                       if(child->FixedCross)
-                               Element->MinCross += child->FixedCross;
-                       else
-                               Element->MinCross += child->MinCross;
-                       if(child->FixedWith)
-                               Element->MinWith += child->FixedWith;
-                       else
-                               Element->MinWith += child->MinWith;
+                       cross = child->FixedCross ? child->FixedCross : child->MinW;
+                       if(minW < cross)        minW = cross;
+                       minH += child->FixedWith  ? child->FixedWith  : child->MinH;
                }
                else
                {
-                       if(child->FixedCross)
-                               Element->MinWith += child->FixedCross;
-                       else
-                               Element->MinWith += child->MinCross;
-                       if(child->FixedWith)
-                               Element->MinCross += child->FixedWith;
-                       else
-                               Element->MinCross += child->MinWith;
+                       cross = child->FixedCross ? child->FixedCross : child->MinH;
+                       minW += child->FixedWith  ? child->FixedWith  : child->MinW;
+                       if(minH < cross)        minH = cross;
                }
+//             _SysDebug("%i/%i cross = %i", Element->ID, child->ID, cross);
+       
+               nChildren ++;
        }
        
+       if( Element->Flags & ELEFLAG_VERTICAL )
+               minH += (nChildren - 1) * Element->GapSize;
+       else
+               minW += (nChildren - 1) * Element->GapSize;
+
+       Element->MinW = Element->PaddingL + minW + Element->PaddingR;
+       Element->MinH = Element->PaddingT + minH + Element->PaddingB;
+
        // Recurse upwards
        Widget_UpdateMinDims(Element->Parent);
 }
@@ -362,7 +392,7 @@ tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
        tElement        *ret, *next, *ele;
        
        next = &Info->RootElement;
-       while(next)
+       do
        {
                ret = next;
                next = NULL;
@@ -375,7 +405,7 @@ tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
                        if(Y >= ele->CachedY + ele->CachedH)    continue;
                        next = ele;
                }
-       }
+       } while(next);
        return ret;
 }
 
@@ -408,6 +438,12 @@ void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg
        _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
                Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
        
+       if(Msg->Type >= ciWM_NumWidgetTypes)
+       {
+               _SysDebug("Widget_NewWidget - Bad widget type %i", Msg->Type);
+               return ;
+       }
+
        // Create
        parent = Widget_GetElementById(Info, Msg->Parent);
        if(!parent)
@@ -433,7 +469,7 @@ void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg
        new->PaddingR = 2;
        new->CachedX = -1;
        
-       if( new->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[new->Type] && gaWM_WidgetTypes[new->Type]->Init )
+       if( gaWM_WidgetTypes[new->Type]->Init )
                gaWM_WidgetTypes[new->Type]->Init(new);
        
        // Add to parent's list
@@ -456,6 +492,13 @@ void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg
        Widget_UpdateMinDims(parent);
 }
 
+void Widget_SetFocus(tWidgetWin *Info, tElement *Ele)
+{
+       // TODO: Callbacks
+
+       Info->FocusedElement = Ele;
+}
+
 void Widget_SetFlags(tWidgetWin *Info, int Len, const tWidgetMsg_SetFlags *Msg)
 {
        tElement        *ele;
@@ -498,9 +541,7 @@ void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg)
        if(!ele)        return ;
 
 
-       if( ele->Type < ciWM_NumWidgetTypes
-        && gaWM_WidgetTypes[ele->Type]
-        && gaWM_WidgetTypes[ele->Type]->UpdateText )
+       if( gaWM_WidgetTypes[ele->Type]->UpdateText )
        {
                gaWM_WidgetTypes[ele->Type]->UpdateText( ele, Msg->Text );
        }
@@ -514,6 +555,7 @@ void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg)
 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
 {
        tWidgetWin      *info = Target->RendererInfo;
+       tElement        *ele;
        switch(Msg)
        {
        case WNDMSG_RESIZE: {
@@ -534,7 +576,6 @@ int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void
        case WNDMSG_MOUSEBTN: {
                const struct sWndMsg_MouseButton        *msg = Data;
                tWidgetMsg_MouseBtn     client_msg;
-               tElement        *ele;
                 int    x, y;
                 int    rv;
                
@@ -545,12 +586,11 @@ int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void
                client_msg.bPressed = msg->bPressed;
 
                ele = Widget_GetElementByPos(info, x, y);
+               Widget_SetFocus(info, ele);
                // Send event to all elements from `ele` upwards
                for( ; ele; ele = ele->Parent )
                {
-                       if(ele->Type < ciWM_NumWidgetTypes
-                        && gaWM_WidgetTypes[ele->Type]
-                        && gaWM_WidgetTypes[ele->Type]->MouseButton)
+                       if(gaWM_WidgetTypes[ele->Type]->MouseButton)
                        {
                                rv = gaWM_WidgetTypes[ele->Type]->MouseButton(
                                        ele,
@@ -571,15 +611,72 @@ int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void
                }
                return 0; }
 
+       case WNDMSG_KEYDOWN: {
+               const struct sWndMsg_KeyAction  *msg = Data;
+               if(Len < sizeof(*msg))  return -1;
+               
+               if(!info->FocusedElement)       return 0;
+               ele = info->FocusedElement;
+
+               if(gaWM_WidgetTypes[ele->Type]->KeyDown)
+                       gaWM_WidgetTypes[ele->Type]->KeyDown(ele, msg->KeySym, msg->UCS32);
+               else
+               {
+                       // TODO: Pass to user
+               }       
+
+               return 0; }
+       
+       case WNDMSG_KEYFIRE: {
+               const struct sWndMsg_KeyAction  *msg = Data;
+               if(Len < sizeof(*msg))  return -1;
+               
+               if(!info->FocusedElement)       return 0;
+               ele = info->FocusedElement;
+
+               if(gaWM_WidgetTypes[ele->Type]->KeyFire)
+                       gaWM_WidgetTypes[ele->Type]->KeyFire(ele, msg->KeySym, msg->UCS32);
+               else
+               {
+                       // TODO: Pass the buck
+               }
+               return 0; }
+       
+       case WNDMSG_KEYUP: {
+               const struct sWndMsg_KeyAction  *msg = Data;
+               if(Len < sizeof(*msg))  return -1;
+               
+               if(!info->FocusedElement)       return 0;
+               ele = info->FocusedElement;
+
+               if(gaWM_WidgetTypes[ele->Type]->KeyUp)
+                       gaWM_WidgetTypes[ele->Type]->KeyUp(ele, msg->KeySym);
+               else
+               {
+                       // TODO: Pass the buck
+               }
+               return 0; }
+
        // New Widget
        case MSG_WIDGET_CREATE:
                Widget_NewWidget(info, Len, Data);
                return 0;
 
+       // Delete a widget
        case MSG_WIDGET_DELETE:
                _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
                return 0;
 
+       // Set focused widget
+       case MSG_WIDGET_SETFOCUS: {
+               tElement        *ele;
+               const tWidgetMsg_SetFocus       *msg = Data;
+               if(Len < sizeof(*msg))  return -1;
+               
+               ele = Widget_GetElementById(info, msg->WidgetID);
+               Widget_SetFocus(info, ele);
+               return 0; }
+
        // Set Flags
        case MSG_WIDGET_SETFLAGS:
                Widget_SetFlags(info, Len, Data);

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