preview-widget.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity theme preview widget */
00004 
00005 /* 
00006  * Copyright (C) 2002 Havoc Pennington
00007  * 
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License as
00010  * published by the Free Software Foundation; either version 2 of the
00011  * License, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * General Public License for more details.
00017  * 
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00021  * 02111-1307, USA.
00022  */
00023 
00024 #define _GNU_SOURCE
00025 #define _XOPEN_SOURCE 600 /* for the maths routines over floats */
00026 
00027 #include <math.h>
00028 #include <gtk/gtkicontheme.h>
00029 #include "preview-widget.h"
00030 
00031 static void     meta_preview_class_init    (MetaPreviewClass *klass);
00032 static void     meta_preview_init          (MetaPreview      *preview);
00033 static void     meta_preview_size_request  (GtkWidget        *widget,
00034                                             GtkRequisition   *req);
00035 static void     meta_preview_size_allocate (GtkWidget        *widget,
00036                                             GtkAllocation    *allocation);
00037 static gboolean meta_preview_expose        (GtkWidget        *widget,
00038                                             GdkEventExpose   *event);
00039 static void     meta_preview_finalize      (GObject          *object);
00040 
00041 static GtkWidgetClass *parent_class;
00042 
00043 GtkType
00044 meta_preview_get_type (void)
00045 {
00046   static GtkType preview_type = 0;
00047 
00048   if (!preview_type)
00049     {
00050       static const GtkTypeInfo preview_info =
00051       {
00052         "MetaPreview",
00053         sizeof (MetaPreview),
00054         sizeof (MetaPreviewClass),
00055         (GtkClassInitFunc) meta_preview_class_init,
00056         (GtkObjectInitFunc) meta_preview_init,
00057         /* reserved_1 */ NULL,
00058         /* reserved_2 */ NULL,
00059         (GtkClassInitFunc) NULL,
00060       };
00061 
00062       preview_type = gtk_type_unique (GTK_TYPE_BIN, &preview_info);
00063     }
00064 
00065   return preview_type;
00066 }
00067 
00068 static void
00069 meta_preview_class_init (MetaPreviewClass *class)
00070 {
00071   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
00072   GtkWidgetClass *widget_class;
00073 
00074   widget_class = (GtkWidgetClass*) class;
00075   parent_class = gtk_type_class (GTK_TYPE_BIN);
00076 
00077   gobject_class->finalize = meta_preview_finalize;
00078 
00079   widget_class->expose_event = meta_preview_expose;
00080   widget_class->size_request = meta_preview_size_request;
00081   widget_class->size_allocate = meta_preview_size_allocate;
00082 }
00083 
00084 static void
00085 meta_preview_init (MetaPreview *preview)
00086 {
00087   int i;
00088   
00089   GTK_WIDGET_SET_FLAGS (preview, GTK_NO_WINDOW);
00090 
00091   i = 0;
00092   while (i < MAX_BUTTONS_PER_CORNER)
00093     {
00094       preview->button_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST;
00095       preview->button_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST;
00096       ++i;
00097     }
00098   
00099   preview->button_layout.left_buttons[0] = META_BUTTON_FUNCTION_MENU;
00100 
00101   preview->button_layout.right_buttons[0] = META_BUTTON_FUNCTION_MINIMIZE;
00102   preview->button_layout.right_buttons[1] = META_BUTTON_FUNCTION_MAXIMIZE;
00103   preview->button_layout.right_buttons[2] = META_BUTTON_FUNCTION_CLOSE;
00104   
00105   preview->type = META_FRAME_TYPE_NORMAL;
00106   preview->flags =
00107     META_FRAME_ALLOWS_DELETE |
00108     META_FRAME_ALLOWS_MENU |
00109     META_FRAME_ALLOWS_MINIMIZE |
00110     META_FRAME_ALLOWS_MAXIMIZE |
00111     META_FRAME_ALLOWS_VERTICAL_RESIZE |
00112     META_FRAME_ALLOWS_HORIZONTAL_RESIZE |
00113     META_FRAME_HAS_FOCUS |
00114     META_FRAME_ALLOWS_SHADE |
00115     META_FRAME_ALLOWS_MOVE;
00116   
00117   preview->left_width = -1;
00118   preview->right_width = -1;
00119   preview->top_height = -1;
00120   preview->bottom_height = -1;
00121 }
00122 
00123 GtkWidget*
00124 meta_preview_new (void)
00125 {
00126   MetaPreview *preview;
00127   
00128   preview = gtk_type_new (META_TYPE_PREVIEW);
00129   
00130   return GTK_WIDGET (preview);
00131 }
00132 
00133 static void
00134 meta_preview_finalize (GObject *object)
00135 {
00136   MetaPreview *preview;
00137 
00138   preview = META_PREVIEW (object);
00139 
00140   g_free (preview->title);
00141   preview->title = NULL;
00142   
00143   G_OBJECT_CLASS (parent_class)->finalize (object);
00144 }
00145 
00146 static void
00147 ensure_info (MetaPreview *preview)
00148 {
00149   GtkWidget *widget;
00150 
00151   widget = GTK_WIDGET (preview);
00152   
00153   if (preview->layout == NULL)
00154     {
00155       PangoFontDescription *font_desc;
00156       double scale;
00157       PangoAttrList *attrs;
00158       PangoAttribute *attr;
00159 
00160       if (preview->theme)        
00161         scale = meta_theme_get_title_scale (preview->theme,
00162                                             preview->type,
00163                                             preview->flags);
00164       else
00165         scale = 1.0;
00166       
00167       preview->layout = gtk_widget_create_pango_layout (widget,
00168                                                         preview->title);
00169       
00170       font_desc = meta_gtk_widget_get_font_desc (widget, scale, NULL);
00171       
00172       preview->text_height =
00173         meta_pango_font_desc_get_text_height (font_desc,
00174                                               gtk_widget_get_pango_context (widget));
00175           
00176       attrs = pango_attr_list_new ();
00177       
00178       attr = pango_attr_size_new (pango_font_description_get_size (font_desc));
00179       attr->start_index = 0;
00180       attr->end_index = G_MAXINT;
00181       
00182       pango_attr_list_insert (attrs, attr);
00183       
00184       pango_layout_set_attributes (preview->layout, attrs);
00185       
00186       pango_attr_list_unref (attrs);      
00187   
00188       pango_font_description_free (font_desc);
00189     }
00190 
00191   if (preview->top_height < 0)
00192     {
00193       if (preview->theme)
00194         {
00195           meta_theme_get_frame_borders (preview->theme,
00196                                         preview->type,
00197                                         preview->text_height,
00198                                         preview->flags,
00199                                         &preview->top_height,
00200                                         &preview->bottom_height,
00201                                         &preview->left_width,
00202                                         &preview->right_width);
00203         }
00204       else
00205         {
00206           preview->top_height = 0;
00207           preview->bottom_height = 0;
00208           preview->left_width = 0;
00209           preview->right_width = 0;
00210         }
00211     }
00212 }
00213 
00214 static gboolean
00215 meta_preview_expose (GtkWidget      *widget,
00216                      GdkEventExpose *event)
00217 {
00218   MetaPreview *preview;
00219   int border_width;
00220   int client_width;
00221   int client_height;
00222   MetaButtonState button_states[META_BUTTON_TYPE_LAST] =
00223   {
00224     META_BUTTON_STATE_NORMAL,
00225     META_BUTTON_STATE_NORMAL,
00226     META_BUTTON_STATE_NORMAL,
00227     META_BUTTON_STATE_NORMAL
00228   };
00229   
00230   g_return_val_if_fail (META_IS_PREVIEW (widget), FALSE);
00231   g_return_val_if_fail (event != NULL, FALSE);
00232 
00233   preview = META_PREVIEW (widget);
00234 
00235   ensure_info (preview);
00236 
00237   border_width = GTK_CONTAINER (widget)->border_width;
00238   
00239   client_width = widget->allocation.width - preview->left_width - preview->right_width - border_width * 2;
00240   client_height = widget->allocation.height - preview->top_height - preview->bottom_height - border_width * 2;
00241 
00242   if (client_width < 0)
00243     client_width = 1;
00244   if (client_height < 0)
00245     client_height = 1;  
00246   
00247   if (preview->theme)
00248     {
00249       border_width = GTK_CONTAINER (widget)->border_width;
00250       
00251       meta_theme_draw_frame (preview->theme,
00252                              widget,
00253                              widget->window,
00254                              &event->area,
00255                              widget->allocation.x + border_width,
00256                              widget->allocation.y + border_width,
00257                              preview->type,
00258                              preview->flags,
00259                              client_width, client_height,
00260                              preview->layout,
00261                              preview->text_height,
00262                              &preview->button_layout,
00263                              button_states,
00264                              meta_preview_get_mini_icon (),
00265                              meta_preview_get_icon ());
00266     }
00267 
00268   /* draw child */
00269   return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
00270 }
00271 
00272 static void
00273 meta_preview_size_request (GtkWidget      *widget,
00274                            GtkRequisition *req)
00275 {
00276   MetaPreview *preview;
00277 
00278   preview = META_PREVIEW (widget);
00279 
00280   ensure_info (preview);
00281 
00282   req->width = preview->left_width + preview->right_width;
00283   req->height = preview->top_height + preview->bottom_height;
00284   
00285   if (GTK_BIN (preview)->child &&
00286       GTK_WIDGET_VISIBLE (GTK_BIN (preview)->child))
00287     {
00288       GtkRequisition child_requisition;
00289 
00290       gtk_widget_size_request (GTK_BIN (preview)->child, &child_requisition);
00291 
00292       req->width += child_requisition.width;
00293       req->height += child_requisition.height;
00294     }
00295   else
00296     {
00297 #define NO_CHILD_WIDTH 80
00298 #define NO_CHILD_HEIGHT 20
00299       req->width += NO_CHILD_WIDTH;
00300       req->height += NO_CHILD_HEIGHT;
00301     }
00302 
00303   req->width += GTK_CONTAINER (widget)->border_width * 2;
00304   req->height += GTK_CONTAINER (widget)->border_width * 2;
00305 }
00306 
00307 static void
00308 meta_preview_size_allocate (GtkWidget         *widget,
00309                             GtkAllocation     *allocation)
00310 {
00311   MetaPreview *preview;
00312   int border_width;
00313   GtkAllocation child_allocation;
00314   
00315   preview = META_PREVIEW (widget);
00316 
00317   ensure_info (preview);
00318   
00319   widget->allocation = *allocation;
00320 
00321   border_width = GTK_CONTAINER (widget)->border_width;
00322   
00323   if (GTK_BIN (widget)->child &&
00324       GTK_WIDGET_VISIBLE (GTK_BIN (widget)->child))
00325     {
00326       child_allocation.x = widget->allocation.x + border_width + preview->left_width;
00327       child_allocation.y = widget->allocation.y + border_width + preview->top_height;
00328       
00329       child_allocation.width = MAX (1, widget->allocation.width - border_width * 2 - preview->left_width - preview->right_width);
00330       child_allocation.height = MAX (1, widget->allocation.height - border_width * 2 - preview->top_height - preview->bottom_height);
00331 
00332       gtk_widget_size_allocate (GTK_BIN (widget)->child, &child_allocation);
00333     }
00334 }
00335 
00336 static void
00337 clear_cache (MetaPreview *preview)
00338 {
00339   if (preview->layout)
00340     {
00341       g_object_unref (G_OBJECT (preview->layout));
00342       preview->layout = NULL;
00343     }
00344 
00345   preview->left_width = -1;
00346   preview->right_width = -1;
00347   preview->top_height = -1;
00348   preview->bottom_height = -1;
00349 }
00350 
00351 void
00352 meta_preview_set_theme (MetaPreview    *preview,
00353                         MetaTheme      *theme)
00354 {
00355   g_return_if_fail (META_IS_PREVIEW (preview));
00356 
00357   preview->theme = theme;
00358   
00359   clear_cache (preview);
00360 
00361   gtk_widget_queue_resize (GTK_WIDGET (preview));
00362 }
00363 
00364 void
00365 meta_preview_set_title (MetaPreview    *preview,
00366                         const char     *title)
00367 {
00368   g_return_if_fail (META_IS_PREVIEW (preview));
00369 
00370   g_free (preview->title);
00371   preview->title = g_strdup (title);
00372   
00373   clear_cache (preview);
00374 
00375   gtk_widget_queue_resize (GTK_WIDGET (preview));
00376 }
00377 
00378 void
00379 meta_preview_set_frame_type (MetaPreview    *preview,
00380                              MetaFrameType   type)
00381 {
00382   g_return_if_fail (META_IS_PREVIEW (preview));
00383 
00384   preview->type = type;
00385 
00386   clear_cache (preview);
00387 
00388   gtk_widget_queue_resize (GTK_WIDGET (preview));
00389 }
00390 
00391 void
00392 meta_preview_set_frame_flags (MetaPreview    *preview,
00393                               MetaFrameFlags  flags)
00394 {
00395   g_return_if_fail (META_IS_PREVIEW (preview));
00396 
00397   preview->flags = flags;
00398 
00399   clear_cache (preview);
00400 
00401   gtk_widget_queue_resize (GTK_WIDGET (preview));
00402 }
00403 
00404 void
00405 meta_preview_set_button_layout (MetaPreview            *preview,
00406                                 const MetaButtonLayout *button_layout)
00407 {
00408   g_return_if_fail (META_IS_PREVIEW (preview));
00409   
00410   preview->button_layout = *button_layout;  
00411   
00412   gtk_widget_queue_draw (GTK_WIDGET (preview));
00413 }
00414 
00415 GdkPixbuf*
00416 meta_preview_get_icon (void)
00417 {
00418   static GdkPixbuf *default_icon = NULL;
00419 
00420   if (default_icon == NULL)
00421     {
00422       GtkIconTheme *theme;
00423       gboolean icon_exists;
00424 
00425       theme = gtk_icon_theme_get_default ();
00426 
00427       icon_exists = gtk_icon_theme_has_icon (theme, META_DEFAULT_ICON_NAME);
00428 
00429       if (icon_exists)
00430           default_icon = gtk_icon_theme_load_icon (theme,
00431                                                    META_DEFAULT_ICON_NAME,
00432                                                    META_ICON_WIDTH,
00433                                                    0,
00434                                                    NULL);
00435       else
00436           default_icon = gtk_icon_theme_load_icon (theme,
00437                                                    "gtk-missing-image",
00438                                                    META_ICON_WIDTH,
00439                                                    0,
00440                                                    NULL);
00441 
00442       g_assert (default_icon);
00443     }
00444   
00445   return default_icon;
00446 }
00447 
00448 GdkPixbuf*
00449 meta_preview_get_mini_icon (void)
00450 {
00451   static GdkPixbuf *default_icon = NULL;
00452 
00453   if (default_icon == NULL)
00454     {
00455       GtkIconTheme *theme;
00456       gboolean icon_exists;
00457 
00458       theme = gtk_icon_theme_get_default ();
00459 
00460       icon_exists = gtk_icon_theme_has_icon (theme, META_DEFAULT_ICON_NAME);
00461 
00462       if (icon_exists)
00463           default_icon = gtk_icon_theme_load_icon (theme,
00464                                                    META_DEFAULT_ICON_NAME,
00465                                                    META_MINI_ICON_WIDTH,
00466                                                    0,
00467                                                    NULL);
00468       else
00469           default_icon = gtk_icon_theme_load_icon (theme,
00470                                                    "gtk-missing-image",
00471                                                    META_MINI_ICON_WIDTH,
00472                                                    0,
00473                                                    NULL);
00474 
00475       g_assert (default_icon);
00476     }
00477   
00478   return default_icon;
00479 }
00480 
00481 GdkRegion *
00482 meta_preview_get_clip_region (MetaPreview *preview, gint new_window_width, gint new_window_height)
00483 {
00484   GdkRectangle xrect;
00485   GdkRegion *corners_xregion, *window_xregion;
00486   gint flags;
00487   MetaFrameLayout *fgeom;
00488   MetaFrameStyle *frame_style;
00489 
00490   g_return_val_if_fail (META_IS_PREVIEW (preview), NULL);
00491 
00492   flags = (META_PREVIEW (preview)->flags);
00493 
00494   window_xregion = gdk_region_new ();
00495 
00496   xrect.x = 0;
00497   xrect.y = 0;
00498   xrect.width = new_window_width;
00499   xrect.height = new_window_height;
00500 
00501   gdk_region_union_with_rect (window_xregion, &xrect);
00502 
00503   if (preview->theme == NULL)
00504     return window_xregion;
00505 
00506   /* Otherwise, we do have a theme, so calculate the corners */
00507   frame_style = meta_theme_get_frame_style (preview->theme,
00508       META_FRAME_TYPE_NORMAL, flags);
00509 
00510   fgeom = frame_style->layout;
00511 
00512   corners_xregion = gdk_region_new ();
00513 
00514   if (fgeom->top_left_corner_rounded_radius != 0)
00515     {
00516       const int corner = fgeom->top_left_corner_rounded_radius;
00517       const float radius = sqrt(corner) + corner;
00518       int i;
00519 
00520       for (i=0; i<corner; i++)
00521         {
00522 
00523           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
00524           xrect.x = 0;
00525           xrect.y = i;
00526           xrect.width = width;
00527           xrect.height = 1;
00528 
00529           gdk_region_union_with_rect (corners_xregion, &xrect);
00530         }
00531     }
00532 
00533   if (fgeom->top_right_corner_rounded_radius != 0)
00534     {
00535       const int corner = fgeom->top_right_corner_rounded_radius;
00536       const float radius = sqrt(corner) + corner;
00537       int i;
00538 
00539       for (i=0; i<corner; i++)
00540         {
00541           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
00542           xrect.x = new_window_width - width;
00543           xrect.y = i;
00544           xrect.width = width;
00545           xrect.height = 1;
00546 
00547           gdk_region_union_with_rect (corners_xregion, &xrect);
00548         }
00549     }
00550 
00551   if (fgeom->bottom_left_corner_rounded_radius != 0)
00552     {
00553       const int corner = fgeom->bottom_left_corner_rounded_radius;
00554       const float radius = sqrt(corner) + corner;
00555       int i;
00556 
00557       for (i=0; i<corner; i++)
00558         {
00559           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
00560           xrect.x = 0;
00561           xrect.y = new_window_height - i - 1;
00562           xrect.width = width;
00563           xrect.height = 1;
00564 
00565           gdk_region_union_with_rect (corners_xregion, &xrect);
00566         }
00567     }
00568 
00569   if (fgeom->bottom_right_corner_rounded_radius != 0)
00570     {
00571       const int corner = fgeom->bottom_right_corner_rounded_radius;
00572       const float radius = sqrt(corner) + corner;
00573       int i;
00574 
00575       for (i=0; i<corner; i++)
00576         {
00577           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
00578           xrect.x = new_window_width - width;
00579           xrect.y = new_window_height - i - 1;
00580           xrect.width = width;
00581           xrect.height = 1;
00582 
00583           gdk_region_union_with_rect (corners_xregion, &xrect);
00584         }
00585     }
00586 
00587   gdk_region_subtract (window_xregion, corners_xregion);
00588   gdk_region_destroy (corners_xregion);
00589 
00590   return window_xregion;
00591 }
00592 
00593 

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