boxes.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Simple box operations */
00004 
00005 /* 
00006  * Copyright (C) 2005, 2006 Elijah Newren
00007  * [meta_rectangle_intersect() is copyright the GTK+ Team according to Havoc,
00008  * see gdkrectangle.c.  As far as Havoc knows, he probably wrote
00009  * meta_rectangle_equal(), and I'm guessing it's (C) Red Hat.  So...]
00010  * Copyright (C) 1995-2000  GTK+ Team
00011  * Copyright (C) 2002 Red Hat, Inc.
00012  * 
00013  * This program is free software; you can redistribute it and/or
00014  * modify it under the terms of the GNU General Public License as
00015  * published by the Free Software Foundation; either version 2 of the
00016  * License, or (at your option) any later version.
00017  *
00018  * This program is distributed in the hope that it will be useful, but
00019  * WITHOUT ANY WARRANTY; without even the implied warranty of
00020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00021  * General Public License for more details.
00022  * 
00023  * You should have received a copy of the GNU General Public License
00024  * along with this program; if not, write to the Free Software
00025  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00026  * 02111-1307, USA.
00027  */
00028 
00029 #include "boxes.h"
00030 #include "util.h"
00031 #include <X11/Xutil.h>  /* Just for the definition of the various gravities */
00032 
00033 char*
00034 meta_rectangle_to_string (const MetaRectangle *rect,
00035                           char                *output)
00036 {
00037   /* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit.
00038    * Should be more than enough space.  Note that of this space, the
00039    * trailing \0 will be overwritten for all but the last rectangle.
00040    */
00041   g_snprintf (output, RECT_LENGTH, "%d,%d +%d,%d", 
00042               rect->x, rect->y, rect->width, rect->height);
00043 
00044   return output;
00045 }
00046 
00047 char*
00048 meta_rectangle_region_to_string (GList      *region,
00049                                  const char *separator_string,
00050                                  char       *output)
00051 {
00052   /* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5
00053    * for each digit.  Should be more than enough space.  Note that of this
00054    * space, the trailing \0 will be overwritten for all but the last
00055    * rectangle.
00056    */
00057   char rect_string[RECT_LENGTH];
00058 
00059   GList *tmp = region;
00060   char *cur = output;
00061 
00062   if (region == NULL)
00063     g_snprintf (output, 10, "(EMPTY)");
00064 
00065   while (tmp)
00066     {
00067       MetaRectangle *rect = tmp->data;
00068       g_snprintf (rect_string, RECT_LENGTH, "[%d,%d +%d,%d]", 
00069                   rect->x, rect->y, rect->width, rect->height);
00070       cur = g_stpcpy (cur, rect_string);
00071       tmp = tmp->next;
00072       if (tmp)
00073         cur = g_stpcpy (cur, separator_string);
00074     }
00075 
00076   return output;
00077 }
00078 
00079 char*
00080 meta_rectangle_edge_to_string (const MetaEdge *edge,
00081                                char           *output)
00082 {
00083   /* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit.
00084    * Should be more than enough space.  Note that of this space, the
00085    * trailing \0 will be overwritten for all but the last rectangle.
00086    *
00087    * Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and
00088    * 2 more spaces, for a total of 10 more.
00089    */
00090   g_snprintf (output, EDGE_LENGTH, "[%d,%d +%d,%d], %2d, %2d", 
00091               edge->rect.x, edge->rect.y, edge->rect.width, edge->rect.height,
00092               edge->side_type, edge->edge_type);
00093 
00094   return output;
00095 }
00096 
00097 char*
00098 meta_rectangle_edge_list_to_string (GList      *edge_list,
00099                                     const char *separator_string,
00100                                     char       *output)
00101 {
00102   /* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5 for
00103    * each digit.  Should be more than enough space.  Note that of this
00104    * space, the trailing \0 will be overwritten for all but the last
00105    * rectangle.
00106    *
00107    * Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and
00108    * 2 more spaces, for a total of 10 more.
00109    */
00110   char rect_string[EDGE_LENGTH];
00111 
00112   char *cur = output;
00113   GList *tmp = edge_list;
00114 
00115   if (edge_list == NULL)
00116     g_snprintf (output, 10, "(EMPTY)");
00117 
00118   while (tmp)
00119     {
00120       MetaEdge      *edge = tmp->data;
00121       MetaRectangle *rect = &edge->rect;
00122       g_snprintf (rect_string, EDGE_LENGTH, "([%d,%d +%d,%d], %2d, %2d)", 
00123                   rect->x, rect->y, rect->width, rect->height,
00124                   edge->side_type, edge->edge_type);
00125       cur = g_stpcpy (cur, rect_string);
00126       tmp = tmp->next;
00127       if (tmp)
00128         cur = g_stpcpy (cur, separator_string);
00129     }
00130 
00131   return output;
00132 }
00133 
00134 MetaRectangle
00135 meta_rect (int x, int y, int width, int height)
00136 {
00137   MetaRectangle temporary;
00138   temporary.x = x;
00139   temporary.y = y;
00140   temporary.width  = width;
00141   temporary.height = height;
00142 
00143   return temporary;
00144 }
00145 
00146 int
00147 meta_rectangle_area (const MetaRectangle *rect)
00148 {
00149   g_return_val_if_fail (rect != NULL, 0);
00150   return rect->width * rect->height;
00151 }
00152 
00153 gboolean
00154 meta_rectangle_intersect (const MetaRectangle *src1,
00155                           const MetaRectangle *src2,
00156                           MetaRectangle *dest)
00157 {
00158   int dest_x, dest_y;
00159   int dest_w, dest_h;
00160   int return_val;
00161 
00162   g_return_val_if_fail (src1 != NULL, FALSE);
00163   g_return_val_if_fail (src2 != NULL, FALSE);
00164   g_return_val_if_fail (dest != NULL, FALSE);
00165 
00166   return_val = FALSE;
00167 
00168   dest_x = MAX (src1->x, src2->x);
00169   dest_y = MAX (src1->y, src2->y);
00170   dest_w = MIN (src1->x + src1->width, src2->x + src2->width) - dest_x;
00171   dest_h = MIN (src1->y + src1->height, src2->y + src2->height) - dest_y;
00172   
00173   if (dest_w > 0 && dest_h > 0)
00174     {
00175       dest->x = dest_x;
00176       dest->y = dest_y;
00177       dest->width = dest_w;
00178       dest->height = dest_h;
00179       return_val = TRUE;
00180     }
00181   else
00182     {
00183       dest->width = 0;
00184       dest->height = 0;
00185     }
00186 
00187   return return_val;
00188 }
00189 
00190 gboolean
00191 meta_rectangle_equal (const MetaRectangle *src1,
00192                       const MetaRectangle *src2)
00193 {
00194   return ((src1->x == src2->x) &&
00195           (src1->y == src2->y) &&
00196           (src1->width == src2->width) &&
00197           (src1->height == src2->height));
00198 }
00199 
00200 gboolean
00201 meta_rectangle_overlap (const MetaRectangle *rect1,
00202                         const MetaRectangle *rect2)
00203 {
00204   g_return_val_if_fail (rect1 != NULL, FALSE);
00205   g_return_val_if_fail (rect2 != NULL, FALSE);
00206 
00207   return !((rect1->x + rect1->width  <= rect2->x) ||
00208            (rect2->x + rect2->width  <= rect1->x) ||
00209            (rect1->y + rect1->height <= rect2->y) ||
00210            (rect2->y + rect2->height <= rect1->y));
00211 }
00212 
00213 gboolean
00214 meta_rectangle_vert_overlap (const MetaRectangle *rect1,
00215                              const MetaRectangle *rect2)
00216 {
00217   return (rect1->y < rect2->y + rect2->height &&
00218           rect2->y < rect1->y + rect1->height);
00219 }
00220 
00221 gboolean
00222 meta_rectangle_horiz_overlap (const MetaRectangle *rect1,
00223                               const MetaRectangle *rect2)
00224 {
00225   return (rect1->x < rect2->x + rect2->width &&
00226           rect2->x < rect1->x + rect1->width);
00227 }
00228 
00229 gboolean
00230 meta_rectangle_could_fit_rect (const MetaRectangle *outer_rect,
00231                                const MetaRectangle *inner_rect)
00232 {
00233   return (outer_rect->width  >= inner_rect->width &&
00234           outer_rect->height >= inner_rect->height);
00235 }
00236 
00237 gboolean
00238 meta_rectangle_contains_rect  (const MetaRectangle *outer_rect,
00239                                const MetaRectangle *inner_rect)
00240 {
00241   return 
00242     inner_rect->x                      >= outer_rect->x &&
00243     inner_rect->y                      >= outer_rect->y &&
00244     inner_rect->x + inner_rect->width  <= outer_rect->x + outer_rect->width &&
00245     inner_rect->y + inner_rect->height <= outer_rect->y + outer_rect->height;
00246 }
00247 
00248 void
00249 meta_rectangle_resize_with_gravity (const MetaRectangle *old_rect,
00250                                     MetaRectangle       *rect,
00251                                     int                  gravity,
00252                                     int                  new_width,
00253                                     int                  new_height)
00254 {
00255   /* FIXME: I'm too deep into this to know whether the below comment is
00256    * still clear or not now that I've moved it out of constraints.c.
00257    * boxes.h has a good comment, but I'm not sure if the below info is also
00258    * helpful on top of that (or whether it has superfluous info).
00259    */
00260  
00261   /* These formulas may look overly simplistic at first but you can work
00262    * everything out with a left_frame_with, right_frame_width,
00263    * border_width, and old and new client area widths (instead of old total
00264    * width and new total width) and you come up with the same formulas.
00265    *
00266    * Also, note that the reason we can treat NorthWestGravity and
00267    * StaticGravity the same is because we're not given a location at
00268    * which to place the window--the window was already placed
00269    * appropriately before.  So, NorthWestGravity for this function
00270    * means to just leave the upper left corner of the outer window
00271    * where it already is, and StaticGravity for this function means to
00272    * just leave the upper left corner of the inner window where it
00273    * already is.  But leaving either of those two corners where they
00274    * already are will ensure that the other corner is fixed as well
00275    * (since frame size doesn't change)--thus making the two
00276    * equivalent.
00277    */
00278 
00279   /* First, the x direction */
00280   int adjust = 0;
00281   switch (gravity)
00282     {
00283     case NorthWestGravity:
00284     case WestGravity:
00285     case SouthWestGravity:
00286       rect->x = old_rect->x;
00287       break;
00288 
00289     case NorthGravity:
00290     case CenterGravity:
00291     case SouthGravity:
00292       /* FIXME: Needing to adjust new_width kind of sucks, but not doing so
00293        * would cause drift.
00294        */
00295       new_width -= (old_rect->width - new_width) % 2;
00296       rect->x = old_rect->x + (old_rect->width - new_width)/2;
00297       break;
00298 
00299     case NorthEastGravity:
00300     case EastGravity:
00301     case SouthEastGravity:
00302       rect->x = old_rect->x + (old_rect->width - new_width);
00303       break;
00304 
00305     case StaticGravity:
00306     default:
00307       rect->x = old_rect->x;
00308       break;
00309     }
00310   rect->width = new_width;
00311   
00312   /* Next, the y direction */
00313   adjust = 0;
00314   switch (gravity)
00315     {
00316     case NorthWestGravity:
00317     case NorthGravity:
00318     case NorthEastGravity:
00319       rect->y = old_rect->y;
00320       break;
00321 
00322     case WestGravity:
00323     case CenterGravity:
00324     case EastGravity:
00325       /* FIXME: Needing to adjust new_height kind of sucks, but not doing so
00326        * would cause drift.
00327        */
00328       new_height -= (old_rect->height - new_height) % 2;
00329       rect->y = old_rect->y + (old_rect->height - new_height)/2;
00330       break;
00331 
00332     case SouthWestGravity:
00333     case SouthGravity:
00334     case SouthEastGravity:
00335       rect->y = old_rect->y + (old_rect->height - new_height);
00336       break;
00337 
00338     case StaticGravity:
00339     default:
00340       rect->y = old_rect->y;
00341       break;
00342     }
00343   rect->height = new_height;
00344 }
00345 
00346 /* Not so simple helper function for get_minimal_spanning_set_for_region() */
00347 static GList*
00348 merge_spanning_rects_in_region (GList *region)
00349 {
00350   /* NOTE FOR ANY OPTIMIZATION PEOPLE OUT THERE: Please see the
00351    * documentation of get_minimal_spanning_set_for_region() for performance
00352    * considerations that also apply to this function.
00353    */
00354 
00355   GList* compare;
00356   compare = region;
00357 
00358   if (region == NULL)
00359     {
00360       meta_warning ("Region to merge was empty!  Either you have a some "
00361                     "pathological STRUT list or there's a bug somewhere!\n");
00362       return NULL;
00363     }
00364 
00365   while (compare && compare->next)
00366     {
00367       MetaRectangle *a = compare->data;
00368       GList *other = compare->next;
00369 
00370       g_assert (a->width > 0 && a->height > 0);
00371 
00372       while (other)
00373         {
00374           MetaRectangle *b = other->data;
00375           GList *delete_me = NULL;
00376 
00377           g_assert (b->width > 0 && b->height > 0);
00378 
00379           /* If a contains b, just remove b */
00380           if (meta_rectangle_contains_rect (a, b))
00381             {
00382               delete_me = other;
00383             }
00384           /* If b contains a, just remove a */
00385           else if (meta_rectangle_contains_rect (a, b))
00386             {
00387               delete_me = compare;
00388             }
00389           /* If a and b might be mergeable horizontally */
00390           else if (a->y == b->y && a->height == b->height)
00391             {
00392               /* If a and b overlap */
00393               if (meta_rectangle_overlap (a, b))
00394                 {
00395                   int new_x = MIN (a->x, b->x);
00396                   a->width = MAX (a->x + a->width, b->x + b->width) - new_x;
00397                   a->x = new_x;
00398                   delete_me = other;
00399                 }
00400               /* If a and b are adjacent */
00401               else if (a->x + a->width == b->x || a->x == b->x + b->width)
00402                 {
00403                   int new_x = MIN (a->x, b->x);
00404                   a->width = MAX (a->x + a->width, b->x + b->width) - new_x;
00405                   a->x = new_x;
00406                   delete_me = other;
00407                 }
00408             }
00409           /* If a and b might be mergeable vertically */
00410           else if (a->x == b->x && a->width == b->width)
00411             {
00412               /* If a and b overlap */
00413               if (meta_rectangle_overlap (a, b))
00414                 {
00415                   int new_y = MIN (a->y, b->y);
00416                   a->height = MAX (a->y + a->height, b->y + b->height) - new_y;
00417                   a->y = new_y;
00418                   delete_me = other;
00419                 }
00420               /* If a and b are adjacent */
00421               else if (a->y + a->height == b->y || a->y == b->y + b->height)
00422                 {
00423                   int new_y = MIN (a->y, b->y);
00424                   a->height = MAX (a->y + a->height, b->y + b->height) - new_y;
00425                   a->y = new_y;
00426                   delete_me = other;
00427                 }
00428             }
00429 
00430           other = other->next;
00431 
00432           /* Delete any rectangle in the list that is no longer wanted */
00433           if (delete_me != NULL)
00434             {
00435               /* Deleting the rect we compare others to is a little tricker */
00436               if (compare == delete_me)
00437                 {
00438                   compare = compare->next;
00439                   other = compare->next;
00440                   a = compare->data;
00441                 }
00442 
00443               /* Okay, we can free it now */
00444               g_free (delete_me->data);
00445               region = g_list_delete_link (region, delete_me);
00446             }
00447 
00448         }
00449 
00450       compare = compare->next;
00451     }
00452 
00453   return region;
00454 }
00455 
00456 /* Simple helper function for get_minimal_spanning_set_for_region()... */
00457 static gint
00458 compare_rect_areas (gconstpointer a, gconstpointer b)
00459 {
00460   const MetaRectangle *a_rect = (gconstpointer) a;
00461   const MetaRectangle *b_rect = (gconstpointer) b;
00462 
00463   int a_area = meta_rectangle_area (a_rect);
00464   int b_area = meta_rectangle_area (b_rect);
00465 
00466   return b_area - a_area; /* positive ret value denotes b > a, ... */
00467 }
00468 
00469 /* This function is trying to find a "minimal spanning set (of rectangles)"
00470  * for a given region.
00471  *
00472  * The region is given by taking basic_rect, then removing the areas
00473  * covered by all the rectangles in the all_struts list, and then expanding
00474  * the resulting region by the given number of pixels in each direction.
00475  *
00476  * A "minimal spanning set (of rectangles)" is the best name I could come
00477  * up with for the concept I had in mind.  Basically, for a given region, I
00478  * want a set of rectangles with the property that a window is contained in
00479  * the region if and only if it is contained within at least one of the
00480  * rectangles.
00481  *
00482  * The GList* returned will be a list of (allocated) MetaRectangles.
00483  * The list will need to be freed by calling
00484  * meta_rectangle_free_spanning_set() on it (or by manually
00485  * implementing that function...)
00486  */
00487 GList*
00488 meta_rectangle_get_minimal_spanning_set_for_region (
00489   const MetaRectangle *basic_rect,
00490   const GSList  *all_struts)
00491 {
00492   /* NOTE FOR OPTIMIZERS: This function *might* be somewhat slow,
00493    * especially due to the call to merge_spanning_rects_in_region() (which
00494    * is O(n^2) where n is the size of the list generated in this function).
00495    * This is made more onerous due to the fact that it involves a fair
00496    * number of memory allocation and deallocation calls.  However, n is 1
00497    * for default installations of Gnome (because partial struts aren't used
00498    * by default and only partial struts increase the size of the spanning
00499    * set generated).  With one partial strut, n will be 2 or 3.  With 2
00500    * partial struts, n will probably be 4 or 5.  So, n probably isn't large
00501    * enough to make this worth bothering.  Further, it is only called from
00502    * workspace.c:ensure_work_areas_validated (at least as of the time of
00503    * writing this comment), which in turn should only be called if the
00504    * strut list changes or the screen or xinerama size changes.  If it ever
00505    * does show up on profiles (most likely because people start using
00506    * ridiculously huge numbers of partial struts), possible optimizations
00507    * include:
00508    *
00509    * (1) rewrite merge_spanning_rects_in_region() to be O(n) or O(nlogn).
00510    *     I'm not totally sure it's possible, but with a couple copies of
00511    *     the list and sorting them appropriately, I believe it might be.
00512    * (2) only call merge_spanning_rects_in_region() with a subset of the
00513    *     full list of rectangles.  I believe from some of my preliminary
00514    *     debugging and thinking about it that it is possible to figure out
00515    *     apriori groups of rectangles which are only merge candidates with
00516    *     each other.  (See testboxes.c:get_screen_region() when which==2
00517    *     and track the steps of this function carefully to see what gave
00518    *     me the hint that this might work)
00519    * (3) figure out how to avoid merge_spanning_rects_in_region().  I think
00520    *     it might be possible to modify this function to make that
00521    *     possible, and I spent just a little while thinking about it, but n
00522    *     wasn't large enough to convince me to care yet.
00523    * (4) Some of the stuff Rob mentioned at http://mail.gnome.org/archives\
00524    *     /metacity-devel-list/2005-November/msg00028.html.  (Sorry for the
00525    *     URL splitting.)
00526    */
00527 
00528   GList         *ret;
00529   GList         *tmp_list;
00530   const GSList  *strut_iter;
00531   MetaRectangle *temp_rect;
00532 
00533   /* The algorithm is basically as follows:
00534    *   Initialize rectangle_set to basic_rect
00535    *   Foreach strut:
00536    *     Foreach rectangle in rectangle_set:
00537    *       - Split the rectangle into new rectangles that don't overlap the
00538    *         strut (but which are as big as possible otherwise)
00539    *       - Remove the old (pre-split) rectangle from the rectangle_set,
00540    *         and replace it with the new rectangles generated from the
00541    *         splitting
00542    */
00543 
00544   temp_rect = g_new (MetaRectangle, 1);
00545   *temp_rect = *basic_rect;
00546   ret = g_list_prepend (NULL, temp_rect);
00547 
00548   strut_iter = all_struts;
00549   for (strut_iter = all_struts; strut_iter; strut_iter = strut_iter->next)
00550     {
00551       GList *rect_iter; 
00552       MetaRectangle *strut_rect = &((MetaStrut*)strut_iter->data)->rect;
00553 
00554       tmp_list = ret;
00555       ret = NULL;
00556       rect_iter = tmp_list;
00557       while (rect_iter)
00558         {
00559           MetaRectangle *rect = (MetaRectangle*) rect_iter->data;
00560           if (!meta_rectangle_overlap (rect, strut_rect))
00561             ret = g_list_prepend (ret, rect);
00562           else
00563             {
00564               /* If there is area in rect left of strut */
00565               if (BOX_LEFT (*rect) < BOX_LEFT (*strut_rect))
00566                 {
00567                   temp_rect = g_new (MetaRectangle, 1);
00568                   *temp_rect = *rect;
00569                   temp_rect->width = BOX_LEFT (*strut_rect) - BOX_LEFT (*rect);
00570                   ret = g_list_prepend (ret, temp_rect);
00571                 }
00572               /* If there is area in rect right of strut */
00573               if (BOX_RIGHT (*rect) > BOX_RIGHT (*strut_rect))
00574                 {
00575                   int new_x;
00576                   temp_rect = g_new (MetaRectangle, 1);
00577                   *temp_rect = *rect;
00578                   new_x = BOX_RIGHT (*strut_rect);
00579                   temp_rect->width = BOX_RIGHT(*rect) - new_x;
00580                   temp_rect->x = new_x;
00581                   ret = g_list_prepend (ret, temp_rect);
00582                 }
00583               /* If there is area in rect above strut */
00584               if (BOX_TOP (*rect) < BOX_TOP (*strut_rect))
00585                 {
00586                   temp_rect = g_new (MetaRectangle, 1);
00587                   *temp_rect = *rect;
00588                   temp_rect->height = BOX_TOP (*strut_rect) - BOX_TOP (*rect);
00589                   ret = g_list_prepend (ret, temp_rect);
00590                 }
00591               /* If there is area in rect below strut */
00592               if (BOX_BOTTOM (*rect) > BOX_BOTTOM (*strut_rect))
00593                 {
00594                   int new_y;
00595                   temp_rect = g_new (MetaRectangle, 1);
00596                   *temp_rect = *rect;
00597                   new_y = BOX_BOTTOM (*strut_rect);
00598                   temp_rect->height = BOX_BOTTOM (*rect) - new_y;
00599                   temp_rect->y = new_y;
00600                   ret = g_list_prepend (ret, temp_rect);
00601                 }
00602               g_free (rect);
00603             }
00604           rect_iter = rect_iter->next;
00605         }
00606       g_list_free (tmp_list);
00607     }
00608 
00609   /* Sort by maximal area, just because I feel like it... */
00610   ret = g_list_sort (ret, compare_rect_areas);
00611 
00612   /* Merge rectangles if possible so that the list really is minimal */
00613   ret = merge_spanning_rects_in_region (ret);
00614 
00615   return ret;
00616 }
00617 
00618 GList*
00619 meta_rectangle_expand_region (GList     *region,
00620                               const int  left_expand,
00621                               const int  right_expand,
00622                               const int  top_expand,
00623                               const int  bottom_expand)
00624 {
00625   return meta_rectangle_expand_region_conditionally (region,
00626                                                      left_expand,
00627                                                      right_expand,
00628                                                      top_expand,
00629                                                      bottom_expand,
00630                                                      0,
00631                                                      0);
00632 }
00633 
00634 GList*
00635 meta_rectangle_expand_region_conditionally (GList     *region,
00636                                             const int  left_expand,
00637                                             const int  right_expand,
00638                                             const int  top_expand,
00639                                             const int  bottom_expand,
00640                                             const int  min_x,
00641                                             const int  min_y)
00642 {
00643   GList *tmp_list = region;
00644   while (tmp_list)
00645     {
00646       MetaRectangle *rect = (MetaRectangle*) tmp_list->data;
00647       if (rect->width >= min_x)
00648         {
00649           rect->x      -= left_expand;
00650           rect->width  += (left_expand + right_expand);
00651         }
00652       if (rect->height >= min_y)
00653         {
00654           rect->y      -= top_expand;
00655           rect->height += (top_expand + bottom_expand);
00656         }
00657       tmp_list = tmp_list->next;
00658     }
00659 
00660   return region;
00661 }
00662 
00663 void
00664 meta_rectangle_expand_to_avoiding_struts (MetaRectangle       *rect,
00665                                           const MetaRectangle *expand_to,
00666                                           const MetaDirection  direction,
00667                                           const GSList        *all_struts)
00668 {
00669   const GSList *strut_iter;
00670 
00671   /* If someone wants this function to handle more fine-grained
00672    * direction expanding in the future (e.g. only left, or fully
00673    * horizontal plus upward), feel free.  But I'm hard-coding for both
00674    * horizontal directions (exclusive-)or both vertical directions.
00675    */
00676   g_assert ((direction == META_DIRECTION_HORIZONTAL) ^
00677             (direction == META_DIRECTION_VERTICAL  ));
00678  
00679   if (direction == META_DIRECTION_HORIZONTAL)
00680     {
00681       rect->x      = expand_to->x;
00682       rect->width  = expand_to->width;
00683     }
00684   else
00685     {
00686       rect->y      = expand_to->y;
00687       rect->height = expand_to->height;
00688     }
00689 
00690  
00691   /* Run over all struts */
00692   for (strut_iter = all_struts; strut_iter; strut_iter = strut_iter->next)
00693     {
00694       MetaStrut *strut = (MetaStrut*) strut_iter->data;
00695  
00696       /* Skip struts that don't overlap */
00697       if (!meta_rectangle_overlap (&strut->rect, rect))
00698         continue;
00699 
00700       if (direction == META_DIRECTION_HORIZONTAL)
00701         {
00702           if (strut->side == META_SIDE_LEFT)
00703             {
00704               int offset = BOX_RIGHT(strut->rect) - BOX_LEFT(*rect);
00705               rect->x     += offset;
00706               rect->width -= offset;
00707             }
00708           else if (strut->side == META_SIDE_RIGHT)
00709             {
00710               int offset = BOX_RIGHT (*rect) - BOX_LEFT(strut->rect);
00711               rect->width -= offset;
00712             }
00713           /* else ignore the strut */
00714         }
00715       else /* direction == META_DIRECTION_VERTICAL */
00716         {
00717           if (strut->side == META_SIDE_TOP)
00718             {
00719               int offset = BOX_BOTTOM(strut->rect) - BOX_TOP(*rect);
00720               rect->y      += offset;
00721               rect->height -= offset;
00722             }
00723           else if (strut->side == META_SIDE_BOTTOM)
00724             {
00725               int offset = BOX_BOTTOM(*rect) - BOX_TOP(strut->rect);
00726               rect->height -= offset;
00727             }
00728           /* else ignore the strut */
00729         }
00730     } /* end loop over struts */
00731 } /* end meta_rectangle_expand_to_avoiding_struts */
00732 
00733 void
00734 meta_rectangle_free_list_and_elements (GList *filled_list)
00735 {
00736   g_list_foreach (filled_list, 
00737                   (void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */
00738                   NULL);
00739   g_list_free (filled_list);
00740 }
00741 
00742 gboolean
00743 meta_rectangle_could_fit_in_region (const GList         *spanning_rects,
00744                                     const MetaRectangle *rect)
00745 {
00746   const GList *temp;
00747   gboolean     could_fit;
00748 
00749   temp = spanning_rects;
00750   could_fit = FALSE;
00751   while (!could_fit && temp != NULL)
00752     {
00753       could_fit = could_fit || meta_rectangle_could_fit_rect (temp->data, rect);
00754       temp = temp->next;
00755     }
00756 
00757   return could_fit;
00758 }
00759 
00760 gboolean
00761 meta_rectangle_contained_in_region (const GList         *spanning_rects,
00762                                     const MetaRectangle *rect)
00763 {
00764   const GList *temp;
00765   gboolean     contained;
00766 
00767   temp = spanning_rects;
00768   contained = FALSE;
00769   while (!contained && temp != NULL)
00770     {
00771       contained = contained || meta_rectangle_contains_rect (temp->data, rect);
00772       temp = temp->next;
00773     }
00774 
00775   return contained;
00776 }
00777 
00778 gboolean
00779 meta_rectangle_overlaps_with_region (const GList         *spanning_rects,
00780                                      const MetaRectangle *rect)
00781 {
00782   const GList *temp;
00783   gboolean     overlaps;
00784 
00785   temp = spanning_rects;
00786   overlaps = FALSE;
00787   while (!overlaps && temp != NULL)
00788     {
00789       overlaps = overlaps || meta_rectangle_overlap (temp->data, rect);
00790       temp = temp->next;
00791     }
00792 
00793   return overlaps;
00794 }
00795 
00796 
00797 void
00798 meta_rectangle_clamp_to_fit_into_region (const GList         *spanning_rects,
00799                                          FixedDirections      fixed_directions,
00800                                          MetaRectangle       *rect,
00801                                          const MetaRectangle *min_size)
00802 {
00803   const GList *temp;
00804   const MetaRectangle *best_rect = NULL;
00805   int                  best_overlap = 0;
00806 
00807   /* First, find best rectangle from spanning_rects to which we can clamp
00808    * rect to fit into.
00809    */
00810   for (temp = spanning_rects; temp; temp = temp->next)
00811     {
00812       MetaRectangle *compare_rect = temp->data;
00813       int            maximal_overlap_amount_for_compare;
00814       
00815       /* If x is fixed and the entire width of rect doesn't fit in compare,
00816        * skip this rectangle.
00817        */
00818       if ((fixed_directions & FIXED_DIRECTION_X) &&
00819           (compare_rect->x > rect->x || 
00820            compare_rect->x + compare_rect->width < rect->x + rect->width))
00821         continue;
00822         
00823       /* If y is fixed and the entire height of rect doesn't fit in compare,
00824        * skip this rectangle.
00825        */
00826       if ((fixed_directions & FIXED_DIRECTION_Y) &&
00827           (compare_rect->y > rect->y || 
00828            compare_rect->y + compare_rect->height < rect->y + rect->height))
00829         continue;
00830 
00831       /* If compare can't hold the min_size window, skip this rectangle. */
00832       if (compare_rect->width  < min_size->width ||
00833           compare_rect->height < min_size->height)
00834         continue;
00835 
00836       /* Determine maximal overlap amount */
00837       maximal_overlap_amount_for_compare =
00838         MIN (rect->width,  compare_rect->width) *
00839         MIN (rect->height, compare_rect->height);
00840 
00841       /* See if this is the best rect so far */
00842       if (maximal_overlap_amount_for_compare > best_overlap)
00843         {
00844           best_rect    = compare_rect;
00845           best_overlap = maximal_overlap_amount_for_compare;
00846         }
00847     }
00848 
00849   /* Clamp rect appropriately */
00850   if (best_rect == NULL)
00851     {
00852       meta_warning ("No rect whose size to clamp to found!\n");
00853 
00854       /* If it doesn't fit, at least make it no bigger than it has to be */
00855       if (!(fixed_directions & FIXED_DIRECTION_X))
00856         rect->width  = min_size->width;
00857       if (!(fixed_directions & FIXED_DIRECTION_Y))
00858         rect->height = min_size->height;
00859     }
00860   else
00861     {
00862       rect->width  = MIN (rect->width,  best_rect->width);
00863       rect->height = MIN (rect->height, best_rect->height);
00864     }
00865 }
00866 
00867 void
00868 meta_rectangle_clip_to_region (const GList         *spanning_rects,
00869                                FixedDirections      fixed_directions,
00870                                MetaRectangle       *rect)
00871 {
00872   const GList *temp;
00873   const MetaRectangle *best_rect = NULL;
00874   int                  best_overlap = 0;
00875 
00876   /* First, find best rectangle from spanning_rects to which we will clip
00877    * rect into.
00878    */
00879   for (temp = spanning_rects; temp; temp = temp->next)
00880     {
00881       MetaRectangle *compare_rect = temp->data;
00882       MetaRectangle  overlap;
00883       int            maximal_overlap_amount_for_compare;
00884      
00885       /* If x is fixed and the entire width of rect doesn't fit in compare,
00886        * skip the rectangle.
00887        */
00888       if ((fixed_directions & FIXED_DIRECTION_X) &&
00889           (compare_rect->x > rect->x || 
00890            compare_rect->x + compare_rect->width < rect->x + rect->width))
00891         continue;
00892         
00893       /* If y is fixed and the entire height of rect doesn't fit in compare,
00894        * skip the rectangle.
00895        */
00896       if ((fixed_directions & FIXED_DIRECTION_Y) &&
00897           (compare_rect->y > rect->y || 
00898            compare_rect->y + compare_rect->height < rect->y + rect->height))
00899         continue;
00900 
00901       /* Determine maximal overlap amount */
00902       meta_rectangle_intersect (rect, compare_rect, &overlap);
00903       maximal_overlap_amount_for_compare = meta_rectangle_area (&overlap);
00904 
00905       /* See if this is the best rect so far */
00906       if (maximal_overlap_amount_for_compare > best_overlap)
00907         {
00908           best_rect    = compare_rect;
00909           best_overlap = maximal_overlap_amount_for_compare;
00910         }
00911     }
00912 
00913   /* Clip rect appropriately */
00914   if (best_rect == NULL)
00915     meta_warning ("No rect to clip to found!\n");
00916   else
00917     {
00918       /* Extra precaution with checking fixed direction shouldn't be needed
00919        * due to logic above, but it shouldn't hurt either.
00920        */
00921       if (!(fixed_directions & FIXED_DIRECTION_X))
00922         {
00923           /* Find the new left and right */
00924           int new_x = MAX (rect->x, best_rect->x);
00925           rect->width = MIN ((rect->x + rect->width)           - new_x,
00926                              (best_rect->x + best_rect->width) - new_x);
00927           rect->x = new_x;
00928         }
00929 
00930       /* Extra precaution with checking fixed direction shouldn't be needed
00931        * due to logic above, but it shouldn't hurt either.
00932        */
00933       if (!(fixed_directions & FIXED_DIRECTION_Y))
00934         {
00935           /* Clip the top, if needed */
00936           int new_y = MAX (rect->y, best_rect->y);
00937           rect->height = MIN ((rect->y + rect->height)           - new_y,
00938                               (best_rect->y + best_rect->height) - new_y);
00939           rect->y = new_y;
00940         }
00941     }
00942 }
00943 
00944 void
00945 meta_rectangle_shove_into_region (const GList         *spanning_rects,
00946                                   FixedDirections      fixed_directions,
00947                                   MetaRectangle       *rect)
00948 {
00949   const GList *temp;
00950   const MetaRectangle *best_rect = NULL;
00951   int                  best_overlap = 0;
00952   int                  shortest_distance = G_MAXINT;
00953 
00954   /* First, find best rectangle from spanning_rects to which we will shove
00955    * rect into.
00956    */
00957   
00958   for (temp = spanning_rects; temp; temp = temp->next)
00959     {
00960       MetaRectangle *compare_rect = temp->data;
00961       int            maximal_overlap_amount_for_compare;
00962       int            dist_to_compare;
00963       
00964       /* If x is fixed and the entire width of rect doesn't fit in compare,
00965        * skip this rectangle.
00966        */
00967       if ((fixed_directions & FIXED_DIRECTION_X) &&
00968           (compare_rect->x > rect->x || 
00969            compare_rect->x + compare_rect->width < rect->x + rect->width))
00970         continue;
00971         
00972       /* If y is fixed and the entire height of rect doesn't fit in compare,
00973        * skip this rectangle.
00974        */
00975       if ((fixed_directions & FIXED_DIRECTION_Y) &&
00976           (compare_rect->y > rect->y || 
00977            compare_rect->y + compare_rect->height < rect->y + rect->height))
00978         continue;
00979 
00980       /* Determine maximal overlap amount between rect & compare_rect */
00981       maximal_overlap_amount_for_compare =
00982         MIN (rect->width,  compare_rect->width) *
00983         MIN (rect->height, compare_rect->height);
00984 
00985       /* Determine distance necessary to put rect into compare_rect */
00986       dist_to_compare = 0;
00987       if (compare_rect->x > rect->x)
00988         dist_to_compare += compare_rect->x - rect->x;
00989       if (compare_rect->x + compare_rect->width < rect->x + rect->width)
00990         dist_to_compare += (rect->x + rect->width) -
00991                            (compare_rect->x + compare_rect->width);
00992       if (compare_rect->y > rect->y)
00993         dist_to_compare += compare_rect->y - rect->y;
00994       if (compare_rect->y + compare_rect->height < rect->y + rect->height)
00995         dist_to_compare += (rect->y + rect->height) -
00996                            (compare_rect->y + compare_rect->height);
00997 
00998       /* See if this is the best rect so far */
00999       if ((maximal_overlap_amount_for_compare > best_overlap) ||
01000           (maximal_overlap_amount_for_compare == best_overlap &&
01001            dist_to_compare                    <  shortest_distance))
01002         {
01003           best_rect         = compare_rect;
01004           best_overlap      = maximal_overlap_amount_for_compare;
01005           shortest_distance = dist_to_compare;
01006         }
01007     }
01008 
01009   /* Shove rect appropriately */
01010   if (best_rect == NULL)
01011     meta_warning ("No rect to shove into found!\n");
01012   else
01013     {
01014       /* Extra precaution with checking fixed direction shouldn't be needed
01015        * due to logic above, but it shouldn't hurt either.
01016        */
01017       if (!(fixed_directions & FIXED_DIRECTION_X))
01018         {
01019           /* Shove to the right, if needed */
01020           if (best_rect->x > rect->x)
01021             rect->x = best_rect->x;
01022 
01023           /* Shove to the left, if needed */
01024           if (best_rect->x + best_rect->width < rect->x + rect->width)
01025             rect->x = (best_rect->x + best_rect->width) - rect->width;
01026         }
01027 
01028       /* Extra precaution with checking fixed direction shouldn't be needed
01029        * due to logic above, but it shouldn't hurt either.
01030        */
01031       if (!(fixed_directions & FIXED_DIRECTION_Y))
01032         {
01033           /* Shove down, if needed */
01034           if (best_rect->y > rect->y)
01035             rect->y = best_rect->y;
01036 
01037           /* Shove up, if needed */
01038           if (best_rect->y + best_rect->height < rect->y + rect->height)
01039             rect->y = (best_rect->y + best_rect->height) - rect->height;
01040         }
01041     }
01042 }
01043 
01044 void
01045 meta_rectangle_find_linepoint_closest_to_point (double x1,
01046                                                 double y1,
01047                                                 double x2,
01048                                                 double y2,
01049                                                 double px,
01050                                                 double py,
01051                                                 double *valx,
01052                                                 double *valy)
01053 {
01054   /* I'll use the shorthand rx, ry for the return values, valx & valy.
01055    * Now, we need (rx,ry) to be on the line between (x1,y1) and (x2,y2).
01056    * For that to happen, we first need the slope of the line from (x1,y1)
01057    * to (rx,ry) must match the slope of (x1,y1) to (x2,y2), i.e.:
01058    *   (ry-y1)   (y2-y1)
01059    *   ------- = -------
01060    *   (rx-x1)   (x2-x1)
01061    * If x1==x2, though, this gives divide by zero errors, so we want to
01062    * rewrite the equation by multiplying both sides by (rx-x1)*(x2-x1):
01063    *   (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
01064    * This is a valid requirement even when x1==x2 (when x1==x2, this latter
01065    * equation will basically just mean that rx must be equal to both x1 and
01066    * x2)
01067    *
01068    * The other requirement that we have is that the line from (rx,ry) to
01069    * (px,py) must be perpendicular to the line from (x1,y1) to (x2,y2).  So
01070    * we just need to get a vector in the direction of each line, take the
01071    * dot product of the two, and ensure that the result is 0:
01072    *   (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
01073    *
01074    * This gives us two equations and two unknowns:
01075    *
01076    *   (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
01077    *   (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
01078    *
01079    * This particular pair of equations is always solvable so long as
01080    * (x1,y1) and (x2,y2) are not the same point (and note that anyone who
01081    * calls this function that way is braindead because it means that they
01082    * really didn't specify a line after all).  However, the caller should
01083    * be careful to avoid making (x1,y1) and (x2,y2) too close (e.g. like
01084    * 10^{-8} apart in each coordinate), otherwise roundoff error could
01085    * cause issues.  Solving these equations by hand (or using Maple(TM) or
01086    * Mathematica(TM) or whatever) results in slightly messy expressions,
01087    * but that's all the below few lines do.
01088    */
01089 
01090   double diffx, diffy, den;
01091   diffx = x2 - x1;
01092   diffy = y2 - y1;
01093   den = diffx * diffx + diffy * diffy;
01094 
01095   *valx = (py * diffx * diffy + px * diffx * diffx +
01096            y2 * x1 * diffy - y1 * x2 * diffy) / den;
01097   *valy = (px * diffx * diffy + py * diffy * diffy +
01098            x2 * y1 * diffx - x1 * y2 * diffx) / den;
01099 }
01100 
01101 /***************************************************************************/
01102 /*                                                                         */
01103 /* Switching gears to code for edges instead of just rectangles            */
01104 /*                                                                         */
01105 /***************************************************************************/
01106 
01107 gboolean
01108 meta_rectangle_edge_aligns (const MetaRectangle *rect, const MetaEdge *edge)
01109 {
01110   /* The reason for the usage of <= below instead of < is because we are
01111    * interested in in-the-way-or-adject'ness.  So, a left (i.e. vertical
01112    * edge) occupying y positions 0-9 (which has a y of 0 and a height of
01113    * 10) and a rectangle with top at y=10 would be considered to "align" by
01114    * this function.
01115    */
01116   switch (edge->side_type)
01117     {
01118     case META_DIRECTION_LEFT:
01119     case META_DIRECTION_RIGHT:
01120       return BOX_TOP (*rect)      <= BOX_BOTTOM (edge->rect) &&
01121              BOX_TOP (edge->rect) <= BOX_BOTTOM (*rect);
01122     case META_DIRECTION_TOP:
01123     case META_DIRECTION_BOTTOM:
01124       return BOX_LEFT (*rect)      <= BOX_RIGHT (edge->rect) &&
01125              BOX_LEFT (edge->rect) <= BOX_RIGHT (*rect);
01126     default:
01127       g_assert_not_reached ();
01128     }
01129 }
01130 
01131 static GList*
01132 get_rect_minus_overlap (const GList   *rect_in_list, 
01133                         MetaRectangle *overlap)
01134 {
01135   MetaRectangle *temp;
01136   MetaRectangle *rect = rect_in_list->data;
01137   GList *ret = NULL;
01138 
01139   if (BOX_LEFT (*rect) < BOX_LEFT (*overlap))
01140     {
01141       temp = g_new (MetaRectangle, 1);
01142       *temp = *rect;
01143       temp->width = BOX_LEFT (*overlap) - BOX_LEFT (*rect);
01144       ret = g_list_prepend (ret, temp);
01145     }
01146   if (BOX_RIGHT (*rect) > BOX_RIGHT (*overlap))
01147     {
01148       temp = g_new (MetaRectangle, 1);
01149       *temp = *rect;
01150       temp->x = BOX_RIGHT (*overlap);
01151       temp->width = BOX_RIGHT (*rect) - BOX_RIGHT (*overlap);
01152       ret = g_list_prepend (ret, temp);
01153     }
01154   if (BOX_TOP (*rect) < BOX_TOP (*overlap))
01155     {
01156       temp = g_new (MetaRectangle, 1);
01157       temp->x      = overlap->x;
01158       temp->width  = overlap->width;
01159       temp->y      = BOX_TOP (*rect);
01160       temp->height = BOX_TOP (*overlap) - BOX_TOP (*rect);
01161       ret = g_list_prepend (ret, temp);
01162     }
01163   if (BOX_BOTTOM (*rect) > BOX_BOTTOM (*overlap))
01164     {
01165       temp = g_new (MetaRectangle, 1);
01166       temp->x      = overlap->x;
01167       temp->width  = overlap->width;
01168       temp->y      = BOX_BOTTOM (*overlap);
01169       temp->height = BOX_BOTTOM (*rect) - BOX_BOTTOM (*overlap);
01170       ret = g_list_prepend (ret, temp);
01171     }
01172 
01173   return ret;
01174 }
01175 
01176 static GList*
01177 replace_rect_with_list (GList *old_element, 
01178                         GList *new_list)
01179 {
01180   GList *ret;
01181   g_assert (old_element != NULL);
01182 
01183   if (!new_list)
01184     {
01185       /* If there is no new list, just remove the old_element */
01186       ret = g_list_remove_link (old_element, old_element);
01187     }
01188   else
01189     {
01190       /* Fix up the prev and next pointers everywhere */
01191       ret = new_list;
01192       if (old_element->prev)
01193         {
01194           old_element->prev->next = new_list;
01195           new_list->prev = old_element->prev;
01196         }
01197       if (old_element->next)
01198         {
01199           GList *tmp = g_list_last (new_list);
01200           old_element->next->prev = tmp;
01201           tmp->next = old_element->next;
01202         }
01203     }
01204 
01205   /* Free the old_element and return the appropriate "next" point */
01206   g_free (old_element->data);
01207   g_list_free_1 (old_element);
01208   return ret;
01209 }
01210 
01211 /* Make a copy of the strut list, make sure that copy only contains parts
01212  * of the old_struts that intersect with the region rect, and then do some
01213  * magic to make all the new struts disjoint (okay, we we break up struts
01214  * that aren't disjoint in a way that the overlapping part is only included
01215  * once, so it's not really magic...).
01216  */
01217 static GList*
01218 get_disjoint_strut_rect_list_in_region (const GSList        *old_struts,
01219                                         const MetaRectangle *region)
01220 {
01221   GList *strut_rects;
01222   GList *tmp;
01223 
01224   /* First, copy the list */
01225   strut_rects = NULL;
01226   while (old_struts)
01227     {
01228       MetaRectangle *cur = &((MetaStrut*)old_struts->data)->rect;
01229       MetaRectangle *copy = g_new (MetaRectangle, 1);
01230       *copy = *cur;
01231       if (meta_rectangle_intersect (copy, region, copy))
01232         strut_rects = g_list_prepend (strut_rects, copy);
01233       else
01234         g_free (copy);
01235 
01236       old_struts = old_struts->next;
01237     }
01238 
01239   /* Now, loop over the list and check for intersections, fixing things up
01240    * where they do intersect.
01241    */
01242   tmp = strut_rects;
01243   while (tmp)
01244     {
01245       GList *compare;
01246 
01247       MetaRectangle *cur = tmp->data;
01248 
01249       compare = tmp->next;
01250       while (compare)
01251         {
01252           MetaRectangle *comp = compare->data;
01253           MetaRectangle overlap;
01254 
01255           if (meta_rectangle_intersect (cur, comp, &overlap))
01256             {
01257               /* Get a list of rectangles for each strut that don't overlap
01258                * the intersection region.
01259                */
01260               GList *cur_leftover  = get_rect_minus_overlap (tmp,  &overlap);
01261               GList *comp_leftover = get_rect_minus_overlap (compare, &overlap);
01262 
01263               /* Add the intersection region to cur_leftover */
01264               MetaRectangle *overlap_allocated = g_new (MetaRectangle, 1);
01265               *overlap_allocated = overlap;
01266               cur_leftover = g_list_prepend (cur_leftover, overlap_allocated);
01267 
01268               /* Fix up tmp, compare, and cur -- maybe struts too */
01269               if (strut_rects == tmp)
01270                 {
01271                   strut_rects = replace_rect_with_list (tmp, cur_leftover);
01272                   tmp = strut_rects;
01273                 }
01274               else
01275                 tmp   = replace_rect_with_list (tmp,     cur_leftover);
01276               compare = replace_rect_with_list (compare, comp_leftover);
01277 
01278               if (compare == NULL)
01279                 break;
01280 
01281               cur = tmp->data;
01282             }
01283 
01284           compare = compare->next;
01285         }
01286 
01287       tmp = tmp->next;
01288     }
01289 
01290   return strut_rects;
01291 }
01292 
01293 gint
01294 meta_rectangle_edge_cmp_ignore_type (gconstpointer a, gconstpointer b)
01295 {
01296   const MetaEdge *a_edge_rect = (gconstpointer) a;
01297   const MetaEdge *b_edge_rect = (gconstpointer) b;
01298   int a_compare, b_compare;
01299 
01300   /* Edges must be both vertical or both horizontal, or it doesn't make
01301    * sense to compare them.
01302    */
01303   g_assert ((a_edge_rect->rect.width  == 0 && b_edge_rect->rect.width == 0) ||
01304             (a_edge_rect->rect.height == 0 && b_edge_rect->rect.height == 0));
01305 
01306   a_compare = b_compare = 0;  /* gcc-3.4.2 sucks at figuring initialized'ness */
01307 
01308   if (a_edge_rect->side_type == META_DIRECTION_LEFT ||
01309       a_edge_rect->side_type == META_DIRECTION_RIGHT)
01310     {
01311       a_compare = a_edge_rect->rect.x;
01312       b_compare = b_edge_rect->rect.x;
01313       if (a_compare == b_compare)
01314         {
01315           a_compare = a_edge_rect->rect.y;
01316           b_compare = b_edge_rect->rect.y;
01317         }
01318     }
01319   else if (a_edge_rect->side_type == META_DIRECTION_TOP ||
01320            a_edge_rect->side_type == META_DIRECTION_BOTTOM)
01321     {
01322       a_compare = a_edge_rect->rect.y;
01323       b_compare = b_edge_rect->rect.y;
01324       if (a_compare == b_compare)
01325         {
01326           a_compare = a_edge_rect->rect.x;
01327           b_compare = b_edge_rect->rect.x;
01328         }
01329     }
01330   else
01331     g_assert ("Some idiot wanted to sort sides of different types.\n");
01332 
01333   return a_compare - b_compare; /* positive value denotes a > b ... */
01334 }
01335 
01336 /* To make things easily testable, provide a nice way of sorting edges */
01337 gint
01338 meta_rectangle_edge_cmp (gconstpointer a, gconstpointer b)
01339 {
01340   const MetaEdge *a_edge_rect = (gconstpointer) a;
01341   const MetaEdge *b_edge_rect = (gconstpointer) b;
01342 
01343   int a_compare, b_compare;
01344 
01345   a_compare = a_edge_rect->side_type;
01346   b_compare = b_edge_rect->side_type;
01347 
01348   if (a_compare == b_compare)
01349     return meta_rectangle_edge_cmp_ignore_type (a, b);
01350 
01351   return a_compare - b_compare; /* positive value denotes a > b ... */
01352 }
01353 
01354 /* Determine whether two given edges overlap */
01355 static gboolean
01356 edges_overlap (const MetaEdge *edge1,
01357                const MetaEdge *edge2)
01358 {
01359   if (edge1->rect.width == 0 && edge2->rect.width == 0)
01360     {
01361       return meta_rectangle_vert_overlap (&edge1->rect, &edge2->rect) &&
01362              edge1->rect.x == edge2->rect.x;
01363     }
01364   else if (edge1->rect.height == 0 && edge2->rect.height == 0)
01365     {
01366       return meta_rectangle_horiz_overlap (&edge1->rect, &edge2->rect) &&
01367              edge1->rect.y == edge2->rect.y;
01368     }
01369   else
01370     {
01371       return FALSE;
01372     }
01373 }
01374 
01375 static gboolean
01376 rectangle_and_edge_intersection (const MetaRectangle *rect,
01377                                  const MetaEdge      *edge,
01378                                  MetaEdge            *overlap,
01379                                  int                 *handle_type)
01380 {
01381   const MetaRectangle *rect2  = &edge->rect;
01382   MetaRectangle *result = &overlap->rect;
01383   gboolean intersect = TRUE;
01384 
01385   /* We don't know how to set these, so set them to invalid values */
01386   overlap->edge_type = -1;
01387   overlap->side_type = -1;
01388 
01389   /* Figure out what the intersection is */  
01390   result->x = MAX (rect->x, rect2->x);
01391   result->y = MAX (rect->y, rect2->y);
01392   result->width  = MIN (BOX_RIGHT (*rect),  BOX_RIGHT (*rect2))  - result->x;
01393   result->height = MIN (BOX_BOTTOM (*rect), BOX_BOTTOM (*rect2)) - result->y;
01394 
01395   /* Find out if the intersection is empty; have to do it this way since
01396    * edges have a thickness of 0
01397    */
01398   if ((result->width <  0 || result->height <  0) ||
01399       (result->width == 0 && result->height == 0))
01400     {
01401       result->width = 0;
01402       result->height = 0;
01403       intersect = FALSE;
01404     }
01405   else
01406     {
01407       /* Need to figure out the handle_type, a somewhat weird quantity:
01408        *   0 - overlap is in middle of rect
01409        *  -1 - overlap is at the side of rect, and is on the opposite side
01410        *       of rect than the edge->side_type side
01411        *   1 - overlap is at the side of rect, and the side of rect it is
01412        *       on is the edge->side_type side
01413        */
01414       switch (edge->side_type)
01415         {
01416         case META_DIRECTION_LEFT:
01417           if (result->x == rect->x)
01418             *handle_type = 1;
01419           else if (result->x == BOX_RIGHT (*rect))
01420             *handle_type = -1;
01421           else
01422             *handle_type = 0;
01423           break;
01424         case META_DIRECTION_RIGHT:
01425           if (result->x == rect->x)
01426             *handle_type = -1;
01427           else if (result->x == BOX_RIGHT (*rect))
01428             *handle_type = 1;
01429           else
01430             *handle_type = 0;
01431           break;
01432         case META_DIRECTION_TOP:
01433           if (result->y == rect->y)
01434             *handle_type = 1;
01435           else if (result->y == BOX_BOTTOM (*rect))
01436             *handle_type = -1;
01437           else
01438             *handle_type = 0;
01439           break;
01440         case META_DIRECTION_BOTTOM:
01441           if (result->y == rect->y)
01442             *handle_type = -1;
01443           else if (result->y == BOX_BOTTOM (*rect))
01444             *handle_type = 1;
01445           else
01446             *handle_type = 0;
01447           break;
01448         default:
01449           g_assert_not_reached ();
01450         }
01451     }
01452   return intersect;
01453 }
01454 
01455 /* Add all edges of the given rect to cur_edges and return the result.  If
01456  * rect_is_internal is false, the side types are switched (LEFT<->RIGHT and
01457  * TOP<->BOTTOM).
01458  */
01459 static GList*
01460 add_edges (GList               *cur_edges, 
01461            const MetaRectangle *rect,
01462            gboolean             rect_is_internal)
01463 {
01464   MetaEdge *temp_edge;
01465   int i;
01466 
01467   for (i=0; i<4; i++)
01468     {
01469       temp_edge = g_new (MetaEdge, 1);
01470       temp_edge->rect = *rect;
01471       switch (i)
01472         {
01473         case 0:
01474           temp_edge->side_type = 
01475             rect_is_internal ? META_DIRECTION_LEFT : META_DIRECTION_RIGHT;
01476           temp_edge->rect.width = 0;
01477           break;
01478         case 1:
01479           temp_edge->side_type = 
01480             rect_is_internal ? META_DIRECTION_RIGHT : META_DIRECTION_LEFT;
01481           temp_edge->rect.x     += temp_edge->rect.width;
01482           temp_edge->rect.width  = 0;
01483           break;
01484         case 2:
01485           temp_edge->side_type = 
01486             rect_is_internal ? META_DIRECTION_TOP : META_DIRECTION_BOTTOM;
01487           temp_edge->rect.height = 0;
01488           break;
01489         case 3:
01490           temp_edge->side_type = 
01491             rect_is_internal ? META_DIRECTION_BOTTOM : META_DIRECTION_TOP;
01492           temp_edge->rect.y      += temp_edge->rect.height;
01493           temp_edge->rect.height  = 0;
01494           break;
01495         }
01496       temp_edge->edge_type = META_EDGE_SCREEN;
01497       cur_edges = g_list_prepend (cur_edges, temp_edge);
01498     }
01499 
01500   return cur_edges;
01501 }
01502 
01503 /* Remove any part of old_edge that intersects remove and add any resulting
01504  * edges to cur_list.  Return cur_list when finished.
01505  */
01506 static GList*
01507 split_edge (GList *cur_list, 
01508             const MetaEdge *old_edge, 
01509             const MetaEdge *remove)
01510 {
01511   MetaEdge *temp_edge;
01512   switch (old_edge->side_type)
01513     {
01514     case META_DIRECTION_LEFT:
01515     case META_DIRECTION_RIGHT:
01516       g_assert (meta_rectangle_vert_overlap (&old_edge->rect, &remove->rect));
01517       if (BOX_TOP (old_edge->rect)  < BOX_TOP (remove->rect))
01518         {
01519           temp_edge = g_new (MetaEdge, 1);
01520           *temp_edge = *old_edge;
01521           temp_edge->rect.height = BOX_TOP (remove->rect)
01522                                  - BOX_TOP (old_edge->rect);
01523           cur_list = g_list_prepend (cur_list, temp_edge);
01524         }
01525       if (BOX_BOTTOM (old_edge->rect) > BOX_BOTTOM (remove->rect))
01526         {
01527           temp_edge = g_new (MetaEdge, 1);
01528           *temp_edge = *old_edge;
01529           temp_edge->rect.y      = BOX_BOTTOM (remove->rect);
01530           temp_edge->rect.height = BOX_BOTTOM (old_edge->rect)
01531                                  - BOX_BOTTOM (remove->rect);
01532           cur_list = g_list_prepend (cur_list, temp_edge);
01533         }
01534       break;
01535     case META_DIRECTION_TOP:
01536     case META_DIRECTION_BOTTOM:
01537       g_assert (meta_rectangle_horiz_overlap (&old_edge->rect, &remove->rect));
01538       if (BOX_LEFT (old_edge->rect)  < BOX_LEFT (remove->rect))
01539         {
01540           temp_edge = g_new (MetaEdge, 1);
01541           *temp_edge = *old_edge;
01542           temp_edge->rect.width = BOX_LEFT (remove->rect)
01543                                 - BOX_LEFT (old_edge->rect);
01544           cur_list = g_list_prepend (cur_list, temp_edge);
01545         }
01546       if (BOX_RIGHT (old_edge->rect) > BOX_RIGHT (remove->rect))
01547         {
01548           temp_edge = g_new (MetaEdge, 1);
01549           *temp_edge = *old_edge;
01550           temp_edge->rect.x     = BOX_RIGHT (remove->rect);
01551           temp_edge->rect.width = BOX_RIGHT (old_edge->rect)
01552                                 - BOX_RIGHT (remove->rect);
01553           cur_list = g_list_prepend (cur_list, temp_edge);
01554         }
01555       break;
01556     default:
01557       g_assert_not_reached ();
01558     }
01559 
01560   return cur_list;
01561 }
01562 
01563 /* Split up edge and remove preliminary edges from strut_edges depending on
01564  * if and how rect and edge intersect.
01565  */
01566 static void
01567 fix_up_edges (MetaRectangle *rect,         MetaEdge *edge, 
01568               GList         **strut_edges, GList    **edge_splits,
01569               gboolean      *edge_needs_removal)
01570 {
01571   MetaEdge overlap;
01572   int      handle_type;
01573 
01574   if (!rectangle_and_edge_intersection (rect, edge, &overlap, &handle_type))
01575     return;
01576 
01577   if (handle_type == 0 || handle_type == 1)
01578     {
01579       /* Put the result of removing overlap from edge into edge_splits */
01580       *edge_splits = split_edge (*edge_splits, edge, &overlap);
01581       *edge_needs_removal = TRUE;
01582     }
01583 
01584   if (handle_type == -1 || handle_type == 1)
01585     {
01586       /* Remove the overlap from strut_edges */
01587       /* First, loop over the edges of the strut */
01588       GList *tmp = *strut_edges;
01589       while (tmp)
01590         {
01591           MetaEdge *cur = tmp->data;
01592           /* If this is the edge that overlaps, then we need to split it */
01593           if (edges_overlap (cur, &overlap))
01594             {
01595               GList *delete_me = tmp;
01596 
01597               /* Split this edge into some new ones */
01598               *strut_edges = split_edge (*strut_edges, cur, &overlap);
01599 
01600               /* Delete the old one */
01601               tmp = tmp->next;
01602               g_free (cur);
01603               *strut_edges = g_list_delete_link (*strut_edges, delete_me);
01604             }
01605           else
01606             tmp = tmp->next;
01607         }
01608     }
01609 }
01610 
01611 /* This function removes intersections of edges with the rectangles from the
01612  * list of edges.
01613  */
01614 GList*
01615 meta_rectangle_remove_intersections_with_boxes_from_edges (
01616   GList        *edges,
01617   const GSList *rectangles)
01618 {
01619   const GSList *rect_iter;
01620   const int opposing = 1;
01621 
01622   /* Now remove all intersections of rectangles with the edge list */
01623   rect_iter = rectangles;
01624   while (rect_iter)
01625     {
01626       MetaRectangle *rect = rect_iter->data;
01627       GList *edge_iter = edges;
01628       while (edge_iter)
01629         {
01630           MetaEdge *edge = edge_iter->data;
01631           MetaEdge overlap;
01632           int      handle;
01633           gboolean edge_iter_advanced = FALSE;
01634 
01635           /* If this edge overlaps with this rect... */
01636           if (rectangle_and_edge_intersection (rect, edge, &overlap, &handle))
01637             {
01638 
01639               /* "Intersections" where the edges touch but are opposite
01640                * sides (e.g. a left edge against the right edge) should not
01641                * be split.  Note that the comments in
01642                * rectangle_and_edge_intersection() say that opposing edges
01643                * occur when handle is -1, BUT you need to remember that we
01644                * treat the left side of a window as a right edge because
01645                * it's what the right side of the window being moved should
01646                * be-resisted-by/snap-to.  So opposing is really 1.  Anyway,
01647                * we just keep track of it in the opposing constant set up
01648                * above and if handle isn't equal to that, then we know the
01649                * edge should be split.
01650                */
01651               if (handle != opposing)
01652                 {
01653                   /* Keep track of this edge so we can delete it below */
01654                   GList *delete_me = edge_iter;
01655                   edge_iter = edge_iter->next;
01656                   edge_iter_advanced = TRUE;
01657 
01658                   /* Split the edge and add the result to beginning of edges */
01659                   edges = split_edge (edges, edge, &overlap);
01660 
01661                   /* Now free the edge... */
01662                   g_free (edge);
01663                   edges = g_list_delete_link (edges, delete_me);
01664                 }
01665             }
01666 
01667           if (!edge_iter_advanced)
01668             edge_iter = edge_iter->next;
01669         }
01670 
01671       rect_iter = rect_iter->next;
01672     }
01673 
01674   return edges;
01675 }
01676 
01677 /* This function is trying to find all the edges of an onscreen region. */
01678 GList*
01679 meta_rectangle_find_onscreen_edges (const MetaRectangle *basic_rect,
01680                                     const GSList        *all_struts)
01681 {
01682   GList        *ret;
01683   GList        *fixed_strut_rects;
01684   GList        *edge_iter; 
01685   const GList  *strut_rect_iter;
01686 
01687   /* The algorithm is basically as follows:
01688    *   Make sure the struts are disjoint
01689    *   Initialize the edge_set to the edges of basic_rect
01690    *   Foreach strut:
01691    *     Put together a preliminary new edge from the edges of the strut
01692    *     Foreach edge in edge_set:
01693    *       - Split the edge if it is partially contained inside the strut
01694    *       - If the edge matches an edge of the strut (i.e. a strut just
01695    *         against the edge of the screen or a not-next-to-edge-of-screen
01696    *         strut adjacent to another), then both the edge from the
01697    *         edge_set and the preliminary edge for the strut will need to
01698    *         be split
01699    *     Add any remaining "preliminary" strut edges to the edge_set
01700    */
01701 
01702   /* Make sure the struts are disjoint */
01703   fixed_strut_rects =
01704     get_disjoint_strut_rect_list_in_region (all_struts, basic_rect);
01705 
01706   /* Start off the list with the edges of basic_rect */
01707   ret = add_edges (NULL, basic_rect, TRUE);
01708 
01709   strut_rect_iter = fixed_strut_rects;
01710   while (strut_rect_iter)
01711     {
01712       MetaRectangle *strut_rect = (MetaRectangle*) strut_rect_iter->data;
01713 
01714       /* Get the new possible edges we may need to add from the strut */
01715       GList *new_strut_edges = add_edges (NULL, strut_rect, FALSE);
01716 
01717       edge_iter = ret;
01718       while (edge_iter)
01719         {
01720           MetaEdge *cur_edge = edge_iter->data;
01721           GList *splits_of_cur_edge = NULL;
01722           gboolean edge_needs_removal = FALSE;
01723 
01724           fix_up_edges (strut_rect,       cur_edge, 
01725                         &new_strut_edges, &splits_of_cur_edge,
01726                         &edge_needs_removal);
01727 
01728           if (edge_needs_removal)
01729             {
01730               /* Delete the old edge */
01731               GList *delete_me = edge_iter;
01732               edge_iter = edge_iter->next;
01733               g_free (cur_edge);
01734               ret = g_list_delete_link (ret, delete_me);
01735 
01736               /* Add the new split parts of the edge */
01737               ret = g_list_concat (splits_of_cur_edge, ret);
01738             }
01739           else
01740             {
01741               edge_iter = edge_iter->next;
01742             }
01743 
01744           /* edge_iter was already advanced above */
01745         }
01746 
01747       ret = g_list_concat (new_strut_edges, ret);
01748       strut_rect_iter = strut_rect_iter->next;
01749     }
01750 
01751   /* Sort the list */
01752   ret = g_list_sort (ret, meta_rectangle_edge_cmp);
01753 
01754   /* Free the fixed struts list */
01755   meta_rectangle_free_list_and_elements (fixed_strut_rects);
01756 
01757   return ret;
01758 }
01759 
01760 GList*
01761 meta_rectangle_find_nonintersected_xinerama_edges (
01762                                     const GList         *xinerama_rects,
01763                                     const GSList        *all_struts)
01764 {
01765   /* This function cannot easily be merged with
01766    * meta_rectangle_find_onscreen_edges() because real screen edges
01767    * and strut edges both are of the type "there ain't anything
01768    * immediately on the other side"; xinerama edges are different.
01769    */
01770   GList *ret;
01771   const GList  *cur;
01772   GSList *temp_rects;
01773 
01774   /* Initialize the return list to be empty */
01775   ret = NULL;
01776 
01777   /* start of ret with all the edges of xineramas that are adjacent to
01778    * another xinerama.
01779    */
01780   cur = xinerama_rects;
01781   while (cur)
01782     {
01783       MetaRectangle *cur_rect = cur->data;
01784       const GList *compare = xinerama_rects;
01785       while (compare)
01786         {
01787           MetaRectangle *compare_rect = compare->data;
01788 
01789           /* Check if cur might be horizontally adjacent to compare */
01790           if (meta_rectangle_vert_overlap(cur_rect, compare_rect))
01791             {
01792               MetaDirection side_type;
01793               int y      = MAX (cur_rect->y, compare_rect->y);
01794               int height = MIN (BOX_BOTTOM (*cur_rect) - y,
01795                                 BOX_BOTTOM (*compare_rect) - y);
01796               int width  = 0;
01797               int x;
01798 
01799               if (BOX_LEFT (*cur_rect)  == BOX_RIGHT (*compare_rect))
01800                 {
01801                   /* compare_rect is to the left of cur_rect */
01802                   x = BOX_LEFT (*cur_rect);
01803                   side_type = META_DIRECTION_LEFT;
01804                 }
01805               else if (BOX_RIGHT (*cur_rect) == BOX_LEFT (*compare_rect))
01806                 {
01807                   /* compare_rect is to the right of cur_rect */
01808                   x = BOX_RIGHT (*cur_rect);
01809                   side_type = META_DIRECTION_RIGHT;
01810                 }
01811               else
01812                 /* These rectangles aren't adjacent after all */
01813                 x = INT_MIN;
01814 
01815               /* If the rectangles really are adjacent */
01816               if (x != INT_MIN)
01817                 {
01818                   /* We need a left edge for the xinerama on the right, and
01819                    * a right edge for the xinerama on the left.  Just fill
01820                    * up the edges and stick 'em on the list.
01821                    */
01822                   MetaEdge *new_edge  = g_new (MetaEdge, 1);
01823 
01824                   new_edge->rect = meta_rect (x, y, width, height);
01825                   new_edge->side_type = side_type;
01826                   new_edge->edge_type = META_EDGE_XINERAMA;
01827 
01828                   ret = g_list_prepend (ret, new_edge);
01829                 }
01830             }
01831 
01832           /* Check if cur might be vertically adjacent to compare */
01833           if (meta_rectangle_horiz_overlap(cur_rect, compare_rect))
01834             {
01835               MetaDirection side_type;
01836               int x      = MAX (cur_rect->x, compare_rect->x);
01837               int width  = MIN (BOX_RIGHT (*cur_rect) - x,
01838                                 BOX_RIGHT (*compare_rect) - x);
01839               int height = 0;
01840               int y;
01841 
01842               if (BOX_TOP (*cur_rect)  == BOX_BOTTOM (*compare_rect))
01843                 {
01844                   /* compare_rect is to the top of cur_rect */
01845                   y = BOX_TOP (*cur_rect);
01846                   side_type = META_DIRECTION_TOP;
01847                 }
01848               else if (BOX_BOTTOM (*cur_rect) == BOX_TOP (*compare_rect))
01849                 {
01850                   /* compare_rect is to the bottom of cur_rect */
01851                   y = BOX_BOTTOM (*cur_rect);
01852                   side_type = META_DIRECTION_BOTTOM;
01853                 }
01854               else
01855                 /* These rectangles aren't adjacent after all */
01856                 y = INT_MIN;
01857 
01858               /* If the rectangles really are adjacent */
01859               if (y != INT_MIN)
01860                 {
01861                   /* We need a top edge for the xinerama on the bottom, and
01862                    * a bottom edge for the xinerama on the top.  Just fill
01863                    * up the edges and stick 'em on the list.
01864                    */
01865                   MetaEdge *new_edge = g_new (MetaEdge, 1);
01866 
01867                   new_edge->rect = meta_rect (x, y, width, height);
01868                   new_edge->side_type = side_type;
01869                   new_edge->edge_type = META_EDGE_XINERAMA;
01870 
01871                   ret = g_list_prepend (ret, new_edge);
01872                 }
01873             }
01874 
01875           compare = compare->next;
01876         }
01877       cur = cur->next;
01878     }
01879 
01880   temp_rects = NULL;
01881   for (; all_struts; all_struts = all_struts->next)
01882     temp_rects = g_slist_prepend (temp_rects,
01883                                   &((MetaStrut*)all_struts->data)->rect);
01884   ret = meta_rectangle_remove_intersections_with_boxes_from_edges (ret, 
01885                                                                    temp_rects);
01886   g_slist_free (temp_rects);
01887 
01888   /* Sort the list */
01889   ret = g_list_sort (ret, meta_rectangle_edge_cmp);
01890 
01891   return ret;
01892 }

Generated on Sat Aug 23 22:04:16 2008 for metacity by  doxygen 1.5.5