1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /*
4  * Copyright (C) 2001 Havoc Pennington
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "config.h"
21 
22 #include "ui/theme-private.h"
23 
24 #include <gtk/gtk.h>
25 #include <math.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "core/util-private.h"
31 #include "meta/prefs.h"
32 #include "ui/frames.h"
33 
34 static void scale_border (GtkBorder *border, double factor);
35 
36 static MetaFrameLayout *
meta_frame_layout_new(void)37 meta_frame_layout_new  (void)
38 {
39   MetaFrameLayout *layout;
40 
41   layout = g_new0 (MetaFrameLayout, 1);
42 
43   /* Spacing as hardcoded in GTK+:
44    * https://git.gnome.org/browse/gtk+/tree/gtk/gtkheaderbar.c?h=gtk-3-14#n53
45    */
46   layout->titlebar_spacing = 6;
47   layout->has_title = TRUE;
48   layout->title_scale = PANGO_SCALE_MEDIUM;
49   layout->icon_size = META_MINI_ICON_WIDTH;
50 
51   return layout;
52 }
53 
54 static void
meta_frame_layout_free(MetaFrameLayout * layout)55 meta_frame_layout_free (MetaFrameLayout *layout)
56 {
57   g_return_if_fail (layout != NULL);
58 
59   g_free (layout);
60 }
61 
62 static void
meta_frame_layout_get_borders(const MetaFrameLayout * layout,int text_height,MetaFrameFlags flags,MetaFrameType type,MetaFrameBorders * borders)63 meta_frame_layout_get_borders (const MetaFrameLayout *layout,
64                                int                    text_height,
65                                MetaFrameFlags         flags,
66                                MetaFrameType          type,
67                                MetaFrameBorders      *borders)
68 {
69   int buttons_height, content_height, draggable_borders;
70   int scale = meta_theme_get_window_scaling_factor ();
71 
72   meta_frame_borders_clear (borders);
73 
74   /* For a full-screen window, we don't have any borders, visible or not. */
75   if (flags & META_FRAME_FULLSCREEN)
76     return;
77 
78   g_return_if_fail (layout != NULL);
79 
80   if (!layout->has_title)
81     text_height = 0;
82   else
83     text_height = layout->title_margin.top + text_height + layout->title_margin.bottom;
84 
85   buttons_height = MAX ((int)layout->icon_size, layout->button_min_size.height) +
86     layout->button_margin.top + layout->button_border.top +
87     layout->button_margin.bottom + layout->button_border.bottom;
88   content_height = MAX (buttons_height, text_height);
89   content_height = MAX (content_height, layout->titlebar_min_size.height) +
90                    layout->titlebar_border.top + layout->titlebar_border.bottom;
91 
92   borders->visible.top    = layout->frame_border.top + content_height;
93   borders->visible.left   = layout->frame_border.left;
94   borders->visible.right  = layout->frame_border.right;
95   borders->visible.bottom = layout->frame_border.bottom;
96 
97   borders->invisible = layout->invisible_border;
98 
99   draggable_borders = meta_prefs_get_draggable_border_width ();
100 
101   if (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE)
102     {
103       borders->invisible.left   = MAX (borders->invisible.left,
104                                        draggable_borders - borders->visible.left);
105       borders->invisible.right  = MAX (borders->invisible.right,
106                                        draggable_borders - borders->visible.right);
107     }
108 
109   if (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE)
110     {
111       borders->invisible.bottom = MAX (borders->invisible.bottom,
112                                        draggable_borders - borders->visible.bottom);
113 
114       /* borders.visible.top is the height of the *title bar*. We can't do the same
115        * algorithm here, titlebars are expectedly much bigger. Just subtract a couple
116        * pixels to get a proper feel. */
117       if (type != META_FRAME_TYPE_ATTACHED)
118         borders->invisible.top    = MAX (borders->invisible.top, draggable_borders - 2);
119     }
120 
121   borders->total.left   = borders->invisible.left   + borders->visible.left;
122   borders->total.right  = borders->invisible.right  + borders->visible.right;
123   borders->total.bottom = borders->invisible.bottom + borders->visible.bottom;
124   borders->total.top    = borders->invisible.top    + borders->visible.top;
125 
126   /* Scale geometry for HiDPI, see comment in meta_frame_layout_draw_with_style() */
127   scale_border (&borders->visible, scale);
128   scale_border (&borders->invisible, scale);
129   scale_border (&borders->total, scale);
130 }
131 
132 int
meta_theme_get_window_scaling_factor(void)133 meta_theme_get_window_scaling_factor (void)
134 {
135   GdkScreen *screen;
136   GValue value = G_VALUE_INIT;
137 
138   g_value_init (&value, G_TYPE_INT);
139 
140   screen = gdk_screen_get_default ();
141   if (gdk_screen_get_setting (screen, "gdk-window-scaling-factor", &value))
142     return g_value_get_int (&value);
143   else
144     return 1;
145 }
146 
147 void
meta_frame_layout_apply_scale(const MetaFrameLayout * layout,PangoFontDescription * font_desc)148 meta_frame_layout_apply_scale (const MetaFrameLayout *layout,
149                                PangoFontDescription  *font_desc)
150 {
151   int size = pango_font_description_get_size (font_desc);
152   double scale = layout->title_scale / meta_theme_get_window_scaling_factor ();
153   pango_font_description_set_size (font_desc, MAX (size * scale, 1));
154 }
155 
156 static MetaButtonSpace*
rect_for_function(MetaFrameGeometry * fgeom,MetaFrameFlags flags,MetaButtonFunction function,MetaTheme * theme)157 rect_for_function (MetaFrameGeometry *fgeom,
158                    MetaFrameFlags     flags,
159                    MetaButtonFunction function,
160                    MetaTheme         *theme)
161 {
162   switch (function)
163     {
164     case META_BUTTON_FUNCTION_MENU:
165       if (flags & META_FRAME_ALLOWS_MENU)
166         return &fgeom->menu_rect;
167       else
168         return NULL;
169     case META_BUTTON_FUNCTION_MINIMIZE:
170       if (flags & META_FRAME_ALLOWS_MINIMIZE)
171         return &fgeom->min_rect;
172       else
173         return NULL;
174     case META_BUTTON_FUNCTION_MAXIMIZE:
175       if (flags & META_FRAME_ALLOWS_MAXIMIZE)
176         return &fgeom->max_rect;
177       else
178         return NULL;
179     case META_BUTTON_FUNCTION_CLOSE:
180       if (flags & META_FRAME_ALLOWS_DELETE)
181         return &fgeom->close_rect;
182       else
183         return NULL;
184 
185     case META_BUTTON_FUNCTION_LAST:
186       return NULL;
187     }
188 
189   return NULL;
190 }
191 
192 static gboolean
strip_button(MetaButtonSpace * func_rects[MAX_BUTTONS_PER_CORNER],int * n_rects,MetaButtonSpace * to_strip)193 strip_button (MetaButtonSpace *func_rects[MAX_BUTTONS_PER_CORNER],
194               int             *n_rects,
195               MetaButtonSpace *to_strip)
196 {
197   int i;
198 
199   i = 0;
200   while (i < *n_rects)
201     {
202       if (func_rects[i] == to_strip)
203         {
204           *n_rects -= 1;
205 
206           /* shift the other rects back in the array */
207           while (i < *n_rects)
208             {
209               func_rects[i] = func_rects[i+1];
210 
211               ++i;
212             }
213 
214           func_rects[i] = NULL;
215 
216           return TRUE;
217         }
218 
219       ++i;
220     }
221 
222   return FALSE; /* did not strip anything */
223 }
224 
225 static void
get_padding_and_border(GtkStyleContext * style,GtkBorder * border)226 get_padding_and_border (GtkStyleContext *style,
227                         GtkBorder       *border)
228 {
229   GtkBorder tmp;
230   GtkStateFlags state = gtk_style_context_get_state (style);
231 
232   gtk_style_context_get_border (style, state, border);
233   gtk_style_context_get_padding (style, state, &tmp);
234 
235   border->left += tmp.left;
236   border->top += tmp.top;
237   border->right += tmp.right;
238   border->bottom += tmp.bottom;
239 }
240 
241 static void
get_min_size(GtkStyleContext * style,GtkRequisition * requisition)242 get_min_size (GtkStyleContext *style,
243               GtkRequisition  *requisition)
244 {
245   gtk_style_context_get (style, gtk_style_context_get_state (style),
246                          "min-width", &requisition->width,
247                          "min-height", &requisition->height,
248                          NULL);
249 }
250 
251 static void
scale_border(GtkBorder * border,double factor)252 scale_border (GtkBorder *border,
253               double     factor)
254 {
255   border->left *= factor;
256   border->right *= factor;
257   border->top *= factor;
258   border->bottom *= factor;
259 }
260 
261 static void
meta_frame_layout_sync_with_style(MetaFrameLayout * layout,MetaStyleInfo * style_info,MetaFrameFlags flags)262 meta_frame_layout_sync_with_style (MetaFrameLayout *layout,
263                                    MetaStyleInfo   *style_info,
264                                    MetaFrameFlags   flags)
265 {
266   GtkStyleContext *style;
267   GtkBorder border;
268   GtkRequisition requisition;
269   GdkRectangle clip_rect;
270   int border_radius, max_radius;
271 
272   meta_style_info_set_flags (style_info, flags);
273 
274   style = style_info->styles[META_STYLE_ELEMENT_FRAME];
275   get_padding_and_border (style, &layout->frame_border);
276   scale_border (&layout->frame_border, layout->title_scale);
277 
278   gtk_render_background_get_clip (style, 0, 0, 0, 0, &clip_rect);
279   layout->invisible_border.left = -clip_rect.x;
280   layout->invisible_border.right = clip_rect.width + clip_rect.x;
281   layout->invisible_border.top = -clip_rect.y;
282   layout->invisible_border.bottom = clip_rect.height + clip_rect.y;
283 
284   if (layout->hide_buttons)
285     layout->icon_size = 0;
286 
287   if (!layout->has_title && layout->hide_buttons)
288     return; /* border-only - be done */
289 
290   style = style_info->styles[META_STYLE_ELEMENT_TITLEBAR];
291   gtk_style_context_get (style, gtk_style_context_get_state (style),
292                          "border-radius", &border_radius,
293                          NULL);
294   /* GTK+ currently does not allow us to look up radii of individual
295    * corners; however we don't clip the client area, so with the
296    * current trend of using small/no visible frame borders, most
297    * themes should work fine with this.
298    */
299   layout->top_left_corner_rounded_radius = border_radius;
300   layout->top_right_corner_rounded_radius = border_radius;
301   max_radius = MIN (layout->frame_border.bottom, layout->frame_border.left);
302   layout->bottom_left_corner_rounded_radius = MAX (border_radius, max_radius);
303   max_radius = MIN (layout->frame_border.bottom, layout->frame_border.right);
304   layout->bottom_right_corner_rounded_radius = MAX (border_radius, max_radius);
305 
306   get_min_size (style, &layout->titlebar_min_size);
307   get_padding_and_border (style, &layout->titlebar_border);
308   scale_border (&layout->titlebar_border, layout->title_scale);
309 
310   style = style_info->styles[META_STYLE_ELEMENT_TITLE];
311   gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
312                                 &layout->title_margin);
313   scale_border (&layout->title_margin, layout->title_scale);
314 
315   style = style_info->styles[META_STYLE_ELEMENT_BUTTON];
316   get_min_size (style, &layout->button_min_size);
317   get_padding_and_border (style, &layout->button_border);
318   scale_border (&layout->button_border, layout->title_scale);
319   gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
320                                 &layout->button_margin);
321   scale_border (&layout->button_margin, layout->title_scale);
322 
323   style = style_info->styles[META_STYLE_ELEMENT_IMAGE];
324   get_min_size (style, &requisition);
325   get_padding_and_border (style, &border);
326   scale_border (&border, layout->title_scale);
327 
328   layout->button_border.left += border.left;
329   layout->button_border.right += border.right;
330   layout->button_border.top += border.top;
331   layout->button_border.bottom += border.bottom;
332 
333   gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
334                                 &border);
335   layout->button_border.left += border.left;
336   layout->button_border.right += border.right;
337   layout->button_border.top += border.top;
338   layout->button_border.bottom += border.bottom;
339 
340   layout->button_min_size.width = MAX(layout->button_min_size.width,
341                                       requisition.width);
342   layout->button_min_size.height = MAX(layout->button_min_size.height,
343                                        requisition.height);
344 }
345 
346 static void
meta_frame_layout_calc_geometry(MetaFrameLayout * layout,MetaStyleInfo * style_info,int text_height,MetaFrameFlags flags,int client_width,int client_height,const MetaButtonLayout * button_layout,MetaFrameType type,MetaFrameGeometry * fgeom,MetaTheme * theme)347 meta_frame_layout_calc_geometry (MetaFrameLayout        *layout,
348                                  MetaStyleInfo          *style_info,
349                                  int                     text_height,
350                                  MetaFrameFlags          flags,
351                                  int                     client_width,
352                                  int                     client_height,
353                                  const MetaButtonLayout *button_layout,
354                                  MetaFrameType           type,
355                                  MetaFrameGeometry      *fgeom,
356                                  MetaTheme              *theme)
357 {
358   int i, n_left, n_right, n_left_spacers, n_right_spacers;
359   int x;
360   int button_y;
361   int title_right_edge;
362   int width, height;
363   int content_width, content_height;
364   int button_width, button_height;
365   int min_size_for_rounding;
366   int scale = meta_theme_get_window_scaling_factor ();
367 
368   /* the left/right rects in order; the max # of rects
369    * is the number of button functions
370    */
371   MetaButtonSpace *left_func_rects[MAX_BUTTONS_PER_CORNER];
372   MetaButtonSpace *right_func_rects[MAX_BUTTONS_PER_CORNER];
373   gboolean left_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
374   gboolean right_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
375 
376   MetaFrameBorders borders;
377 
378   meta_frame_layout_sync_with_style (layout, style_info, flags);
379 
380   meta_frame_layout_get_borders (layout, text_height,
381                                  flags, type,
382                                  &borders);
383 
384   fgeom->borders = borders;
385 
386   /* Scale geometry for HiDPI, see comment in meta_frame_layout_draw_with_style() */
387   fgeom->content_border = layout->frame_border;
388   fgeom->content_border.left   += layout->titlebar_border.left * scale;
389   fgeom->content_border.right  += layout->titlebar_border.right * scale;
390   fgeom->content_border.top    += layout->titlebar_border.top * scale;
391   fgeom->content_border.bottom += layout->titlebar_border.bottom * scale;
392 
393   width = client_width + borders.total.left + borders.total.right;
394 
395   height = borders.total.top + borders.total.bottom;
396   if (!(flags & META_FRAME_SHADED))
397     height += client_height;
398 
399   fgeom->width = width;
400   fgeom->height = height;
401 
402   content_width = width -
403                   (fgeom->content_border.left + borders.invisible.left) -
404                   (fgeom->content_border.right + borders.invisible.right);
405   content_height = borders.visible.top - fgeom->content_border.top - fgeom->content_border.bottom;
406 
407   button_width = MAX ((int)layout->icon_size, layout->button_min_size.width) +
408                  layout->button_border.left + layout->button_border.right;
409   button_height = MAX ((int)layout->icon_size, layout->button_min_size.height) +
410                   layout->button_border.top + layout->button_border.bottom;
411   button_width *= scale;
412   button_height *= scale;
413 
414   /* FIXME all this code sort of pretends that duplicate buttons
415    * with the same function are allowed, but that breaks the
416    * code in frames.c, so isn't really allowed right now.
417    * Would need left_close_rect, right_close_rect, etc.
418    */
419 
420   /* Init all button rects to 0, lame hack */
421   memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0',
422           LENGTH_OF_BUTTON_RECTS);
423 
424   n_left = 0;
425   n_right = 0;
426   n_left_spacers = 0;
427   n_right_spacers = 0;
428 
429   if (!layout->hide_buttons)
430     {
431       /* Try to fill in rects */
432       for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
433         {
434           left_func_rects[n_left] = rect_for_function (fgeom, flags,
435                                                        button_layout->left_buttons[i],
436                                                        theme);
437           if (left_func_rects[n_left] != NULL)
438             {
439               left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i];
440               if (button_layout->left_buttons_has_spacer[i])
441                 ++n_left_spacers;
442 
443               ++n_left;
444             }
445         }
446 
447       for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
448         {
449           right_func_rects[n_right] = rect_for_function (fgeom, flags,
450                                                          button_layout->right_buttons[i],
451                                                          theme);
452           if (right_func_rects[n_right] != NULL)
453             {
454               right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i];
455               if (button_layout->right_buttons_has_spacer[i])
456                 ++n_right_spacers;
457 
458               ++n_right;
459             }
460         }
461     }
462 
463   /* Be sure buttons fit */
464   while (n_left > 0 || n_right > 0)
465     {
466       int space_used_by_buttons;
467 
468       space_used_by_buttons = 0;
469 
470       space_used_by_buttons += layout->button_margin.left * scale * n_left;
471       space_used_by_buttons += button_width * n_left;
472       space_used_by_buttons += layout->button_margin.right * scale * n_left;
473       space_used_by_buttons += (button_width * 0.75) * n_left_spacers;
474       space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_left - 1, 0);
475 
476       space_used_by_buttons += layout->button_margin.left * scale * n_right;
477       space_used_by_buttons += button_width * n_right;
478       space_used_by_buttons += layout->button_margin.right * scale * n_right;
479       space_used_by_buttons += (button_width * 0.75) * n_right_spacers;
480       space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_right - 1, 0);
481 
482       if (space_used_by_buttons <= content_width)
483         break; /* Everything fits, bail out */
484 
485       /* First try to remove separators */
486       if (n_left_spacers > 0)
487         {
488           left_buttons_has_spacer[--n_left_spacers] = FALSE;
489           continue;
490         }
491       else if (n_right_spacers > 0)
492         {
493           right_buttons_has_spacer[--n_right_spacers] = FALSE;
494           continue;
495         }
496 
497       /* Otherwise we need to shave out a button. Shave
498        * min, max, close, then menu (menu is most useful);
499        * prefer the default button locations.
500        */
501       if (strip_button (left_func_rects, &n_left, &fgeom->min_rect))
502         continue;
503       else if (strip_button (right_func_rects, &n_right, &fgeom->min_rect))
504         continue;
505       else if (strip_button (left_func_rects, &n_left, &fgeom->max_rect))
506         continue;
507       else if (strip_button (right_func_rects, &n_right, &fgeom->max_rect))
508         continue;
509       else if (strip_button (left_func_rects, &n_left, &fgeom->close_rect))
510         continue;
511       else if (strip_button (right_func_rects, &n_right, &fgeom->close_rect))
512         continue;
513       else if (strip_button (right_func_rects, &n_right, &fgeom->menu_rect))
514         continue;
515       else if (strip_button (left_func_rects, &n_left, &fgeom->menu_rect))
516         continue;
517       else
518         {
519           meta_bug ("Could not find a button to strip. n_left = %d n_right = %d",
520                     n_left, n_right);
521         }
522     }
523 
524   /* Save the button layout */
525   fgeom->button_layout = *button_layout;
526   fgeom->n_left_buttons = n_left;
527   fgeom->n_right_buttons = n_right;
528 
529   /* center buttons vertically */
530   button_y = fgeom->content_border.top + borders.invisible.top +
531              (content_height - button_height) / 2;
532 
533   /* right edge of farthest-right button */
534   x = width - fgeom->content_border.right - borders.invisible.right;
535 
536   i = n_right - 1;
537   while (i >= 0)
538     {
539       MetaButtonSpace *rect;
540 
541       if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */
542         break;
543 
544       x -= layout->button_margin.right * scale;
545 
546       rect = right_func_rects[i];
547       rect->visible.x = x - button_width;
548       if (right_buttons_has_spacer[i])
549         rect->visible.x -= (button_width * 0.75);
550 
551       rect->visible.y = button_y;
552       rect->visible.width = button_width;
553       rect->visible.height = button_height;
554 
555       if (flags & META_FRAME_MAXIMIZED ||
556           flags & META_FRAME_TILED_LEFT ||
557           flags & META_FRAME_TILED_RIGHT)
558         {
559           rect->clickable.x = rect->visible.x;
560           rect->clickable.y = 0;
561           rect->clickable.width = rect->visible.width;
562           rect->clickable.height = button_height + button_y;
563 
564           if (i == n_right - 1)
565             rect->clickable.width += fgeom->content_border.right;
566 
567         }
568       else
569         memmove (&(rect->clickable), &(rect->visible), sizeof (rect->clickable));
570 
571       x = rect->visible.x - layout->button_margin.left * scale;
572 
573       if (i > 0)
574         x -= layout->titlebar_spacing * scale;
575 
576       --i;
577     }
578 
579   /* save right edge of titlebar for later use */
580   title_right_edge = x;
581 
582   /* Now x changes to be position from the left and we go through
583    * the left-side buttons
584    */
585   x = fgeom->content_border.left + borders.invisible.left;
586   for (i = 0; i < n_left; i++)
587     {
588       MetaButtonSpace *rect;
589 
590       x += layout->button_margin.left * scale;
591 
592       rect = left_func_rects[i];
593 
594       rect->visible.x = x;
595       rect->visible.y = button_y;
596       rect->visible.width = button_width;
597       rect->visible.height = button_height;
598 
599       if (flags & META_FRAME_MAXIMIZED)
600         {
601           if (i==0)
602             {
603               rect->clickable.x = 0;
604               rect->clickable.width = button_width + x;
605             }
606           else
607             {
608               rect->clickable.x = rect->visible.x;
609               rect->clickable.width = button_width;
610             }
611 
612           rect->clickable.y = 0;
613           rect->clickable.height = button_height + button_y;
614         }
615       else
616         memmove (&(rect->clickable), &(rect->visible), sizeof (rect->clickable));
617 
618       x = rect->visible.x + rect->visible.width + layout->button_margin.right * scale;
619       if (i < n_left - 1)
620         x += layout->titlebar_spacing * scale;
621       if (left_buttons_has_spacer[i])
622         x += (button_width * 0.75);
623     }
624 
625   /* Center vertically in the available content area */
626   fgeom->title_rect.x = x;
627   fgeom->title_rect.y = fgeom->content_border.top + borders.invisible.top +
628                         (content_height - text_height) / 2;
629   fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x;
630   fgeom->title_rect.height = text_height;
631 
632   /* Nuke title if it won't fit */
633   if (fgeom->title_rect.width < 0 ||
634       fgeom->title_rect.height < 0)
635     {
636       fgeom->title_rect.width = 0;
637       fgeom->title_rect.height = 0;
638     }
639 
640   if (flags & META_FRAME_SHADED)
641     min_size_for_rounding = 0;
642   else
643     min_size_for_rounding = 5 * scale;
644 
645   fgeom->top_left_corner_rounded_radius = 0;
646   fgeom->top_right_corner_rounded_radius = 0;
647   fgeom->bottom_left_corner_rounded_radius = 0;
648   fgeom->bottom_right_corner_rounded_radius = 0;
649 
650   if (borders.visible.top + borders.visible.left >= min_size_for_rounding)
651     fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius * scale;
652   if (borders.visible.top + borders.visible.right >= min_size_for_rounding)
653     fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius * scale;
654 
655   if (borders.visible.bottom + borders.visible.left >= min_size_for_rounding)
656     fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius * scale;
657   if (borders.visible.bottom + borders.visible.right >= min_size_for_rounding)
658     fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius * scale;
659 }
660 
661 static void
get_button_rect(MetaButtonType type,const MetaFrameGeometry * fgeom,GdkRectangle * rect)662 get_button_rect (MetaButtonType           type,
663                  const MetaFrameGeometry *fgeom,
664                  GdkRectangle            *rect)
665 {
666   switch (type)
667     {
668     case META_BUTTON_TYPE_CLOSE:
669       *rect = fgeom->close_rect.visible;
670       break;
671 
672     case META_BUTTON_TYPE_MAXIMIZE:
673       *rect = fgeom->max_rect.visible;
674       break;
675 
676     case META_BUTTON_TYPE_MINIMIZE:
677       *rect = fgeom->min_rect.visible;
678       break;
679 
680     case META_BUTTON_TYPE_MENU:
681       *rect = fgeom->menu_rect.visible;
682       break;
683 
684     default:
685     case META_BUTTON_TYPE_LAST:
686       g_assert_not_reached ();
687       break;
688     }
689 }
690 
691 static const char *
get_class_from_button_type(MetaButtonType type)692 get_class_from_button_type (MetaButtonType type)
693 {
694   switch (type)
695     {
696     case META_BUTTON_TYPE_CLOSE:
697       return "close";
698     case META_BUTTON_TYPE_MAXIMIZE:
699       return "maximize";
700     case META_BUTTON_TYPE_MINIMIZE:
701       return "minimize";
702     default:
703       return NULL;
704     }
705 }
706 
707 static void
meta_frame_layout_draw_with_style(MetaFrameLayout * layout,MetaStyleInfo * style_info,cairo_t * cr,const MetaFrameGeometry * fgeom,PangoLayout * title_layout,MetaFrameFlags flags,MetaButtonState button_states[META_BUTTON_TYPE_LAST],cairo_surface_t * mini_icon)708 meta_frame_layout_draw_with_style (MetaFrameLayout         *layout,
709                                    MetaStyleInfo           *style_info,
710                                    cairo_t                 *cr,
711                                    const MetaFrameGeometry *fgeom,
712                                    PangoLayout             *title_layout,
713                                    MetaFrameFlags           flags,
714                                    MetaButtonState          button_states[META_BUTTON_TYPE_LAST],
715                                    cairo_surface_t         *mini_icon)
716 {
717   GtkStyleContext *style;
718   GtkStateFlags state;
719   MetaButtonType button_type;
720   GdkRectangle visible_rect;
721   GdkRectangle titlebar_rect;
722   GdkRectangle button_rect;
723   const MetaFrameBorders *borders;
724   cairo_surface_t *frame_surface;
725   double xscale, yscale;
726   int scale;
727 
728   /* We opt out of GTK+/Clutter's HiDPI handling, so we have to do the scaling
729    * ourselves; the nitty-gritty is a bit confusing, so here is an overview:
730    *  - the values in MetaFrameLayout are always as they appear in the theme,
731    *    i.e. unscaled
732    *  - calculated values (borders, MetaFrameGeometry) include the scale - as
733    *    the geometry is comprised of scaled decorations and the client size
734    *    which we must not scale, we don't have another option
735    *  - for drawing, we scale the canvas to have GTK+ render elements (borders,
736    *    radii, ...) at the correct scale - as a result, we have to "unscale"
737    *    the geometry again to not apply the scaling twice
738    *  - As per commit e36b629c GTK expects the device scale to be set and match
739    *    the final scaling or the surface caching won't take this in account
740    *    breaking -gtk-scaled items.
741    */
742   scale = meta_theme_get_window_scaling_factor ();
743   frame_surface = cairo_get_target (cr);
744   cairo_surface_get_device_scale (frame_surface, &xscale, &yscale);
745   cairo_surface_set_device_scale (frame_surface, scale, scale);
746 
747   borders = &fgeom->borders;
748 
749   visible_rect.x = borders->invisible.left / scale;
750   visible_rect.y = borders->invisible.top / scale;
751   visible_rect.width = (fgeom->width - borders->invisible.left - borders->invisible.right) / scale;
752   visible_rect.height = (fgeom->height - borders->invisible.top - borders->invisible.bottom) / scale;
753 
754   meta_style_info_set_flags (style_info, flags);
755 
756   style = style_info->styles[META_STYLE_ELEMENT_FRAME];
757   gtk_render_background (style, cr,
758                          visible_rect.x, visible_rect.y,
759                          visible_rect.width, visible_rect.height);
760   gtk_render_frame (style, cr,
761                     visible_rect.x, visible_rect.y,
762                     visible_rect.width, visible_rect.height);
763 
764   titlebar_rect.x = visible_rect.x;
765   titlebar_rect.y = visible_rect.y;
766   titlebar_rect.width = visible_rect.width;
767   titlebar_rect.height = borders->visible.top / scale;
768 
769   style = style_info->styles[META_STYLE_ELEMENT_TITLEBAR];
770   gtk_render_background (style, cr,
771                          titlebar_rect.x, titlebar_rect.y,
772                          titlebar_rect.width, titlebar_rect.height);
773   gtk_render_frame (style, cr,
774                     titlebar_rect.x, titlebar_rect.y,
775                     titlebar_rect.width, titlebar_rect.height);
776 
777   if (layout->has_title && title_layout)
778     {
779       PangoRectangle logical;
780       int text_width, x, y;
781 
782       pango_layout_set_width (title_layout, -1);
783       pango_layout_get_pixel_extents (title_layout, NULL, &logical);
784 
785       text_width = MIN(fgeom->title_rect.width / scale, logical.width);
786 
787       if (text_width < logical.width)
788         pango_layout_set_width (title_layout, PANGO_SCALE * text_width);
789 
790       /* Center within the frame if possible */
791       x = titlebar_rect.x + (titlebar_rect.width - text_width) / 2;
792       y = titlebar_rect.y + (titlebar_rect.height - logical.height) / 2;
793 
794       if (x < fgeom->title_rect.x / scale)
795         x = fgeom->title_rect.x / scale;
796       else if (x + text_width > (fgeom->title_rect.x + fgeom->title_rect.width) / scale)
797         x = (fgeom->title_rect.x + fgeom->title_rect.width) / scale - text_width;
798 
799       style = style_info->styles[META_STYLE_ELEMENT_TITLE];
800       gtk_render_layout (style, cr, x, y, title_layout);
801     }
802 
803   style = style_info->styles[META_STYLE_ELEMENT_BUTTON];
804   state = gtk_style_context_get_state (style);
805   for (button_type = META_BUTTON_TYPE_CLOSE; button_type < META_BUTTON_TYPE_LAST; button_type++)
806     {
807       const char *button_class = get_class_from_button_type (button_type);
808       if (button_class)
809         gtk_style_context_add_class (style, button_class);
810 
811       get_button_rect (button_type, fgeom, &button_rect);
812 
813       button_rect.x /= scale;
814       button_rect.y /= scale;
815       button_rect.width /= scale;
816       button_rect.height /= scale;
817 
818       if (button_states[button_type] == META_BUTTON_STATE_PRELIGHT)
819         gtk_style_context_set_state (style, state | GTK_STATE_PRELIGHT);
820       else if (button_states[button_type] == META_BUTTON_STATE_PRESSED)
821         gtk_style_context_set_state (style, state | GTK_STATE_ACTIVE);
822       else
823         gtk_style_context_set_state (style, state);
824 
825       cairo_save (cr);
826 
827       if (button_rect.width > 0 && button_rect.height > 0)
828         {
829           cairo_surface_t *surface = NULL;
830           const char *icon_name = NULL;
831 
832           gtk_render_background (style, cr,
833                                  button_rect.x, button_rect.y,
834                                  button_rect.width, button_rect.height);
835           gtk_render_frame (style, cr,
836                             button_rect.x, button_rect.y,
837                             button_rect.width, button_rect.height);
838 
839           switch (button_type)
840             {
841             case META_BUTTON_TYPE_CLOSE:
842                icon_name = "window-close-symbolic";
843                break;
844             case META_BUTTON_TYPE_MAXIMIZE:
845                if (flags & META_FRAME_MAXIMIZED)
846                  icon_name = "window-restore-symbolic";
847                else
848                  icon_name = "window-maximize-symbolic";
849                break;
850             case META_BUTTON_TYPE_MINIMIZE:
851                icon_name = "window-minimize-symbolic";
852                break;
853             case META_BUTTON_TYPE_MENU:
854                icon_name = "open-menu-symbolic";
855                break;
856             default:
857                icon_name = NULL;
858                break;
859             }
860 
861           if (icon_name)
862             {
863               GtkIconTheme *theme = gtk_icon_theme_get_default ();
864               g_autoptr (GtkIconInfo) info = NULL;
865               g_autoptr (GdkPixbuf) pixbuf = NULL;
866 
867               info = gtk_icon_theme_lookup_icon_for_scale (theme, icon_name,
868                                                            layout->icon_size, scale, 0);
869               pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL);
870               surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
871             }
872 
873           if (surface)
874             {
875               float width, height;
876               int x, y;
877 
878               width = cairo_image_surface_get_width (surface) / scale;
879               height = cairo_image_surface_get_height (surface) / scale;
880               x = button_rect.x + (button_rect.width - layout->icon_size) / 2;
881               y = button_rect.y + (button_rect.height - layout->icon_size) / 2;
882 
883               cairo_translate (cr, x, y);
884               cairo_scale (cr,
885                            layout->icon_size / width,
886                            layout->icon_size / height);
887               cairo_set_source_surface (cr, surface, 0, 0);
888               cairo_paint (cr);
889 
890               cairo_surface_destroy (surface);
891             }
892         }
893       cairo_restore (cr);
894       if (button_class)
895         gtk_style_context_remove_class (style, button_class);
896       gtk_style_context_set_state (style, state);
897     }
898 
899   cairo_surface_set_device_scale (frame_surface, xscale, yscale);
900 }
901 
902 /**
903  * meta_theme_get_default: (skip)
904  *
905  */
906 MetaTheme*
meta_theme_get_default(void)907 meta_theme_get_default (void)
908 {
909   static MetaTheme *theme = NULL;
910   int frame_type;
911 
912   if (theme)
913     return theme;
914 
915   theme = meta_theme_new ();
916 
917   for (frame_type = 0; frame_type < META_FRAME_TYPE_LAST; frame_type++)
918     {
919       MetaFrameLayout *layout = meta_frame_layout_new ();
920 
921       switch (frame_type)
922         {
923         case META_FRAME_TYPE_NORMAL:
924         case META_FRAME_TYPE_DIALOG:
925         case META_FRAME_TYPE_MODAL_DIALOG:
926         case META_FRAME_TYPE_ATTACHED:
927           break;
928         case META_FRAME_TYPE_MENU:
929         case META_FRAME_TYPE_UTILITY:
930           layout->title_scale = PANGO_SCALE_SMALL;
931           break;
932         case META_FRAME_TYPE_BORDER:
933           layout->has_title = FALSE;
934           layout->hide_buttons = TRUE;
935           break;
936         default:
937           g_assert_not_reached ();
938         }
939 
940       theme->layouts[frame_type] = layout;
941     }
942   return theme;
943 }
944 
945 /**
946  * meta_theme_new: (skip)
947  *
948  */
949 MetaTheme*
meta_theme_new(void)950 meta_theme_new (void)
951 {
952   return g_new0 (MetaTheme, 1);
953 }
954 
955 
956 void
meta_theme_free(MetaTheme * theme)957 meta_theme_free (MetaTheme *theme)
958 {
959   int i;
960 
961   g_return_if_fail (theme != NULL);
962 
963   for (i = 0; i < META_FRAME_TYPE_LAST; i++)
964     if (theme->layouts[i])
965       meta_frame_layout_free (theme->layouts[i]);
966 
967   g_free (theme);
968 }
969 
970 MetaFrameLayout*
meta_theme_get_frame_layout(MetaTheme * theme,MetaFrameType type)971 meta_theme_get_frame_layout (MetaTheme     *theme,
972                              MetaFrameType  type)
973 {
974   g_return_val_if_fail (type < META_FRAME_TYPE_LAST, NULL);
975 
976   return theme->layouts[type];
977 }
978 
979 static GtkStyleContext *
create_style_context(GType widget_type,GtkStyleContext * parent_style,GtkCssProvider * provider,const char * object_name,const char * first_class,...)980 create_style_context (GType            widget_type,
981                       GtkStyleContext *parent_style,
982                       GtkCssProvider  *provider,
983                       const char      *object_name,
984                       const char      *first_class,
985                       ...)
986 {
987   GtkStyleContext *style;
988   GtkStateFlags state;
989   GtkWidgetPath *path;
990   const char *name;
991   va_list ap;
992 
993   style = gtk_style_context_new ();
994   gtk_style_context_set_scale (style, meta_theme_get_window_scaling_factor ());
995   gtk_style_context_set_parent (style, parent_style);
996 
997   if (parent_style)
998     path = gtk_widget_path_copy (gtk_style_context_get_path (parent_style));
999   else
1000     path = gtk_widget_path_new ();
1001 
1002   gtk_widget_path_append_type (path, widget_type);
1003 
1004   if (object_name)
1005     gtk_widget_path_iter_set_object_name (path, -1, object_name);
1006 
1007   state = gtk_style_context_get_state (style);
1008   if (meta_get_locale_direction() == META_LOCALE_DIRECTION_RTL)
1009     {
1010       state |= GTK_STATE_FLAG_DIR_RTL;
1011       state &= ~GTK_STATE_FLAG_DIR_LTR;
1012     }
1013   else
1014     {
1015       state |= GTK_STATE_FLAG_DIR_LTR;
1016       state &= ~GTK_STATE_FLAG_DIR_RTL;
1017     }
1018   gtk_style_context_set_state (style, state);
1019 
1020   va_start (ap, first_class);
1021   for (name = first_class; name; name = va_arg (ap, const char *))
1022     gtk_widget_path_iter_add_class (path, -1, name);
1023   va_end (ap);
1024 
1025   gtk_style_context_set_path (style, path);
1026   gtk_widget_path_unref (path);
1027 
1028   gtk_style_context_add_provider (style, GTK_STYLE_PROVIDER (provider),
1029                                   GTK_STYLE_PROVIDER_PRIORITY_SETTINGS);
1030 
1031   return style;
1032 }
1033 
1034 static inline GtkCssProvider *
get_css_provider_for_theme_name(const gchar * theme_name,const gchar * variant)1035 get_css_provider_for_theme_name (const gchar *theme_name,
1036                                  const gchar *variant)
1037 {
1038   static GtkCssProvider *default_provider = NULL;
1039 
1040   if (!theme_name || *theme_name == '\0')
1041     {
1042       if (G_UNLIKELY (default_provider == NULL))
1043         default_provider = gtk_css_provider_new ();
1044 
1045       return default_provider;
1046     }
1047 
1048   return gtk_css_provider_get_named (theme_name, variant);
1049 }
1050 
1051 MetaStyleInfo *
meta_theme_create_style_info(GdkScreen * screen,const gchar * variant)1052 meta_theme_create_style_info (GdkScreen   *screen,
1053                               const gchar *variant)
1054 {
1055   MetaStyleInfo *style_info;
1056   GtkCssProvider *provider;
1057   char *theme_name;
1058 
1059   g_object_get (gtk_settings_get_for_screen (screen),
1060                 "gtk-theme-name", &theme_name,
1061                 NULL);
1062 
1063   provider = get_css_provider_for_theme_name (theme_name, variant);
1064   g_free (theme_name);
1065 
1066   style_info = g_new0 (MetaStyleInfo, 1);
1067   style_info->refcount = 1;
1068 
1069   style_info->styles[META_STYLE_ELEMENT_WINDOW] =
1070     create_style_context (META_TYPE_FRAMES,
1071                           NULL,
1072                           provider,
1073                           "window",
1074                           GTK_STYLE_CLASS_BACKGROUND,
1075                           "ssd",
1076                           NULL);
1077   style_info->styles[META_STYLE_ELEMENT_FRAME] =
1078     create_style_context (META_TYPE_FRAMES,
1079                           style_info->styles[META_STYLE_ELEMENT_WINDOW],
1080                           provider,
1081                           "decoration",
1082                           NULL);
1083   style_info->styles[META_STYLE_ELEMENT_TITLEBAR] =
1084     create_style_context (GTK_TYPE_HEADER_BAR,
1085                           style_info->styles[META_STYLE_ELEMENT_FRAME],
1086                           provider,
1087                           "headerbar",
1088                           GTK_STYLE_CLASS_TITLEBAR,
1089                           GTK_STYLE_CLASS_HORIZONTAL,
1090                           "default-decoration",
1091                           NULL);
1092   style_info->styles[META_STYLE_ELEMENT_TITLE] =
1093     create_style_context (GTK_TYPE_LABEL,
1094                           style_info->styles[META_STYLE_ELEMENT_TITLEBAR],
1095                           provider,
1096                           "label",
1097                           GTK_STYLE_CLASS_TITLE,
1098                           NULL);
1099   style_info->styles[META_STYLE_ELEMENT_BUTTON] =
1100     create_style_context (GTK_TYPE_BUTTON,
1101                           style_info->styles[META_STYLE_ELEMENT_TITLEBAR],
1102                           provider,
1103                           "button",
1104                           "titlebutton",
1105                           NULL);
1106   style_info->styles[META_STYLE_ELEMENT_IMAGE] =
1107     create_style_context (GTK_TYPE_IMAGE,
1108                           style_info->styles[META_STYLE_ELEMENT_BUTTON],
1109                           provider,
1110                           "image",
1111                           NULL);
1112   return style_info;
1113 }
1114 
1115 MetaStyleInfo *
meta_style_info_ref(MetaStyleInfo * style_info)1116 meta_style_info_ref (MetaStyleInfo *style_info)
1117 {
1118   g_return_val_if_fail (style_info != NULL, NULL);
1119   g_return_val_if_fail (style_info->refcount > 0, NULL);
1120 
1121   g_atomic_int_inc ((volatile int *)&style_info->refcount);
1122   return style_info;
1123 }
1124 
1125 void
meta_style_info_unref(MetaStyleInfo * style_info)1126 meta_style_info_unref (MetaStyleInfo *style_info)
1127 {
1128   g_return_if_fail (style_info != NULL);
1129   g_return_if_fail (style_info->refcount > 0);
1130 
1131   if (g_atomic_int_dec_and_test ((volatile int *)&style_info->refcount))
1132     {
1133       int i;
1134       for (i = 0; i < META_STYLE_ELEMENT_LAST; i++)
1135         g_object_unref (style_info->styles[i]);
1136       g_free (style_info);
1137     }
1138 }
1139 
1140 static void
add_toplevel_class(GtkStyleContext * style,const char * class_name)1141 add_toplevel_class (GtkStyleContext *style,
1142                     const char      *class_name)
1143 {
1144   if (gtk_style_context_get_parent (style))
1145     {
1146       GtkWidgetPath *path;
1147 
1148       path = gtk_widget_path_copy (gtk_style_context_get_path (style));
1149       gtk_widget_path_iter_add_class (path, 0, class_name);
1150       gtk_style_context_set_path (style, path);
1151       gtk_widget_path_unref (path);
1152     }
1153   else
1154     gtk_style_context_add_class (style, class_name);
1155 }
1156 
1157 static void
remove_toplevel_class(GtkStyleContext * style,const char * class_name)1158 remove_toplevel_class (GtkStyleContext *style,
1159                        const char      *class_name)
1160 {
1161   if (gtk_style_context_get_parent (style))
1162     {
1163       GtkWidgetPath *path;
1164 
1165       path = gtk_widget_path_copy (gtk_style_context_get_path (style));
1166       gtk_widget_path_iter_remove_class (path, 0, class_name);
1167       gtk_style_context_set_path (style, path);
1168       gtk_widget_path_unref (path);
1169     }
1170   else
1171     gtk_style_context_remove_class (style, class_name);
1172 }
1173 
1174 void
meta_style_info_set_flags(MetaStyleInfo * style_info,MetaFrameFlags flags)1175 meta_style_info_set_flags (MetaStyleInfo  *style_info,
1176                            MetaFrameFlags  flags)
1177 {
1178   GtkStyleContext *style;
1179   const char *class_name = NULL;
1180   gboolean backdrop;
1181   GtkStateFlags state;
1182   int i;
1183 
1184   backdrop = !(flags & META_FRAME_HAS_FOCUS);
1185 
1186   if (flags & META_FRAME_MAXIMIZED)
1187     class_name = "maximized";
1188   else if (flags & META_FRAME_TILED_LEFT ||
1189            flags & META_FRAME_TILED_RIGHT)
1190     class_name = "tiled";
1191 
1192   for (i = 0; i < META_STYLE_ELEMENT_LAST; i++)
1193     {
1194       style = style_info->styles[i];
1195 
1196       state = gtk_style_context_get_state (style);
1197       if (backdrop)
1198         gtk_style_context_set_state (style, state | GTK_STATE_FLAG_BACKDROP);
1199       else
1200         gtk_style_context_set_state (style, state & ~GTK_STATE_FLAG_BACKDROP);
1201 
1202       remove_toplevel_class (style, "maximized");
1203       remove_toplevel_class (style, "tiled");
1204 
1205       if (class_name)
1206         add_toplevel_class (style, class_name);
1207     }
1208 }
1209 
1210 PangoFontDescription*
meta_style_info_create_font_desc(MetaStyleInfo * style_info)1211 meta_style_info_create_font_desc (MetaStyleInfo *style_info)
1212 {
1213   PangoFontDescription *font_desc;
1214   const PangoFontDescription *override = meta_prefs_get_titlebar_font ();
1215   GtkStyleContext *context = style_info->styles[META_STYLE_ELEMENT_TITLE];
1216 
1217   gtk_style_context_get (context,
1218                          gtk_style_context_get_state (context),
1219                          "font", &font_desc, NULL);
1220 
1221   if (override)
1222     pango_font_description_merge (font_desc, override, TRUE);
1223 
1224   return font_desc;
1225 }
1226 
1227 void
meta_theme_draw_frame(MetaTheme * theme,MetaStyleInfo * style_info,cairo_t * cr,MetaFrameType type,MetaFrameFlags flags,int client_width,int client_height,PangoLayout * title_layout,int text_height,const MetaButtonLayout * button_layout,MetaButtonState button_states[META_BUTTON_TYPE_LAST],cairo_surface_t * mini_icon)1228 meta_theme_draw_frame (MetaTheme              *theme,
1229                        MetaStyleInfo          *style_info,
1230                        cairo_t                *cr,
1231                        MetaFrameType           type,
1232                        MetaFrameFlags          flags,
1233                        int                     client_width,
1234                        int                     client_height,
1235                        PangoLayout            *title_layout,
1236                        int                     text_height,
1237                        const MetaButtonLayout *button_layout,
1238                        MetaButtonState         button_states[META_BUTTON_TYPE_LAST],
1239                        cairo_surface_t        *mini_icon)
1240 {
1241   MetaFrameGeometry fgeom;
1242   MetaFrameLayout *layout;
1243 
1244   g_return_if_fail (type < META_FRAME_TYPE_LAST);
1245 
1246   layout = theme->layouts[type];
1247 
1248   /* Parser is not supposed to allow this currently */
1249   if (layout == NULL)
1250     return;
1251 
1252   meta_frame_layout_calc_geometry (layout,
1253                                    style_info,
1254                                    text_height,
1255                                    flags,
1256                                    client_width, client_height,
1257                                    button_layout,
1258                                    type,
1259                                    &fgeom,
1260                                    theme);
1261 
1262   meta_frame_layout_draw_with_style (layout,
1263                                      style_info,
1264                                      cr,
1265                                      &fgeom,
1266                                      title_layout,
1267                                      flags,
1268                                      button_states,
1269                                      mini_icon);
1270 }
1271 
1272 void
meta_theme_get_frame_borders(MetaTheme * theme,MetaStyleInfo * style_info,MetaFrameType type,int text_height,MetaFrameFlags flags,MetaFrameBorders * borders)1273 meta_theme_get_frame_borders (MetaTheme        *theme,
1274                               MetaStyleInfo    *style_info,
1275                               MetaFrameType     type,
1276                               int               text_height,
1277                               MetaFrameFlags    flags,
1278                               MetaFrameBorders *borders)
1279 {
1280   MetaFrameLayout *layout;
1281 
1282   g_return_if_fail (type < META_FRAME_TYPE_LAST);
1283 
1284   layout = theme->layouts[type];
1285 
1286   meta_frame_borders_clear (borders);
1287 
1288   /* Parser is not supposed to allow this currently */
1289   if (layout == NULL)
1290     return;
1291 
1292   meta_frame_layout_sync_with_style (layout, style_info, flags);
1293 
1294   meta_frame_layout_get_borders (layout,
1295                                  text_height,
1296                                  flags, type,
1297                                  borders);
1298 }
1299 
1300 void
meta_theme_calc_geometry(MetaTheme * theme,MetaStyleInfo * style_info,MetaFrameType type,int text_height,MetaFrameFlags flags,int client_width,int client_height,const MetaButtonLayout * button_layout,MetaFrameGeometry * fgeom)1301 meta_theme_calc_geometry (MetaTheme              *theme,
1302                           MetaStyleInfo          *style_info,
1303                           MetaFrameType           type,
1304                           int                     text_height,
1305                           MetaFrameFlags          flags,
1306                           int                     client_width,
1307                           int                     client_height,
1308                           const MetaButtonLayout *button_layout,
1309                           MetaFrameGeometry      *fgeom)
1310 {
1311   MetaFrameLayout *layout;
1312 
1313   g_return_if_fail (type < META_FRAME_TYPE_LAST);
1314 
1315   layout = theme->layouts[type];
1316 
1317   /* Parser is not supposed to allow this currently */
1318   if (layout == NULL)
1319     return;
1320 
1321   meta_frame_layout_calc_geometry (layout,
1322                                    style_info,
1323                                    text_height,
1324                                    flags,
1325                                    client_width, client_height,
1326                                    button_layout,
1327                                    type,
1328                                    fgeom,
1329                                    theme);
1330 }
1331 
1332 /**
1333  * meta_pango_font_desc_get_text_height:
1334  * @font_desc: the font
1335  * @context: the context of the font
1336  *
1337  * Returns the height of the letters in a particular font.
1338  *
1339  * Returns: the height of the letters
1340  */
1341 int
meta_pango_font_desc_get_text_height(const PangoFontDescription * font_desc,PangoContext * context)1342 meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc,
1343                                       PangoContext         *context)
1344 {
1345   PangoFontMetrics *metrics;
1346   PangoLanguage *lang;
1347   int retval;
1348 
1349   lang = pango_context_get_language (context);
1350   metrics = pango_context_get_metrics (context, font_desc, lang);
1351 
1352   retval = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
1353                          pango_font_metrics_get_descent (metrics));
1354 
1355   pango_font_metrics_unref (metrics);
1356 
1357   return retval;
1358 }
1359 
1360 /**
1361  * meta_frame_type_to_string:
1362  * @type: a #MetaFrameType
1363  *
1364  * Converts a frame type enum value to the name string that would
1365  * appear in the theme definition file.
1366  *
1367  * Return value: the string value
1368  */
1369 const char*
meta_frame_type_to_string(MetaFrameType type)1370 meta_frame_type_to_string (MetaFrameType type)
1371 {
1372   switch (type)
1373     {
1374     case META_FRAME_TYPE_NORMAL:
1375       return "normal";
1376     case META_FRAME_TYPE_DIALOG:
1377       return "dialog";
1378     case META_FRAME_TYPE_MODAL_DIALOG:
1379       return "modal_dialog";
1380     case META_FRAME_TYPE_UTILITY:
1381       return "utility";
1382     case META_FRAME_TYPE_MENU:
1383       return "menu";
1384     case META_FRAME_TYPE_BORDER:
1385       return "border";
1386     case META_FRAME_TYPE_ATTACHED:
1387       return "attached";
1388 #if 0
1389     case META_FRAME_TYPE_TOOLBAR:
1390       return "toolbar";
1391 #endif
1392     case  META_FRAME_TYPE_LAST:
1393       break;
1394     }
1395 
1396   return "<unknown>";
1397 }
1398