00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #define _GNU_SOURCE
00025 #define _XOPEN_SOURCE 600
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 NULL,
00058 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
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
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