place.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity window placement */
00004 
00005 /* 
00006  * Copyright (C) 2001 Havoc Pennington
00007  * Copyright (C) 2002, 2003 Red Hat, Inc.
00008  * Copyright (C) 2003 Rob Adams
00009  * Copyright (C) 2005 Elijah Newren
00010  * 
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License as
00013  * published by the Free Software Foundation; either version 2 of the
00014  * License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful, but
00017  * WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * General Public License for more details.
00020  * 
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00024  * 02111-1307, USA.
00025  */
00026 
00027 #include <config.h>
00028 
00029 #include "place.h"
00030 #include "workspace.h"
00031 #include "prefs.h"
00032 #include <gdk/gdkregion.h>
00033 #include <math.h>
00034 #include <stdlib.h>
00035 
00036 typedef enum
00037 {
00038   META_LEFT,
00039   META_RIGHT,
00040   META_TOP,
00041   META_BOTTOM
00042 } MetaWindowDirection;
00043 
00044 static gint
00045 northwestcmp (gconstpointer a, gconstpointer b)
00046 {
00047   MetaWindow *aw = (gpointer) a;
00048   MetaWindow *bw = (gpointer) b;
00049   int from_origin_a;
00050   int from_origin_b;
00051   int ax, ay, bx, by;
00052 
00053   /* we're interested in the frame position for cascading,
00054    * not meta_window_get_position()
00055    */
00056   if (aw->frame)
00057     {
00058       ax = aw->frame->rect.x;
00059       ay = aw->frame->rect.y;
00060     }
00061   else
00062     {
00063       ax = aw->rect.x;
00064       ay = aw->rect.y;
00065     }
00066 
00067   if (bw->frame)
00068     {
00069       bx = bw->frame->rect.x;
00070       by = bw->frame->rect.y;
00071     }
00072   else
00073     {
00074       bx = bw->rect.x;
00075       by = bw->rect.y;
00076     }
00077   
00078   /* probably there's a fast good-enough-guess we could use here. */
00079   from_origin_a = sqrt (ax * ax + ay * ay);
00080   from_origin_b = sqrt (bx * bx + by * by);
00081     
00082   if (from_origin_a < from_origin_b)
00083     return -1;
00084   else if (from_origin_a > from_origin_b)
00085     return 1;
00086   else
00087     return 0;
00088 }
00089 
00090 static void
00091 find_next_cascade (MetaWindow *window,
00092                    MetaFrameGeometry *fgeom,
00093                    /* visible windows on relevant workspaces */
00094                    GList      *windows,
00095                    int         x,
00096                    int         y,
00097                    int        *new_x,
00098                    int        *new_y)
00099 {
00100   GList *tmp;
00101   GList *sorted;
00102   int cascade_x, cascade_y;
00103   int x_threshold, y_threshold;
00104   int window_width, window_height;
00105   int cascade_stage;
00106   MetaRectangle work_area;
00107   const MetaXineramaScreenInfo* current;
00108   
00109   sorted = g_list_copy (windows);
00110   sorted = g_list_sort (sorted, northwestcmp);
00111 
00112   /* This is a "fuzzy" cascade algorithm. 
00113    * For each window in the list, we find where we'd cascade a
00114    * new window after it. If a window is already nearly at that
00115    * position, we move on.
00116    */
00117   
00118   /* arbitrary-ish threshold, honors user attempts to
00119    * manually cascade.
00120    */
00121 #define CASCADE_FUZZ 15
00122   if (fgeom)
00123     {
00124       x_threshold = MAX (fgeom->left_width, CASCADE_FUZZ);
00125       y_threshold = MAX (fgeom->top_height, CASCADE_FUZZ);
00126     }
00127   else
00128     {
00129       x_threshold = CASCADE_FUZZ;
00130       y_threshold = CASCADE_FUZZ;
00131     }
00132   
00133   /* Find furthest-SE origin of all workspaces.
00134    * cascade_x, cascade_y are the target position
00135    * of NW corner of window frame.
00136    */
00137 
00138   current = meta_screen_get_current_xinerama (window->screen);
00139   meta_window_get_work_area_for_xinerama (window, current->number, &work_area);
00140 
00141   cascade_x = MAX (0, work_area.x);
00142   cascade_y = MAX (0, work_area.y);
00143   
00144   /* Find first cascade position that's not used. */
00145   
00146   window_width = window->frame ? window->frame->rect.width : window->rect.width;
00147   window_height = window->frame ? window->frame->rect.height : window->rect.height;
00148   
00149   cascade_stage = 0;
00150   tmp = sorted;
00151   while (tmp != NULL)
00152     {
00153       MetaWindow *w;
00154       int wx, wy;
00155       
00156       w = tmp->data;
00157 
00158       /* we want frame position, not window position */
00159       if (w->frame)
00160         {
00161           wx = w->frame->rect.x;
00162           wy = w->frame->rect.y;
00163         }
00164       else
00165         {
00166           wx = w->rect.x;
00167           wy = w->rect.y;
00168         }
00169       
00170       if (ABS (wx - cascade_x) < x_threshold &&
00171           ABS (wy - cascade_y) < y_threshold)
00172         {
00173           /* This window is "in the way", move to next cascade
00174            * point. The new window frame should go at the origin
00175            * of the client window we're stacking above.
00176            */
00177           meta_window_get_position (w, &wx, &wy);
00178           cascade_x = wx;
00179           cascade_y = wy;
00180           
00181           /* If we go off the screen, start over with a new cascade */
00182           if (((cascade_x + window_width) >
00183                (work_area.x + work_area.width)) ||
00184               ((cascade_y + window_height) >
00185                (work_area.y + work_area.height)))
00186             {
00187               cascade_x = MAX (0, work_area.x);
00188               cascade_y = MAX (0, work_area.y);
00189               
00190 #define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */
00191               cascade_stage += 1;
00192               cascade_x += CASCADE_INTERVAL * cascade_stage;
00193               
00194               /* start over with a new cascade translated to the right, unless
00195                * we are out of space
00196                */
00197               if ((cascade_x + window_width) <
00198                   (work_area.x + work_area.width))
00199                 {
00200                   tmp = sorted;
00201                   continue;
00202                 }
00203               else
00204                 {
00205                   /* All out of space, this cascade_x won't work */
00206                   cascade_x = MAX (0, work_area.x);
00207                   break;
00208                 }
00209             }
00210         }
00211       else
00212         {
00213           /* Keep searching for a further-down-the-diagonal window. */
00214         }
00215         
00216       tmp = tmp->next;
00217     }
00218 
00219   /* cascade_x and cascade_y will match the last window in the list
00220    * that was "in the way" (in the approximate cascade diagonal)
00221    */
00222   
00223   g_list_free (sorted);
00224 
00225   /* Convert coords to position of window, not position of frame. */
00226   if (fgeom == NULL)
00227     {
00228       *new_x = cascade_x;
00229       *new_y = cascade_y;
00230     }
00231   else
00232     {
00233       *new_x = cascade_x + fgeom->left_width;
00234       *new_y = cascade_y + fgeom->top_height;
00235     }
00236 }
00237 
00238 static void
00239 find_most_freespace (MetaWindow *window,
00240                      MetaFrameGeometry *fgeom,
00241                      /* visible windows on relevant workspaces */
00242                      MetaWindow *focus_window,
00243                      int         x,
00244                      int         y,
00245                      int        *new_x,
00246                      int        *new_y)
00247 {
00248   MetaWindowDirection side;
00249   int max_area;
00250   int max_width, max_height, left, right, top, bottom;
00251   int left_space, right_space, top_space, bottom_space;
00252   int frame_size_left, frame_size_top;
00253   MetaRectangle work_area;
00254   MetaRectangle avoid;
00255   MetaRectangle outer;
00256 
00257   frame_size_left = fgeom ? fgeom->left_width : 0;
00258   frame_size_top  = fgeom ? fgeom->top_height : 0;
00259 
00260   meta_window_get_work_area_current_xinerama (focus_window, &work_area);
00261   meta_window_get_outer_rect (focus_window, &avoid);
00262   meta_window_get_outer_rect (window, &outer);
00263 
00264   /* Find the areas of choosing the various sides of the focus window */
00265   max_width  = MIN (avoid.width, outer.width);
00266   max_height = MIN (avoid.height, outer.height);
00267   left_space   = avoid.x - work_area.x;
00268   right_space  = work_area.width - (avoid.x + avoid.width - work_area.x);
00269   top_space    = avoid.y - work_area.y;
00270   bottom_space = work_area.height - (avoid.y + avoid.height - work_area.y);
00271   left   = MIN (left_space,   outer.width);
00272   right  = MIN (right_space,  outer.width);
00273   top    = MIN (top_space,    outer.height);
00274   bottom = MIN (bottom_space, outer.height);
00275 
00276   /* Find out which side of the focus_window can show the most of the window */
00277   side = META_LEFT;
00278   max_area = left*max_height;
00279   if (right*max_height > max_area)
00280     {
00281       side = META_RIGHT;
00282       max_area = right*max_height;
00283     }
00284   if (top*max_width > max_area)
00285     {
00286       side = META_TOP;
00287       max_area = top*max_width;
00288     }
00289   if (bottom*max_width > max_area)
00290     {
00291       side = META_BOTTOM;
00292       max_area = bottom*max_width;
00293     }
00294 
00295   /* Give up if there's no where to put it (i.e. focus window is maximized) */
00296   if (max_area == 0)
00297     return;
00298 
00299   /* Place the window on the relevant side; if the whole window fits,
00300    * make it adjacent to the focus window; if not, make sure the
00301    * window doesn't go off the edge of the screen.
00302    */
00303   switch (side)
00304     {
00305     case META_LEFT:
00306       *new_y = avoid.y + frame_size_top;
00307       if (left_space > outer.width)
00308         *new_x = avoid.x - outer.width + frame_size_left;
00309       else
00310         *new_x = work_area.x + frame_size_left;
00311       break;
00312     case META_RIGHT:
00313       *new_y = avoid.y + frame_size_top;
00314       if (right_space > outer.width)
00315         *new_x = avoid.x + avoid.width + frame_size_left;
00316       else
00317         *new_x = work_area.x + work_area.width - outer.width + frame_size_left;
00318       break;
00319     case META_TOP:
00320       *new_x = avoid.x + frame_size_left;
00321       if (top_space > outer.height)
00322         *new_y = avoid.y - outer.height + frame_size_top;
00323       else
00324         *new_y = work_area.y + frame_size_top;
00325       break;
00326     case META_BOTTOM:
00327       *new_x = avoid.x + frame_size_left;
00328       if (bottom_space > outer.height)
00329         *new_y = avoid.y + avoid.height + frame_size_top;
00330       else
00331         *new_y = work_area.y + work_area.height - outer.height + frame_size_top;
00332       break;
00333     }
00334 }
00335 
00336 static void
00337 avoid_being_obscured_as_second_modal_dialog (MetaWindow *window,
00338                                              MetaFrameGeometry *fgeom,
00339                                              int        *x,
00340                                              int        *y)
00341 {
00342   /* We can't center this dialog if it was denied focus and it
00343    * overlaps with the focus window and this dialog is modal and this
00344    * dialog is in the same app as the focus window (*phew*...please
00345    * don't make me say that ten times fast). See bug 307875 comment 11
00346    * and 12 for details, but basically it means this is probably a
00347    * second modal dialog for some app while the focus window is the
00348    * first modal dialog.  We should probably make them simultaneously
00349    * visible in general, but it becomes mandatory to do so due to
00350    * buggy apps (e.g. those using gtk+ *sigh*) because in those cases
00351    * this second modal dialog also happens to be modal to the first
00352    * dialog in addition to the main window, while it has only let us
00353    * know about the modal-to-the-main-window part.
00354    */
00355 
00356   MetaWindow *focus_window;
00357   MetaRectangle overlap;
00358 
00359   focus_window = window->display->focus_window;
00360 
00361   if (window->denied_focus_and_not_transient &&
00362       window->wm_state_modal && /* FIXME: Maybe do this for all transients? */
00363       meta_window_same_application (window, focus_window) &&
00364       meta_rectangle_intersect (&window->rect,
00365                                 &focus_window->rect,
00366                                 &overlap))
00367     {
00368       find_most_freespace (window, fgeom, focus_window, *x, *y, x, y);
00369       meta_topic (META_DEBUG_PLACEMENT,
00370                   "Dialog window %s was denied focus but may be modal "
00371                   "to the focus window; had to move it to avoid the "
00372                   "focus window\n",
00373                   window->desc);
00374     }
00375 }
00376 
00377 static gboolean
00378 rectangle_overlaps_some_window (MetaRectangle *rect,
00379                                 GList         *windows)
00380 {
00381   GList *tmp;
00382   MetaRectangle dest;
00383   
00384   tmp = windows;
00385   while (tmp != NULL)
00386     {
00387       MetaWindow *other = tmp->data;
00388       MetaRectangle other_rect;      
00389 
00390       switch (other->type)
00391         {
00392         case META_WINDOW_DOCK:
00393         case META_WINDOW_SPLASHSCREEN:
00394         case META_WINDOW_DESKTOP:
00395         case META_WINDOW_DIALOG:
00396         case META_WINDOW_MODAL_DIALOG:
00397           break;
00398 
00399         case META_WINDOW_NORMAL:
00400         case META_WINDOW_UTILITY:
00401         case META_WINDOW_TOOLBAR:
00402         case META_WINDOW_MENU:
00403           meta_window_get_outer_rect (other, &other_rect);
00404           
00405           if (meta_rectangle_intersect (rect, &other_rect, &dest))
00406             return TRUE;
00407           break;
00408         }
00409       
00410       tmp = tmp->next;
00411     }
00412 
00413   return FALSE;
00414 }
00415 
00416 static gint
00417 leftmost_cmp (gconstpointer a, gconstpointer b)
00418 {
00419   MetaWindow *aw = (gpointer) a;
00420   MetaWindow *bw = (gpointer) b;
00421   int ax, bx;
00422 
00423   /* we're interested in the frame position for cascading,
00424    * not meta_window_get_position()
00425    */
00426   if (aw->frame)
00427     ax = aw->frame->rect.x;
00428   else
00429     ax = aw->rect.x;
00430 
00431   if (bw->frame)
00432     bx = bw->frame->rect.x;
00433   else
00434     bx = bw->rect.x;
00435 
00436   if (ax < bx)
00437     return -1;
00438   else if (ax > bx)
00439     return 1;
00440   else
00441     return 0;
00442 }
00443 
00444 static gint
00445 topmost_cmp (gconstpointer a, gconstpointer b)
00446 {
00447   MetaWindow *aw = (gpointer) a;
00448   MetaWindow *bw = (gpointer) b;
00449   int ay, by;
00450 
00451   /* we're interested in the frame position for cascading,
00452    * not meta_window_get_position()
00453    */
00454   if (aw->frame)
00455     ay = aw->frame->rect.y;
00456   else
00457     ay = aw->rect.y;
00458 
00459   if (bw->frame)
00460     by = bw->frame->rect.y;
00461   else
00462     by = bw->rect.y;
00463 
00464   if (ay < by)
00465     return -1;
00466   else if (ay > by)
00467     return 1;
00468   else
00469     return 0;
00470 }
00471 
00472 static void
00473 center_tile_rect_in_area (MetaRectangle *rect,
00474                           MetaRectangle *work_area)
00475 {
00476   int fluff;
00477 
00478   /* The point here is to tile a window such that "extra"
00479    * space is equal on either side (i.e. so a full screen
00480    * of windows tiled this way would center the windows
00481    * as a group)
00482    */
00483 
00484   fluff = (work_area->width % (rect->width+1)) / 2;
00485   rect->x = work_area->x + fluff;
00486   fluff = (work_area->height % (rect->height+1)) / 3;
00487   rect->y = work_area->y + fluff;
00488 }
00489 
00490 /* Find the leftmost, then topmost, empty area on the workspace
00491  * that can contain the new window.
00492  *
00493  * Cool feature to have: if we can't fit the current window size,
00494  * try shrinking the window (within geometry constraints). But
00495  * beware windows such as Emacs with no sane minimum size, we
00496  * don't want to create a 1x1 Emacs.
00497  */
00498 static gboolean
00499 find_first_fit (MetaWindow *window,
00500                 MetaFrameGeometry *fgeom,
00501                 /* visible windows on relevant workspaces */
00502                 GList      *windows,
00503                 int         xinerama,
00504                 int         x,
00505                 int         y,
00506                 int        *new_x,
00507                 int        *new_y)
00508 {
00509   /* This algorithm is limited - it just brute-force tries
00510    * to fit the window in a small number of locations that are aligned
00511    * with existing windows. It tries to place the window on
00512    * the bottom of each existing window, and then to the right
00513    * of each existing window, aligned with the left/top of the
00514    * existing window in each of those cases.
00515    */  
00516   int retval;
00517   GList *below_sorted;
00518   GList *right_sorted;
00519   GList *tmp;
00520   MetaRectangle rect;
00521   MetaRectangle work_area;
00522   
00523   retval = FALSE;
00524 
00525   /* Below each window */
00526   below_sorted = g_list_copy (windows);
00527   below_sorted = g_list_sort (below_sorted, leftmost_cmp);
00528   below_sorted = g_list_sort (below_sorted, topmost_cmp);  
00529 
00530   /* To the right of each window */
00531   right_sorted = g_list_copy (windows);
00532   right_sorted = g_list_sort (right_sorted, topmost_cmp);
00533   right_sorted = g_list_sort (right_sorted, leftmost_cmp);
00534   
00535   rect.width = window->rect.width;
00536   rect.height = window->rect.height;
00537   
00538   if (fgeom)
00539     {
00540       rect.width += fgeom->left_width + fgeom->right_width;
00541       rect.height += fgeom->top_height + fgeom->bottom_height;
00542     }
00543 
00544 #ifdef WITH_VERBOSE_MODE
00545     {
00546       char xinerama_location_string[RECT_LENGTH];
00547       meta_rectangle_to_string (&window->screen->xinerama_infos[xinerama].rect,
00548                                 xinerama_location_string);
00549       meta_topic (META_DEBUG_XINERAMA,
00550                   "Natural xinerama is %s\n",
00551                   xinerama_location_string);
00552     }
00553 #endif
00554 
00555     meta_window_get_work_area_for_xinerama (window, xinerama, &work_area);
00556 
00557     center_tile_rect_in_area (&rect, &work_area);
00558 
00559     if (meta_rectangle_contains_rect (&work_area, &rect) &&
00560         !rectangle_overlaps_some_window (&rect, windows))
00561       {
00562         *new_x = rect.x;
00563         *new_y = rect.y;
00564         if (fgeom)
00565           {
00566             *new_x += fgeom->left_width;
00567             *new_y += fgeom->top_height;
00568           }
00569     
00570         retval = TRUE;
00571        
00572         goto out;
00573       }
00574 
00575     /* try below each window */
00576     tmp = below_sorted;
00577     while (tmp != NULL)
00578       {
00579         MetaWindow *w = tmp->data;
00580         MetaRectangle outer_rect;
00581 
00582         meta_window_get_outer_rect (w, &outer_rect);
00583       
00584         rect.x = outer_rect.x;
00585         rect.y = outer_rect.y + outer_rect.height;
00586       
00587         if (meta_rectangle_contains_rect (&work_area, &rect) &&
00588             !rectangle_overlaps_some_window (&rect, below_sorted))
00589           {
00590             *new_x = rect.x;
00591             *new_y = rect.y;
00592             if (fgeom)
00593               {
00594                 *new_x += fgeom->left_width;
00595                 *new_y += fgeom->top_height;
00596               }
00597           
00598             retval = TRUE;
00599           
00600             goto out;
00601           }
00602 
00603         tmp = tmp->next;
00604       }
00605 
00606     /* try to the right of each window */
00607     tmp = right_sorted;
00608     while (tmp != NULL)
00609       {
00610         MetaWindow *w = tmp->data;
00611         MetaRectangle outer_rect;
00612    
00613         meta_window_get_outer_rect (w, &outer_rect);
00614      
00615         rect.x = outer_rect.x + outer_rect.width;
00616         rect.y = outer_rect.y;
00617    
00618         if (meta_rectangle_contains_rect (&work_area, &rect) &&
00619             !rectangle_overlaps_some_window (&rect, right_sorted))
00620           {
00621             *new_x = rect.x;
00622             *new_y = rect.y;
00623             if (fgeom)
00624               {
00625                 *new_x += fgeom->left_width;
00626                 *new_y += fgeom->top_height;
00627               }
00628         
00629             retval = TRUE;
00630        
00631             goto out;
00632           }
00633 
00634         tmp = tmp->next;
00635       }
00636       
00637  out:
00638 
00639   g_list_free (below_sorted);
00640   g_list_free (right_sorted);
00641   return retval;
00642 }
00643 
00644 void
00645 meta_window_place (MetaWindow        *window,
00646                    MetaFrameGeometry *fgeom,
00647                    int                x,
00648                    int                y,
00649                    int               *new_x,
00650                    int               *new_y)
00651 {
00652   GList *windows;
00653   const MetaXineramaScreenInfo *xi;
00654 
00655   /* frame member variables should NEVER be used in here, only
00656    * MetaFrameGeometry. But remember fgeom == NULL
00657    * for undecorated windows. Also, this function should
00658    * NEVER have side effects other than computing the
00659    * placement coordinates.
00660    */
00661   
00662   meta_topic (META_DEBUG_PLACEMENT, "Placing window %s\n", window->desc);
00663 
00664   windows = NULL;
00665   
00666   switch (window->type)
00667     {
00668       /* Run placement algorithm on these. */
00669     case META_WINDOW_NORMAL:
00670     case META_WINDOW_DIALOG:
00671     case META_WINDOW_MODAL_DIALOG:
00672     case META_WINDOW_SPLASHSCREEN:
00673       break;
00674           
00675       /* Assume the app knows best how to place these, no placement
00676        * algorithm ever (other than "leave them as-is")
00677        */
00678     case META_WINDOW_DESKTOP:
00679     case META_WINDOW_DOCK:
00680     case META_WINDOW_TOOLBAR:
00681     case META_WINDOW_MENU:
00682     case META_WINDOW_UTILITY:
00683       goto done_no_constraints;
00684     }
00685   
00686   if (meta_prefs_get_disable_workarounds ())
00687     {
00688       switch (window->type)
00689         {
00690           /* Only accept USPosition on normal windows because the app is full
00691            * of shit claiming the user set -geometry for a dialog or dock
00692            */
00693         case META_WINDOW_NORMAL:
00694           if (window->size_hints.flags & USPosition)
00695             {
00696               /* don't constrain with placement algorithm */
00697               meta_topic (META_DEBUG_PLACEMENT,
00698                           "Honoring USPosition for %s instead of using placement algorithm\n", window->desc);
00699 
00700               goto done;
00701             }
00702           break;
00703 
00704           /* Ignore even USPosition on dialogs, splashscreen */
00705         case META_WINDOW_DIALOG:
00706         case META_WINDOW_MODAL_DIALOG:
00707         case META_WINDOW_SPLASHSCREEN:
00708           break;
00709           
00710           /* Assume the app knows best how to place these. */
00711         case META_WINDOW_DESKTOP:
00712         case META_WINDOW_DOCK:
00713         case META_WINDOW_TOOLBAR:
00714         case META_WINDOW_MENU:
00715         case META_WINDOW_UTILITY:
00716           if (window->size_hints.flags & PPosition)
00717             {
00718               meta_topic (META_DEBUG_PLACEMENT,
00719                           "Not placing non-normal non-dialog window with PPosition set\n");
00720               goto done_no_constraints;
00721             }
00722           break;
00723         }
00724     }
00725   else
00726     {
00727       /* workarounds enabled */
00728       
00729       if ((window->size_hints.flags & PPosition) ||
00730           (window->size_hints.flags & USPosition))
00731         {
00732           meta_topic (META_DEBUG_PLACEMENT,
00733                       "Not placing window with PPosition or USPosition set\n");
00734           avoid_being_obscured_as_second_modal_dialog (window, fgeom, &x, &y);
00735           goto done_no_constraints;
00736         }
00737     }
00738   
00739   if ((window->type == META_WINDOW_DIALOG ||
00740        window->type == META_WINDOW_MODAL_DIALOG) &&
00741       window->xtransient_for != None)
00742     {
00743       /* Center horizontally, at top of parent vertically */
00744 
00745       MetaWindow *parent;
00746           
00747       parent =
00748         meta_display_lookup_x_window (window->display,
00749                                       window->xtransient_for);
00750 
00751       if (parent)
00752         {
00753           int w;
00754 
00755           meta_window_get_position (parent, &x, &y);
00756           w = parent->rect.width;
00757 
00758           /* center of parent */
00759           x = x + w / 2;
00760           /* center of child over center of parent */
00761           x -= window->rect.width / 2;
00762 
00763           /* "visually" center window over parent, leaving twice as
00764            * much space below as on top.
00765            */
00766           y += (parent->rect.height - window->rect.height)/3;
00767 
00768           /* put top of child's frame, not top of child's client */
00769           if (fgeom)
00770             y += fgeom->top_height;
00771 
00772           meta_topic (META_DEBUG_PLACEMENT, "Centered window %s over transient parent\n",
00773                       window->desc);
00774           
00775           avoid_being_obscured_as_second_modal_dialog (window, fgeom, &x, &y);
00776 
00777           goto done;
00778         }
00779     }
00780   
00781   /* FIXME UTILITY with transient set should be stacked up
00782    * on the sides of the parent window or something.
00783    */
00784   
00785   if (window->type == META_WINDOW_DIALOG ||
00786       window->type == META_WINDOW_MODAL_DIALOG ||
00787       window->type == META_WINDOW_SPLASHSCREEN)
00788     {
00789       /* Center on current xinerama (i.e. on current monitor) */
00790       int w, h;
00791 
00792       /* Warning, this function is a round trip! */
00793       xi = meta_screen_get_current_xinerama (window->screen);
00794 
00795       w = xi->rect.width;
00796       h = xi->rect.height;
00797 
00798       x = (w - window->rect.width) / 2;
00799       y = (h - window->rect.height) / 2;
00800 
00801       x += xi->rect.x;
00802       y += xi->rect.y;
00803       
00804       meta_topic (META_DEBUG_PLACEMENT, "Centered window %s on screen %d xinerama %d\n",
00805                   window->desc, window->screen->number, xi->number);
00806 
00807       goto done_check_denied_focus;
00808     }
00809   
00810   /* Find windows that matter (not minimized, on same workspace
00811    * as placed window, may be shaded - if shaded we pretend it isn't
00812    * for placement purposes)
00813    */
00814   {
00815     GSList *all_windows;
00816     GSList *tmp;
00817     
00818     all_windows = meta_display_list_windows (window->display);
00819 
00820     tmp = all_windows;
00821     while (tmp != NULL)
00822       {
00823         MetaWindow *w = tmp->data;
00824 
00825         if (meta_window_showing_on_its_workspace (w) &&
00826             w != window && 
00827             (window->workspace == w->workspace ||
00828              window->on_all_workspaces || w->on_all_workspaces))
00829           windows = g_list_prepend (windows, w);
00830 
00831         tmp = tmp->next;
00832       }
00833 
00834     g_slist_free (all_windows);
00835   }
00836 
00837   /* Warning, this is a round trip! */
00838   xi = meta_screen_get_current_xinerama (window->screen);
00839   
00840   /* "Origin" placement algorithm */
00841   x = xi->rect.x;
00842   y = xi->rect.y;
00843 
00844   if (find_first_fit (window, fgeom, windows,
00845                       xi->number,
00846                       x, y, &x, &y))
00847     goto done_check_denied_focus;
00848 
00849   /* Maximize windows if they are too big for their work area (bit of
00850    * a hack here). Assume undecorated windows probably don't intend to
00851    * be maximized.  
00852    */
00853   if (window->has_maximize_func && window->decorated &&
00854       !window->fullscreen)
00855     {
00856       MetaRectangle workarea;
00857       MetaRectangle outer;
00858 
00859       meta_window_get_work_area_for_xinerama (window,
00860                                               xi->number,
00861                                               &workarea);      
00862       meta_window_get_outer_rect (window, &outer);
00863       
00864       /* If the window is bigger than the screen, then automaximize.  Do NOT
00865        * auto-maximize the directions independently.  See #419810.
00866        */
00867       if (outer.width >= workarea.width && outer.height >= workarea.height)
00868         {
00869           window->maximize_horizontally_after_placement = TRUE;
00870           window->maximize_vertically_after_placement = TRUE;
00871         }
00872     }
00873 
00874   /* If no placement has been done, revert to cascade to avoid 
00875    * fully overlapping window (e.g. starting multiple terminals)
00876    * */
00877   if (x == xi->rect.x && y == xi->rect.y)  
00878     find_next_cascade (window, fgeom, windows, x, y, &x, &y);
00879 
00880  done_check_denied_focus:
00881   /* If the window is being denied focus and isn't a transient of the
00882    * focus window, we do NOT want it to overlap with the focus window
00883    * if at all possible.  This is guaranteed to only be called if the
00884    * focus_window is non-NULL, and we try to avoid that window.
00885    */
00886   if (window->denied_focus_and_not_transient)
00887     {
00888       gboolean       found_fit;
00889       MetaWindow    *focus_window;
00890       MetaRectangle  overlap;
00891 
00892       focus_window = window->display->focus_window;
00893       g_assert (focus_window != NULL);
00894 
00895       /* No need to do anything if the window doesn't overlap at all */
00896       found_fit = !meta_rectangle_intersect (&window->rect,
00897                                              &focus_window->rect,
00898                                              &overlap);
00899 
00900       /* Try to do a first fit again, this time only taking into account the
00901        * focus window.
00902        */
00903       if (!found_fit)
00904         {
00905           GList *focus_window_list;
00906           focus_window_list = g_list_prepend (NULL, focus_window);
00907 
00908           /* Reset x and y ("origin" placement algorithm) */
00909           x = xi->rect.x;
00910           y = xi->rect.y;
00911 
00912           found_fit = find_first_fit (window, fgeom, focus_window_list,
00913                                       xi->number,
00914                                       x, y, &x, &y);
00915           g_list_free (focus_window_list);
00916         }
00917 
00918       /* If that still didn't work, just place it where we can see as much
00919        * as possible.
00920        */
00921       if (!found_fit)
00922         find_most_freespace (window, fgeom, focus_window, x, y, &x, &y);
00923     }
00924   
00925  done:
00926   g_list_free (windows);
00927   
00928  done_no_constraints:
00929 
00930   *new_x = x;
00931   *new_y = y;
00932 }

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