Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals  

charlayout.c

Go to the documentation of this file.
00001 /*
00002  *  charlayout.c - Character-based layout algorithm.
00003  *                 This file is part of the FreeLCD package.
00004  *  
00005  *  $Id: charlayout_8c-source.html,v 1.1 2003/02/16 22:50:41 unicorn Exp $
00006  *
00007  *  This program is free software; you can redistribute it and/or modify it
00008  *  under the terms of the GNU General Public License as published by the
00009  *  Free Software Foundation; either version 2 of the License, or (at your
00010  *  option) any later version.
00011  * 
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00020  *  MA  02111-1307  USA
00021  *
00022  *  Copyright (c) 2002, 2003, Jeroen van den Berg <unicorn@hippie.nu>
00023  */
00024 
00028 
00029 #include <stdio.h>
00030 #include <string.h>
00031 #include <assert.h>
00032 
00033 #include <common/xmalloc.h>
00034 #include "charrenderer.h"
00035 
00036 /*------------------------------------------------------------------------*/
00037 /* XML dictionaries */
00038  
00039 typedef enum
00040 {
00041   ROW = 1, COL, FIELD
00042 } 
00043 tag_t;
00044 
00045 static tag_t cl_tagindex_[] =
00046 {
00047   ROW, COL, FIELD
00048 };
00049 
00050 static dict_pair cl_tags_d[] = 
00051 {
00052   { "col",   &(cl_tagindex_[1]) },
00053   { "field", &(cl_tagindex_[2]) },
00054   { "row",   &(cl_tagindex_[0]) }
00055 };  
00056 
00057 static dictionary cd_tags = 
00058                        { cl_tags_d, sizeof (cl_tags_d) / sizeof (dict_pair) };
00059 
00060 
00061 typedef enum
00062 {
00063   TYPE, ID, NAME, VALIGN, HALIGN, EXPAND
00064 }
00065 attr_t;
00066 
00067 static attr_t cl_attrindex_[] =
00068 {
00069   TYPE, ID, NAME, VALIGN, HALIGN, EXPAND
00070 };
00071 
00072 static dict_pair cl_attr_d[] =
00073 {
00074   { "expand", &(cl_attrindex_[5])  },
00075   { "halign", &(cl_attrindex_[4])  },
00076   { "id",     &(cl_attrindex_[1])  },
00077   { "name",   &(cl_attrindex_[2])  },
00078   { "type",   &(cl_attrindex_[0])  },
00079   { "valign", &(cl_attrindex_[3])  }
00080 };
00081 
00082 static dictionary cd_attrs = 
00083                      { cl_attr_d, sizeof (cl_attr_d) / sizeof (dict_pair) };
00084 
00085 
00086 /*------------------------------------------------------------------------
00087  * Layout data structure type definitions
00088  *------------------------------------------------------------------------
00089  */
00090 
00092 typedef enum
00093 {
00094   ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTERED
00095 }
00096 align_hor_t;
00097 
00099 typedef enum
00100 {
00101   ALIGN_TOP, ALIGN_BOTTOM, ALIGN_MIDDLE
00102 }
00103 align_ver_t;
00104 
00105 /*------------------------------------------------------------------------
00106  * Widget data structure                                                  
00107  *------------------------------------------------------------------------
00108  */
00109 
00111 typedef enum
00112 {
00113   CROW,     
00114   CCOL,     
00115   CFIELD    
00116 } 
00117 widget_class_t;
00118 
00126 typedef struct _widget_size_hint
00127 {
00128   int width;  
00129   int height; 
00130   int penalty; 
00135   struct _widget_size_hint **children_layout;
00136 }
00137 widget_size_hint;
00138 
00139 typedef struct
00140 {
00141   widget_class_t type; 
00142   slist children;      
00143   slist layout_hints;  
00150   int expand_x;
00151   int expand_y; 
00157   align_hor_t align_hor;
00158   align_ver_t align_ver;  
00161   void *data;
00162 }
00163 cl_widget;
00164 
00165 
00167 typedef struct
00168 {
00169   slist      *list;
00170   slist_iter iter;
00171 }
00172 list_iter_pair;
00173 
00174 void _widget_calc_hints (void *widget_, void *dummy);
00175 
00176 /*-------------------------------------------------------- _widget_init --*/
00180 static void
00181 _widget_init (cl_widget *widget, widget_class_t type)
00182 {
00183   widget->type = type;
00184   slist_init (&widget->children);
00185   slist_init (&widget->layout_hints);
00186   widget->expand_x = 0;
00187   widget->expand_y = 0;
00188   widget->data = 0;
00189 }
00190 
00195 static int
00196 _compare_hints (const void* _data, const void* _compare)
00197 {
00198   widget_size_hint *data    = (widget_size_hint*)_data;
00199   widget_size_hint *compare = (widget_size_hint*)_compare;
00200 
00201   return (data->width == compare->width && data->height == compare->height);
00202 }
00203 
00206 static void
00207 _hint_init (widget_size_hint *hint)
00208 {
00209   hint->width = 0;
00210   hint->height = 0;
00211   hint->penalty = 0;
00212   hint->children_layout = 0;
00213 }
00214 
00218 static widget_size_hint *
00219 _hint_dup (widget_size_hint *hint)
00220 {
00221   widget_size_hint *new_hint = xmalloc (sizeof (widget_size_hint));
00222 
00223   new_hint->width = hint->width;
00224   new_hint->height = hint->height;
00225   new_hint->penalty = hint->penalty;
00226   new_hint->children_layout = 0;
00227   
00228   return new_hint;
00229 }
00230 
00231 void
00232 _layout_strategy_row (widget_size_hint *hint, widget_size_hint *new_hint)
00233 {
00234   if (hint->height > new_hint->height) 
00235     new_hint->height = hint->height;
00236   
00237   new_hint->width += hint->width;
00238 }
00239 
00240 void
00241 _layout_strategy_col (widget_size_hint *hint, widget_size_hint *new_hint)
00242 {
00243   if (hint->width > new_hint->width) 
00244     new_hint->width = hint->width;
00245   
00246   new_hint->height += hint->height;
00247 }
00248 
00249 void
00250 _widget_calc_penalties (cl_widget *widget, 
00251                         void(*strategy)(widget_size_hint*,
00252                                         widget_size_hint*))
00253 {
00254   slist_iter     iter;
00255   list_iter_pair *pairs;
00256   int            index_; /* "index" was already taken by string.h */
00257   int            array_size;
00258   
00259   /* All children are instructed to gather their own layout hints. */
00260   slist_for_each (&widget->children, _widget_calc_hints, 0);
00261 
00262   /* Reserve enough memory for storing the iterators to the children's
00263    * lists of layout hints, and store them in the pairs array. */
00264   array_size = slist_length (&widget->children);
00265   pairs = xmalloc (sizeof (list_iter_pair) * array_size);
00266   
00267   iter = slist_begin_iter (&widget->children);
00268   index_ = 0;
00269   while (!slist_iter_at_end (iter))
00270     {
00271       cl_widget *child = (cl_widget*)slist_iter_and_next (&iter);
00272       pairs[index_].list = &child->layout_hints;
00273       pairs[index_].iter = slist_begin_iter (&child->layout_hints);
00274       ++index_;
00275     }
00276 
00277   /* Loop until we have exhausted all layout combinations. */
00278   for (;;) 
00279     {
00280       widget_size_hint new_hint;
00281       slist_iter found;
00282 
00283       _hint_init (&new_hint);
00284       
00285       /* Try the current combination and see what turns up. */
00286       for (index_ = 0; index_ < array_size; ++index_)
00287         {
00288           widget_size_hint *hint;
00289           hint = (widget_size_hint*)slist_at_iter (pairs[index_].iter);
00290           strategy (hint, &new_hint);
00291           new_hint.penalty += hint->penalty;
00292         }
00293 
00294       /* See if the final dimensions were already in the list. */
00295       found = slist_find_if (&widget->layout_hints, 
00296                              &new_hint, _compare_hints);
00297 
00298       if (slist_iter_at_end (found))
00299         {
00300           widget_size_hint *copy = _hint_dup (&new_hint);
00301           widget_size_hint **array;
00302 
00303           array = xmalloc (sizeof (widget_size_hint*) * array_size);
00304           for (index_ = 0; index_ < array_size; ++index_)
00305             array[index_] = slist_at_iter (pairs[index_].iter);
00306           
00307           copy->children_layout = array;
00308           slist_append (&widget->layout_hints, copy);
00309         }
00310       else
00311         {
00312           widget_size_hint *found_hint = slist_at_iter (found);
00313 
00314           if (found_hint->penalty > new_hint.penalty)
00315             {
00316               widget_size_hint **array = found_hint->children_layout;
00317 
00318               found_hint->penalty = new_hint.penalty;
00319               for (index_ = 0; index_ < array_size; ++index_)
00320                 array[index_] = slist_at_iter (pairs[index_].iter);
00321             }
00322         }
00323       
00324       /* Find the next combination of children layouts. */
00325       for (index_ = 0; index_ < array_size; ++index_)
00326         {
00327           slist_iter_and_next (&pairs[index_].iter);
00328           
00329           if (slist_iter_at_end (pairs[index_].iter))
00330               pairs[index_].iter = slist_begin_iter (pairs[index_].list);
00331           else
00332               break;
00333         }
00334 
00335       /* Exit the loop if the possibilities are exhausted. */
00336       if (index_ == array_size)
00337         break;
00338     }
00339 }
00340 
00341 /*-------------------------------------------------- _widget_calc_hints --*/
00342 void
00343 _widget_calc_hints (void *widget_, void *dummy)
00344 {
00345   cl_widget *widget = (cl_widget*)widget_;
00346   (void)dummy;
00347   
00348   switch (widget->type)
00349     {
00350     case CROW:
00351       _widget_calc_penalties (widget, _layout_strategy_row);
00352       break;
00353 
00354     case CCOL:
00355       _widget_calc_penalties (widget, _layout_strategy_col);
00356       break;
00357 
00358     case CFIELD:
00359       {
00360         widget_size_hint *new_hint = xmalloc (sizeof (widget_size_hint));
00361         new_hint->width   = strlen ((char*)(widget->data));
00362         new_hint->height  = 1;
00363         new_hint->penalty = 0;
00364         slist_append (&widget->layout_hints, new_hint);
00365 
00366         new_hint = xmalloc (sizeof (widget_size_hint));
00367         new_hint->width   = strlen ((char*)(widget->data)) / 2;
00368         new_hint->height  = 1;
00369         new_hint->penalty = 1;
00370         slist_append (&widget->layout_hints, new_hint);
00371 
00372         new_hint = xmalloc (sizeof (widget_size_hint));
00373         new_hint->width   = 0;
00374         new_hint->height  = 0;
00375         new_hint->penalty = 3;
00376         slist_append (&widget->layout_hints, new_hint);
00377       }
00378       break;
00379 
00380     default:
00381       assert (0);
00382     }
00383 }
00384 
00385 int
00386 _widget_allocate_space (cl_widget *widget, int w, int h, int x, int y, 
00387                         cl_layout* layout)
00388 {
00389   widget_size_hint *best_hint = 0;
00390   unsigned int index_;
00391   
00392   slist_iter iter = slist_begin_iter (&widget->layout_hints);
00393   while (!slist_iter_at_end (iter))
00394     {
00395       widget_size_hint *hint = slist_iter_and_next (&iter);
00396       if (hint->width > w || hint->height > h)
00397         continue;
00398       
00399       if (best_hint == 0 || best_hint->penalty > hint->penalty)
00400         best_hint = hint;
00401     }
00402 
00403   if (best_hint == 0)
00404     return 0;
00405   
00406   index_ = 0;
00407   iter = slist_begin_iter (&widget->children);
00408   while (!slist_iter_at_end (iter))
00409     {
00410       cl_widget* child = slist_iter_and_next (&iter);
00411       widget_size_hint *best_layout = best_hint->children_layout[index_];
00412       _widget_allocate_space (child, best_layout->width, best_layout->height,
00413                               x, y, layout);
00414 
00415       switch (widget->type)
00416         {
00417         case CROW:
00418           x += best_layout->width; break;
00419         case CCOL:
00420           y += best_layout->height; break;
00421         case CFIELD:
00422           break;
00423         }
00424       
00425       ++index_;
00426     }
00427 
00428   if (widget->type == CFIELD)
00429     {
00430       cl_layout_elem *elem = layout->layout_data + layout->layout_data_size++;
00431       elem->x = x;
00432       elem->y = y;
00433       elem->width = w;
00434       elem->height = h;
00435       elem->utf8_data = strdup (widget->data);
00436     }
00437   
00438   return 1;
00439 }
00440  
00441 /*------------------------------------------------- _is_layout_or_field --*/
00442 static int
00443 _is_layout_or_field (xml_node *node)
00444 {
00445   int tag = node->tag;
00446   return tag == ROW || tag == COL || tag == FIELD;
00447 }
00448 
00449 /*------------------------------------------------------- _parse_widget --*/
00450 static cl_widget *
00451 _parse_widget (xml_node *node)
00452 {
00453   cl_widget   *result = xmalloc (sizeof (cl_widget));
00454   xml_node    *iter   = 0;
00455 
00456   switch (node->tag)
00457     {
00458     case ROW:
00459       _widget_init (result, CROW); break;
00460     case COL:
00461       _widget_init (result, CCOL); break;
00462     case FIELD:
00463       _widget_init (result, CFIELD); 
00464       result->data = strdup("Test"); 
00465       break;
00466     default:
00467       return 0;
00468     }
00469 
00470   if (node->tag != FIELD)
00471     {
00472       while ((iter = xmlt_find_if (node, iter, _is_layout_or_field)) != 0)
00473         slist_append (&result->children, (void *)_parse_widget (iter));
00474     }
00475   return result; 
00476 }
00477 
00478 /*------------------------------------------------------ cl_make_layout --*/
00479 cl_layout *
00480 cl_make_layout (xml_node *layout)
00481 {
00482   cl_widget *widgets = 0;
00483   cl_layout *result = xmalloc (sizeof (cl_layout)); 
00484   xml_node  *node = 0;
00485   
00486   assert (layout);
00487 
00488   result->layout_data_size = 0;
00489   result->layout_data = xmalloc (sizeof (cl_layout_elem) * 20 * 4);
00490 
00491   /* The XML data is not scanned for our namespace yet. */
00492   xmlt_rescan_document (layout, &cd_tags, &cd_attrs);
00493 
00494   /* Make a preliminary layout */
00495   node = xmlt_find_if (layout, node, _is_layout_or_field);
00496   if (!node)
00497     return 0;
00498 
00499   widgets = _parse_widget (node);
00500   _widget_calc_hints (widgets, 0);
00501   if (!_widget_allocate_space (widgets, 20, 4, 0, 0, result))
00502     printf ("Cannot fit elements.\n");
00503   
00504   return result;
00505 }
00506 

Generated on Sun Feb 16 23:39:49 2003 for FreeLCD by doxygen1.2.18