frames.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity window frame manager widget */
00004 
00005 /* 
00006  * Copyright (C) 2001 Havoc Pennington
00007  * Copyright (C) 2003 Red Hat, Inc.
00008  * Copyright (C) 2005, 2006 Elijah Newren
00009  * 
00010  * This program is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU General Public License as
00012  * published by the Free Software Foundation; either version 2 of the
00013  * License, or (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * General Public License for more details.
00019  * 
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00023  * 02111-1307, USA.
00024  */
00025 
00026 #include <config.h>
00027 #include <math.h>
00028 #include "boxes.h"
00029 #include "frames.h"
00030 #include "util.h"
00031 #include "core.h"
00032 #include "menu.h"
00033 #include "fixedtip.h"
00034 #include "theme.h"
00035 #include "prefs.h"
00036 #include "ui.h"
00037 
00038 #ifdef HAVE_SHAPE
00039 #include <X11/extensions/shape.h>
00040 #endif
00041 
00042 #define DEFAULT_INNER_BUTTON_BORDER 3
00043 
00044 static void meta_frames_class_init (MetaFramesClass *klass);
00045 static void meta_frames_init       (MetaFrames      *frames);
00046 static void meta_frames_destroy    (GtkObject       *object);
00047 static void meta_frames_finalize   (GObject         *object);
00048 static void meta_frames_style_set  (GtkWidget       *widget,
00049                                     GtkStyle        *prev_style);
00050 static void meta_frames_realize    (GtkWidget       *widget);
00051 static void meta_frames_unrealize  (GtkWidget       *widget);
00052 
00053 static void meta_frames_update_prelit_control (MetaFrames      *frames,
00054                                                MetaUIFrame     *frame,
00055                                                MetaFrameControl control);
00056 static gboolean meta_frames_button_press_event    (GtkWidget           *widget,
00057                                                    GdkEventButton      *event);
00058 static gboolean meta_frames_button_release_event  (GtkWidget           *widget,
00059                                                    GdkEventButton      *event);
00060 static gboolean meta_frames_motion_notify_event   (GtkWidget           *widget,
00061                                                    GdkEventMotion      *event);
00062 static gboolean meta_frames_destroy_event         (GtkWidget           *widget,
00063                                                    GdkEventAny         *event);
00064 static gboolean meta_frames_expose_event          (GtkWidget           *widget,
00065                                                    GdkEventExpose      *event);
00066 static gboolean meta_frames_enter_notify_event    (GtkWidget           *widget,
00067                                                    GdkEventCrossing    *event);
00068 static gboolean meta_frames_leave_notify_event    (GtkWidget           *widget,
00069                                                    GdkEventCrossing    *event);
00070 
00071 static void meta_frames_paint_to_drawable (MetaFrames   *frames,
00072                                            MetaUIFrame  *frame,
00073                                            GdkDrawable  *drawable,
00074                                            GdkRegion    *region,
00075                                            int           x_offset,
00076                                            int           y_offset);
00077 
00078 static void meta_frames_set_window_background (MetaFrames   *frames,
00079                                                MetaUIFrame  *frame);
00080 
00081 static void meta_frames_calc_geometry (MetaFrames        *frames,
00082                                        MetaUIFrame         *frame,
00083                                        MetaFrameGeometry *fgeom);
00084 
00085 static void meta_frames_ensure_layout (MetaFrames      *frames,
00086                                        MetaUIFrame     *frame);
00087 
00088 static MetaUIFrame* meta_frames_lookup_window (MetaFrames *frames,
00089                                                Window      xwindow);
00090 
00091 static void meta_frames_font_changed          (MetaFrames *frames);
00092 static void meta_frames_button_layout_changed (MetaFrames *frames);
00093 
00094 
00095 static GdkRectangle*    control_rect (MetaFrameControl   control,
00096                                       MetaFrameGeometry *fgeom);
00097 static MetaFrameControl get_control  (MetaFrames        *frames,
00098                                       MetaUIFrame       *frame,
00099                                       int                x,
00100                                       int                y);
00101 static void clear_tip (MetaFrames *frames);
00102 static void invalidate_all_caches (MetaFrames *frames);
00103 static void invalidate_whole_window (MetaFrames *frames,
00104                                      MetaUIFrame *frame);
00105 
00106 static GtkWidgetClass *parent_class = NULL;
00107 
00108 GtkType
00109 meta_frames_get_type (void)
00110 {
00111   static GtkType frames_type = 0;
00112 
00113   if (!frames_type)
00114     {
00115       static const GtkTypeInfo frames_info =
00116       {
00117         "MetaFrames",
00118         sizeof (MetaFrames),
00119         sizeof (MetaFramesClass),
00120         (GtkClassInitFunc) meta_frames_class_init,
00121         (GtkObjectInitFunc) meta_frames_init,
00122         /* reserved_1 */ NULL,
00123         /* reserved_2 */ NULL,
00124         (GtkClassInitFunc) NULL,
00125       };
00126 
00127       frames_type = gtk_type_unique (GTK_TYPE_WINDOW, &frames_info);
00128     }
00129 
00130   return frames_type;
00131 }
00132 
00133 static void
00134 meta_frames_class_init (MetaFramesClass *class)
00135 {
00136   GObjectClass   *gobject_class;
00137   GtkObjectClass *object_class;
00138   GtkWidgetClass *widget_class;
00139 
00140   gobject_class = G_OBJECT_CLASS (class);
00141   object_class = (GtkObjectClass*) class;
00142   widget_class = (GtkWidgetClass*) class;
00143 
00144   parent_class = g_type_class_peek_parent (class);
00145 
00146   gobject_class->finalize = meta_frames_finalize;
00147   object_class->destroy = meta_frames_destroy;
00148 
00149   widget_class->style_set = meta_frames_style_set;
00150 
00151   widget_class->realize = meta_frames_realize;
00152   widget_class->unrealize = meta_frames_unrealize;
00153   
00154   widget_class->expose_event = meta_frames_expose_event;
00155   widget_class->destroy_event = meta_frames_destroy_event;  
00156   widget_class->button_press_event = meta_frames_button_press_event;
00157   widget_class->button_release_event = meta_frames_button_release_event;
00158   widget_class->motion_notify_event = meta_frames_motion_notify_event;
00159   widget_class->enter_notify_event = meta_frames_enter_notify_event;
00160   widget_class->leave_notify_event = meta_frames_leave_notify_event;
00161 }
00162 
00163 static gint
00164 unsigned_long_equal (gconstpointer v1,
00165                      gconstpointer v2)
00166 {
00167   return *((const gulong*) v1) == *((const gulong*) v2);
00168 }
00169 
00170 static guint
00171 unsigned_long_hash (gconstpointer v)
00172 {
00173   gulong val = * (const gulong *) v;
00174 
00175   /* I'm not sure this works so well. */
00176 #if GLIB_SIZEOF_LONG > 4
00177   return (guint) (val ^ (val >> 32));
00178 #else
00179   return val;
00180 #endif
00181 }
00182 
00183 static void
00184 prefs_changed_callback (MetaPreference pref,
00185                         void          *data)
00186 {
00187   switch (pref)
00188     {
00189     case META_PREF_TITLEBAR_FONT:
00190       meta_frames_font_changed (META_FRAMES (data));
00191       break;
00192     case META_PREF_BUTTON_LAYOUT:
00193       meta_frames_button_layout_changed (META_FRAMES (data));
00194       break;
00195     default:
00196       break;
00197     }
00198 }
00199 
00200 static void
00201 meta_frames_init (MetaFrames *frames)
00202 {
00203   GTK_WINDOW (frames)->type = GTK_WINDOW_POPUP;
00204 
00205   frames->text_heights = g_hash_table_new (NULL, NULL);
00206   
00207   frames->frames = g_hash_table_new (unsigned_long_hash, unsigned_long_equal);
00208 
00209   frames->tooltip_timeout = 0;
00210 
00211   frames->expose_delay_count = 0;
00212 
00213   frames->invalidate_cache_timeout_id = 0;
00214   frames->invalidate_frames = NULL;
00215   frames->cache = g_hash_table_new (g_direct_hash, g_direct_equal);
00216 
00217   gtk_widget_set_double_buffered (GTK_WIDGET (frames), FALSE);
00218 
00219   meta_prefs_add_listener (prefs_changed_callback, frames);
00220 }
00221 
00222 static void
00223 listify_func (gpointer key, gpointer value, gpointer data)
00224 {
00225   GSList **listp;
00226 
00227   listp = data;
00228   *listp = g_slist_prepend (*listp, value);
00229 }
00230 
00231 static void
00232 meta_frames_destroy (GtkObject *object)
00233 {
00234   GSList *winlist;
00235   GSList *tmp;
00236   MetaFrames *frames;
00237   
00238   frames = META_FRAMES (object);
00239 
00240   clear_tip (frames);
00241   
00242   winlist = NULL;
00243   g_hash_table_foreach (frames->frames, listify_func, &winlist);
00244 
00245   /* Unmanage all frames */
00246   for (tmp = winlist; tmp != NULL; tmp = tmp->next)
00247     {
00248       MetaUIFrame *frame;
00249 
00250       frame = tmp->data;
00251 
00252       meta_frames_unmanage_window (frames, frame->xwindow);
00253     }
00254   g_slist_free (winlist);
00255 
00256   GTK_OBJECT_CLASS (parent_class)->destroy (object);
00257 }
00258 
00259 static void
00260 meta_frames_finalize (GObject *object)
00261 {
00262   MetaFrames *frames;
00263   
00264   frames = META_FRAMES (object);
00265 
00266   meta_prefs_remove_listener (prefs_changed_callback, frames);
00267   
00268   g_hash_table_destroy (frames->text_heights);
00269 
00270   invalidate_all_caches (frames);
00271   if (frames->invalidate_cache_timeout_id)
00272     g_source_remove (frames->invalidate_cache_timeout_id);
00273   
00274   g_assert (g_hash_table_size (frames->frames) == 0);
00275   g_hash_table_destroy (frames->frames);
00276   g_hash_table_destroy (frames->cache);
00277 
00278   G_OBJECT_CLASS (parent_class)->finalize (object);
00279 }
00280 
00281 typedef struct
00282 {
00283   MetaRectangle rect;
00284   GdkPixmap *pixmap;
00285 } CachedFramePiece;
00286 
00287 typedef struct
00288 {
00289   /* Caches of the four rendered sides in a MetaFrame.
00290    * Order: top (titlebar), left, right, bottom.
00291    */
00292   CachedFramePiece piece[4];
00293 } CachedPixels;
00294 
00295 static CachedPixels *
00296 get_cache (MetaFrames *frames,
00297            MetaUIFrame *frame)
00298 {
00299   CachedPixels *pixels;
00300   
00301   pixels = g_hash_table_lookup (frames->cache, frame);
00302 
00303   if (!pixels)
00304     {
00305       pixels = g_new0 (CachedPixels, 1);
00306       g_hash_table_insert (frames->cache, frame, pixels);
00307     }
00308 
00309   return pixels;
00310 }
00311 
00312 static void
00313 invalidate_cache (MetaFrames *frames,
00314                   MetaUIFrame *frame)
00315 {
00316   CachedPixels *pixels = get_cache (frames, frame);
00317   int i;
00318   
00319   for (i = 0; i < 4; i++)
00320     if (pixels->piece[i].pixmap)
00321       g_object_unref (pixels->piece[i].pixmap);
00322   
00323   g_free (pixels);
00324   g_hash_table_remove (frames->cache, frame);
00325 }
00326 
00327 static void
00328 invalidate_all_caches (MetaFrames *frames)
00329 {
00330   GList *l;
00331 
00332   for (l = frames->invalidate_frames; l; l = l->next)
00333     {
00334       MetaUIFrame *frame = l->data;
00335 
00336       invalidate_cache (frames, frame);
00337     }
00338   
00339   g_list_free (frames->invalidate_frames);
00340   frames->invalidate_frames = NULL;
00341 }
00342 
00343 static gboolean
00344 invalidate_cache_timeout (gpointer data)
00345 {
00346   MetaFrames *frames = data;
00347   
00348   invalidate_all_caches (frames);
00349   frames->invalidate_cache_timeout_id = 0;
00350   return FALSE;
00351 }
00352 
00353 static void
00354 queue_recalc_func (gpointer key, gpointer value, gpointer data)
00355 {
00356   MetaUIFrame *frame;
00357   MetaFrames *frames;
00358 
00359   frames = META_FRAMES (data);
00360   frame = value;
00361 
00362   /* If a resize occurs it will cause a redraw, but the
00363    * resize may not actually be needed so we always redraw
00364    * in case of color change.
00365    */
00366   meta_frames_set_window_background (frames, frame);
00367   
00368   invalidate_whole_window (frames, frame);
00369   meta_core_queue_frame_resize (gdk_display,
00370                                 frame->xwindow);
00371   if (frame->layout)
00372     {
00373       /* save title to recreate layout */
00374       g_free (frame->title);
00375       
00376       frame->title = g_strdup (pango_layout_get_text (frame->layout));
00377 
00378       g_object_unref (G_OBJECT (frame->layout));
00379       frame->layout = NULL;
00380     }
00381 }
00382 
00383 static void
00384 meta_frames_font_changed (MetaFrames *frames)
00385 {
00386   if (g_hash_table_size (frames->text_heights) > 0)
00387     {
00388       g_hash_table_destroy (frames->text_heights);
00389       frames->text_heights = g_hash_table_new (NULL, NULL);
00390     }
00391   
00392   /* Queue a draw/resize on all frames */
00393   g_hash_table_foreach (frames->frames,
00394                         queue_recalc_func, frames);
00395 
00396 }
00397 
00398 static void
00399 queue_draw_func (gpointer key, gpointer value, gpointer data)
00400 {
00401   MetaUIFrame *frame;
00402   MetaFrames *frames;
00403 
00404   frames = META_FRAMES (data);
00405   frame = value;
00406 
00407   /* If a resize occurs it will cause a redraw, but the
00408    * resize may not actually be needed so we always redraw
00409    * in case of color change.
00410    */
00411   meta_frames_set_window_background (frames, frame);
00412 
00413   invalidate_whole_window (frames, frame);
00414 }
00415 
00416 static void
00417 meta_frames_button_layout_changed (MetaFrames *frames)
00418 {
00419   g_hash_table_foreach (frames->frames,
00420                         queue_draw_func, frames);
00421 }
00422 
00423 static void
00424 meta_frames_style_set  (GtkWidget *widget,
00425                         GtkStyle  *prev_style)
00426 {
00427   MetaFrames *frames;
00428 
00429   frames = META_FRAMES (widget);
00430 
00431   meta_frames_font_changed (frames);
00432 
00433   GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
00434 }
00435 
00436 static void
00437 meta_frames_ensure_layout (MetaFrames  *frames,
00438                            MetaUIFrame *frame)
00439 {
00440   GtkWidget *widget;
00441   MetaFrameFlags flags;
00442   MetaFrameType type;
00443   MetaFrameStyle *style;
00444   
00445   g_return_if_fail (GTK_WIDGET_REALIZED (frames));
00446 
00447   widget = GTK_WIDGET (frames);
00448       
00449   meta_core_get (gdk_display, frame->xwindow,
00450                  META_CORE_GET_FRAME_FLAGS, &flags,
00451                  META_CORE_GET_FRAME_TYPE, &type,
00452                  META_CORE_GET_END);
00453 
00454   style = meta_theme_get_frame_style (meta_theme_get_current (),
00455                                       type, flags);
00456 
00457   if (style != frame->cache_style)
00458     {
00459       if (frame->layout)
00460         {
00461           /* save title to recreate layout */
00462           g_free (frame->title);
00463           
00464           frame->title = g_strdup (pango_layout_get_text (frame->layout));
00465 
00466           g_object_unref (G_OBJECT (frame->layout));
00467           frame->layout = NULL;
00468         }
00469     }
00470 
00471   frame->cache_style = style;
00472   
00473   if (frame->layout == NULL)
00474     {
00475       gpointer key, value;
00476       PangoFontDescription *font_desc;
00477       double scale;
00478       int size;
00479       
00480       scale = meta_theme_get_title_scale (meta_theme_get_current (),
00481                                           type,
00482                                           flags);
00483       
00484       frame->layout = gtk_widget_create_pango_layout (widget, frame->title);
00485 
00486       pango_layout_set_auto_dir (frame->layout, FALSE);
00487       
00488       font_desc = meta_gtk_widget_get_font_desc (widget, scale,
00489                                                  meta_prefs_get_titlebar_font ());
00490 
00491       size = pango_font_description_get_size (font_desc);
00492 
00493       if (g_hash_table_lookup_extended (frames->text_heights,
00494                                         GINT_TO_POINTER (size),
00495                                         &key, &value))
00496         {
00497           frame->text_height = GPOINTER_TO_INT (value);
00498         }
00499       else
00500         {
00501           frame->text_height =
00502             meta_pango_font_desc_get_text_height (font_desc,
00503                                                   gtk_widget_get_pango_context (widget));
00504 
00505           g_hash_table_replace (frames->text_heights,
00506                                 GINT_TO_POINTER (size),
00507                                 GINT_TO_POINTER (frame->text_height));
00508         }
00509       
00510       pango_layout_set_font_description (frame->layout, 
00511                                          font_desc);
00512       
00513       pango_font_description_free (font_desc);
00514 
00515       /* Save some RAM */
00516       g_free (frame->title);
00517       frame->title = NULL;
00518     }
00519 }
00520 
00521 static void
00522 meta_frames_calc_geometry (MetaFrames        *frames,
00523                            MetaUIFrame       *frame,
00524                            MetaFrameGeometry *fgeom)
00525 {
00526   int width, height;
00527   MetaFrameFlags flags;
00528   MetaFrameType type;
00529   MetaButtonLayout button_layout;
00530   
00531   meta_core_get (gdk_display, frame->xwindow,
00532                  META_CORE_GET_CLIENT_WIDTH, &width,
00533                  META_CORE_GET_CLIENT_HEIGHT, &height,
00534                  META_CORE_GET_FRAME_FLAGS, &flags,
00535                  META_CORE_GET_FRAME_TYPE, &type,
00536                  META_CORE_GET_END);
00537 
00538   meta_frames_ensure_layout (frames, frame);
00539 
00540   meta_prefs_get_button_layout (&button_layout);
00541   
00542   meta_theme_calc_geometry (meta_theme_get_current (),
00543                             type,
00544                             frame->text_height,
00545                             flags,
00546                             width, height,
00547                             &button_layout,
00548                             fgeom);
00549 }
00550 
00551 MetaFrames*
00552 meta_frames_new (int screen_number)
00553 {
00554   GdkScreen *screen;
00555 
00556   screen = gdk_display_get_screen (gdk_display_get_default (),
00557                                    screen_number);
00558 
00559   return g_object_new (META_TYPE_FRAMES,
00560                        "screen", screen,
00561                        NULL);  
00562 }
00563 
00564 void
00565 meta_frames_manage_window (MetaFrames *frames,
00566                            Window      xwindow,
00567                            GdkWindow  *window)
00568 {
00569   MetaUIFrame *frame;
00570 
00571   g_assert (window);
00572 
00573   frame = g_new (MetaUIFrame, 1);
00574   
00575   frame->window = window;
00576 
00577   gdk_window_set_user_data (frame->window, frames);
00578 
00579   /* Don't set event mask here, it's in frame.c */
00580   
00581   frame->xwindow = xwindow;
00582   frame->cache_style = NULL;
00583   frame->layout = NULL;
00584   frame->text_height = -1;
00585   frame->title = NULL;
00586   frame->expose_delayed = FALSE;
00587   frame->shape_applied = FALSE;
00588   frame->prelit_control = META_FRAME_CONTROL_NONE;
00589 
00590   /* Don't set the window background yet; we need frame->xwindow to be
00591    * registered with its MetaWindow, which happens after this function
00592    * and meta_ui_create_frame_window() return to meta_window_ensure_frame().
00593    */
00594   
00595   meta_core_grab_buttons (gdk_display, frame->xwindow);
00596   
00597   g_hash_table_replace (frames->frames, &frame->xwindow, frame);
00598 }
00599 
00600 void
00601 meta_frames_unmanage_window (MetaFrames *frames,
00602                              Window      xwindow)
00603 {
00604   MetaUIFrame *frame;
00605 
00606   clear_tip (frames);
00607   
00608   frame = g_hash_table_lookup (frames->frames, &xwindow);
00609 
00610   if (frame)
00611     {
00612       /* invalidating all caches ensures the frame
00613        * is not actually referenced anymore
00614        */
00615       invalidate_all_caches (frames);
00616       
00617       /* restore the cursor */
00618       meta_core_set_screen_cursor (gdk_display,
00619                                    frame->xwindow,
00620                                    META_CURSOR_DEFAULT);
00621 
00622       gdk_window_set_user_data (frame->window, NULL);
00623 
00624       if (frames->last_motion_frame == frame)
00625         frames->last_motion_frame = NULL;
00626       
00627       g_hash_table_remove (frames->frames, &frame->xwindow);
00628 
00629       gdk_window_destroy (frame->window);
00630 
00631       if (frame->layout)
00632         g_object_unref (G_OBJECT (frame->layout));
00633 
00634       if (frame->title)
00635         g_free (frame->title);
00636       
00637       g_free (frame);
00638     }
00639   else
00640     meta_warning ("Frame 0x%lx not managed, can't unmanage\n", xwindow);
00641 }
00642 
00643 static void
00644 meta_frames_realize (GtkWidget *widget)
00645 {
00646   if (GTK_WIDGET_CLASS (parent_class)->realize)
00647     GTK_WIDGET_CLASS (parent_class)->realize (widget);
00648 }
00649 
00650 static void
00651 meta_frames_unrealize (GtkWidget *widget)
00652 {
00653   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
00654     GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
00655 }
00656 
00657 static MetaUIFrame*
00658 meta_frames_lookup_window (MetaFrames *frames,
00659                            Window      xwindow)
00660 {
00661   MetaUIFrame *frame;
00662 
00663   frame = g_hash_table_lookup (frames->frames, &xwindow);
00664 
00665   return frame;
00666 }
00667 
00668 void
00669 meta_frames_get_geometry (MetaFrames *frames,
00670                           Window xwindow,
00671                           int *top_height, int *bottom_height,
00672                           int *left_width, int *right_width)
00673 {
00674   MetaFrameFlags flags;
00675   MetaUIFrame *frame;
00676   MetaFrameType type;
00677   
00678   frame = meta_frames_lookup_window (frames, xwindow);
00679 
00680   if (frame == NULL)
00681     meta_bug ("No such frame 0x%lx\n", xwindow);
00682   
00683   meta_core_get (gdk_display, frame->xwindow,
00684                  META_CORE_GET_FRAME_FLAGS, &flags,
00685                  META_CORE_GET_FRAME_TYPE, &type,
00686                  META_CORE_GET_END);
00687 
00688   g_return_if_fail (type < META_FRAME_TYPE_LAST);
00689 
00690   meta_frames_ensure_layout (frames, frame);
00691   
00692   /* We can't get the full geometry, because that depends on
00693    * the client window size and probably we're being called
00694    * by the core move/resize code to decide on the client
00695    * window size
00696    */
00697   meta_theme_get_frame_borders (meta_theme_get_current (),
00698                                 type,
00699                                 frame->text_height,
00700                                 flags,
00701                                 top_height, bottom_height,
00702                                 left_width, right_width);
00703 }
00704 
00705 void
00706 meta_frames_reset_bg (MetaFrames *frames,
00707                       Window  xwindow)
00708 {
00709   MetaUIFrame *frame;
00710   
00711   frame = meta_frames_lookup_window (frames, xwindow);
00712 
00713   meta_frames_set_window_background (frames, frame);
00714 }
00715 
00716 static void
00717 set_background_none (Display *xdisplay,
00718                      Window   xwindow)
00719 {
00720   XSetWindowAttributes attrs;
00721 
00722   attrs.background_pixmap = None;
00723   XChangeWindowAttributes (xdisplay, xwindow,
00724                            CWBackPixmap, &attrs);
00725 }
00726 
00727 void
00728 meta_frames_unflicker_bg (MetaFrames *frames,
00729                           Window      xwindow,
00730                           int         target_width,
00731                           int         target_height)
00732 {
00733   MetaUIFrame *frame;
00734   
00735   frame = meta_frames_lookup_window (frames, xwindow);
00736   g_return_if_fail (frame != NULL);
00737 
00738 #if 0
00739   pixmap = gdk_pixmap_new (frame->window,
00740                            width, height,
00741                            -1);
00742 
00743   /* Oops, no way to get the background here */
00744   
00745   meta_frames_paint_to_drawable (frames, frame, pixmap);
00746 #endif
00747 
00748   set_background_none (gdk_display, frame->xwindow);
00749 }
00750 
00751 void
00752 meta_frames_apply_shapes (MetaFrames *frames,
00753                           Window      xwindow,
00754                           int         new_window_width,
00755                           int         new_window_height,
00756                           gboolean    window_has_shape)
00757 {
00758 #ifdef HAVE_SHAPE
00759   /* Apply shapes as if window had new_window_width, new_window_height */
00760   MetaUIFrame *frame;
00761   MetaFrameGeometry fgeom;
00762   XRectangle xrect;
00763   Region corners_xregion;
00764   Region window_xregion;
00765   
00766   frame = meta_frames_lookup_window (frames, xwindow);
00767   g_return_if_fail (frame != NULL);
00768 
00769   meta_frames_calc_geometry (frames, frame, &fgeom);
00770 
00771   if (!(fgeom.top_left_corner_rounded_radius != 0 ||
00772         fgeom.top_right_corner_rounded_radius != 0 ||
00773         fgeom.bottom_left_corner_rounded_radius != 0 ||
00774         fgeom.bottom_right_corner_rounded_radius != 0 ||
00775         window_has_shape))
00776     {
00777       if (frame->shape_applied)
00778         {
00779           meta_topic (META_DEBUG_SHAPES,
00780                       "Unsetting shape mask on frame 0x%lx\n",
00781                       frame->xwindow);
00782           
00783           XShapeCombineMask (gdk_display, frame->xwindow,
00784                              ShapeBounding, 0, 0, None, ShapeSet);
00785           frame->shape_applied = FALSE;
00786         }
00787       else
00788         {
00789           meta_topic (META_DEBUG_SHAPES,
00790                       "Frame 0x%lx still doesn't need a shape mask\n",
00791                       frame->xwindow);
00792         }
00793       
00794       return; /* nothing to do */
00795     }
00796   
00797   corners_xregion = XCreateRegion ();
00798   
00799   if (fgeom.top_left_corner_rounded_radius != 0)
00800     {
00801       const int corner = fgeom.top_left_corner_rounded_radius;
00802       const float radius = sqrt(corner) + corner;
00803       int i;
00804 
00805       for (i=0; i<corner; i++)
00806         {
00807           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
00808           xrect.x = 0;
00809           xrect.y = i;
00810           xrect.width = width;
00811           xrect.height = 1;
00812           
00813           XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
00814         }
00815     }
00816 
00817   if (fgeom.top_right_corner_rounded_radius != 0)
00818     {
00819       const int corner = fgeom.top_right_corner_rounded_radius;
00820       const float radius = sqrt(corner) + corner;
00821       int i;
00822 
00823       for (i=0; i<corner; i++)
00824         {
00825           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
00826           xrect.x = new_window_width - width;
00827           xrect.y = i;
00828           xrect.width = width;
00829           xrect.height = 1;
00830           
00831           XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
00832         }
00833     }
00834 
00835   if (fgeom.bottom_left_corner_rounded_radius != 0)
00836     {
00837       const int corner = fgeom.bottom_left_corner_rounded_radius;
00838       const float radius = sqrt(corner) + corner;
00839       int i;
00840 
00841       for (i=0; i<corner; i++)
00842         {
00843           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
00844           xrect.x = 0;
00845           xrect.y = new_window_height - i - 1;
00846           xrect.width = width;
00847           xrect.height = 1;
00848           
00849           XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
00850         }
00851     }
00852 
00853   if (fgeom.bottom_right_corner_rounded_radius != 0)
00854     {
00855       const int corner = fgeom.bottom_right_corner_rounded_radius;
00856       const float radius = sqrt(corner) + corner;
00857       int i;
00858 
00859       for (i=0; i<corner; i++)
00860         {
00861           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
00862           xrect.x = new_window_width - width;
00863           xrect.y = new_window_height - i - 1;
00864           xrect.width = width;
00865           xrect.height = 1;
00866           
00867           XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion);
00868         }
00869     }
00870   
00871   window_xregion = XCreateRegion ();
00872   
00873   xrect.x = 0;
00874   xrect.y = 0;
00875   xrect.width = new_window_width;
00876   xrect.height = new_window_height;
00877 
00878   XUnionRectWithRegion (&xrect, window_xregion, window_xregion);
00879 
00880   XSubtractRegion (window_xregion, corners_xregion, window_xregion);
00881 
00882   XDestroyRegion (corners_xregion);
00883   
00884   if (window_has_shape)
00885     {
00886       /* The client window is oclock or something and has a shape
00887        * mask. To avoid a round trip to get its shape region, we
00888        * create a fake window that's never mapped, build up our shape
00889        * on that, then combine. Wasting the window is assumed cheaper
00890        * than a round trip, but who really knows for sure.
00891        */
00892       XSetWindowAttributes attrs;      
00893       Window shape_window;
00894       Window client_window;
00895       Region client_xregion;
00896       GdkScreen *screen;
00897       int screen_number;
00898       
00899       meta_topic (META_DEBUG_SHAPES,
00900                   "Frame 0x%lx needs to incorporate client shape\n",
00901                   frame->xwindow);
00902 
00903       screen = gtk_widget_get_screen (GTK_WIDGET (frames));
00904       screen_number = gdk_x11_screen_get_screen_number (screen);
00905       
00906       attrs.override_redirect = True;
00907       
00908       shape_window = XCreateWindow (gdk_display,
00909                                     RootWindow (gdk_display, screen_number),
00910                                     -5000, -5000,
00911                                     new_window_width,
00912                                     new_window_height,
00913                                     0,
00914                                     CopyFromParent,
00915                                     CopyFromParent,
00916                                     (Visual *)CopyFromParent,
00917                                     CWOverrideRedirect,
00918                                     &attrs);
00919 
00920       /* Copy the client's shape to the temporary shape_window */
00921       meta_core_get (gdk_display, frame->xwindow,
00922                      META_CORE_GET_CLIENT_XWINDOW, &client_window,
00923                      META_CORE_GET_END);
00924 
00925       XShapeCombineShape (gdk_display, shape_window, ShapeBounding,
00926                           fgeom.left_width,
00927                           fgeom.top_height,
00928                           client_window,
00929                           ShapeBounding,
00930                           ShapeSet);
00931 
00932       /* Punch the client area out of the normal frame shape,
00933        * then union it with the shape_window's existing shape
00934        */
00935       client_xregion = XCreateRegion ();
00936   
00937       xrect.x = fgeom.left_width;
00938       xrect.y = fgeom.top_height;
00939       xrect.width = new_window_width - fgeom.right_width - xrect.x;
00940       xrect.height = new_window_height - fgeom.bottom_height - xrect.y;
00941 
00942       XUnionRectWithRegion (&xrect, client_xregion, client_xregion);
00943       
00944       XSubtractRegion (window_xregion, client_xregion, window_xregion);
00945 
00946       XDestroyRegion (client_xregion);
00947       
00948       XShapeCombineRegion (gdk_display, shape_window,
00949                            ShapeBounding, 0, 0, window_xregion, ShapeUnion);
00950       
00951       /* Now copy shape_window shape to the real frame */
00952       XShapeCombineShape (gdk_display, frame->xwindow, ShapeBounding,
00953                           0, 0,
00954                           shape_window,
00955                           ShapeBounding,
00956                           ShapeSet);
00957 
00958       XDestroyWindow (gdk_display, shape_window);
00959     }
00960   else
00961     {
00962       /* No shape on the client, so just do simple stuff */
00963 
00964       meta_topic (META_DEBUG_SHAPES,
00965                   "Frame 0x%lx has shaped corners\n",
00966                   frame->xwindow);
00967       
00968       XShapeCombineRegion (gdk_display, frame->xwindow,
00969                            ShapeBounding, 0, 0, window_xregion, ShapeSet);
00970     }
00971   
00972   frame->shape_applied = TRUE;
00973   
00974   XDestroyRegion (window_xregion);
00975 #endif /* HAVE_SHAPE */
00976 }
00977 
00978 void
00979 meta_frames_move_resize_frame (MetaFrames *frames,
00980                                Window      xwindow,
00981                                int         x,
00982                                int         y,
00983                                int         width,
00984                                int         height)
00985 {
00986   MetaUIFrame *frame = meta_frames_lookup_window (frames, xwindow);
00987   int old_x, old_y, old_width, old_height;
00988   
00989   gdk_drawable_get_size (frame->window, &old_width, &old_height);
00990   gdk_window_get_position (frame->window, &old_x, &old_y);
00991 
00992   gdk_window_move_resize (frame->window, x, y, width, height);
00993 
00994   if (old_width != width || old_height != height)
00995     invalidate_whole_window (frames, frame);
00996 }
00997 
00998 void
00999 meta_frames_queue_draw (MetaFrames *frames,
01000                         Window      xwindow)
01001 {
01002   MetaUIFrame *frame;
01003   
01004   frame = meta_frames_lookup_window (frames, xwindow);
01005 
01006   invalidate_whole_window (frames, frame);
01007 }
01008 
01009 void
01010 meta_frames_set_title (MetaFrames *frames,
01011                        Window      xwindow,
01012                        const char *title)
01013 {
01014   MetaUIFrame *frame;
01015   
01016   frame = meta_frames_lookup_window (frames, xwindow);
01017 
01018   g_assert (frame);
01019   
01020   g_free (frame->title);
01021   frame->title = g_strdup (title);
01022   
01023   if (frame->layout)
01024     {
01025       g_object_unref (frame->layout);
01026       frame->layout = NULL;
01027     }
01028 
01029   invalidate_whole_window (frames, frame);
01030 }
01031 
01032 void
01033 meta_frames_repaint_frame (MetaFrames *frames,
01034                            Window      xwindow)
01035 {
01036   MetaUIFrame *frame;
01037   
01038   frame = meta_frames_lookup_window (frames, xwindow);
01039 
01040   g_assert (frame);
01041 
01042   /* repaint everything, so the other frame don't
01043    * lag behind if they are exposed
01044    */
01045   gdk_window_process_all_updates ();
01046 }
01047 
01048 static void
01049 show_tip_now (MetaFrames *frames)
01050 {
01051   const char *tiptext;
01052   MetaUIFrame *frame;
01053   int x, y, root_x, root_y;
01054   Window root, child;
01055   guint mask;
01056   MetaFrameControl control;
01057   
01058   frame = frames->last_motion_frame;
01059   if (frame == NULL)
01060     return;
01061 
01062   XQueryPointer (gdk_display,
01063                  frame->xwindow,
01064                  &root, &child,
01065                  &root_x, &root_y,
01066                  &x, &y,
01067                  &mask);
01068   
01069   control = get_control (frames, frame, x, y);
01070   
01071   tiptext = NULL;
01072   switch (control)
01073     {
01074     case META_FRAME_CONTROL_TITLE:
01075       break;
01076     case META_FRAME_CONTROL_DELETE:
01077       tiptext = _("Close Window");
01078       break;
01079     case META_FRAME_CONTROL_MENU:
01080       tiptext = _("Window Menu");
01081       break;
01082     case META_FRAME_CONTROL_MINIMIZE:
01083       tiptext = _("Minimize Window");
01084       break;
01085     case META_FRAME_CONTROL_MAXIMIZE:
01086       tiptext = _("Maximize Window");
01087       break;
01088     case META_FRAME_CONTROL_UNMAXIMIZE:
01089       tiptext = _("Unmaximize Window");
01090       break;
01091     case META_FRAME_CONTROL_SHADE:
01092       tiptext = _("Roll Up Window");
01093       break;
01094     case META_FRAME_CONTROL_UNSHADE:
01095       tiptext = _("Unroll Window");
01096       break;
01097     case META_FRAME_CONTROL_ABOVE:
01098       tiptext = _("Keep Window On Top");
01099       break;
01100     case META_FRAME_CONTROL_UNABOVE:
01101       tiptext = _("Remove Window From Top");
01102       break;
01103     case META_FRAME_CONTROL_STICK:
01104       tiptext = _("Always On Visible Workspace");
01105       break;
01106     case META_FRAME_CONTROL_UNSTICK:
01107       tiptext = _("Put Window On Only One Workspace");
01108       break;
01109     case META_FRAME_CONTROL_RESIZE_SE:
01110       break;
01111     case META_FRAME_CONTROL_RESIZE_S:
01112       break;
01113     case META_FRAME_CONTROL_RESIZE_SW:
01114       break;
01115     case META_FRAME_CONTROL_RESIZE_N:
01116       break;
01117     case META_FRAME_CONTROL_RESIZE_NE:
01118       break;
01119     case META_FRAME_CONTROL_RESIZE_NW:
01120       break;
01121     case META_FRAME_CONTROL_RESIZE_W:
01122       break;
01123     case META_FRAME_CONTROL_RESIZE_E:
01124       break;
01125     case META_FRAME_CONTROL_NONE:      
01126       break;
01127     case META_FRAME_CONTROL_CLIENT_AREA:
01128       break;
01129     }
01130 
01131   if (tiptext)
01132     {
01133       MetaFrameGeometry fgeom;
01134       GdkRectangle *rect;
01135       int dx, dy;
01136       int screen_number;
01137       
01138       meta_frames_calc_geometry (frames, frame, &fgeom);
01139       
01140       rect = control_rect (control, &fgeom);
01141 
01142       /* get conversion delta for root-to-frame coords */
01143       dx = root_x - x;
01144       dy = root_y - y;
01145 
01146       /* Align the tooltip to the button right end if RTL */
01147       if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
01148         dx += rect->width;
01149 
01150       screen_number = gdk_screen_get_number (gtk_widget_get_screen (GTK_WIDGET (frames)));
01151       
01152       meta_fixed_tip_show (gdk_display,
01153                            screen_number,
01154                            rect->x + dx,
01155                            rect->y + rect->height + 2 + dy,
01156                            tiptext);
01157     }
01158 }
01159 
01160 static gboolean
01161 tip_timeout_func (gpointer data)
01162 {
01163   MetaFrames *frames;
01164 
01165   frames = data;
01166 
01167   show_tip_now (frames);
01168 
01169   return FALSE;
01170 }
01171 
01172 #define TIP_DELAY 450
01173 static void
01174 queue_tip (MetaFrames *frames)
01175 {
01176   clear_tip (frames);
01177   
01178   frames->tooltip_timeout = g_timeout_add (TIP_DELAY,
01179                                            tip_timeout_func,
01180                                            frames);  
01181 }
01182 
01183 static void
01184 clear_tip (MetaFrames *frames)
01185 {
01186   if (frames->tooltip_timeout)
01187     {
01188       g_source_remove (frames->tooltip_timeout);
01189       frames->tooltip_timeout = 0;
01190     }
01191   meta_fixed_tip_hide ();
01192 }
01193 
01194 static void
01195 redraw_control (MetaFrames *frames,
01196                 MetaUIFrame *frame,
01197                 MetaFrameControl control)
01198 {
01199   MetaFrameGeometry fgeom;
01200   GdkRectangle *rect;
01201   
01202   meta_frames_calc_geometry (frames, frame, &fgeom);
01203 
01204   rect = control_rect (control, &fgeom);
01205 
01206   gdk_window_invalidate_rect (frame->window, rect, FALSE);
01207   invalidate_cache (frames, frame);
01208 }
01209 
01210 static gboolean
01211 meta_frame_titlebar_event (MetaUIFrame    *frame,
01212                            GdkEventButton *event,
01213                            int            action)
01214 {
01215   MetaFrameFlags flags;
01216   
01217   switch (action)
01218     {
01219     case META_ACTION_TITLEBAR_TOGGLE_SHADE:
01220       {
01221         meta_core_get (gdk_display, frame->xwindow,
01222                        META_CORE_GET_FRAME_FLAGS, &flags,
01223                        META_CORE_GET_END);
01224         
01225         if (flags & META_FRAME_ALLOWS_SHADE)
01226           {
01227             if (flags & META_FRAME_SHADED)
01228               meta_core_unshade (gdk_display,
01229                                  frame->xwindow,
01230                                  event->time);
01231             else
01232               meta_core_shade (gdk_display,
01233                                frame->xwindow,
01234                                event->time);
01235           }
01236       }
01237       break;          
01238       
01239     case META_ACTION_TITLEBAR_TOGGLE_MAXIMIZE:
01240       {
01241         meta_core_get (gdk_display, frame->xwindow,
01242                        META_CORE_GET_FRAME_FLAGS, &flags,
01243                        META_CORE_GET_END);
01244         
01245         if (flags & META_FRAME_ALLOWS_MAXIMIZE)
01246           {
01247             meta_core_toggle_maximize (gdk_display, frame->xwindow);
01248           }
01249       }
01250       break;
01251 
01252     case META_ACTION_TITLEBAR_TOGGLE_MAXIMIZE_HORIZONTALLY:
01253       {
01254         meta_core_get (gdk_display, frame->xwindow,
01255                        META_CORE_GET_FRAME_FLAGS, &flags,
01256                        META_CORE_GET_END);
01257         
01258         if (flags & META_FRAME_ALLOWS_MAXIMIZE)
01259           {
01260             meta_core_toggle_maximize_horizontally (gdk_display, frame->xwindow);
01261           }
01262       }
01263       break;
01264 
01265     case META_ACTION_TITLEBAR_TOGGLE_MAXIMIZE_VERTICALLY:
01266       {
01267         meta_core_get (gdk_display, frame->xwindow,
01268                        META_CORE_GET_FRAME_FLAGS, &flags,
01269                        META_CORE_GET_END);
01270         
01271         if (flags & META_FRAME_ALLOWS_MAXIMIZE)
01272           {
01273             meta_core_toggle_maximize_vertically (gdk_display, frame->xwindow);
01274           }
01275       }
01276       break;
01277 
01278     case META_ACTION_TITLEBAR_MINIMIZE:
01279       {
01280         meta_core_get (gdk_display, frame->xwindow,
01281                        META_CORE_GET_FRAME_FLAGS, &flags,
01282                        META_CORE_GET_END);
01283         
01284         if (flags & META_FRAME_ALLOWS_MINIMIZE)
01285           {
01286             meta_core_minimize (gdk_display, frame->xwindow);
01287           }
01288       }
01289       break;
01290 
01291     case META_ACTION_TITLEBAR_NONE:
01292       /* Yaay, a sane user that doesn't use that other weird crap! */
01293       break;
01294     
01295     case META_ACTION_TITLEBAR_LOWER:
01296       meta_core_user_lower_and_unfocus (gdk_display,
01297                                         frame->xwindow,
01298                                         event->time);
01299       break;
01300 
01301     case META_ACTION_TITLEBAR_MENU:
01302       meta_core_show_window_menu (gdk_display,
01303                                   frame->xwindow,
01304                                   event->x_root,
01305                                   event->y_root,
01306                                   event->button,
01307                                   event->time);
01308       break;
01309 
01310     case META_ACTION_TITLEBAR_LAST:
01311       break;
01312     }
01313   
01314   return TRUE;
01315 }
01316 
01317 static gboolean
01318 meta_frame_double_click_event (MetaUIFrame    *frame,
01319                                GdkEventButton *event)
01320 {
01321   int action = meta_prefs_get_action_double_click_titlebar ();
01322   
01323   return meta_frame_titlebar_event (frame, event, action);
01324 }
01325 
01326 static gboolean
01327 meta_frame_middle_click_event (MetaUIFrame    *frame,
01328                                GdkEventButton *event)
01329 {
01330   int action = meta_prefs_get_action_middle_click_titlebar();
01331   
01332   return meta_frame_titlebar_event (frame, event, action);
01333 }
01334 
01335 static gboolean
01336 meta_frame_right_click_event(MetaUIFrame     *frame,
01337                              GdkEventButton  *event)
01338 {
01339   int action = meta_prefs_get_action_right_click_titlebar();
01340   
01341   return meta_frame_titlebar_event (frame, event, action);
01342 }
01343 
01344 static gboolean
01345 meta_frames_button_press_event (GtkWidget      *widget,
01346                                 GdkEventButton *event)
01347 {
01348   MetaUIFrame *frame;
01349   MetaFrames *frames;
01350   MetaFrameControl control;
01351   
01352   frames = META_FRAMES (widget);
01353 
01354   /* Remember that the display may have already done something with this event.
01355    * If so there's probably a GrabOp in effect.
01356    */
01357   
01358   frame = meta_frames_lookup_window (frames, GDK_WINDOW_XID (event->window));
01359   if (frame == NULL)
01360     return FALSE;
01361 
01362   clear_tip (frames);
01363   
01364   control = get_control (frames, frame, event->x, event->y);
01365 
01366   /* focus on click, even if click was on client area */
01367   if (event->button == 1 &&
01368       !(control == META_FRAME_CONTROL_MINIMIZE ||
01369         control == META_FRAME_CONTROL_DELETE ||
01370         control == META_FRAME_CONTROL_MAXIMIZE))
01371     {
01372       meta_topic (META_DEBUG_FOCUS,
01373                   "Focusing window with frame 0x%lx due to button 1 press\n",
01374                   frame->xwindow);
01375       meta_core_user_focus (gdk_display,
01376                             frame->xwindow,
01377                             event->time);      
01378     }
01379 
01380   /* don't do the rest of this if on client area */
01381   if (control == META_FRAME_CONTROL_CLIENT_AREA)
01382     return FALSE; /* not on the frame, just passed through from client */
01383   
01384   /* We want to shade even if we have a GrabOp, since we'll have a move grab
01385    * if we double click the titlebar.
01386    */
01387   if (control == META_FRAME_CONTROL_TITLE &&
01388       event->button == 1 &&
01389       event->type == GDK_2BUTTON_PRESS)
01390     {
01391       return meta_frame_double_click_event (frame, event);
01392     }
01393 
01394   if (meta_core_get_grab_op (gdk_display) !=
01395       META_GRAB_OP_NONE)
01396     return FALSE; /* already up to something */  
01397 
01398   if (event->button == 1 &&
01399       (control == META_FRAME_CONTROL_MAXIMIZE ||
01400        control == META_FRAME_CONTROL_UNMAXIMIZE ||
01401        control == META_FRAME_CONTROL_MINIMIZE ||
01402        control == META_FRAME_CONTROL_DELETE ||
01403        control == META_FRAME_CONTROL_SHADE ||
01404        control == META_FRAME_CONTROL_UNSHADE ||
01405        control == META_FRAME_CONTROL_ABOVE ||
01406        control == META_FRAME_CONTROL_UNABOVE ||
01407        control == META_FRAME_CONTROL_STICK ||
01408        control == META_FRAME_CONTROL_UNSTICK ||
01409        control == META_FRAME_CONTROL_MENU))
01410     {
01411       MetaGrabOp op = META_GRAB_OP_NONE;
01412 
01413       switch (control)
01414         {
01415         case META_FRAME_CONTROL_MINIMIZE:
01416           op = META_GRAB_OP_CLICKING_MINIMIZE;
01417           break;
01418         case META_FRAME_CONTROL_MAXIMIZE:
01419           op = META_GRAB_OP_CLICKING_MAXIMIZE;
01420           break;
01421         case META_FRAME_CONTROL_UNMAXIMIZE:
01422           op = META_GRAB_OP_CLICKING_UNMAXIMIZE;
01423           break;
01424         case META_FRAME_CONTROL_DELETE:
01425           op = META_GRAB_OP_CLICKING_DELETE;
01426           break;
01427         case META_FRAME_CONTROL_MENU:
01428           op = META_GRAB_OP_CLICKING_MENU;
01429           break;
01430         case META_FRAME_CONTROL_SHADE:
01431           op = META_GRAB_OP_CLICKING_SHADE;
01432           break;
01433         case META_FRAME_CONTROL_UNSHADE:
01434           op = META_GRAB_OP_CLICKING_UNSHADE;
01435           break;
01436         case META_FRAME_CONTROL_ABOVE:
01437           op = META_GRAB_OP_CLICKING_ABOVE;
01438           break;
01439         case META_FRAME_CONTROL_UNABOVE:
01440           op = META_GRAB_OP_CLICKING_UNABOVE;
01441           break;
01442         case META_FRAME_CONTROL_STICK:
01443           op = META_GRAB_OP_CLICKING_STICK;
01444           break;
01445         case META_FRAME_CONTROL_UNSTICK:
01446           op = META_GRAB_OP_CLICKING_UNSTICK;
01447           break;
01448         default:
01449           g_assert_not_reached ();
01450           break;
01451         }
01452 
01453       meta_core_begin_grab_op (gdk_display,
01454                                frame->xwindow,
01455                                op,
01456                                TRUE,
01457                                TRUE,
01458                                event->button,
01459                                0,
01460                                event->time,
01461                                event->x_root,
01462                                event->y_root);      
01463       
01464       frame->prelit_control = control;
01465       redraw_control (frames, frame, control);
01466 
01467       if (op == META_GRAB_OP_CLICKING_MENU)
01468         {
01469           MetaFrameGeometry fgeom;
01470           GdkRectangle *rect;
01471           int dx, dy;
01472           
01473           meta_frames_calc_geometry (frames, frame, &fgeom);
01474           
01475           rect = control_rect (META_FRAME_CONTROL_MENU, &fgeom);
01476 
01477           /* get delta to convert to root coords */
01478           dx = event->x_root - event->x;
01479           dy = event->y_root - event->y;
01480           
01481           /* Align to the right end of the menu rectangle if RTL */
01482           if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
01483             dx += rect->width;
01484 
01485           meta_core_show_window_menu (gdk_display,
01486                                       frame->xwindow,
01487                                       rect->x + dx,
01488                                       rect->y + rect->height + dy,
01489                                       event->button,
01490                                       event->time);
01491         }
01492     }
01493   else if (event->button == 1 &&
01494            (control == META_FRAME_CONTROL_RESIZE_SE ||
01495             control == META_FRAME_CONTROL_RESIZE_S ||
01496             control == META_FRAME_CONTROL_RESIZE_SW ||
01497             control == META_FRAME_CONTROL_RESIZE_NE ||
01498             control == META_FRAME_CONTROL_RESIZE_N ||
01499             control == META_FRAME_CONTROL_RESIZE_NW ||
01500             control == META_FRAME_CONTROL_RESIZE_E ||
01501             control == META_FRAME_CONTROL_RESIZE_W))
01502     {
01503       MetaGrabOp op;
01504       gboolean titlebar_is_onscreen;
01505       
01506       op = META_GRAB_OP_NONE;
01507       
01508       switch (control)
01509         {
01510         case META_FRAME_CONTROL_RESIZE_SE:
01511           op = META_GRAB_OP_RESIZING_SE;
01512           break;
01513         case META_FRAME_CONTROL_RESIZE_S:
01514           op = META_GRAB_OP_RESIZING_S;
01515           break;
01516         case META_FRAME_CONTROL_RESIZE_SW:
01517           op = META_GRAB_OP_RESIZING_SW;
01518           break;
01519         case META_FRAME_CONTROL_RESIZE_NE:
01520           op = META_GRAB_OP_RESIZING_NE;
01521           break;
01522         case META_FRAME_CONTROL_RESIZE_N:
01523           op = META_GRAB_OP_RESIZING_N;
01524           break;
01525         case META_FRAME_CONTROL_RESIZE_NW:
01526           op = META_GRAB_OP_RESIZING_NW;
01527           break;
01528         case META_FRAME_CONTROL_RESIZE_E:
01529           op = META_GRAB_OP_RESIZING_E;
01530           break;
01531         case META_FRAME_CONTROL_RESIZE_W:
01532           op = META_GRAB_OP_RESIZING_W;
01533           break;
01534         default:
01535           g_assert_not_reached ();
01536           break;
01537         }
01538 
01539       meta_core_get (gdk_display, frame->xwindow,
01540                      META_CORE_IS_TITLEBAR_ONSCREEN, &titlebar_is_onscreen,
01541                      META_CORE_GET_END);
01542 
01543       if (!titlebar_is_onscreen)
01544         meta_core_show_window_menu (gdk_display,
01545                                     frame->xwindow,
01546                                     event->x_root,
01547                                     event->y_root,
01548                                     event->button,
01549                                     event->time);
01550       else
01551         meta_core_begin_grab_op (gdk_display,
01552                                  frame->xwindow,
01553                                  op,
01554                                  TRUE,
01555                                  TRUE,
01556                                  event->button,
01557                                  0,
01558                                  event->time,
01559                                  event->x_root,
01560                                  event->y_root);
01561     }
01562   else if (control == META_FRAME_CONTROL_TITLE &&
01563            event->button == 1)
01564     {
01565       MetaFrameFlags flags;
01566 
01567       meta_core_get (gdk_display, frame->xwindow,
01568                      META_CORE_GET_FRAME_FLAGS, &flags,
01569                      META_CORE_GET_END);
01570 
01571       if (flags & META_FRAME_ALLOWS_MOVE)
01572         {          
01573           meta_core_begin_grab_op (gdk_display,
01574                                    frame->xwindow,
01575                                    META_GRAB_OP_MOVING,
01576                                    TRUE,
01577                                    TRUE,
01578                                    event->button,
01579                                    0,
01580                                    event->time,
01581                                    event->x_root,
01582                                    event->y_root);
01583         }
01584     }
01585   else if (event->button == 2)
01586     {
01587       return meta_frame_middle_click_event (frame, event);
01588     }
01589   else if (event->button == 3)
01590     {
01591       return meta_frame_right_click_event (frame, event);
01592     }
01593   
01594   return TRUE;
01595 }
01596 
01597 void
01598 meta_frames_notify_menu_hide (MetaFrames *frames)
01599 {
01600   if (meta_core_get_grab_op (gdk_display) ==
01601       META_GRAB_OP_CLICKING_MENU)
01602     {
01603       Window grab_frame;
01604 
01605       grab_frame = meta_core_get_grab_frame (gdk_display);
01606 
01607       if (grab_frame != None)
01608         {
01609           MetaUIFrame *frame;
01610 
01611           frame = meta_frames_lookup_window (frames, grab_frame);
01612 
01613           if (frame)
01614             {
01615               redraw_control (frames, frame,
01616                               META_FRAME_CONTROL_MENU);
01617               meta_core_end_grab_op (gdk_display, CurrentTime);
01618             }
01619         }
01620     }
01621 }
01622 
01623 static gboolean
01624 meta_frames_button_release_event    (GtkWidget           *widget,
01625                                      GdkEventButton      *event)
01626 {
01627   MetaUIFrame *frame;
01628   MetaFrames *frames;
01629   MetaGrabOp op;
01630   
01631   frames = META_FRAMES (widget);
01632   
01633   frame = meta_frames_lookup_window (frames, GDK_WINDOW_XID (event->window));
01634   if (frame == NULL)
01635     return FALSE;
01636 
01637   clear_tip (frames);
01638 
01639   op = meta_core_get_grab_op (gdk_display);
01640 
01641   if (op == META_GRAB_OP_NONE)
01642     return FALSE;
01643 
01644   /* We only handle the releases we handled the presses for (things
01645    * involving frame controls). Window ops that don't require a
01646    * frame are handled in the Xlib part of the code, display.c/window.c
01647    */
01648   if (frame->xwindow == meta_core_get_grab_frame (gdk_display) &&
01649       ((int) event->button) == meta_core_get_grab_button (gdk_display))
01650     {
01651       MetaFrameControl control;
01652 
01653       control = get_control (frames, frame, event->x, event->y);
01654       
01655       switch (op)
01656         {
01657         case META_GRAB_OP_CLICKING_MINIMIZE:
01658           if (control == META_FRAME_CONTROL_MINIMIZE)
01659             meta_core_minimize (gdk_display, frame->xwindow);
01660           
01661           meta_core_end_grab_op (gdk_display, event->time);
01662           break;
01663 
01664         case META_GRAB_OP_CLICKING_MAXIMIZE:
01665           if (control == META_FRAME_CONTROL_MAXIMIZE)
01666           {
01667             /* Focus the window on the maximize */
01668             meta_core_user_focus (gdk_display,
01669                             frame->xwindow,
01670                             event->time);      
01671             meta_core_maximize (gdk_display, frame->xwindow);
01672           }
01673           meta_core_end_grab_op (gdk_display, event->time);
01674           break;
01675 
01676         case META_GRAB_OP_CLICKING_UNMAXIMIZE:
01677           if (control == META_FRAME_CONTROL_UNMAXIMIZE)
01678             meta_core_unmaximize (gdk_display, frame->xwindow);
01679           
01680           meta_core_end_grab_op (gdk_display, event->time);
01681           break;
01682           
01683         case META_GRAB_OP_CLICKING_DELETE:
01684           if (control == META_FRAME_CONTROL_DELETE)
01685             meta_core_delete (gdk_display, frame->xwindow, event->time);
01686           
01687           meta_core_end_grab_op (gdk_display, event->time);
01688           break;
01689           
01690         case META_GRAB_OP_CLICKING_MENU:
01691           meta_core_end_grab_op (gdk_display, event->time);
01692           break;
01693 
01694         case META_GRAB_OP_CLICKING_SHADE:
01695           if (control == META_FRAME_CONTROL_SHADE)
01696             meta_core_shade (gdk_display, frame->xwindow, event->time);
01697           
01698           meta_core_end_grab_op (gdk_display, event->time);
01699           break;
01700  
01701         case META_GRAB_OP_CLICKING_UNSHADE:
01702           if (control == META_FRAME_CONTROL_UNSHADE)
01703             meta_core_unshade (gdk_display, frame->xwindow, event->time);
01704 
01705           meta_core_end_grab_op (gdk_display, event->time);
01706           break;
01707 
01708         case META_GRAB_OP_CLICKING_ABOVE:
01709           if (control == META_FRAME_CONTROL_ABOVE)
01710             meta_core_make_above (gdk_display, frame->xwindow);
01711           
01712           meta_core_end_grab_op (gdk_display, event->time);
01713           break;
01714  
01715         case META_GRAB_OP_CLICKING_UNABOVE:
01716           if (control == META_FRAME_CONTROL_UNABOVE)
01717             meta_core_unmake_above (gdk_display, frame->xwindow);
01718 
01719           meta_core_end_grab_op (gdk_display, event->time);
01720           break;
01721 
01722         case META_GRAB_OP_CLICKING_STICK:
01723           if (control == META_FRAME_CONTROL_STICK)
01724             meta_core_stick (gdk_display, frame->xwindow);
01725 
01726           meta_core_end_grab_op (gdk_display, event->time);
01727           break;
01728  
01729         case META_GRAB_OP_CLICKING_UNSTICK:
01730           if (control == META_FRAME_CONTROL_UNSTICK)
01731             meta_core_unstick (gdk_display, frame->xwindow);
01732 
01733           meta_core_end_grab_op (gdk_display, event->time);
01734           break;
01735           
01736         default:
01737           break;
01738         }
01739 
01740       /* Update the prelit control regardless of what button the mouse
01741        * was released over; needed so that the new button can become
01742        * prelit so to let the user know that it can now be pressed.
01743        * :)
01744        */
01745       meta_frames_update_prelit_control (frames, frame, control);
01746     }
01747   
01748   return TRUE;
01749 }
01750 
01751 static void
01752 meta_frames_update_prelit_control (MetaFrames      *frames,
01753                                    MetaUIFrame     *frame,
01754                                    MetaFrameControl control)
01755 {
01756   MetaFrameControl old_control;
01757   MetaCursor cursor;
01758 
01759 
01760   meta_verbose ("Updating prelit control from %u to %u\n",
01761                 frame->prelit_control, control);
01762   
01763   cursor = META_CURSOR_DEFAULT;
01764   
01765   switch (control)
01766     {
01767     case META_FRAME_CONTROL_CLIENT_AREA:
01768       break;
01769     case META_FRAME_CONTROL_NONE:
01770       break;
01771     case META_FRAME_CONTROL_TITLE:
01772       break;
01773     case META_FRAME_CONTROL_DELETE:
01774       break;
01775     case META_FRAME_CONTROL_MENU:
01776       break;
01777     case META_FRAME_CONTROL_MINIMIZE:
01778       break;
01779     case META_FRAME_CONTROL_MAXIMIZE:
01780       break;
01781     case META_FRAME_CONTROL_UNMAXIMIZE:
01782       break;
01783     case META_FRAME_CONTROL_SHADE:
01784       break;
01785     case META_FRAME_CONTROL_UNSHADE:
01786       break;
01787     case META_FRAME_CONTROL_ABOVE:
01788       break;
01789     case META_FRAME_CONTROL_UNABOVE:
01790       break;
01791     case META_FRAME_CONTROL_STICK:
01792       break;
01793     case META_FRAME_CONTROL_UNSTICK:
01794       break;
01795     case META_FRAME_CONTROL_RESIZE_SE:
01796       cursor = META_CURSOR_SE_RESIZE;
01797       break;
01798     case META_FRAME_CONTROL_RESIZE_S:
01799       cursor = META_CURSOR_SOUTH_RESIZE;
01800       break;
01801     case META_FRAME_CONTROL_RESIZE_SW:
01802       cursor = META_CURSOR_SW_RESIZE;
01803       break;
01804     case META_FRAME_CONTROL_RESIZE_N:
01805       cursor = META_CURSOR_NORTH_RESIZE;
01806       break;
01807     case META_FRAME_CONTROL_RESIZE_NE:
01808       cursor = META_CURSOR_NE_RESIZE;
01809       break;
01810     case META_FRAME_CONTROL_RESIZE_NW:
01811       cursor = META_CURSOR_NW_RESIZE;
01812       break;
01813     case META_FRAME_CONTROL_RESIZE_W:
01814       cursor = META_CURSOR_WEST_RESIZE;
01815       break;
01816     case META_FRAME_CONTROL_RESIZE_E:
01817       cursor = META_CURSOR_EAST_RESIZE;
01818       break;
01819     }        
01820 
01821   /* set/unset the prelight cursor */
01822   meta_core_set_screen_cursor (gdk_display,
01823                                frame->xwindow,
01824                                cursor);  
01825 
01826   switch (control)
01827     {
01828     case META_FRAME_CONTROL_MENU:
01829     case META_FRAME_CONTROL_MINIMIZE:
01830     case META_FRAME_CONTROL_MAXIMIZE:
01831     case META_FRAME_CONTROL_DELETE:
01832     case META_FRAME_CONTROL_SHADE:
01833     case META_FRAME_CONTROL_UNSHADE:
01834     case META_FRAME_CONTROL_ABOVE:
01835     case META_FRAME_CONTROL_UNABOVE:
01836     case META_FRAME_CONTROL_STICK:
01837     case META_FRAME_CONTROL_UNSTICK:
01838     case META_FRAME_CONTROL_UNMAXIMIZE:
01839       /* leave control set */
01840       break;
01841     default:
01842       /* Only prelight buttons */
01843       control = META_FRAME_CONTROL_NONE;
01844       break;
01845     }      
01846 
01847   if (control == frame->prelit_control)
01848     return;
01849 
01850   /* Save the old control so we can unprelight it */
01851   old_control = frame->prelit_control;
01852 
01853   frame->prelit_control = control;
01854 
01855   redraw_control (frames, frame, old_control);
01856   redraw_control (frames, frame, control);
01857 }
01858 
01859 static gboolean
01860 meta_frames_motion_notify_event     (GtkWidget           *widget,
01861                                      GdkEventMotion      *event)
01862 {
01863   MetaUIFrame *frame;
01864   MetaFrames *frames;
01865   MetaGrabOp grab_op;
01866   
01867   frames = META_FRAMES (widget);
01868   
01869   frame = meta_frames_lookup_window (frames, GDK_WINDOW_XID (event->window));
01870   if (frame == NULL)
01871     return FALSE;
01872 
01873   clear_tip (frames);
01874 
01875   frames->last_motion_frame = frame;
01876 
01877   grab_op = meta_core_get_grab_op (gdk_display);
01878   
01879   switch (grab_op)
01880     {
01881     case META_GRAB_OP_CLICKING_MENU:
01882     case META_GRAB_OP_CLICKING_DELETE:
01883     case META_GRAB_OP_CLICKING_MINIMIZE:
01884     case META_GRAB_OP_CLICKING_MAXIMIZE:
01885     case META_GRAB_OP_CLICKING_UNMAXIMIZE:
01886     case META_GRAB_OP_CLICKING_SHADE:
01887     case META_GRAB_OP_CLICKING_UNSHADE:
01888     case META_GRAB_OP_CLICKING_ABOVE:
01889     case META_GRAB_OP_CLICKING_UNABOVE:
01890     case META_GRAB_OP_CLICKING_STICK:
01891     case META_GRAB_OP_CLICKING_UNSTICK:
01892       {
01893         MetaFrameControl control;
01894         int x, y;
01895         
01896         gdk_window_get_pointer (frame->window, &x, &y, NULL);
01897 
01898         /* Control is set to none unless it matches
01899          * the current grab
01900          */
01901         control = get_control (frames, frame, x, y);
01902         if (! ((control == META_FRAME_CONTROL_MENU &&
01903                 grab_op == META_GRAB_OP_CLICKING_MENU) ||
01904                (control == META_FRAME_CONTROL_DELETE &&
01905                 grab_op == META_GRAB_OP_CLICKING_DELETE) ||
01906                (control == META_FRAME_CONTROL_MINIMIZE &&
01907                 grab_op == META_GRAB_OP_CLICKING_MINIMIZE) ||
01908                ((control == META_FRAME_CONTROL_MAXIMIZE ||
01909                  control == META_FRAME_CONTROL_UNMAXIMIZE) &&
01910                 (grab_op == META_GRAB_OP_CLICKING_MAXIMIZE ||
01911                  grab_op == META_GRAB_OP_CLICKING_UNMAXIMIZE)) ||
01912                (control == META_FRAME_CONTROL_SHADE &&
01913                 grab_op == META_GRAB_OP_CLICKING_SHADE) ||
01914                (control == META_FRAME_CONTROL_UNSHADE &&
01915                 grab_op == META_GRAB_OP_CLICKING_UNSHADE) ||
01916                (control == META_FRAME_CONTROL_ABOVE &&
01917                 grab_op == META_GRAB_OP_CLICKING_ABOVE) ||
01918                (control == META_FRAME_CONTROL_UNABOVE &&
01919                 grab_op == META_GRAB_OP_CLICKING_UNABOVE) ||
01920                (control == META_FRAME_CONTROL_STICK &&
01921                 grab_op == META_GRAB_OP_CLICKING_STICK) ||
01922                (control == META_FRAME_CONTROL_UNSTICK &&
01923                 grab_op == META_GRAB_OP_CLICKING_UNSTICK)))
01924            control = META_FRAME_CONTROL_NONE;
01925         
01926         /* Update prelit control and cursor */
01927         meta_frames_update_prelit_control (frames, frame, control);
01928 
01929         /* No tooltip while in the process of clicking */
01930       }
01931       break;
01932     case META_GRAB_OP_NONE:
01933       {
01934         MetaFrameControl control;
01935         int x, y;
01936         
01937         gdk_window_get_pointer (frame->window, &x, &y, NULL);
01938 
01939         control = get_control (frames, frame, x, y);
01940 
01941         /* Update prelit control and cursor */
01942         meta_frames_update_prelit_control (frames, frame, control);
01943         
01944         queue_tip (frames);
01945       }
01946       break;
01947 
01948     default:
01949       break;
01950     }
01951       
01952   return TRUE;
01953 }
01954 
01955 static gboolean
01956 meta_frames_destroy_event           (GtkWidget           *widget,
01957                                      GdkEventAny         *event)
01958 {
01959   MetaUIFrame *frame;
01960   MetaFrames *frames;
01961 
01962   frames = META_FRAMES (widget);
01963 
01964   frame = meta_frames_lookup_window (frames, GDK_WINDOW_XID (event->window));
01965   if (frame == NULL)
01966     return FALSE;
01967   
01968   return TRUE;
01969 }
01970 
01971 /* Cut and paste from GDK */
01972 static GdkGC *
01973 get_bg_gc (GdkWindow *window, int x_offset, int y_offset)
01974 {
01975   GdkWindowObject *private = (GdkWindowObject *)window;
01976   guint gc_mask = 0;
01977   GdkGCValues gc_values;
01978 
01979   if (private->bg_pixmap == GDK_PARENT_RELATIVE_BG && private->parent)
01980     {
01981       return get_bg_gc (GDK_WINDOW (private->parent),
01982                         x_offset + private->x,
01983                         y_offset + private->y);
01984     }
01985   else if (private->bg_pixmap && 
01986            private->bg_pixmap != GDK_PARENT_RELATIVE_BG && 
01987            private->bg_pixmap != GDK_NO_BG)
01988     {
01989       gc_values.fill = GDK_TILED;
01990       gc_values.tile = private->bg_pixmap;
01991       gc_values.ts_x_origin = x_offset;
01992       gc_values.ts_y_origin = y_offset;
01993       
01994       gc_mask = GDK_GC_FILL | GDK_GC_TILE | GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN;
01995 
01996       return gdk_gc_new_with_values (window, &gc_values, gc_mask);
01997     }
01998   else
01999     {
02000       GdkGC *gc = gdk_gc_new (window);
02001 
02002       gdk_gc_set_foreground (gc, &(private->bg_color));
02003 
02004       return gc;
02005     }
02006 }
02007 
02008 static void
02009 clear_backing (GdkPixmap *pixmap,
02010                GdkWindow *window,
02011                int xoffset, int yoffset)
02012 {
02013   GdkGC *tmp_gc = get_bg_gc (window, xoffset, yoffset);
02014 
02015   gdk_draw_rectangle (pixmap, tmp_gc, TRUE,
02016                       0, 0, -1, -1);
02017   
02018   g_object_unref (tmp_gc);
02019 }
02020 
02021 /* Returns a pixmap with a piece of the windows frame painted on it.
02022 */
02023 
02024 static GdkPixmap *
02025 generate_pixmap (MetaFrames *frames,
02026                  MetaUIFrame *frame,
02027                  MetaRectangle rect)
02028 {
02029   GdkRectangle rectangle;
02030   GdkRegion *region;
02031   GdkPixmap *result;
02032 
02033   rectangle.x = rect.x;
02034   rectangle.y = rect.y;
02035   rectangle.width = MAX (rect.width, 1);
02036   rectangle.height = MAX (rect.height, 1);
02037   
02038   result = gdk_pixmap_new (frame->window,
02039                            rectangle.width, rectangle.height, -1);
02040   
02041   clear_backing (result, frame->window, rectangle.x, rectangle.y);
02042 
02043   region = gdk_region_rectangle (&rectangle);
02044 
02045   meta_frames_paint_to_drawable (frames, frame, result, region,
02046                                  -rectangle.x, -rectangle.y);
02047 
02048   gdk_region_destroy (region);
02049 
02050   return result;
02051 }
02052 
02053 
02054 static void
02055 populate_cache (MetaFrames *frames,
02056                 MetaUIFrame *frame)
02057 {
02058   int top, bottom, left, right;
02059   int width, height;
02060   int frame_width, frame_height, screen_width, screen_height;
02061   CachedPixels *pixels;
02062   MetaFrameType frame_type;
02063   MetaFrameFlags frame_flags;
02064   int i;
02065 
02066   meta_core_get (gdk_display, frame->xwindow,
02067                  META_CORE_GET_FRAME_WIDTH, &frame_width,
02068                  META_CORE_GET_FRAME_HEIGHT, &frame_height,
02069                  META_CORE_GET_SCREEN_WIDTH, &screen_width,
02070                  META_CORE_GET_SCREEN_HEIGHT, &screen_height,
02071                  META_CORE_GET_CLIENT_WIDTH, &width,
02072                  META_CORE_GET_CLIENT_HEIGHT, &height,
02073                  META_CORE_GET_FRAME_TYPE, &frame_type,
02074                  META_CORE_GET_FRAME_FLAGS, &frame_flags,
02075                  META_CORE_GET_END);
02076 
02077   /* don't cache extremely large windows */
02078   if (frame_width > 2 * screen_width ||
02079       frame_height > 2 * screen_height)
02080     {
02081       return;
02082     }
02083   
02084   meta_theme_get_frame_borders (meta_theme_get_current (),
02085                                 frame_type,
02086                                 frame->text_height,
02087                                 frame_flags,
02088                                 &top, &bottom, &left, &right);
02089 
02090   pixels = get_cache (frames, frame);
02091 
02092   /* Setup the rectangles for the four frame borders. First top, then
02093      left, right and bottom. */
02094   pixels->piece[0].rect.x = 0;
02095   pixels->piece[0].rect.y = 0;
02096   pixels->piece[0].rect.width = left + width + right;
02097   pixels->piece[0].rect.height = top;
02098 
02099   pixels->piece[1].rect.x = 0;
02100   pixels->piece[1].rect.y = top;
02101   pixels->piece[1].rect.width = left;
02102   pixels->piece[1].rect.height = height;
02103 
02104   pixels->piece[2].rect.x = left + width;
02105   pixels->piece[2].rect.y = top;
02106   pixels->piece[2].rect.width = right;
02107   pixels->piece[2].rect.height = height;
02108 
02109   pixels->piece[3].rect.x = 0;
02110   pixels->piece[3].rect.y = top + height;
02111   pixels->piece[3].rect.width = left + width + right;
02112   pixels->piece[3].rect.height = bottom;
02113 
02114   for (i = 0; i < 4; i++)
02115     {
02116       CachedFramePiece *piece = &pixels->piece[i];
02117       if (!piece->pixmap)
02118         piece->pixmap = generate_pixmap (frames, frame, piece->rect);
02119     }
02120   
02121   if (frames->invalidate_cache_timeout_id)
02122     g_source_remove (frames->invalidate_cache_timeout_id);
02123   
02124   frames->invalidate_cache_timeout_id = g_timeout_add (1000, invalidate_cache_timeout, frames);
02125 
02126   if (!g_list_find (frames->invalidate_frames, frame))
02127     frames->invalidate_frames =
02128       g_list_prepend (frames->invalidate_frames, frame);
02129 }
02130 
02131 static void
02132 clip_to_screen (GdkRegion *region, MetaUIFrame *frame)
02133 {
02134   GdkRectangle frame_area;
02135   GdkRectangle screen_area = { 0, 0, 0, 0 };
02136   GdkRegion *tmp_region;
02137   
02138   /* Chop off stuff outside the screen; this optimization
02139    * is crucial to handle huge client windows,
02140    * like "xterm -geometry 1000x1000"
02141    */
02142   meta_core_get (gdk_display, frame->xwindow,
02143                  META_CORE_GET_FRAME_X, &frame_area.x,
02144                  META_CORE_GET_FRAME_Y, &frame_area.y,
02145                  META_CORE_GET_FRAME_WIDTH, &frame_area.width,
02146                  META_CORE_GET_FRAME_HEIGHT, &frame_area.height,
02147                  META_CORE_GET_SCREEN_WIDTH, &screen_area.height,
02148                  META_CORE_GET_SCREEN_HEIGHT, &screen_area.height,
02149                  META_CORE_GET_END);
02150 
02151   gdk_region_offset (region, frame_area.x, frame_area.y);
02152 
02153   tmp_region = gdk_region_rectangle (&frame_area);
02154   gdk_region_intersect (region, tmp_region);
02155   gdk_region_destroy (tmp_region);
02156 
02157   gdk_region_offset (region, - frame_area.x, - frame_area.y);
02158 }
02159 
02160 static void
02161 subtract_from_region (GdkRegion *region, GdkDrawable *drawable,
02162                       gint x, gint y)
02163 {
02164   GdkRectangle rect;
02165   GdkRegion *reg_rect;
02166 
02167   gdk_drawable_get_size (drawable, &rect.width, &rect.height);
02168   rect.x = x;
02169   rect.y = y;
02170 
02171   reg_rect = gdk_region_rectangle (&rect);
02172   gdk_region_subtract (region, reg_rect);
02173   gdk_region_destroy (reg_rect);
02174 }
02175 
02176 static void
02177 cached_pixels_draw (CachedPixels *pixels,
02178                     GdkWindow *window,
02179                     GdkRegion *region)
02180 {
02181   GdkGC *gc;
02182   int i;
02183 
02184   gc = gdk_gc_new (window);
02185 
02186   for (i = 0; i < 4; i++)
02187     {
02188       CachedFramePiece *piece;
02189       piece = &pixels->piece[i];
02190       
02191       if (piece->pixmap)
02192         {
02193           gdk_draw_drawable (window, gc, piece->pixmap,
02194                              0, 0,
02195                              piece->rect.x, piece->rect.y,
02196                              -1, -1);
02197           subtract_from_region (region, piece->pixmap,
02198           piece->rect.x, piece->rect.y);
02199         }
02200     }
02201   
02202   g_object_unref (gc);
02203 }
02204 
02205 static gboolean
02206 meta_frames_expose_event (GtkWidget           *widget,
02207                           GdkEventExpose      *event)
02208 {
02209   MetaUIFrame *frame;
02210   MetaFrames *frames;
02211   GdkRegion *region;
02212   CachedPixels *pixels;
02213 
02214   frames = META_FRAMES (widget);
02215 
02216   frame = meta_frames_lookup_window (frames, GDK_WINDOW_XID (event->window));
02217   if (frame == NULL)
02218     return FALSE;
02219 
02220   if (frames->expose_delay_count > 0)
02221     {
02222       /* Redraw this entire frame later */
02223       frame->expose_delayed = TRUE;
02224       return TRUE;
02225     }
02226 
02227   populate_cache (frames, frame);
02228 
02229   region = gdk_region_copy (event->region);
02230   
02231   pixels = get_cache (frames, frame);
02232 
02233   cached_pixels_draw (pixels, frame->window, region);
02234   
02235   clip_to_screen (region, frame);
02236   meta_frames_paint_to_drawable (frames, frame, frame->window, region, 0, 0);
02237 
02238   gdk_region_destroy (region);
02239   
02240   return TRUE;
02241 }
02242 
02243 /* How far off the screen edge the window decorations should
02244  * be drawn. Used only in meta_frames_paint_to_drawable, below.
02245  */
02246 #define DECORATING_BORDER 100
02247 
02248 static void
02249 meta_frames_paint_to_drawable (MetaFrames   *frames,
02250                                MetaUIFrame  *frame,
02251                                GdkDrawable  *drawable,
02252                                GdkRegion    *region,
02253                                int           x_offset,
02254                                int           y_offset)
02255 {
02256   GtkWidget *widget;
02257   MetaFrameFlags flags;
02258   MetaFrameType type;
02259   GdkPixbuf *mini_icon;
02260   GdkPixbuf *icon;
02261   int w, h;
02262   MetaButtonState button_states[META_BUTTON_TYPE_LAST];
02263   Window grab_frame;
02264   int i;
02265   MetaButtonLayout button_layout;
02266   MetaGrabOp grab_op;
02267   
02268   widget = GTK_WIDGET (frames);
02269 
02270   for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
02271     button_states[i] = META_BUTTON_STATE_NORMAL;
02272 
02273   grab_frame = meta_core_get_grab_frame (gdk_display);
02274   grab_op = meta_core_get_grab_op (gdk_display);
02275   if (grab_frame != frame->xwindow)
02276     grab_op = META_GRAB_OP_NONE;
02277   
02278   /* Set prelight state */
02279   switch (frame->prelit_control)
02280     {
02281     case META_FRAME_CONTROL_MENU:
02282       if (grab_op == META_GRAB_OP_CLICKING_MENU)
02283         button_states[META_BUTTON_TYPE_MENU] = META_BUTTON_STATE_PRESSED;
02284       else
02285         button_states[META_BUTTON_TYPE_MENU] = META_BUTTON_STATE_PRELIGHT;
02286       break;
02287     case META_FRAME_CONTROL_MINIMIZE:
02288       if (grab_op == META_GRAB_OP_CLICKING_MINIMIZE)
02289         button_states[META_BUTTON_TYPE_MINIMIZE] = META_BUTTON_STATE_PRESSED;
02290       else
02291         button_states[META_BUTTON_TYPE_MINIMIZE] = META_BUTTON_STATE_PRELIGHT;
02292       break;
02293     case META_FRAME_CONTROL_MAXIMIZE:
02294       if (grab_op == META_GRAB_OP_CLICKING_MAXIMIZE)
02295         button_states[META_BUTTON_TYPE_MAXIMIZE] = META_BUTTON_STATE_PRESSED;
02296       else
02297         button_states[META_BUTTON_TYPE_MAXIMIZE] = META_BUTTON_STATE_PRELIGHT;
02298       break;
02299     case META_FRAME_CONTROL_UNMAXIMIZE:
02300       if (grab_op == META_GRAB_OP_CLICKING_UNMAXIMIZE)
02301         button_states[META_BUTTON_TYPE_MAXIMIZE] = META_BUTTON_STATE_PRESSED;
02302       else
02303         button_states[META_BUTTON_TYPE_MAXIMIZE] = META_BUTTON_STATE_PRELIGHT;
02304       break;
02305     case META_FRAME_CONTROL_SHADE:
02306       if (grab_op == META_GRAB_OP_CLICKING_SHADE)
02307         button_states[META_BUTTON_TYPE_SHADE] = META_BUTTON_STATE_PRESSED;
02308       else
02309         button_states[META_BUTTON_TYPE_SHADE] = META_BUTTON_STATE_PRELIGHT;
02310       break;
02311     case META_FRAME_CONTROL_UNSHADE:
02312       if (grab_op == META_GRAB_OP_CLICKING_UNSHADE)
02313         button_states[META_BUTTON_TYPE_UNSHADE] = META_BUTTON_STATE_PRESSED;
02314       else
02315         button_states[META_BUTTON_TYPE_UNSHADE] = META_BUTTON_STATE_PRELIGHT;
02316       break;
02317     case META_FRAME_CONTROL_ABOVE:
02318       if (grab_op == META_GRAB_OP_CLICKING_ABOVE)
02319         button_states[META_BUTTON_TYPE_ABOVE] = META_BUTTON_STATE_PRESSED;
02320       else
02321         button_states[META_BUTTON_TYPE_ABOVE] = META_BUTTON_STATE_PRELIGHT;
02322       break;
02323     case META_FRAME_CONTROL_UNABOVE:
02324       if (grab_op == META_GRAB_OP_CLICKING_UNABOVE)
02325         button_states[META_BUTTON_TYPE_UNABOVE] = META_BUTTON_STATE_PRESSED;
02326       else
02327         button_states[META_BUTTON_TYPE_UNABOVE] = META_BUTTON_STATE_PRELIGHT;
02328       break;
02329     case META_FRAME_CONTROL_STICK:
02330       if (grab_op == META_GRAB_OP_CLICKING_STICK)
02331         button_states[META_BUTTON_TYPE_STICK] = META_BUTTON_STATE_PRESSED;
02332       else
02333         button_states[META_BUTTON_TYPE_STICK] = META_BUTTON_STATE_PRELIGHT;
02334       break;
02335     case META_FRAME_CONTROL_UNSTICK:
02336       if (grab_op == META_GRAB_OP_CLICKING_UNSTICK)
02337         button_states[META_BUTTON_TYPE_UNSTICK] = META_BUTTON_STATE_PRESSED;
02338       else
02339         button_states[META_BUTTON_TYPE_UNSTICK] = META_BUTTON_STATE_PRELIGHT;
02340       break;
02341     case META_FRAME_CONTROL_DELETE:
02342       if (grab_op == META_GRAB_OP_CLICKING_DELETE)
02343         button_states[META_BUTTON_TYPE_CLOSE] = META_BUTTON_STATE_PRESSED;
02344       else
02345         button_states[META_BUTTON_TYPE_CLOSE] = META_BUTTON_STATE_PRELIGHT;
02346       break;
02347     default:
02348       break;
02349     }
02350 
02351   /* Map button function states to button position states */
02352   button_states[META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND] =
02353     button_states[META_BUTTON_TYPE_MENU];
02354   button_states[META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND] =
02355     META_BUTTON_STATE_NORMAL;
02356   button_states[META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND] =
02357     META_BUTTON_STATE_NORMAL;
02358   button_states[META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND] =
02359     button_states[META_BUTTON_TYPE_MINIMIZE];
02360   button_states[META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND] =
02361     button_states[META_BUTTON_TYPE_MAXIMIZE];
02362   button_states[META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND] =
02363     button_states[META_BUTTON_TYPE_CLOSE];
02364   
02365   meta_core_get (gdk_display, frame->xwindow,
02366                  META_CORE_GET_FRAME_FLAGS, &flags,
02367                  META_CORE_GET_FRAME_TYPE, &type,
02368                  META_CORE_GET_MINI_ICON, &mini_icon,
02369                  META_CORE_GET_ICON, &icon,
02370                  META_CORE_GET_CLIENT_WIDTH, &w,
02371                  META_CORE_GET_CLIENT_HEIGHT, &h,
02372                  META_CORE_GET_END);
02373 
02374   meta_frames_ensure_layout (frames, frame);
02375 
02376   meta_prefs_get_button_layout (&button_layout);
02377 
02378   if (G_LIKELY (GDK_IS_WINDOW (drawable)))
02379     {
02380       /* A window; happens about 2/3 of the time */
02381 
02382       GdkRectangle area, *areas;
02383       int n_areas;
02384       int screen_width, screen_height;
02385       GdkRegion *edges, *tmp_region;
02386       int top, bottom, left, right;
02387  
02388       /* Repaint each side of the frame */
02389 
02390       meta_theme_get_frame_borders (meta_theme_get_current (),
02391                              type, frame->text_height, flags, 
02392                              &top, &bottom, &left, &right);
02393 
02394       meta_core_get (gdk_display, frame->xwindow,
02395                      META_CORE_GET_SCREEN_WIDTH, &screen_width,
02396                      META_CORE_GET_SCREEN_HEIGHT, &screen_height,
02397                      META_CORE_GET_END);
02398 
02399       edges = gdk_region_copy (region);
02400 
02401       /* Punch out the client area */
02402 
02403       area.x = left;
02404       area.y = top;
02405       area.width = w;
02406       area.height = h;
02407       tmp_region = gdk_region_rectangle (&area);
02408       gdk_region_subtract (edges, tmp_region);
02409       gdk_region_destroy (tmp_region);
02410 
02411       /* Now draw remaining portion of region */
02412 
02413       gdk_region_get_rectangles (edges, &areas, &n_areas);
02414 
02415       for (i = 0; i < n_areas; i++)
02416         {
02417           /* Bug 399529: clamp areas[i] so that it doesn't go too far
02418            * off the edge of the screen. This works around a GDK bug
02419            * which makes gdk_window_begin_paint_rect cause an X error
02420            * if the window is insanely huge. If the client is a GDK program
02421            * and does this, it will still probably cause an X error in that
02422            * program, but the last thing we want is for Metacity to crash
02423            * because it attempted to decorate the silly window.
02424            */
02425 
02426           areas[i].x = MAX (areas[i].x, -DECORATING_BORDER); 
02427           areas[i].y = MAX (areas[i].y, -DECORATING_BORDER); 
02428           if (areas[i].x+areas[i].width  > screen_width  + DECORATING_BORDER)
02429             areas[i].width  = MIN (0, screen_width  - areas[i].x);
02430           if (areas[i].y+areas[i].height > screen_height + DECORATING_BORDER)
02431             areas[i].height = MIN (0, screen_height - areas[i].y);
02432 
02433           /* Okay, so let's start painting. */
02434 
02435           gdk_window_begin_paint_rect (drawable, &areas[i]);
02436 
02437           meta_theme_draw_frame (meta_theme_get_current (),
02438             widget,
02439             drawable,
02440             NULL, /* &areas[i], */
02441             x_offset, y_offset,
02442             type,
02443             flags,
02444             w, h,
02445             frame->layout,
02446             frame->text_height,
02447             &button_layout,
02448             button_states,
02449             mini_icon, icon);
02450 
02451           gdk_window_end_paint (drawable);
02452         }
02453 
02454       g_free (areas);
02455       gdk_region_destroy (edges);
02456 
02457     }
02458   else
02459     {
02460       /* Not a window; happens about 1/3 of the time */
02461 
02462       meta_theme_draw_frame (meta_theme_get_current (),
02463                              widget,
02464                              drawable,
02465                              NULL,
02466                              x_offset, y_offset,
02467                              type,
02468                              flags,
02469                              w, h,
02470                              frame->layout,
02471                              frame->text_height,
02472                              &button_layout,
02473                              button_states,
02474                              mini_icon, icon);
02475     }
02476 
02477 }
02478 
02479 static void
02480 meta_frames_set_window_background (MetaFrames   *frames,
02481                                    MetaUIFrame  *frame)
02482 {
02483   MetaFrameFlags flags;
02484   MetaFrameType type;
02485   MetaFrameStyle *style;
02486   gboolean frame_exists;
02487 
02488   meta_core_get (gdk_display, frame->xwindow,
02489                  META_CORE_WINDOW_HAS_FRAME, &frame_exists,
02490                  META_CORE_GET_FRAME_FLAGS, &flags,
02491                  META_CORE_GET_FRAME_TYPE, &type,
02492                  META_CORE_GET_END);
02493 
02494   if (frame_exists)
02495     {
02496       style = meta_theme_get_frame_style (meta_theme_get_current (),
02497                                           type, flags);
02498     }
02499 
02500   if (frame_exists && style->window_background_color != NULL)
02501     {
02502       GdkColor color;
02503       GdkVisual *visual;
02504 
02505       meta_color_spec_render (style->window_background_color,
02506                               GTK_WIDGET (frames),
02507                               &color);
02508 
02509       /* Fill in color.pixel */
02510 
02511       gdk_rgb_find_color (gtk_widget_get_colormap (GTK_WIDGET (frames)),
02512                           &color);
02513 
02514       /* Set A in ARGB to window_background_alpha, if we have ARGB */
02515 
02516       visual = gtk_widget_get_visual (GTK_WIDGET (frames));
02517       if (visual->depth == 32) /* we have ARGB */
02518         {
02519           color.pixel = (color.pixel & 0xffffff) &
02520             style->window_background_alpha << 24;
02521         }
02522 
02523       gdk_window_set_background (frame->window, &color);
02524     }
02525   else
02526     {
02527       gtk_style_set_background (GTK_WIDGET (frames)->style,
02528                                 frame->window, GTK_STATE_NORMAL);
02529     }
02530  }
02531 
02532 static gboolean
02533 meta_frames_enter_notify_event      (GtkWidget           *widget,
02534                                      GdkEventCrossing    *event)
02535 {
02536   MetaUIFrame *frame;
02537   MetaFrames *frames;
02538   MetaFrameControl control;
02539   
02540   frames = META_FRAMES (widget);
02541 
02542   frame = meta_frames_lookup_window (frames, GDK_WINDOW_XID (event->window));
02543   if (frame == NULL)
02544     return FALSE;
02545 
02546   control = get_control (frames, frame, event->x, event->y);
02547   meta_frames_update_prelit_control (frames, frame, control);
02548   
02549   return TRUE;
02550 }
02551 
02552 static gboolean
02553 meta_frames_leave_notify_event      (GtkWidget           *widget,
02554                                      GdkEventCrossing    *event)
02555 {
02556   MetaUIFrame *frame;
02557   MetaFrames *frames;
02558 
02559   frames = META_FRAMES (widget);
02560 
02561   frame = meta_frames_lookup_window (frames, GDK_WINDOW_XID (event->window));
02562   if (frame == NULL)
02563     return FALSE;
02564 
02565   meta_frames_update_prelit_control (frames, frame, META_FRAME_CONTROL_NONE);
02566   
02567   clear_tip (frames);
02568 
02569   return TRUE;
02570 }
02571 
02572 static GdkRectangle*
02573 control_rect (MetaFrameControl control,
02574               MetaFrameGeometry *fgeom)
02575 {
02576   GdkRectangle *rect;
02577   
02578   rect = NULL;
02579   switch (control)
02580     {
02581     case META_FRAME_CONTROL_TITLE:
02582       rect = &fgeom->title_rect;
02583       break;
02584     case META_FRAME_CONTROL_DELETE:
02585       rect = &fgeom->close_rect.visible;
02586       break;
02587     case META_FRAME_CONTROL_MENU:
02588       rect = &fgeom->menu_rect.visible;
02589       break;
02590     case META_FRAME_CONTROL_MINIMIZE:
02591       rect = &fgeom->min_rect.visible;
02592       break;
02593     case META_FRAME_CONTROL_MAXIMIZE:
02594     case META_FRAME_CONTROL_UNMAXIMIZE:
02595       rect = &fgeom->max_rect.visible;
02596       break;
02597     case META_FRAME_CONTROL_SHADE:
02598       rect = &fgeom->shade_rect.visible;
02599       break;
02600     case META_FRAME_CONTROL_UNSHADE:
02601       rect = &fgeom->unshade_rect.visible;
02602       break;
02603     case META_FRAME_CONTROL_ABOVE:
02604       rect = &fgeom->above_rect.visible;
02605       break;
02606     case META_FRAME_CONTROL_UNABOVE:
02607       rect = &fgeom->unabove_rect.visible;
02608       break;
02609     case META_FRAME_CONTROL_STICK:
02610       rect = &fgeom->stick_rect.visible;
02611       break;
02612     case META_FRAME_CONTROL_UNSTICK:
02613       rect = &fgeom->unstick_rect.visible;
02614       break;
02615     case META_FRAME_CONTROL_RESIZE_SE:
02616       break;
02617     case META_FRAME_CONTROL_RESIZE_S:
02618       break;
02619     case META_FRAME_CONTROL_RESIZE_SW:
02620       break;
02621     case META_FRAME_CONTROL_RESIZE_N:
02622       break;
02623     case META_FRAME_CONTROL_RESIZE_NE:
02624       break;
02625     case META_FRAME_CONTROL_RESIZE_NW:
02626       break;
02627     case META_FRAME_CONTROL_RESIZE_W:
02628       break;
02629     case META_FRAME_CONTROL_RESIZE_E:
02630       break;
02631     case META_FRAME_CONTROL_NONE:
02632       break;
02633     case META_FRAME_CONTROL_CLIENT_AREA:
02634       break;
02635     }
02636 
02637   return rect;
02638 }
02639 
02640 #define RESIZE_EXTENDS 15
02641 #define TOP_RESIZE_HEIGHT 2
02642 static MetaFrameControl
02643 get_control (MetaFrames *frames,
02644              MetaUIFrame *frame,
02645              int x, int y)
02646 {
02647   MetaFrameGeometry fgeom;
02648   MetaFrameFlags flags;
02649   gboolean has_vert, has_horiz;
02650   GdkRectangle client;
02651   
02652   meta_frames_calc_geometry (frames, frame, &fgeom);
02653 
02654   client.x = fgeom.left_width;
02655   client.y = fgeom.top_height;
02656   client.width = fgeom.width - fgeom.left_width - fgeom.right_width;
02657   client.height = fgeom.height - fgeom.top_height - fgeom.bottom_height;  
02658 
02659   if (POINT_IN_RECT (x, y, client))
02660     return META_FRAME_CONTROL_CLIENT_AREA;
02661   
02662   if (POINT_IN_RECT (x, y, fgeom.close_rect.clickable))
02663     return META_FRAME_CONTROL_DELETE;
02664 
02665   if (POINT_IN_RECT (x, y, fgeom.min_rect.clickable))
02666     return META_FRAME_CONTROL_MINIMIZE;
02667 
02668   if (POINT_IN_RECT (x, y, fgeom.menu_rect.clickable))
02669     return META_FRAME_CONTROL_MENU;
02670 
02671   meta_core_get (gdk_display, frame->xwindow,
02672                  META_CORE_GET_FRAME_FLAGS, &flags,
02673                  META_CORE_GET_END);
02674   
02675   has_vert = (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) != 0;
02676   has_horiz = (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) != 0;
02677   
02678   if (POINT_IN_RECT (x, y, fgeom.title_rect))
02679     {
02680       if (has_vert && y <= TOP_RESIZE_HEIGHT)
02681         return META_FRAME_CONTROL_RESIZE_N;
02682       else
02683         return META_FRAME_CONTROL_TITLE;
02684     }
02685 
02686   if (POINT_IN_RECT (x, y, fgeom.max_rect.clickable))
02687     {
02688       if (flags & META_FRAME_MAXIMIZED)
02689         return META_FRAME_CONTROL_UNMAXIMIZE;
02690       else
02691         return META_FRAME_CONTROL_MAXIMIZE;
02692     }
02693       
02694   if (POINT_IN_RECT (x, y, fgeom.shade_rect.clickable))
02695     {
02696       return META_FRAME_CONTROL_SHADE;
02697     }
02698 
02699   if (POINT_IN_RECT (x, y, fgeom.unshade_rect.clickable))
02700     {
02701       return META_FRAME_CONTROL_UNSHADE;
02702     }
02703 
02704   if (POINT_IN_RECT (x, y, fgeom.above_rect.clickable))
02705     {
02706       return META_FRAME_CONTROL_ABOVE;
02707     }
02708 
02709   if (POINT_IN_RECT (x, y, fgeom.unabove_rect.clickable))
02710     {
02711       return META_FRAME_CONTROL_UNABOVE;
02712     }
02713 
02714   if (POINT_IN_RECT (x, y, fgeom.stick_rect.clickable))
02715     {
02716       return META_FRAME_CONTROL_STICK;
02717     }
02718 
02719   if (POINT_IN_RECT (x, y, fgeom.unstick_rect.clickable))
02720     {
02721       return META_FRAME_CONTROL_UNSTICK;
02722     }
02723 
02724   /* South resize always has priority over north resize,
02725    * in case of overlap.
02726    */
02727 
02728   if (y >= (fgeom.height - fgeom.bottom_height - RESIZE_EXTENDS) &&
02729       x >= (fgeom.width - fgeom.right_width - RESIZE_EXTENDS))
02730     {
02731       if (has_vert && has_horiz)
02732         return META_FRAME_CONTROL_RESIZE_SE;
02733       else if (has_vert)
02734         return META_FRAME_CONTROL_RESIZE_S;
02735       else if (has_horiz)
02736         return META_FRAME_CONTROL_RESIZE_E;
02737     }
02738   else if (y >= (fgeom.height - fgeom.bottom_height - RESIZE_EXTENDS) &&
02739            x <= (fgeom.left_width + RESIZE_EXTENDS))
02740     {
02741       if (has_vert && has_horiz)
02742         return META_FRAME_CONTROL_RESIZE_SW;
02743       else if (has_vert)
02744         return META_FRAME_CONTROL_RESIZE_S;
02745       else if (has_horiz)
02746         return META_FRAME_CONTROL_RESIZE_W;
02747     }
02748   else if (y < (fgeom.top_height + RESIZE_EXTENDS) &&
02749            x < RESIZE_EXTENDS)
02750     {
02751       if (has_vert && has_horiz)
02752         return META_FRAME_CONTROL_RESIZE_NW;
02753       else if (has_vert)
02754         return META_FRAME_CONTROL_RESIZE_N;
02755       else if (has_horiz)
02756         return META_FRAME_CONTROL_RESIZE_W;
02757     }
02758   else if (y < (fgeom.top_height + RESIZE_EXTENDS) &&
02759            x >= (fgeom.width - RESIZE_EXTENDS))
02760     {
02761       if (has_vert && has_horiz)
02762         return META_FRAME_CONTROL_RESIZE_NE;
02763       else if (has_vert)
02764         return META_FRAME_CONTROL_RESIZE_N;
02765       else if (has_horiz)
02766         return META_FRAME_CONTROL_RESIZE_E;
02767     }
02768   else if (y >= (fgeom.height - fgeom.bottom_height - RESIZE_EXTENDS))
02769     {
02770       if (has_vert)
02771         return META_FRAME_CONTROL_RESIZE_S;
02772     }
02773   else if (y <= TOP_RESIZE_HEIGHT)
02774     {
02775       if (has_vert)
02776         return META_FRAME_CONTROL_RESIZE_N;
02777       else if (has_horiz)
02778         return META_FRAME_CONTROL_TITLE;
02779     }
02780   else if (x <= fgeom.left_width)
02781     {
02782       if (has_horiz)
02783         return META_FRAME_CONTROL_RESIZE_W;
02784     }
02785   else if (x >= (fgeom.width - fgeom.right_width))
02786     {
02787       if (has_horiz)
02788         return META_FRAME_CONTROL_RESIZE_E;
02789     }
02790 
02791   if (y >= fgeom.top_height)
02792     return META_FRAME_CONTROL_NONE;
02793   else
02794     return META_FRAME_CONTROL_TITLE;
02795 }
02796 
02797 void
02798 meta_frames_push_delay_exposes (MetaFrames *frames)
02799 {
02800   if (frames->expose_delay_count == 0)
02801     {
02802       /* Make sure we've repainted things */
02803       gdk_window_process_all_updates ();
02804       XFlush (gdk_display);
02805     }
02806   
02807   frames->expose_delay_count += 1;
02808 }
02809 
02810 static void
02811 queue_pending_exposes_func (gpointer key, gpointer value, gpointer data)
02812 {
02813   MetaUIFrame *frame;
02814   MetaFrames *frames;
02815 
02816   frames = META_FRAMES (data);
02817   frame = value;
02818 
02819   if (frame->expose_delayed)
02820     {
02821       invalidate_whole_window (frames, frame);
02822       frame->expose_delayed = FALSE;
02823     }
02824 }
02825 
02826 void
02827 meta_frames_pop_delay_exposes  (MetaFrames *frames)
02828 {
02829   g_return_if_fail (frames->expose_delay_count > 0);
02830   
02831   frames->expose_delay_count -= 1;
02832 
02833   if (frames->expose_delay_count == 0)
02834     {
02835       g_hash_table_foreach (frames->frames,
02836                             queue_pending_exposes_func,
02837                             frames);
02838     }
02839 }
02840 
02841 static void
02842 invalidate_whole_window (MetaFrames *frames,
02843                          MetaUIFrame *frame)
02844 {
02845   gdk_window_invalidate_rect (frame->window, NULL, FALSE);
02846   invalidate_cache (frames, frame);
02847 }

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