diff --git a/ChangeLog b/ChangeLog index e47448ba8..377b988f2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,167 @@ +2006-10-07 Thomas Thurman + + * common.h: Added "above" to the list of flags a frame can have, so + that we know when to mark it as always on top. Added six grab ops, + one to do and one to undo each of the three new titlebar buttons + (shade, above, stick). Added six new button functions, similarly. + (#96229) + + * frame.c (meta_frame_get_flags): If a frame has the WM_STATE_ABOVE X + attribute, set META_FRAME_ABOVE in its flags. + + * frames.c (meta_frames_apply_shapes): Allow variable amounts of + rounding. (#113162) + + * frames.c (show_tip_now, meta_frames_paint_to_drawable, control_rect, + get_control): extend handling of existing buttons to the + 3*2 new kinds of button. (#96229) + + * frames.c (meta_frames_button_press_event): translate clicks on the 3*2 + new kinds of button to the new grab ops. (#96229) + + * frames.c (meta_frames_button_release_event): implement the various + actions for the 3*2 new kinds of button. (#96229) + + * frames.c (meta_frames_update_prelit_control, + meta_frames_motion_notify_event): extend existing motion + notifications for buttons to the 3*2 new kinds of button. (#96229) + + * frames.c (meta_frames_set_window_background): handle specified + background colours and alpha transparency. (#151261) + + * frames.h (MetaFrameControl): New control types for the 3*2 new kinds + of button. (#96229) + + * iconcache.[ch] (meta_read_icons): use theme's fallback icons if a + window has no icon; use metacity's fallback icons only if the theme + does not provide any. (#11363) + + * iconcache.[ch] (meta_invalidate_default_icons (new function)): clear + icon cache on windows using default icons, and update them. (#11363) + + * main.c (main): added \n to error message. + + * prefs.c (button_function_from_string): extend for 3 new button + types. (#96229) + + * prefs.c (button_opposite_function (new function)): return a button + function's inverse (shade -> unshade, etc) (#96229) + + * prefs.c (update_button_layout): allocate space for a button's + inverse, if it has one. (#96229) + + * theme-parser.c (ParseState): add state for fallback icons (#11363) + + * theme-parser.c (ParseInfo): add format_version; remove + menu_icon_* (#114305) + + * theme-parser.c (parse_positive_integer): add lookup for integer + constants (#331356) + + * theme-parser.c (parse_rounding (new function)): parse window + rounding amount (#113162) + + * theme-parser.c (parse_alpha): don't set error if the number can't + be parsed since it'll already be set; change tolerance in comparison + from 1e6 to 1e-6 + + * theme-parser.c (parse_color (new function)): parse colour, including + possible constant lookup. + + * theme-parser.c (parse_toplevel_element): allow defining of various + new kinds of constant; allow + hide_buttons (#121639) and more detailed rounding attributes on + (#113162); allow background and alpha attributes on + ; (#151261) remove support for except as + stub; (#114305) add support for loading stock images (#113465); add + support for . (#11363)) + + * theme-parser.c (parse_draw_op_element): add from and to attribute + for arcs. (#121603) + + * theme-parser.c (parse_style_element): add check for theme version + supporting a button function. (#96229) + + * theme-parser.c (parse_style_set_element): add ability for shaded + windows to be resizable (#114304) + + * theme-parser.c (meta_theme_load): add theme versioning routine. + + * theme.c ( meta_frame_layout_get_borders): return rectangles for + the new 3*2 kinds of button, except where they're + inapplicable. (#96229) + + * theme.c (meta_frame_layout_calc_geometry): don't format buttons on + windows with no buttons (#121639); strip the 3*2 new kinds of button + correctly (#96229); allow variable amounts of rounding (#113162). + + * theme.c (meta_frame_style_new): set alpha to 255 by + default. (#151261) + + * theme.c (meta_frame_style_unref): free colour spec if + allocated. (#151261) + + * theme.c (meta_frame_style_validate): it's only an error not to + include a button if that button is valid in the current + theme. (#96229) + + * theme.c (button_rect): return rectangles for the new 3*2 kinds + of button. (#96229) + + * theme.c (meta_frame_style_set_unref): free differently resizable + shaded styles. (#114304) + + * theme.c (get_style): look up differently resizable styles + for shaded windows. (#114304) + + * theme.c (free_menu_ops (removed function), get_menu_icon + (removed function), meta_theme_draw_menu_icon (removed function), + meta_menu_icon_type_from_string (removed function), + meta_menu_icon_type_to_string (removed function), + meta_theme_free, meta_theme_validate): removed menu icon code. (#114305) + + * theme.c (meta_theme_load_image): add size_of_theme_icons + parameter. (#113465) + + * theme.c (meta_theme_define_color_constant (new function), + meta_theme_lookup_color_constant (new function)): allow + definition of colour constants. (#129747) + + * theme.c (meta_button_type_from_string, meta_button_type_to_string): + add the 3*2 new kinds of button. (#96229) + + * theme.c (meta_theme_earliest_version_with_button (new function)): + return the theme version each button was introduced in. (#96229) + + * theme.h ( MetaFrameLayout): add "hide_buttons" flag (#121639) and + corner radiuses. (#113162) + + * theme.h (MetaFrameGeometry): add rectangles for the 3*2 new + buttons. (#96229) + + * theme.h (MetaButtonType): the 3*2 new buttons. (#96229) + + * theme.h (MetaFrameStyle): add window_background_color and + window_background_alpha so that we can specify background on a + . (#151261) + + * theme.h (MetaFrameStyleSet): shaded_styles gets resize + dimension. (#114304) + + * theme.h (MetaTheme): added format_version, color_constants + hash, (#129747) fallback_icon and fallback_mini_icon, (#11363) + and removed menu_icons. (#114305) + + * theme.h (META_THEME_ALLOWS (new macro)): return whether a theme + supports a given feature. Also, several macros representing + new features in v2. + + * ui.c (meta_ui_set_current_theme)): also invalidate default + icons. (#11363) + + * window.[ch] (meta_window_update_icon_now)): became + non-static. (#11363) + 2006-10-06 Elijah Newren * src/metacity-dialog.c (kill_window_question): Be nice to diff --git a/src/common.h b/src/common.h index e5602d821..7a53aa7d8 100644 --- a/src/common.h +++ b/src/common.h @@ -46,7 +46,8 @@ typedef enum META_FRAME_ALLOWS_SHADE = 1 << 10, META_FRAME_ALLOWS_MOVE = 1 << 11, META_FRAME_FULLSCREEN = 1 << 12, - META_FRAME_IS_FLASHING = 1 << 13 + META_FRAME_IS_FLASHING = 1 << 13, + META_FRAME_ABOVE = 1 << 14 } MetaFrameFlags; typedef enum @@ -131,7 +132,13 @@ typedef enum META_GRAB_OP_CLICKING_MAXIMIZE, META_GRAB_OP_CLICKING_UNMAXIMIZE, META_GRAB_OP_CLICKING_DELETE, - META_GRAB_OP_CLICKING_MENU + META_GRAB_OP_CLICKING_MENU, + META_GRAB_OP_CLICKING_SHADE, + META_GRAB_OP_CLICKING_UNSHADE, + META_GRAB_OP_CLICKING_ABOVE, + META_GRAB_OP_CLICKING_UNABOVE, + META_GRAB_OP_CLICKING_STICK, + META_GRAB_OP_CLICKING_UNSTICK } MetaGrabOp; typedef enum @@ -226,6 +233,12 @@ typedef enum META_BUTTON_FUNCTION_MINIMIZE, META_BUTTON_FUNCTION_MAXIMIZE, META_BUTTON_FUNCTION_CLOSE, + META_BUTTON_FUNCTION_SHADE, + META_BUTTON_FUNCTION_ABOVE, + META_BUTTON_FUNCTION_STICK, + META_BUTTON_FUNCTION_UNSHADE, + META_BUTTON_FUNCTION_UNABOVE, + META_BUTTON_FUNCTION_UNSTICK, META_BUTTON_FUNCTION_LAST } MetaButtonFunction; diff --git a/src/core.c b/src/core.c index 32ce97325..832e0b3ef 100644 --- a/src/core.c +++ b/src/core.c @@ -29,6 +29,17 @@ #include "workspace.h" #include "prefs.h" +/* Looks up the MetaWindow representing the frame of the given X window. + * Used as a helper function by a bunch of the functions below. + * + * FIXME: The functions that use this function throw the result away + * after use. Many of these functions tend to be called in small groups, + * which results in get_window() getting called several times in succession + * with the same parameters. We should profile to see whether this wastes + * much time, and if it does we should look into a generalised + * meta_core_get_window_info() which takes a bunch of pointers to variables + * to put its results in, and only fills in the non-null ones. + */ static MetaWindow * get_window (Display *xdisplay, Window frame_xwindow) @@ -62,6 +73,19 @@ meta_core_get_client_size (Display *xdisplay, *height = window->rect.height; } +gboolean +meta_core_window_has_frame (Display *xdisplay, + Window frame_xwindow) +{ + MetaDisplay *display; + MetaWindow *window; + + display = meta_display_for_x_display (xdisplay); + window = meta_display_lookup_x_window (display, frame_xwindow); + + return window != NULL && window->frame != NULL; +} + gboolean meta_core_titlebar_is_onscreen (Display *xdisplay, Window frame_xwindow) @@ -368,6 +392,24 @@ meta_core_unstick (Display *xdisplay, meta_window_unstick (window); } +void +meta_core_make_above (Display *xdisplay, + Window frame_xwindow) +{ + MetaWindow *window = get_window (xdisplay, frame_xwindow); + + meta_window_make_above (window); +} + +void +meta_core_unmake_above (Display *xdisplay, + Window frame_xwindow) +{ + MetaWindow *window = get_window (xdisplay, frame_xwindow); + + meta_window_unmake_above (window); +} + void meta_core_stick (Display *xdisplay, Window frame_xwindow) @@ -712,3 +754,31 @@ meta_core_increment_event_serial (Display *xdisplay) meta_display_increment_event_serial (display); } +void +meta_invalidate_default_icons (void) +{ + GSList *displays, *windows; + + for (displays = meta_displays_list (); + displays != NULL; + displays = displays->next) + { + + for (windows = meta_display_list_windows (displays->data); + windows != NULL; + windows = windows->next) + { + + MetaWindow *window = (MetaWindow*)windows->data; + + if (window->icon_cache.origin == USING_FALLBACK_ICON) + { + meta_icon_cache_free (&(window->icon_cache)); + meta_window_update_icon_now (window); + } + } + + g_slist_free (windows); + } +} + diff --git a/src/core.h b/src/core.h index 6d6da141e..1f0f1825f 100644 --- a/src/core.h +++ b/src/core.h @@ -37,6 +37,10 @@ void meta_core_get_client_size (Display *xdisplay, gboolean meta_core_titlebar_is_onscreen (Display *xdisplay, Window frame_xwindow); +gboolean meta_core_window_has_frame (Display *xdisplay, + Window frame_xwindow); + + Window meta_core_get_client_xwindow (Display *xdisplay, Window frame_xwindow); @@ -106,6 +110,10 @@ void meta_core_unstick (Display *xdisplay, Window frame_xwindow); void meta_core_stick (Display *xdisplay, Window frame_xwindow); +void meta_core_unmake_above (Display *xdisplay, + Window frame_xwindow); +void meta_core_make_above (Display *xdisplay, + Window frame_xwindow); void meta_core_change_workspace (Display *xdisplay, Window frame_xwindow, int new_workspace); @@ -175,6 +183,8 @@ void meta_core_increment_event_serial (Display *display); int meta_ui_get_last_event_serial (Display *xdisplay); +void meta_invalidate_default_icons (void); + #endif diff --git a/src/frame.c b/src/frame.c index 467e36b36..922443d3c 100644 --- a/src/frame.c +++ b/src/frame.c @@ -279,6 +279,9 @@ meta_frame_get_flags (MetaFrame *frame) if (frame->is_flashing) flags |= META_FRAME_IS_FLASHING; + if (frame->window->wm_state_above) + flags |= META_FRAME_ABOVE; + return flags; } diff --git a/src/frames.c b/src/frames.c index c5827dc12..a2971dec5 100644 --- a/src/frames.c +++ b/src/frames.c @@ -24,6 +24,7 @@ */ #include +#include #include "boxes.h" #include "frames.h" #include "util.h" @@ -757,10 +758,10 @@ meta_frames_apply_shapes (MetaFrames *frames, meta_frames_calc_geometry (frames, frame, &fgeom); - if (!(fgeom.top_left_corner_rounded || - fgeom.top_right_corner_rounded || - fgeom.bottom_left_corner_rounded || - fgeom.bottom_right_corner_rounded || + if (!(fgeom.top_left_corner_rounded_radius != 0 || + fgeom.top_right_corner_rounded_radius != 0 || + fgeom.bottom_left_corner_rounded_radius != 0 || + fgeom.bottom_right_corner_rounded_radius != 0 || window_has_shape)) { if (frame->shape_applied) @@ -785,102 +786,72 @@ meta_frames_apply_shapes (MetaFrames *frames, corners_xregion = XCreateRegion (); - if (fgeom.top_left_corner_rounded) + if (fgeom.top_left_corner_rounded_radius != 0) { - xrect.x = 0; - xrect.y = 0; - xrect.width = 5; - xrect.height = 1; - - XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion); + const int radius = fgeom.top_left_corner_rounded_radius; + int i; - xrect.y = 1; - xrect.width = 3; - XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion); - - xrect.y = 2; - xrect.width = 2; - XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion); - - xrect.y = 3; - xrect.width = 1; - xrect.height = 2; - XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion); + for (i=0; itime); break; + case META_GRAB_OP_CLICKING_SHADE: + if (control == META_FRAME_CONTROL_SHADE) + meta_core_shade (gdk_display, frame->xwindow, event->time); + + meta_core_end_grab_op (gdk_display, event->time); + break; + + case META_GRAB_OP_CLICKING_UNSHADE: + if (control == META_FRAME_CONTROL_UNSHADE) + meta_core_unshade (gdk_display, frame->xwindow, event->time); + + meta_core_end_grab_op (gdk_display, event->time); + break; + + case META_GRAB_OP_CLICKING_ABOVE: + if (control == META_FRAME_CONTROL_ABOVE) + meta_core_make_above (gdk_display, frame->xwindow); + + meta_core_end_grab_op (gdk_display, event->time); + break; + + case META_GRAB_OP_CLICKING_UNABOVE: + if (control == META_FRAME_CONTROL_UNABOVE) + meta_core_unmake_above (gdk_display, frame->xwindow); + + meta_core_end_grab_op (gdk_display, event->time); + break; + + case META_GRAB_OP_CLICKING_STICK: + if (control == META_FRAME_CONTROL_STICK) + meta_core_stick (gdk_display, frame->xwindow); + + meta_core_end_grab_op (gdk_display, event->time); + break; + + case META_GRAB_OP_CLICKING_UNSTICK: + if (control == META_FRAME_CONTROL_UNSTICK) + meta_core_unstick (gdk_display, frame->xwindow); + + meta_core_end_grab_op (gdk_display, event->time); + break; + default: break; } @@ -1590,6 +1645,7 @@ meta_frames_update_prelit_control (MetaFrames *frames, MetaFrameControl old_control; MetaCursor cursor; + meta_verbose ("Updating prelit control from %u to %u\n", frame->prelit_control, control); @@ -1613,6 +1669,18 @@ meta_frames_update_prelit_control (MetaFrames *frames, break; case META_FRAME_CONTROL_UNMAXIMIZE: break; + case META_FRAME_CONTROL_SHADE: + break; + case META_FRAME_CONTROL_UNSHADE: + break; + case META_FRAME_CONTROL_ABOVE: + break; + case META_FRAME_CONTROL_UNABOVE: + break; + case META_FRAME_CONTROL_STICK: + break; + case META_FRAME_CONTROL_UNSTICK: + break; case META_FRAME_CONTROL_RESIZE_SE: cursor = META_CURSOR_SE_RESIZE; break; @@ -1650,6 +1718,12 @@ meta_frames_update_prelit_control (MetaFrames *frames, case META_FRAME_CONTROL_MINIMIZE: case META_FRAME_CONTROL_MAXIMIZE: case META_FRAME_CONTROL_DELETE: + case META_FRAME_CONTROL_SHADE: + case META_FRAME_CONTROL_UNSHADE: + case META_FRAME_CONTROL_ABOVE: + case META_FRAME_CONTROL_UNABOVE: + case META_FRAME_CONTROL_STICK: + case META_FRAME_CONTROL_UNSTICK: case META_FRAME_CONTROL_UNMAXIMIZE: /* leave control set */ break; @@ -1698,6 +1772,12 @@ meta_frames_motion_notify_event (GtkWidget *widget, case META_GRAB_OP_CLICKING_MINIMIZE: case META_GRAB_OP_CLICKING_MAXIMIZE: case META_GRAB_OP_CLICKING_UNMAXIMIZE: + case META_GRAB_OP_CLICKING_SHADE: + case META_GRAB_OP_CLICKING_UNSHADE: + case META_GRAB_OP_CLICKING_ABOVE: + case META_GRAB_OP_CLICKING_UNABOVE: + case META_GRAB_OP_CLICKING_STICK: + case META_GRAB_OP_CLICKING_UNSTICK: { MetaFrameControl control; int x, y; @@ -1716,8 +1796,20 @@ meta_frames_motion_notify_event (GtkWidget *widget, grab_op == META_GRAB_OP_CLICKING_MINIMIZE) || (control == META_FRAME_CONTROL_MAXIMIZE && (grab_op == META_GRAB_OP_CLICKING_MAXIMIZE || - grab_op == META_GRAB_OP_CLICKING_UNMAXIMIZE)))) - control = META_FRAME_CONTROL_NONE; + grab_op == META_GRAB_OP_CLICKING_UNMAXIMIZE)) || + (control == META_FRAME_CONTROL_SHADE && + grab_op == META_GRAB_OP_CLICKING_SHADE) || + (control == META_FRAME_CONTROL_UNSHADE && + grab_op == META_GRAB_OP_CLICKING_UNSHADE) || + (control == META_FRAME_CONTROL_ABOVE && + grab_op == META_GRAB_OP_CLICKING_ABOVE) || + (control == META_FRAME_CONTROL_UNABOVE && + grab_op == META_GRAB_OP_CLICKING_UNABOVE) || + (control == META_FRAME_CONTROL_STICK && + grab_op == META_GRAB_OP_CLICKING_STICK) || + (control == META_FRAME_CONTROL_UNSTICK && + grab_op == META_GRAB_OP_CLICKING_UNSTICK))) + control = META_FRAME_CONTROL_NONE; /* Update prelit control and cursor */ meta_frames_update_prelit_control (frames, frame, control); @@ -2097,6 +2189,42 @@ meta_frames_paint_to_drawable (MetaFrames *frames, else button_states[META_BUTTON_TYPE_MAXIMIZE] = META_BUTTON_STATE_PRELIGHT; break; + case META_FRAME_CONTROL_SHADE: + if (grab_op == META_GRAB_OP_CLICKING_SHADE) + button_states[META_BUTTON_TYPE_SHADE] = META_BUTTON_STATE_PRESSED; + else + button_states[META_BUTTON_TYPE_SHADE] = META_BUTTON_STATE_PRELIGHT; + break; + case META_FRAME_CONTROL_UNSHADE: + if (grab_op == META_GRAB_OP_CLICKING_UNSHADE) + button_states[META_BUTTON_TYPE_UNSHADE] = META_BUTTON_STATE_PRESSED; + else + button_states[META_BUTTON_TYPE_UNSHADE] = META_BUTTON_STATE_PRELIGHT; + break; + case META_FRAME_CONTROL_ABOVE: + if (grab_op == META_GRAB_OP_CLICKING_ABOVE) + button_states[META_BUTTON_TYPE_ABOVE] = META_BUTTON_STATE_PRESSED; + else + button_states[META_BUTTON_TYPE_ABOVE] = META_BUTTON_STATE_PRELIGHT; + break; + case META_FRAME_CONTROL_UNABOVE: + if (grab_op == META_GRAB_OP_CLICKING_UNABOVE) + button_states[META_BUTTON_TYPE_UNABOVE] = META_BUTTON_STATE_PRESSED; + else + button_states[META_BUTTON_TYPE_UNABOVE] = META_BUTTON_STATE_PRELIGHT; + break; + case META_FRAME_CONTROL_STICK: + if (grab_op == META_GRAB_OP_CLICKING_STICK) + button_states[META_BUTTON_TYPE_STICK] = META_BUTTON_STATE_PRESSED; + else + button_states[META_BUTTON_TYPE_STICK] = META_BUTTON_STATE_PRELIGHT; + break; + case META_FRAME_CONTROL_UNSTICK: + if (grab_op == META_GRAB_OP_CLICKING_UNSTICK) + button_states[META_BUTTON_TYPE_UNSTICK] = META_BUTTON_STATE_PRESSED; + else + button_states[META_BUTTON_TYPE_UNSTICK] = META_BUTTON_STATE_PRELIGHT; + break; case META_FRAME_CONTROL_DELETE: if (grab_op == META_GRAB_OP_CLICKING_DELETE) button_states[META_BUTTON_TYPE_CLOSE] = META_BUTTON_STATE_PRESSED; @@ -2182,17 +2310,52 @@ static void meta_frames_set_window_background (MetaFrames *frames, MetaUIFrame *frame) { - gtk_style_set_background (GTK_WIDGET (frames)->style, - frame->window, GTK_STATE_NORMAL); + MetaFrameFlags flags; + MetaFrameType type; + MetaFrameStyle *style; + gboolean frame_exists; -#if 0 - /* This is what we want for transparent background */ - { - col.pixel = 0; - gdk_window_set_background (window, &col); - } -#endif -} + frame_exists = meta_core_window_has_frame (gdk_display, frame->xwindow); + + if (frame_exists) + { + flags = meta_core_get_frame_flags (gdk_display, frame->xwindow); + type = meta_core_get_frame_type (gdk_display, frame->xwindow); + style = meta_theme_get_frame_style (meta_theme_get_current (), + type, flags); + } + + if (frame_exists && style->window_background_color != NULL) + { + GdkColor color; + GdkVisual *visual; + + meta_color_spec_render (style->window_background_color, + GTK_WIDGET (frames), + &color); + + /* Fill in color.pixel */ + + gdk_rgb_find_color (gtk_widget_get_colormap (GTK_WIDGET (frames)), + &color); + + /* Set A in ARGB to window_background_alpha, if we have ARGB */ + + visual = gtk_widget_get_visual (GTK_WIDGET (frames)); + if (visual->depth == 32) /* we have ARGB */ + { + color.pixel = (color.pixel & 0xffffff) & + style->window_background_alpha << 24; + } + + gdk_window_set_background (frame->window, &color); + } + else + { + gtk_style_set_background (GTK_WIDGET (frames)->style, + frame->window, GTK_STATE_NORMAL); + } + } static gboolean meta_frames_enter_notify_event (GtkWidget *widget, @@ -2259,6 +2422,24 @@ control_rect (MetaFrameControl control, case META_FRAME_CONTROL_UNMAXIMIZE: rect = &fgeom->max_rect.visible; break; + case META_FRAME_CONTROL_SHADE: + rect = &fgeom->shade_rect.visible; + break; + case META_FRAME_CONTROL_UNSHADE: + rect = &fgeom->unshade_rect.visible; + break; + case META_FRAME_CONTROL_ABOVE: + rect = &fgeom->above_rect.visible; + break; + case META_FRAME_CONTROL_UNABOVE: + rect = &fgeom->unabove_rect.visible; + break; + case META_FRAME_CONTROL_STICK: + rect = &fgeom->stick_rect.visible; + break; + case META_FRAME_CONTROL_UNSTICK: + rect = &fgeom->unstick_rect.visible; + break; case META_FRAME_CONTROL_RESIZE_SE: break; case META_FRAME_CONTROL_RESIZE_S: @@ -2336,6 +2517,36 @@ get_control (MetaFrames *frames, return META_FRAME_CONTROL_MAXIMIZE; } + if (POINT_IN_RECT (x, y, fgeom.shade_rect.clickable)) + { + return META_FRAME_CONTROL_SHADE; + } + + if (POINT_IN_RECT (x, y, fgeom.unshade_rect.clickable)) + { + return META_FRAME_CONTROL_UNSHADE; + } + + if (POINT_IN_RECT (x, y, fgeom.above_rect.clickable)) + { + return META_FRAME_CONTROL_ABOVE; + } + + if (POINT_IN_RECT (x, y, fgeom.unabove_rect.clickable)) + { + return META_FRAME_CONTROL_UNABOVE; + } + + if (POINT_IN_RECT (x, y, fgeom.stick_rect.clickable)) + { + return META_FRAME_CONTROL_STICK; + } + + if (POINT_IN_RECT (x, y, fgeom.unstick_rect.clickable)) + { + return META_FRAME_CONTROL_UNSTICK; + } + /* South resize always has priority over north resize, * in case of overlap. */ diff --git a/src/frames.h b/src/frames.h index 1d674eaba..c4ad8bfb0 100644 --- a/src/frames.h +++ b/src/frames.h @@ -38,6 +38,12 @@ typedef enum META_FRAME_CONTROL_MINIMIZE, META_FRAME_CONTROL_MAXIMIZE, META_FRAME_CONTROL_UNMAXIMIZE, + META_FRAME_CONTROL_SHADE, + META_FRAME_CONTROL_UNSHADE, + META_FRAME_CONTROL_ABOVE, + META_FRAME_CONTROL_UNABOVE, + META_FRAME_CONTROL_STICK, + META_FRAME_CONTROL_UNSTICK, META_FRAME_CONTROL_RESIZE_SE, META_FRAME_CONTROL_RESIZE_S, META_FRAME_CONTROL_RESIZE_SW, diff --git a/src/iconcache.c b/src/iconcache.c index 5c8ac19ff..939a08959 100644 --- a/src/iconcache.c +++ b/src/iconcache.c @@ -25,6 +25,7 @@ #include "iconcache.h" #include "ui.h" #include "errors.h" +#include "theme.h" #include @@ -499,19 +500,6 @@ get_kwm_win_icon (MetaDisplay *display, return; } -typedef enum -{ - /* These MUST be in ascending order of preference; - * i.e. if we get _NET_WM_ICON and already have - * WM_HINTS, we prefer _NET_WM_ICON - */ - USING_NO_ICON, - USING_FALLBACK_ICON, - USING_KWM_WIN_ICON, - USING_WM_HINTS, - USING_NET_WM_ICON -} IconOrigin; - void meta_icon_cache_init (MetaIconCache *icon_cache) { @@ -830,13 +818,23 @@ meta_read_icons (MetaScreen *screen, if (icon_cache->want_fallback && icon_cache->origin < USING_FALLBACK_ICON) { - get_fallback_icons (screen, - iconp, - ideal_width, - ideal_height, - mini_iconp, - ideal_mini_width, - ideal_mini_height); + MetaTheme *theme = meta_theme_get_current (); + + if (theme->fallback_icon == NULL || theme->fallback_mini_icon == NULL) + { + get_fallback_icons (screen, + iconp, + ideal_width, + ideal_height, + mini_iconp, + ideal_mini_width, + ideal_mini_height); + } + + if (theme->fallback_icon != NULL) + *iconp = theme->fallback_icon; + if (theme->fallback_mini_icon != NULL) + *mini_iconp = theme->fallback_mini_icon; replace_cache (icon_cache, USING_FALLBACK_ICON, *iconp, *mini_iconp); diff --git a/src/iconcache.h b/src/iconcache.h index 1cb73a65d..7a2ad7f3f 100644 --- a/src/iconcache.h +++ b/src/iconcache.h @@ -28,6 +28,19 @@ typedef struct _MetaIconCache MetaIconCache; +typedef enum +{ + /* These MUST be in ascending order of preference; + * i.e. if we get _NET_WM_ICON and already have + * WM_HINTS, we prefer _NET_WM_ICON + */ + USING_NO_ICON, + USING_FALLBACK_ICON, + USING_KWM_WIN_ICON, + USING_WM_HINTS, + USING_NET_WM_ICON +} IconOrigin; + struct _MetaIconCache { int origin; diff --git a/src/main.c b/src/main.c index 49c30b8f8..9b6c4a5c0 100644 --- a/src/main.c +++ b/src/main.c @@ -365,7 +365,7 @@ main (int argc, char **argv) } if (!meta_ui_have_a_theme ()) - meta_fatal (_("Could not find a theme! Be sure %s exists and contains the usual themes."), + meta_fatal (_("Could not find a theme! Be sure %s exists and contains the usual themes.\n"), METACITY_DATADIR"/themes"); /* Connect to SM as late as possible - but before managing display, diff --git a/src/prefs.c b/src/prefs.c index c50049114..3690a7984 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -1381,10 +1381,42 @@ button_function_from_string (const char *str) return META_BUTTON_FUNCTION_MAXIMIZE; else if (strcmp (str, "close") == 0) return META_BUTTON_FUNCTION_CLOSE; - else + else if (strcmp (str, "shade") == 0) + return META_BUTTON_FUNCTION_SHADE; + else if (strcmp (str, "above") == 0) + return META_BUTTON_FUNCTION_ABOVE; + else if (strcmp (str, "stick") == 0) + return META_BUTTON_FUNCTION_STICK; + else + /* don't know; give up */ return META_BUTTON_FUNCTION_LAST; } +static MetaButtonFunction +button_opposite_function (MetaButtonFunction ofwhat) +{ + switch (ofwhat) + { + case META_BUTTON_FUNCTION_SHADE: + return META_BUTTON_FUNCTION_UNSHADE; + case META_BUTTON_FUNCTION_UNSHADE: + return META_BUTTON_FUNCTION_SHADE; + + case META_BUTTON_FUNCTION_ABOVE: + return META_BUTTON_FUNCTION_UNABOVE; + case META_BUTTON_FUNCTION_UNABOVE: + return META_BUTTON_FUNCTION_ABOVE; + + case META_BUTTON_FUNCTION_STICK: + return META_BUTTON_FUNCTION_UNSTICK; + case META_BUTTON_FUNCTION_UNSTICK: + return META_BUTTON_FUNCTION_STICK; + + default: + return META_BUTTON_FUNCTION_LAST; + } +} + static gboolean update_button_layout (const char *value) { @@ -1435,6 +1467,11 @@ update_button_layout (const char *value) new_layout.left_buttons[i] = f; used[f] = TRUE; ++i; + + f = button_opposite_function (f); + + if (f != META_BUTTON_FUNCTION_LAST) + new_layout.left_buttons[i++] = f; } else { @@ -1473,7 +1510,12 @@ update_button_layout (const char *value) new_layout.right_buttons[i] = f; used[f] = TRUE; ++i; - } + + f = button_opposite_function (f); + + if (f != META_BUTTON_FUNCTION_LAST) + new_layout.right_buttons[i++] = f; + } else { meta_topic (META_DEBUG_PREFS, "Ignoring unknown or already-used button name \"%s\"\n", diff --git a/src/theme-parser.c b/src/theme-parser.c index 1013c2b8e..68db8e79e 100644 --- a/src/theme-parser.c +++ b/src/theme-parser.c @@ -73,7 +73,9 @@ typedef enum /* assigning style sets to windows */ STATE_WINDOW, /* and menu icons */ - STATE_MENU_ICON + STATE_MENU_ICON, + /* fallback icons */ + STATE_FALLBACK } ParseState; typedef struct @@ -84,6 +86,7 @@ typedef struct char *theme_file; /* theme filename */ char *theme_dir; /* dir the theme is inside */ MetaTheme *theme; /* theme being parsed */ + guint format_version; /* version of format of theme file */ char *name; /* name of named thing being parsed */ MetaFrameLayout *layout; /* layout being parsed if any */ MetaDrawOpList *op_list; /* op list being parsed if any */ @@ -93,8 +96,6 @@ typedef struct MetaFramePiece piece; /* position of piece being parsed */ MetaButtonType button_type; /* type of button/menuitem being parsed */ MetaButtonState button_state; /* state of button being parsed */ - MetaMenuIconType menu_icon_type; /* type of menu icon being parsed */ - GtkStateType menu_icon_state; /* state of menu icon being parsed */ } ParseInfo; static void set_error (GError **err, @@ -451,33 +452,48 @@ static gboolean parse_positive_integer (const char *str, int *val, GMarkupParseContext *context, + MetaTheme *theme, GError **error) { char *end; long l; + int j; *val = 0; end = NULL; - l = strtol (str, &end, 10); + /* Is str a constant? */ - if (end == NULL || end == str) + if (META_THEME_ALLOWS (theme, META_THEME_UBIQUITOUS_CONSTANTS) && + meta_theme_lookup_int_constant (theme, str, &j)) { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Could not parse \"%s\" as an integer"), - str); - return FALSE; + /* Yes. */ + l = j; } - - if (*end != '\0') + else { - set_error (error, context, G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("Did not understand trailing characters \"%s\" in string \"%s\""), - end, str); - return FALSE; + /* No. Let's try parsing it instead. */ + + l = strtol (str, &end, 10); + + if (end == NULL || end == str) + { + set_error (error, context, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Could not parse \"%s\" as an integer"), + str); + return FALSE; + } + + if (*end != '\0') + { + set_error (error, context, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Did not understand trailing characters \"%s\" in string \"%s\""), + end, str); + return FALSE; + } } if (l < 0) @@ -559,6 +575,41 @@ parse_boolean (const char *str, return TRUE; } +static gboolean +parse_rounding (const char *str, + guint *val, + GMarkupParseContext *context, + MetaTheme *theme, + GError **error) +{ + if (strcmp ("true", str) == 0) + *val = 5; /* historical "true" value */ + else if (strcmp ("false", str) == 0) + *val = 0; + else + { + int tmp; + gboolean result; + if (!META_THEME_ALLOWS (theme, META_THEME_VARIED_ROUND_CORNERS)) + { + /* Not known in this version, so bail. */ + set_error (error, context, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Boolean values must be \"true\" or \"false\" not \"%s\""), + str); + return FALSE; + } + + result = parse_positive_integer (str, &tmp, context, theme, error); + + *val = tmp; + + return result; + } + + return TRUE; +} + static gboolean parse_angle (const char *str, double *val, @@ -624,17 +675,14 @@ parse_alpha (const char *str, if (!parse_double (split[i], &v, context, error)) { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Could not parse \"%s\" as a floating point number"), - split[i]); - + /* clear up, but don't set error: it was set by parse_double */ g_strfreev (split); meta_alpha_gradient_spec_free (spec); return FALSE; } - if (v < (0.0 - 1e6) || v > (1.0 + 1e6)) + if (v < (0.0 - 1e-6) || v > (1.0 + 1e-6)) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, _("Alpha must be between 0.0 (invisible) and 1.0 (fully opaque), was %g\n"), @@ -658,6 +706,25 @@ parse_alpha (const char *str, return TRUE; } +static MetaColorSpec* +parse_color (MetaTheme *theme, + const char *str, + GError **err) +{ + char* referent; + + if (META_THEME_ALLOWS (theme, META_THEME_COLOR_CONSTANTS) && + meta_theme_lookup_color_constant (theme, str, &referent)) + { + if (referent) + return meta_color_spec_new_from_string (referent, err); + + /* no need to free referent: it's a pointer into the actual hash table */ + } + + return meta_color_spec_new_from_string (str, err); +} + static gboolean parse_title_scale (const char *str, double *val, @@ -716,8 +783,8 @@ parse_toplevel_element (GMarkupParseContext *context, { const char *name; const char *value; - int ival; - double dval; + int ival = 0; + double dval = 0.0; if (!locate_attributes (context, element_name, attribute_names, attribute_values, error, @@ -741,11 +808,9 @@ parse_toplevel_element (GMarkupParseContext *context, return; } - if (strchr (value, '.')) + if (strchr (value, '.') && parse_double (value, &dval, context, error)) { - dval = 0.0; - if (!parse_double (value, &dval, context, error)) - return; + g_clear_error (error); if (!meta_theme_define_float_constant (info->theme, name, @@ -756,11 +821,9 @@ parse_toplevel_element (GMarkupParseContext *context, return; } } - else + else if (parse_positive_integer (value, &ival, context, info->theme, error)) { - ival = 0; - if (!parse_positive_integer (value, &ival, context, error)) - return; + g_clear_error (error); if (!meta_theme_define_int_constant (info->theme, name, @@ -771,6 +834,19 @@ parse_toplevel_element (GMarkupParseContext *context, return; } } + else + { + g_clear_error (error); + + if (!meta_theme_define_color_constant (info->theme, + name, + value, + error)) + { + add_context_to_error (error, context); + return; + } + } push_state (info, STATE_CONSTANT); } @@ -784,11 +860,13 @@ parse_toplevel_element (GMarkupParseContext *context, const char *rounded_top_right = NULL; const char *rounded_bottom_left = NULL; const char *rounded_bottom_right = NULL; + const char *hide_buttons = NULL; gboolean has_title_val; - gboolean rounded_top_left_val; - gboolean rounded_top_right_val; - gboolean rounded_bottom_left_val; - gboolean rounded_bottom_right_val; + guint rounded_top_left_val; + guint rounded_top_right_val; + guint rounded_bottom_left_val; + guint rounded_bottom_right_val; + gboolean hide_buttons_val; double title_scale_val; MetaFrameLayout *parent_layout; @@ -800,6 +878,7 @@ parse_toplevel_element (GMarkupParseContext *context, "rounded_top_right", &rounded_top_right, "rounded_bottom_left", &rounded_bottom_left, "rounded_bottom_right", &rounded_bottom_right, + "hide_buttons", &hide_buttons, NULL)) return; @@ -815,18 +894,22 @@ parse_toplevel_element (GMarkupParseContext *context, if (has_title && !parse_boolean (has_title, &has_title_val, context, error)) return; - rounded_top_left_val = FALSE; - rounded_top_right_val = FALSE; - rounded_bottom_left_val = FALSE; - rounded_bottom_right_val = FALSE; + hide_buttons_val = FALSE; + if (hide_buttons && !parse_boolean (hide_buttons, &hide_buttons_val, context, error)) + return; - if (rounded_top_left && !parse_boolean (rounded_top_left, &rounded_top_left_val, context, error)) + rounded_top_left_val = 0; + rounded_top_right_val = 0; + rounded_bottom_left_val = 0; + rounded_bottom_right_val = 0; + + if (rounded_top_left && !parse_rounding (rounded_top_left, &rounded_top_left_val, context, info->theme, error)) return; - if (rounded_top_right && !parse_boolean (rounded_top_right, &rounded_top_right_val, context, error)) + if (rounded_top_right && !parse_rounding (rounded_top_right, &rounded_top_right_val, context, info->theme, error)) return; - if (rounded_bottom_left && !parse_boolean (rounded_bottom_left, &rounded_bottom_left_val, context, error)) + if (rounded_bottom_left && !parse_rounding (rounded_bottom_left, &rounded_bottom_left_val, context, info->theme, error)) return; - if (rounded_bottom_right && !parse_boolean (rounded_bottom_right, &rounded_bottom_right_val, context, error)) + if (rounded_bottom_right && !parse_rounding (rounded_bottom_right, &rounded_bottom_right_val, context, info->theme, error)) return; title_scale_val = 1.0; @@ -864,20 +947,23 @@ parse_toplevel_element (GMarkupParseContext *context, if (has_title) /* only if explicit, otherwise inherit */ info->layout->has_title = has_title_val; + if (META_THEME_ALLOWS (info->theme, META_THEME_HIDDEN_BUTTONS) && hide_buttons_val) + info->layout->hide_buttons = hide_buttons_val; + if (title_scale) info->layout->title_scale = title_scale_val; if (rounded_top_left) - info->layout->top_left_corner_rounded = rounded_top_left_val; + info->layout->top_left_corner_rounded_radius = rounded_top_left_val; if (rounded_top_right) - info->layout->top_right_corner_rounded = rounded_top_right_val; + info->layout->top_right_corner_rounded_radius = rounded_top_right_val; if (rounded_bottom_left) - info->layout->bottom_left_corner_rounded = rounded_bottom_left_val; + info->layout->bottom_left_corner_rounded_radius = rounded_bottom_left_val; if (rounded_bottom_right) - info->layout->bottom_right_corner_rounded = rounded_bottom_right_val; + info->layout->bottom_right_corner_rounded_radius = rounded_bottom_right_val; meta_theme_insert_layout (info->theme, name, info->layout); @@ -921,6 +1007,8 @@ parse_toplevel_element (GMarkupParseContext *context, const char *name = NULL; const char *parent = NULL; const char *geometry = NULL; + const char *background = NULL; + const char *alpha = NULL; MetaFrameStyle *parent_style; MetaFrameLayout *layout; @@ -928,6 +1016,8 @@ parse_toplevel_element (GMarkupParseContext *context, error, "name", &name, "parent", &parent, "geometry", &geometry, + "background", &background, + "alpha", &alpha, NULL)) return; @@ -992,6 +1082,40 @@ parse_toplevel_element (GMarkupParseContext *context, meta_frame_layout_ref (layout); info->style->layout = layout; + if (background != NULL && META_THEME_ALLOWS (info->theme, META_THEME_FRAME_BACKGROUNDS)) + { + info->style->window_background_color = meta_color_spec_new_from_string (background, error); + if (!info->style->window_background_color) + return; + + if (alpha != NULL) + { + + gboolean success; + MetaAlphaGradientSpec *alpha_vector; + + g_clear_error (error); + /* fortunately, we already have a routine to parse alpha values, + * though it produces a vector of them, which is a superset of + * what we want. + */ + success = parse_alpha (alpha, &alpha_vector, context, error); + if (!success) + return; + + /* alpha_vector->alphas must contain at least one element */ + info->style->window_background_alpha = alpha_vector->alphas[0]; + + meta_alpha_gradient_spec_free (alpha_vector); + } + } + else if (alpha != NULL) + { + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + _("You must specify a background for an alpha value to be meaningful")); + return; + } + meta_theme_insert_style (info->theme, name, info->style); push_state (info, STATE_FRAME_STYLE); @@ -1110,84 +1234,52 @@ parse_toplevel_element (GMarkupParseContext *context, } else if (ELEMENT_IS ("menu_icon")) { - const char *function = NULL; - const char *state = NULL; - const char *draw_ops = NULL; + /* Not supported any more, but we have to parse it if they include it, + * for backwards compatibility. + */ + g_assert (info->op_list == NULL); + + push_state (info, STATE_MENU_ICON); + } + else if (ELEMENT_IS ("fallback")) + { + const char *icon = NULL; + const char *mini_icon = NULL; if (!locate_attributes (context, element_name, attribute_names, attribute_values, error, - "function", &function, - "state", &state, - "draw_ops", &draw_ops, + "icon", &icon, + "mini_icon", &mini_icon, NULL)) return; - if (function == NULL) + if (icon) { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No \"%s\" attribute on <%s> element"), - "function", element_name); - return; - } - - if (state == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No \"%s\" attribute on <%s> element"), - "state", element_name); - return; - } - - info->menu_icon_type = meta_menu_icon_type_from_string (function); - if (info->menu_icon_type == META_BUTTON_TYPE_LAST) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Unknown function \"%s\" for menu icon"), - function); - return; - } - - info->menu_icon_state = meta_gtk_state_from_string (state); - if (((int) info->menu_icon_state) == -1) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Unknown state \"%s\" for menu icon"), - state); - return; - } - - if (info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] != NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Theme already has a menu icon for function %s state %s"), - function, state); - return; - } - - g_assert (info->op_list == NULL); - - if (draw_ops) - { - MetaDrawOpList *op_list; - - op_list = meta_theme_lookup_draw_op_list (info->theme, - draw_ops); - - if (op_list == NULL) + if (info->theme->fallback_icon != NULL) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No with the name \"%s\" has been defined"), - draw_ops); + _("Theme already has a fallback icon")); return; } - meta_draw_op_list_ref (op_list); - info->op_list = op_list; + info->theme->fallback_icon = meta_theme_load_image(info->theme, icon, 64, error); } - push_state (info, STATE_MENU_ICON); + if (mini_icon) + { + if (info->theme->fallback_mini_icon != NULL) + { + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + _("Theme already has a fallback mini_icon")); + return; + } + + info->theme->fallback_mini_icon = meta_theme_load_image(info->theme, mini_icon, 16, error); + } + + push_state (info, STATE_FALLBACK); } - else + else { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, @@ -1293,7 +1385,7 @@ parse_distance (GMarkupParseContext *context, } val = 0; - if (!parse_positive_integer (value, &val, context, error)) + if (!parse_positive_integer (value, &val, context, info->theme, error)) return; g_assert (val >= 0); /* yeah, "non-negative" not "positive" get over it */ @@ -1471,19 +1563,19 @@ parse_border (GMarkupParseContext *context, } top_val = 0; - if (!parse_positive_integer (top, &top_val, context, error)) + if (!parse_positive_integer (top, &top_val, context, info->theme, error)) return; bottom_val = 0; - if (!parse_positive_integer (bottom, &bottom_val, context, error)) + if (!parse_positive_integer (bottom, &bottom_val, context, info->theme, error)) return; left_val = 0; - if (!parse_positive_integer (left, &left_val, context, error)) + if (!parse_positive_integer (left, &left_val, context, info->theme, error)) return; right_val = 0; - if (!parse_positive_integer (right, &right_val, context, error)) + if (!parse_positive_integer (right, &right_val, context, info->theme, error)) return; g_assert (info->layout); @@ -1697,23 +1789,23 @@ parse_draw_op_element (GMarkupParseContext *context, dash_on_val = 0; if (dash_on_length && - !parse_positive_integer (dash_on_length, &dash_on_val, context, error)) + !parse_positive_integer (dash_on_length, &dash_on_val, context, info->theme, error)) return; dash_off_val = 0; if (dash_off_length && - !parse_positive_integer (dash_off_length, &dash_off_val, context, error)) + !parse_positive_integer (dash_off_length, &dash_off_val, context, info->theme, error)) return; width_val = 0; if (width && - !parse_positive_integer (width, &width_val, context, error)) + !parse_positive_integer (width, &width_val, context, info->theme, error)) return; /* Check last so we don't have to free it when other * stuff fails */ - color_spec = meta_color_spec_new_from_string (color, error); + color_spec = parse_color (info->theme, color, error); if (color_spec == NULL) { add_context_to_error (error, context); @@ -1812,7 +1904,7 @@ parse_draw_op_element (GMarkupParseContext *context, /* Check last so we don't have to free it when other * stuff fails */ - color_spec = meta_color_spec_new_from_string (color, error); + color_spec = parse_color (info->theme, color, error); if (color_spec == NULL) { add_context_to_error (error, context); @@ -1845,6 +1937,8 @@ parse_draw_op_element (GMarkupParseContext *context, const char *filled; const char *start_angle; const char *extent_angle; + const char *from; + const char *to; gboolean filled_val; double start_angle_val; double extent_angle_val; @@ -1858,6 +1952,8 @@ parse_draw_op_element (GMarkupParseContext *context, "filled", &filled, "start_angle", &start_angle, "extent_angle", &extent_angle, + "from", &from, + "to", &to, NULL)) return; @@ -1896,20 +1992,40 @@ parse_draw_op_element (GMarkupParseContext *context, return; } - if (start_angle == NULL) + if (META_THEME_ALLOWS (info->theme, META_THEME_DEGREES_IN_ARCS) ) { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No \"start_angle\" attribute on element <%s>"), element_name); - return; + if (start_angle == NULL && from == NULL) + { + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + _("No \"start_angle\" or \"from\" attribute on element <%s>"), element_name); + return; + } + + if (extent_angle == NULL && to == NULL) + { + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + _("No \"extent_angle\" or \"to\" attribute on element <%s>"), element_name); + return; + } + } + else + { + if (start_angle == NULL) + { + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + _("No \"start_angle\" attribute on element <%s>"), element_name); + return; + } + + if (extent_angle == NULL) + { + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + _("No \"extent_angle\" attribute on element <%s>"), element_name); + return; + } } - if (extent_angle == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No \"extent_angle\" attribute on element <%s>"), element_name); - return; - } - + if (!check_expression (x, FALSE, info->theme, context, error)) return; @@ -1922,12 +2038,32 @@ parse_draw_op_element (GMarkupParseContext *context, if (!check_expression (height, FALSE, info->theme, context, error)) return; - if (!parse_angle (start_angle, &start_angle_val, context, error)) - return; - - if (!parse_angle (extent_angle, &extent_angle_val, context, error)) - return; + if (start_angle == NULL) + { + if (!parse_angle (from, &start_angle_val, context, error)) + return; + + start_angle_val = (180-start_angle_val)/360.0; + } + else + { + if (!parse_angle (start_angle, &start_angle_val, context, error)) + return; + } + if (extent_angle == NULL) + { + if (!parse_angle (to, &extent_angle_val, context, error)) + return; + + extent_angle_val = ((180-extent_angle_val)/360.0) - start_angle_val; + } + else + { + if (!parse_angle (extent_angle, &extent_angle_val, context, error)) + return; + } + filled_val = FALSE; if (filled && !parse_boolean (filled, &filled_val, context, error)) return; @@ -1935,7 +2071,7 @@ parse_draw_op_element (GMarkupParseContext *context, /* Check last so we don't have to free it when other * stuff fails */ - color_spec = meta_color_spec_new_from_string (color, error); + color_spec = parse_color (info->theme, color, error); if (color_spec == NULL) { add_context_to_error (error, context); @@ -2109,7 +2245,7 @@ parse_draw_op_element (GMarkupParseContext *context, /* Check last so we don't have to free it when other * stuff fails */ - color_spec = meta_color_spec_new_from_string (color, error); + color_spec = parse_color (info->theme, color, error); if (color_spec == NULL) { if (alpha_spec) @@ -2254,7 +2390,7 @@ parse_draw_op_element (GMarkupParseContext *context, "x", &x, "y", &y, "width", &width, "height", &height, "alpha", &alpha, "filename", &filename, - "colorize", &colorize, + "colorize", &colorize, "fill_type", &fill_type, NULL)) return; @@ -2321,9 +2457,12 @@ parse_draw_op_element (GMarkupParseContext *context, } /* Check last so we don't have to free it when other - * stuff fails + * stuff fails. + * + * If it's a theme image, ask for it at 64px, which is + * the largest possible. We scale it anyway. */ - pixbuf = meta_theme_load_image (info->theme, filename, error); + pixbuf = meta_theme_load_image (info->theme, filename, 64, error); if (pixbuf == NULL) { @@ -2333,7 +2472,7 @@ parse_draw_op_element (GMarkupParseContext *context, if (colorize) { - colorize_spec = meta_color_spec_new_from_string (colorize, error); + colorize_spec = parse_color (info->theme, colorize, error); if (colorize_spec == NULL) { @@ -2892,7 +3031,7 @@ parse_draw_op_element (GMarkupParseContext *context, /* Check last so we don't have to free it when other * stuff fails */ - color_spec = meta_color_spec_new_from_string (color, error); + color_spec = parse_color (info->theme, color, error); if (color_spec == NULL) { add_context_to_error (error, context); @@ -3154,7 +3293,7 @@ parse_gradient_element (GMarkupParseContext *context, return; } - color_spec = meta_color_spec_new_from_string (value, error); + color_spec = parse_color (info->theme, value, error); if (color_spec == NULL) { add_context_to_error (error, context); @@ -3281,7 +3420,7 @@ parse_style_element (GMarkupParseContext *context, return; } - info->button_type = meta_button_type_from_string (function); + info->button_type = meta_button_type_from_string (function, info->theme); if (info->button_type == META_BUTTON_TYPE_LAST) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, @@ -3290,6 +3429,18 @@ parse_style_element (GMarkupParseContext *context, return; } + if (meta_theme_earliest_version_with_button (info->button_type) > + info->theme->format_version) + { + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + _("Button function \"%s\" does not exist in this version (%d, need %d)"), + function, + info->theme->format_version, + meta_theme_earliest_version_with_button (info->button_type) + ); + return; + } + info->button_state = meta_button_state_from_string (state); if (info->button_state == META_BUTTON_STATE_LAST) { @@ -3421,8 +3572,9 @@ parse_style_set_element (GMarkupParseContext *context, return; } - if (frame_state == META_FRAME_STATE_NORMAL) + switch (frame_state) { + case META_FRAME_STATE_NORMAL: if (resize == NULL) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, @@ -3440,13 +3592,51 @@ parse_style_set_element (GMarkupParseContext *context, focus); return; } - } - else - { + + break; + + case META_FRAME_STATE_SHADED: + if (META_THEME_ALLOWS (info->theme, META_THEME_UNRESIZABLE_SHADED_STYLES)) + { + if (resize == NULL) + /* In state="normal" we would complain here. But instead we accept + * not having a resize attribute and default to resize="both", since + * that most closely mimics what we did in v1, and thus people can + * upgrade a theme to v2 without as much hassle. + */ + frame_resize = META_FRAME_RESIZE_BOTH; + else + { + frame_resize = meta_frame_resize_from_string (resize); + if (frame_resize == META_FRAME_RESIZE_LAST) + { + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + _("\"%s\" is not a valid value for resize attribute"), + focus); + return; + } + } + } + else /* v1 theme */ + { + if (resize != NULL) + { + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + _("Should not have \"resize\" attribute on <%s> element for maximized/shaded states"), + element_name); + return; + } + + /* resize="both" is equivalent to the old behaviour */ + frame_resize = META_FRAME_RESIZE_BOTH; + } + break; + + default: if (resize != NULL) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Should not have \"resize\" attribute on <%s> element for maximized/shaded states"), + _("Should not have \"resize\" attribute on <%s> element for maximized states"), element_name); return; } @@ -3479,15 +3669,15 @@ parse_style_set_element (GMarkupParseContext *context, info->style_set->maximized_styles[frame_focus] = frame_style; break; case META_FRAME_STATE_SHADED: - if (info->style_set->shaded_styles[frame_focus]) + if (info->style_set->shaded_styles[frame_resize][frame_focus]) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Style has already been specified for state %s focus %s"), - state, focus); + _("Style has already been specified for state %s resize %s focus %s"), + state, resize, focus); return; } meta_frame_style_ref (frame_style); - info->style_set->shaded_styles[frame_focus] = frame_style; + info->style_set->shaded_styles[frame_resize][frame_focus] = frame_style; break; case META_FRAME_STATE_MAXIMIZED_AND_SHADED: if (info->style_set->maximized_and_shaded_styles[frame_focus]) @@ -3650,6 +3840,7 @@ start_element_handler (GMarkupParseContext *context, info->theme->name = g_strdup (info->theme_name); info->theme->filename = g_strdup (info->theme_file); info->theme->dirname = g_strdup (info->theme_dir); + info->theme->format_version = info->format_version; push_state (info, STATE_THEME); } @@ -3762,6 +3953,11 @@ start_element_handler (GMarkupParseContext *context, _("Element <%s> is not allowed inside a <%s> element"), element_name, "window"); break; + case STATE_FALLBACK: + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + _("Element <%s> is not allowed inside a <%s> element"), + element_name, "fallback"); + break; } } @@ -3962,6 +4158,7 @@ end_element_handler (GMarkupParseContext *context, g_assert (info->style); if (!meta_frame_style_validate (info->style, + info->theme->format_version, error)) { add_context_to_error (error, context); @@ -4007,16 +4204,9 @@ end_element_handler (GMarkupParseContext *context, break; case STATE_MENU_ICON: g_assert (info->theme); - if (info->op_list == NULL) + if (info->op_list != NULL) { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No draw_ops provided for menu icon")); - } - else - { - g_assert (info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] == NULL); - info->theme->menu_icons[info->menu_icon_type][info->menu_icon_state] = - info->op_list; + meta_draw_op_list_unref (info->op_list); info->op_list = NULL; } pop_state (info); @@ -4047,6 +4237,10 @@ end_element_handler (GMarkupParseContext *context, pop_state (info); g_assert (peek_state (info) == STATE_THEME); break; + case STATE_FALLBACK: + pop_state (info); + g_assert (peek_state (info) == STATE_THEME); + break; } } @@ -4239,19 +4433,26 @@ text_handler (GMarkupParseContext *context, case STATE_WINDOW: NO_TEXT ("window"); break; + case STATE_FALLBACK: + NO_TEXT ("fallback"); + break; } } -/* We change the filename when we break the format, - * so themes can work with various metacity versions - * (note, this is obsolete now because we are versioning - * the directory this file is inside, so oh well) +/* We were intending to put the version number + * in the subdirectory name, but we ended up + * using the filename instead. The "-1" survives + * as a fossil. */ -#define THEME_FILENAME "metacity-theme-1.xml" - -/* now this is versioned, /usr/share/themes/NAME/THEME_SUBDIR/THEME_FILENAME */ #define THEME_SUBDIR "metacity-1" +/* Highest version of the theme format to + * look out for. + */ +#define THEME_VERSION 2 + +#define METACITY_THEME_FILENAME_FORMAT "metacity-theme-%d.xml" + MetaTheme* meta_theme_load (const char *theme_name, GError **err) @@ -4264,6 +4465,7 @@ meta_theme_load (const char *theme_name, char *theme_file; char *theme_dir; MetaTheme *retval; + guint version; text = NULL; length = 0; @@ -4275,11 +4477,14 @@ meta_theme_load (const char *theme_name, if (meta_is_debugging ()) { + gchar *theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT, + THEME_VERSION); + /* Try in themes in our source tree */ theme_dir = g_build_filename ("./themes", theme_name, NULL); theme_file = g_build_filename (theme_dir, - THEME_FILENAME, + theme_filename, NULL); error = NULL; @@ -4295,12 +4500,19 @@ meta_theme_load (const char *theme_name, g_free (theme_file); theme_file = NULL; } + version = THEME_VERSION; + + g_free (theme_filename); } - /* We try in home dir, then system dir for themes */ - - if (text == NULL) + /* We try all supported versions from current to oldest */ + for (version = THEME_VERSION; (version > 0) && (text == NULL); version--) { + gchar *theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT, + version); + + /* We try in home dir, then system dir for themes */ + theme_dir = g_build_filename (g_get_home_dir (), ".themes", theme_name, @@ -4308,7 +4520,7 @@ meta_theme_load (const char *theme_name, NULL); theme_file = g_build_filename (theme_dir, - THEME_FILENAME, + theme_filename, NULL); error = NULL; @@ -4324,45 +4536,57 @@ meta_theme_load (const char *theme_name, g_free (theme_file); theme_file = NULL; } + + if (text == NULL) + { + theme_dir = g_build_filename (METACITY_DATADIR, + "themes", + theme_name, + THEME_SUBDIR, + NULL); + + theme_file = g_build_filename (theme_dir, + theme_filename, + NULL); + + error = NULL; + if (!g_file_get_contents (theme_file, + &text, + &length, + &error)) + { + meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n", + theme_file, error->message); + g_error_free (error); + g_free (theme_dir); + g_free (theme_file); + theme_file = NULL; + } + } + + g_free (theme_filename); } if (text == NULL) { - theme_dir = g_build_filename (METACITY_DATADIR, - "themes", - theme_name, - THEME_SUBDIR, - NULL); - - theme_file = g_build_filename (theme_dir, - THEME_FILENAME, - NULL); + g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_FAILED, + _("Failed to find a valid file for theme %s\n"), + theme_name); - error = NULL; - if (!g_file_get_contents (theme_file, - &text, - &length, - &error)) - { - meta_warning (_("Failed to read theme from file %s: %s\n"), - theme_file, error->message); - g_propagate_error (err, error); - g_free (theme_file); - g_free (theme_dir); - return NULL; /* all fallbacks failed */ - } + return NULL; /* all fallbacks failed */ } - g_assert (text); - meta_topic (META_DEBUG_THEMES, "Parsing theme file %s\n", theme_file); + parse_info_init (&info); info.theme_name = theme_name; /* pass ownership to info so we free it with the info */ info.theme_file = theme_file; info.theme_dir = theme_dir; + + info.format_version = version + 1; context = g_markup_parse_context_new (&metacity_theme_parser, 0, &info, NULL); @@ -4385,7 +4609,9 @@ meta_theme_load (const char *theme_name, if (context) g_markup_parse_context_free (context); g_free (text); - + + info.theme->format_version = info.format_version; + if (error) { g_propagate_error (err, error); diff --git a/src/theme.c b/src/theme.c index 0bdd0628e..702344f18 100644 --- a/src/theme.c +++ b/src/theme.c @@ -27,6 +27,8 @@ #include "util.h" #include "gradient.h" #include +#include +#include #include #include #include @@ -394,8 +396,51 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout, static MetaButtonSpace* rect_for_function (MetaFrameGeometry *fgeom, MetaFrameFlags flags, - MetaButtonFunction function) + MetaButtonFunction function, + MetaTheme *theme) { + + /* Firstly, check version-specific things. */ + + if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS)) + { + switch (function) + { + case META_BUTTON_FUNCTION_SHADE: + if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED)) + return &fgeom->shade_rect; + else + return NULL; + case META_BUTTON_FUNCTION_ABOVE: + if (!(flags & META_FRAME_ABOVE)) + return &fgeom->above_rect; + else + return NULL; + case META_BUTTON_FUNCTION_STICK: + if (!(flags & META_FRAME_STUCK)) + return &fgeom->stick_rect; + else + return NULL; + case META_BUTTON_FUNCTION_UNSHADE: + if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED)) + return &fgeom->unshade_rect; + else + return NULL; + case META_BUTTON_FUNCTION_UNABOVE: + if (flags & META_FRAME_ABOVE) + return &fgeom->unabove_rect; + else + return NULL; + case META_BUTTON_FUNCTION_UNSTICK: + if (flags & META_FRAME_STUCK) + return &fgeom->unstick_rect; + default: + /* just go on to the next switch block */; + } + } + + /* now consider the buttons which exist in all versions */ + switch (function) { case META_BUTTON_FUNCTION_MENU: @@ -418,6 +463,19 @@ rect_for_function (MetaFrameGeometry *fgeom, return &fgeom->close_rect; else return NULL; + case META_BUTTON_FUNCTION_STICK: + case META_BUTTON_FUNCTION_SHADE: + case META_BUTTON_FUNCTION_ABOVE: + case META_BUTTON_FUNCTION_UNSTICK: + case META_BUTTON_FUNCTION_UNSHADE: + case META_BUTTON_FUNCTION_UNABOVE: + /* we are being asked for a >v1 button which hasn't been handled yet, + * so obviously we're not in a theme which supports that version. + * therefore, we don't show the button. return NULL and all will + * be well. + */ + return NULL; + case META_BUTTON_FUNCTION_LAST: return NULL; } @@ -468,7 +526,8 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, int client_width, int client_height, const MetaButtonLayout *button_layout, - MetaFrameGeometry *fgeom) + MetaFrameGeometry *fgeom, + MetaTheme *theme) { int i, n_left, n_right; int x; @@ -544,18 +603,20 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, right_func_rects[i] = NULL; /* Try to fill in rects */ - if (button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST) + if (button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST && !layout->hide_buttons) { left_func_rects[n_left] = rect_for_function (fgeom, flags, - button_layout->left_buttons[i]); + button_layout->left_buttons[i], + theme); if (left_func_rects[n_left] != NULL) ++n_left; } - if (button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST) + if (button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST && !layout->hide_buttons) { right_func_rects[n_right] = rect_for_function (fgeom, flags, - button_layout->right_buttons[i]); + button_layout->right_buttons[i], + theme); if (right_func_rects[n_right] != NULL) ++n_right; } @@ -610,10 +671,28 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, break; /* Everything fits, bail out */ /* Otherwise we need to shave out a button. Shave - * min, max, close, then menu (menu is most useful); + * above, stick, shade, min, max, close, then menu (menu is most useful); * prefer the default button locations. */ if (strip_button (left_func_rects, left_bg_rects, + &n_left, &fgeom->above_rect)) + continue; + else if (strip_button (right_func_rects, right_bg_rects, + &n_right, &fgeom->above_rect)) + continue; + else if (strip_button (left_func_rects, left_bg_rects, + &n_left, &fgeom->stick_rect)) + continue; + else if (strip_button (right_func_rects, right_bg_rects, + &n_right, &fgeom->stick_rect)) + continue; + else if (strip_button (left_func_rects, left_bg_rects, + &n_left, &fgeom->shade_rect)) + continue; + else if (strip_button (right_func_rects, right_bg_rects, + &n_right, &fgeom->shade_rect)) + continue; + else if (strip_button (left_func_rects, left_bg_rects, &n_left, &fgeom->min_rect)) continue; else if (strip_button (right_func_rects, right_bg_rects, @@ -751,20 +830,20 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, else min_size_for_rounding = 5; - fgeom->top_left_corner_rounded = FALSE; - fgeom->top_right_corner_rounded = FALSE; - fgeom->bottom_left_corner_rounded = FALSE; - fgeom->bottom_right_corner_rounded = FALSE; + fgeom->top_left_corner_rounded_radius = 0; + fgeom->top_right_corner_rounded_radius = 0; + fgeom->bottom_left_corner_rounded_radius = 0; + fgeom->bottom_right_corner_rounded_radius = 0; if (fgeom->top_height + fgeom->left_width >= min_size_for_rounding) - fgeom->top_left_corner_rounded = layout->top_left_corner_rounded; + fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius; if (fgeom->top_height + fgeom->right_width >= min_size_for_rounding) - fgeom->top_right_corner_rounded = layout->top_right_corner_rounded; + fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius; if (fgeom->bottom_height + fgeom->left_width >= min_size_for_rounding) - fgeom->bottom_left_corner_rounded = layout->bottom_left_corner_rounded; + fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius; if (fgeom->bottom_height + fgeom->right_width >= min_size_for_rounding) - fgeom->bottom_right_corner_rounded = layout->bottom_right_corner_rounded; + fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius; } MetaGradientSpec* @@ -3743,6 +3822,9 @@ meta_frame_style_new (MetaFrameStyle *parent) style->refcount = 1; + /* Default alpha is fully opaque */ + style->window_background_alpha = 255; + style->parent = parent; if (parent) meta_frame_style_ref (parent); @@ -3790,6 +3872,9 @@ meta_frame_style_unref (MetaFrameStyle *style) if (style->layout) meta_frame_layout_unref (style->layout); + if (style->window_background_color) + meta_color_spec_free (style->window_background_color); + /* we hold a reference to any parent style */ if (style->parent) meta_frame_style_unref (style->parent); @@ -3841,6 +3926,7 @@ get_button (MetaFrameStyle *style, gboolean meta_frame_style_validate (MetaFrameStyle *style, + guint current_theme_version, GError **error) { int i, j; @@ -3855,7 +3941,9 @@ meta_frame_style_validate (MetaFrameStyle *style, { for (j = 0; j < META_BUTTON_STATE_LAST; j++) { - if (get_button (style, i, j) == NULL) + if (get_button (style, i, j) == NULL && + meta_theme_earliest_version_with_button (i) <= current_theme_version + ) { g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED, @@ -3907,6 +3995,30 @@ button_rect (MetaButtonType type, *rect = fgeom->close_rect.visible; break; + case META_BUTTON_TYPE_SHADE: + *rect = fgeom->shade_rect.visible; + break; + + case META_BUTTON_TYPE_UNSHADE: + *rect = fgeom->unshade_rect.visible; + break; + + case META_BUTTON_TYPE_ABOVE: + *rect = fgeom->above_rect.visible; + break; + + case META_BUTTON_TYPE_UNABOVE: + *rect = fgeom->unabove_rect.visible; + break; + + case META_BUTTON_TYPE_STICK: + *rect = fgeom->stick_rect.visible; + break; + + case META_BUTTON_TYPE_UNSTICK: + *rect = fgeom->unstick_rect.visible; + break; + case META_BUTTON_TYPE_MAXIMIZE: *rect = fgeom->max_rect.visible; break; @@ -4218,10 +4330,12 @@ meta_frame_style_set_unref (MetaFrameStyleSet *style_set) int i; for (i = 0; i < META_FRAME_RESIZE_LAST; i++) - free_focus_styles (style_set->normal_styles[i]); + { + free_focus_styles (style_set->normal_styles[i]); + free_focus_styles (style_set->shaded_styles[i]); + } free_focus_styles (style_set->maximized_styles); - free_focus_styles (style_set->shaded_styles); free_focus_styles (style_set->maximized_and_shaded_styles); if (style_set->parent) @@ -4243,47 +4357,53 @@ get_style (MetaFrameStyleSet *style_set, style = NULL; - if (state == META_FRAME_STATE_NORMAL) + switch (state) { - style = style_set->normal_styles[resize][focus]; + case META_FRAME_STATE_NORMAL: + case META_FRAME_STATE_SHADED: + { + if (state == META_FRAME_STATE_SHADED) + style = style_set->shaded_styles[resize][focus]; + else + style = style_set->normal_styles[resize][focus]; - /* Try parent if we failed here */ - if (style == NULL && style_set->parent) - style = get_style (style_set->parent, state, resize, focus); + /* Try parent if we failed here */ + if (style == NULL && style_set->parent) + style = get_style (style_set->parent, state, resize, focus); - /* Allow people to omit the vert/horz/none resize modes */ - if (style == NULL && - resize != META_FRAME_RESIZE_BOTH) - style = get_style (style_set, state, META_FRAME_RESIZE_BOTH, focus); - } - else - { - MetaFrameStyle **styles; + /* Allow people to omit the vert/horz/none resize modes */ + if (style == NULL && + resize != META_FRAME_RESIZE_BOTH) + style = get_style (style_set, state, META_FRAME_RESIZE_BOTH, focus); + } + break; + default: + { + MetaFrameStyle **styles; - styles = NULL; + styles = NULL; - switch (state) - { - case META_FRAME_STATE_SHADED: - styles = style_set->shaded_styles; - break; - case META_FRAME_STATE_MAXIMIZED: - styles = style_set->maximized_styles; - break; - case META_FRAME_STATE_MAXIMIZED_AND_SHADED: - styles = style_set->maximized_and_shaded_styles; - break; - case META_FRAME_STATE_NORMAL: - case META_FRAME_STATE_LAST: - g_assert_not_reached (); - break; - } + switch (state) + { + case META_FRAME_STATE_MAXIMIZED: + styles = style_set->maximized_styles; + break; + case META_FRAME_STATE_MAXIMIZED_AND_SHADED: + styles = style_set->maximized_and_shaded_styles; + break; + case META_FRAME_STATE_NORMAL: + case META_FRAME_STATE_SHADED: + case META_FRAME_STATE_LAST: + g_assert_not_reached (); + break; + } - style = styles[focus]; + style = styles[focus]; - /* Try parent if we failed here */ - if (style == NULL && style_set->parent) - style = get_style (style_set->parent, state, resize, focus); + /* Try parent if we failed here */ + if (style == NULL && style_set->parent) + style = get_style (style_set->parent, state, resize, focus); + } } return style; @@ -4430,17 +4550,6 @@ meta_theme_new (void) } -static void -free_menu_ops (MetaDrawOpList *op_lists[META_MENU_ICON_TYPE_LAST][N_GTK_STATES]) -{ - int i, j; - - for (i = 0; i < META_MENU_ICON_TYPE_LAST; i++) - for (j = 0; j < N_GTK_STATES; j++) - if (op_lists[i][j]) - meta_draw_op_list_unref (op_lists[i][j]); -} - void meta_theme_free (MetaTheme *theme) { @@ -4476,34 +4585,15 @@ meta_theme_free (MetaTheme *theme) if (theme->style_sets_by_type[i]) meta_frame_style_set_unref (theme->style_sets_by_type[i]); - free_menu_ops (theme->menu_icons); - DEBUG_FILL_STRUCT (theme); g_free (theme); } -static MetaDrawOpList* -get_menu_icon (MetaTheme *theme, - MetaMenuIconType type, - GtkStateType state) -{ - MetaDrawOpList *op_list; - - op_list = theme->menu_icons[type][state]; - - /* We fall back to normal if other states aren't found */ - if (op_list == NULL && - state != GTK_STATE_NORMAL) - return get_menu_icon (theme, type, GTK_STATE_NORMAL); - - return op_list; -} - gboolean meta_theme_validate (MetaTheme *theme, GError **error) { - int i, j; + int i; g_return_val_if_fail (theme != NULL, FALSE); @@ -4558,24 +4648,13 @@ meta_theme_validate (MetaTheme *theme, return FALSE; } - for (i = 0; i < META_MENU_ICON_TYPE_LAST; i++) - for (j = 0; j < N_GTK_STATES; j++) - if (get_menu_icon (theme, i, j) == NULL) - { - g_set_error (error, META_THEME_ERROR, - META_THEME_ERROR_FAILED, - _(" must be specified for this theme"), - meta_menu_icon_type_to_string (i), - meta_gtk_state_to_string (j)); - return FALSE; - } - return TRUE; } GdkPixbuf* meta_theme_load_image (MetaTheme *theme, const char *filename, + guint size_of_theme_icons, GError **error) { GdkPixbuf *pixbuf; @@ -4585,19 +4664,32 @@ meta_theme_load_image (MetaTheme *theme, if (pixbuf == NULL) { - char *full_path; - - full_path = g_build_filename (theme->dirname, filename, NULL); - - pixbuf = gdk_pixbuf_new_from_file (full_path, error); - if (pixbuf == NULL) + + if (g_str_has_prefix (filename, "theme:") && + META_THEME_ALLOWS (theme, META_THEME_IMAGES_FROM_ICON_THEMES)) { + pixbuf = gtk_icon_theme_load_icon ( + gtk_icon_theme_get_default (), + filename+6, + size_of_theme_icons, + 0, + error); + if (pixbuf == NULL) return NULL; + } + else + { + char *full_path; + full_path = g_build_filename (theme->dirname, filename, NULL); + + pixbuf = gdk_pixbuf_new_from_file (full_path, error); + if (pixbuf == NULL) + { + g_free (full_path); + return NULL; + } + g_free (full_path); - return NULL; - } - - g_free (full_path); - + } g_hash_table_replace (theme->images_by_filename, g_strdup (filename), pixbuf); @@ -4749,7 +4841,8 @@ meta_theme_draw_frame (MetaTheme *theme, flags, client_width, client_height, button_layout, - &fgeom); + &fgeom, + theme); meta_frame_style_draw (style, widget, @@ -4764,37 +4857,6 @@ meta_theme_draw_frame (MetaTheme *theme, mini_icon, icon); } -void -meta_theme_draw_menu_icon (MetaTheme *theme, - GtkWidget *widget, - GdkDrawable *drawable, - const GdkRectangle *clip, - MetaRectangle offset_rect, - MetaMenuIconType type) -{ - MetaDrawInfo info; - MetaDrawOpList *op_list; - - g_return_if_fail (type < META_BUTTON_TYPE_LAST); - - op_list = get_menu_icon (theme, type, - GTK_WIDGET_STATE (widget)); - - info.mini_icon = NULL; - info.icon = NULL; - info.title_layout = NULL; - info.title_layout_width = 0; - info.title_layout_height = 0; - info.fgeom = NULL; - - meta_draw_op_list_draw (op_list, - widget, - drawable, - clip, - &info, - offset_rect); -} - void meta_theme_get_frame_borders (MetaTheme *theme, MetaFrameType type, @@ -4856,7 +4918,8 @@ meta_theme_calc_geometry (MetaTheme *theme, flags, client_width, client_height, button_layout, - fgeom); + fgeom, + theme); } MetaFrameLayout* @@ -5054,6 +5117,68 @@ meta_theme_lookup_float_constant (MetaTheme *theme, } } +gboolean +meta_theme_define_color_constant (MetaTheme *theme, + const char *name, + const char *value, + GError **error) +{ + if (theme->color_constants == NULL) + theme->color_constants = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + + if (!first_uppercase (name)) + { + g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED, + _("User-defined constants must begin with a capital letter; \"%s\" does not"), + name); + return FALSE; + } + + if (g_hash_table_lookup_extended (theme->color_constants, name, NULL, NULL)) + { + g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED, + _("Constant \"%s\" has already been defined"), + name); + + return FALSE; + } + + g_hash_table_insert (theme->color_constants, + g_strdup (name), + g_strdup (value)); + + return TRUE; +} + +gboolean +meta_theme_lookup_color_constant (MetaTheme *theme, + const char *name, + char **value) +{ + char *result; + + *value = NULL; + + if (theme->color_constants == NULL) + return FALSE; + + result = g_hash_table_lookup (theme->color_constants, name); + + if (result) + { + *value = result; + return TRUE; + } + else + { + return FALSE; + } +} + + PangoFontDescription* meta_gtk_widget_get_font_desc (GtkWidget *widget, double scale, @@ -5176,8 +5301,24 @@ meta_button_state_to_string (MetaButtonState state) } MetaButtonType -meta_button_type_from_string (const char *str) +meta_button_type_from_string (const char *str, MetaTheme *theme) { + if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS)) + { + if (strcmp ("shade", str) == 0) + return META_BUTTON_TYPE_SHADE; + else if (strcmp ("above", str) == 0) + return META_BUTTON_TYPE_ABOVE; + else if (strcmp ("stick", str) == 0) + return META_BUTTON_TYPE_STICK; + else if (strcmp ("unshade", str) == 0) + return META_BUTTON_TYPE_UNSHADE; + else if (strcmp ("unabove", str) == 0) + return META_BUTTON_TYPE_UNABOVE; + else if (strcmp ("unstick", str) == 0) + return META_BUTTON_TYPE_UNSTICK; + } + if (strcmp ("close", str) == 0) return META_BUTTON_TYPE_CLOSE; else if (strcmp ("maximize", str) == 0) @@ -5213,7 +5354,19 @@ meta_button_type_to_string (MetaButtonType type) return "maximize"; case META_BUTTON_TYPE_MINIMIZE: return "minimize"; - case META_BUTTON_TYPE_MENU: + case META_BUTTON_TYPE_SHADE: + return "shade"; + case META_BUTTON_TYPE_ABOVE: + return "above"; + case META_BUTTON_TYPE_STICK: + return "stick"; + case META_BUTTON_TYPE_UNSHADE: + return "unshade"; + case META_BUTTON_TYPE_UNABOVE: + return "unabove"; + case META_BUTTON_TYPE_UNSTICK: + return "unstick"; + case META_BUTTON_TYPE_MENU: return "menu"; case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND: return "left_left_background"; @@ -5234,41 +5387,6 @@ meta_button_type_to_string (MetaButtonType type) return ""; } -MetaMenuIconType -meta_menu_icon_type_from_string (const char *str) -{ - if (strcmp ("close", str) == 0) - return META_MENU_ICON_TYPE_CLOSE; - else if (strcmp ("maximize", str) == 0) - return META_MENU_ICON_TYPE_MAXIMIZE; - else if (strcmp ("minimize", str) == 0) - return META_MENU_ICON_TYPE_MINIMIZE; - else if (strcmp ("unmaximize", str) == 0) - return META_MENU_ICON_TYPE_UNMAXIMIZE; - else - return META_MENU_ICON_TYPE_LAST; -} - -const char* -meta_menu_icon_type_to_string (MetaMenuIconType type) -{ - switch (type) - { - case META_MENU_ICON_TYPE_CLOSE: - return "close"; - case META_MENU_ICON_TYPE_MAXIMIZE: - return "maximize"; - case META_MENU_ICON_TYPE_MINIMIZE: - return "minimize"; - case META_MENU_ICON_TYPE_UNMAXIMIZE: - return "unmaximize"; - case META_MENU_ICON_TYPE_LAST: - break; - } - - return ""; -} - MetaFramePiece meta_frame_piece_from_string (const char *str) { @@ -6044,3 +6162,34 @@ draw_bg_gradient_composite (const MetaTextureSpec *bg, } } #endif + +guint +meta_theme_earliest_version_with_button (MetaButtonType type) +{ + switch (type) + { + case META_BUTTON_TYPE_CLOSE: + case META_BUTTON_TYPE_MAXIMIZE: + case META_BUTTON_TYPE_MINIMIZE: + case META_BUTTON_TYPE_MENU: + case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND: + case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND: + case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND: + case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND: + case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND: + case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND: + return 1; + + case META_BUTTON_TYPE_SHADE: + case META_BUTTON_TYPE_ABOVE: + case META_BUTTON_TYPE_STICK: + case META_BUTTON_TYPE_UNSHADE: + case META_BUTTON_TYPE_UNABOVE: + case META_BUTTON_TYPE_UNSTICK: + return 2; + + default: + meta_warning("Unknown button %d\n", type); + return 1; + } +} diff --git a/src/theme.h b/src/theme.h index f817dce4e..005842ec3 100644 --- a/src/theme.h +++ b/src/theme.h @@ -100,11 +100,14 @@ struct _MetaFrameLayout /* Whether title text will be displayed */ guint has_title : 1; + /* Whether we should hide the buttons */ + guint hide_buttons : 1; + /* Round corners */ - guint top_left_corner_rounded : 1; - guint top_right_corner_rounded : 1; - guint bottom_left_corner_rounded : 1; - guint bottom_right_corner_rounded : 1; + guint top_left_corner_rounded_radius; + guint top_right_corner_rounded_radius; + guint bottom_left_corner_rounded_radius; + guint bottom_right_corner_rounded_radius; }; struct _MetaButtonSpace @@ -145,6 +148,12 @@ struct _MetaFrameGeometry MetaButtonSpace max_rect; MetaButtonSpace min_rect; MetaButtonSpace menu_rect; + MetaButtonSpace shade_rect; + MetaButtonSpace above_rect; + MetaButtonSpace stick_rect; + MetaButtonSpace unshade_rect; + MetaButtonSpace unabove_rect; + MetaButtonSpace unstick_rect; #define MAX_MIDDLE_BACKGROUNDS (MAX_BUTTONS_PER_CORNER - 2) GdkRectangle left_left_background; @@ -156,10 +165,10 @@ struct _MetaFrameGeometry /* End of button rects (if changed adjust memset hack) */ /* Round corners */ - guint top_left_corner_rounded : 1; - guint top_right_corner_rounded : 1; - guint bottom_left_corner_rounded : 1; - guint bottom_right_corner_rounded : 1; + guint top_left_corner_rounded_radius; + guint top_right_corner_rounded_radius; + guint bottom_left_corner_rounded_radius; + guint bottom_right_corner_rounded_radius; }; typedef enum @@ -438,6 +447,12 @@ typedef enum META_BUTTON_TYPE_MAXIMIZE, META_BUTTON_TYPE_MINIMIZE, META_BUTTON_TYPE_MENU, + META_BUTTON_TYPE_SHADE, + META_BUTTON_TYPE_ABOVE, + META_BUTTON_TYPE_STICK, + META_BUTTON_TYPE_UNSHADE, + META_BUTTON_TYPE_UNABOVE, + META_BUTTON_TYPE_UNSTICK, META_BUTTON_TYPE_LAST } MetaButtonType; @@ -503,6 +518,9 @@ struct _MetaFrameStyle MetaDrawOpList *buttons[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST]; MetaDrawOpList *pieces[META_FRAME_PIECE_LAST]; MetaFrameLayout *layout; + MetaColorSpec *window_background_color; /* can be NULL to use the standard + GTK theme engine */ + guint8 window_background_alpha; /* 0=transparent; 255=opaque */ }; /* Kinds of frame... @@ -552,7 +570,7 @@ struct _MetaFrameStyleSet MetaFrameStyleSet *parent; MetaFrameStyle *normal_styles[META_FRAME_RESIZE_LAST][META_FRAME_FOCUS_LAST]; MetaFrameStyle *maximized_styles[META_FRAME_FOCUS_LAST]; - MetaFrameStyle *shaded_styles[META_FRAME_FOCUS_LAST]; + MetaFrameStyle *shaded_styles[META_FRAME_RESIZE_LAST][META_FRAME_FOCUS_LAST]; MetaFrameStyle *maximized_and_shaded_styles[META_FRAME_FOCUS_LAST]; }; @@ -566,16 +584,19 @@ struct _MetaTheme char *copyright; char *date; char *description; + guint format_version; GHashTable *integer_constants; GHashTable *float_constants; + GHashTable *color_constants; GHashTable *images_by_filename; GHashTable *layouts_by_name; GHashTable *draw_op_lists_by_name; GHashTable *styles_by_name; GHashTable *style_sets_by_name; MetaFrameStyleSet *style_sets_by_type[META_FRAME_TYPE_LAST]; - MetaDrawOpList *menu_icons[META_MENU_ICON_TYPE_LAST][N_GTK_STATES]; + + GdkPixbuf *fallback_icon, *fallback_mini_icon; }; struct _MetaPositionExprEnv @@ -616,7 +637,8 @@ void meta_frame_layout_calc_geometry (const MetaFrameLayout *layout int client_width, int client_height, const MetaButtonLayout *button_layout, - MetaFrameGeometry *fgeom); + MetaFrameGeometry *fgeom, + MetaTheme *theme); gboolean meta_frame_layout_validate (const MetaFrameLayout *layout, GError **error); @@ -703,6 +725,7 @@ void meta_frame_style_draw (MetaFrameStyle *style, gboolean meta_frame_style_validate (MetaFrameStyle *style, + guint current_theme_version, GError **error); MetaFrameStyleSet* meta_frame_style_set_new (MetaFrameStyleSet *parent); @@ -722,6 +745,7 @@ gboolean meta_theme_validate (MetaTheme *theme, GError **error); GdkPixbuf* meta_theme_load_image (MetaTheme *theme, const char *filename, + guint size_of_theme_icons, GError **error); MetaFrameStyle* meta_theme_get_frame_style (MetaTheme *theme, @@ -749,13 +773,6 @@ void meta_theme_draw_frame (MetaTheme *theme, GdkPixbuf *mini_icon, GdkPixbuf *icon); -void meta_theme_draw_menu_icon (MetaTheme *theme, - GtkWidget *widget, - GdkDrawable *drawable, - const GdkRectangle *clip, - MetaRectangle offset_rect, - MetaMenuIconType type); - void meta_theme_get_frame_borders (MetaTheme *theme, MetaFrameType type, int text_height, @@ -808,6 +825,14 @@ gboolean meta_theme_lookup_float_constant (MetaTheme *theme, const char *name, double *value); +gboolean meta_theme_define_color_constant (MetaTheme *theme, + const char *name, + const char *value, + GError **error); +gboolean meta_theme_lookup_color_constant (MetaTheme *theme, + const char *name, + char **value); + char* meta_theme_replace_constants (MetaTheme *theme, const char *expr, GError **err); @@ -826,10 +851,9 @@ MetaGtkColorComponent meta_color_component_from_string (const char *s const char* meta_color_component_to_string (MetaGtkColorComponent component); MetaButtonState meta_button_state_from_string (const char *str); const char* meta_button_state_to_string (MetaButtonState state); -MetaButtonType meta_button_type_from_string (const char *str); +MetaButtonType meta_button_type_from_string (const char *str, + MetaTheme *theme); const char* meta_button_type_to_string (MetaButtonType type); -MetaMenuIconType meta_menu_icon_type_from_string (const char *str); -const char* meta_menu_icon_type_to_string (MetaMenuIconType type); MetaFramePiece meta_frame_piece_from_string (const char *str); const char* meta_frame_piece_to_string (MetaFramePiece piece); MetaFrameState meta_frame_state_from_string (const char *str); @@ -851,5 +875,19 @@ const char* meta_gtk_arrow_to_string (GtkArrowType a MetaImageFillType meta_image_fill_type_from_string (const char *str); const char* meta_image_fill_type_to_string (MetaImageFillType fill_type); +guint meta_theme_earliest_version_with_button (MetaButtonType type); + +#define META_THEME_ALLOWS(theme, feature) (theme->format_version >= feature) + +/* What version of the theme file format were various features introduced in? */ +#define META_THEME_SHADE_STICK_ABOVE_BUTTONS 2 +#define META_THEME_UBIQUITOUS_CONSTANTS 2 +#define META_THEME_VARIED_ROUND_CORNERS 2 +#define META_THEME_IMAGES_FROM_ICON_THEMES 2 +#define META_THEME_UNRESIZABLE_SHADED_STYLES 2 +#define META_THEME_DEGREES_IN_ARCS 2 +#define META_THEME_HIDDEN_BUTTONS 2 +#define META_THEME_COLOR_CONSTANTS 2 +#define META_THEME_FRAME_BACKGROUNDS 2 #endif diff --git a/src/ui.c b/src/ui.c index 4a16a5804..dbbe698d9 100644 --- a/src/ui.c +++ b/src/ui.c @@ -30,6 +30,7 @@ #include "menu.h" #include "core.h" #include "theme.h" +#include "iconcache.h" #include "inlinepixbufs.h" @@ -736,6 +737,7 @@ meta_ui_set_current_theme (const char *name, gboolean force_reload) { meta_theme_set_current (name, force_reload); + meta_invalidate_default_icons (); } gboolean diff --git a/src/window.c b/src/window.c index 2f46f6402..2cba81788 100644 --- a/src/window.c +++ b/src/window.c @@ -110,7 +110,6 @@ static void meta_window_flush_calc_showing (MetaWindow *window); static void meta_window_unqueue_move_resize (MetaWindow *window); -static void meta_window_update_icon_now (MetaWindow *window); static void meta_window_unqueue_update_icon (MetaWindow *window); static gboolean queue_calc_showing_func (MetaWindow *window, @@ -5484,7 +5483,7 @@ redraw_icon (MetaWindow *window) meta_ui_queue_frame_draw (window->screen->ui, window->frame->xwindow); } -static void +void meta_window_update_icon_now (MetaWindow *window) { GdkPixbuf *icon; diff --git a/src/window.h b/src/window.h index ff4b944b6..e33f3e0d9 100644 --- a/src/window.h +++ b/src/window.h @@ -607,4 +607,7 @@ void meta_window_set_user_time (MetaWindow *window, void meta_window_set_demands_attention (MetaWindow *window); void meta_window_unset_demands_attention (MetaWindow *window); + +void meta_window_update_icon_now (MetaWindow *window); + #endif