Index: fvwm/style.c =================================================================== --- fvwm/style.c (revision 1) +++ fvwm/style.c (revision 14) @@ -409,6 +409,22 @@ *merged_style, SGET_WINDOW_FONT(*add_style)); } } + if (S_HAS_INACTIVE_WINDOW_FONT(SCF(*add_style))) + { + if (do_free_src_and_alloc_copy) + { + SAFEFREE(SGET_INACTIVE_WINDOW_FONT(*merged_style)); + SSET_INACTIVE_WINDOW_FONT( + *merged_style, (SGET_INACTIVE_WINDOW_FONT(*add_style)) ? + safestrdup(SGET_INACTIVE_WINDOW_FONT(*add_style)) : + NULL); + } + else + { + SSET_INACTIVE_WINDOW_FONT( + *merged_style, SGET_INACTIVE_WINDOW_FONT(*add_style)); + } + } if (add_style->flags.use_start_on_desk) { SSET_START_DESK(*merged_style, SGET_START_DESK(*add_style)); @@ -504,6 +520,10 @@ { SSET_HANDLE_WIDTH(*merged_style, SGET_HANDLE_WIDTH(*add_style)); } + if (add_style->flags.has_corner_width) + { + SSET_CORNER_WIDTH(*merged_style, SGET_CORNER_WIDTH(*add_style)); + } if (add_style->flags.has_icon_size_limits) { SSET_MIN_ICON_WIDTH( @@ -735,6 +755,10 @@ { SAFEFREE(SGET_WINDOW_FONT(*style)); } + if (pmask->common.has_inactive_window_font) + { + SAFEFREE(SGET_INACTIVE_WINDOW_FONT(*style)); + } if (pmask->has_icon) { SAFEFREE(SGET_ICON_NAME(*style)); @@ -2055,6 +2079,12 @@ { rest = style_parse_button_style(ps, rest, on); } + else if (StrEquals(token, "BORDERUNDERTITLE")) + { + S_SET_HAS_BORDER_UNDER_TITLE(SCF(*ps), on); + S_SET_HAS_BORDER_UNDER_TITLE(SCM(*ps), 1); + S_SET_HAS_BORDER_UNDER_TITLE(SCC(*ps), 1); + } else if (StrEquals(token, "BorderWidth")) { if (GetIntegerArguments(rest, &rest, val, 1)) @@ -2265,6 +2295,20 @@ ps->change_mask.has_color_back = 1; break; } + else if (StrEquals(token, "CornerWidth")) + { + if (GetIntegerArguments(rest, &rest, val, 1)) + { + SSET_CORNER_WIDTH(*ps, (short)*val); + ps->flags.has_corner_width = 1; + } + else + { + ps->flags.has_corner_width = 0; + } + ps->flag_mask.has_corner_width = 1; + ps->change_mask.has_corner_width = 1; + } else if (StrEquals(token, "CirculateSkipIcon")) { S_SET_DO_CIRCULATE_SKIP_ICON(SCF(*ps), on); @@ -2481,6 +2525,12 @@ S_SET_HAS_MWM_BORDER(SCM(*ps), 1); S_SET_HAS_MWM_BORDER(SCC(*ps), 1); } + else if (StrEquals(token, "FluxboxHandles")) + { + S_SET_HAS_FLUXBOX_HANDLES(SCF(*ps), on); + S_SET_HAS_FLUXBOX_HANDLES(SCM(*ps), 1); + S_SET_HAS_FLUXBOX_HANDLES(SCC(*ps), 1); + } else if (StrEquals(token, "FocusFollowsMouse")) { style_set_old_focus_policy(ps, 1); @@ -2828,6 +2878,15 @@ S_SET_IS_UNICONIFIABLE(SCM(*ps), 1); S_SET_IS_UNICONIFIABLE(SCC(*ps), 1); } + else if (StrEquals(token, "InactiveFont")) + { + SAFEFREE(SGET_INACTIVE_WINDOW_FONT(*ps)); + rest = GetNextToken(rest, &token); + SSET_INACTIVE_WINDOW_FONT(*ps, token); + S_SET_HAS_INACTIVE_WINDOW_FONT(SCF(*ps), (token != NULL)); + S_SET_HAS_INACTIVE_WINDOW_FONT(SCM(*ps), 1); + S_SET_HAS_INACTIVE_WINDOW_FONT(SCC(*ps), 1); + } else if (StrEquals(token, "IndexedWindowName")) { S_SET_USE_INDEXED_WINDOW_NAME(SCF(*ps), on); @@ -3403,6 +3462,12 @@ S_SET_DO_RESIZE_OPAQUE(SCM(*ps), 1); S_SET_DO_RESIZE_OPAQUE(SCC(*ps), 1); } + else if (StrEquals(token, "ResizeOutlineThin")) + { + S_SET_DO_RESIZE_OUTLINE_THIN(SCF(*ps), on); + S_SET_DO_RESIZE_OUTLINE_THIN(SCM(*ps), 1); + S_SET_DO_RESIZE_OUTLINE_THIN(SCC(*ps), 1); + } else if (StrEquals(token, "RightTitleRotatedCW")) { S_SET_IS_RIGHT_TITLE_ROTATED_CW(SCF(*ps), on); @@ -3415,6 +3480,27 @@ S_SET_IS_RIGHT_TITLE_ROTATED_CW(SCM(*ps), 1); S_SET_IS_RIGHT_TITLE_ROTATED_CW(SCC(*ps), 1); } + else if (StrEquals(token, "RoundedCorners")) + { + S_SET_HAS_ROUNDED_CORNERS_TOP(SCF(*ps), on); + S_SET_HAS_ROUNDED_CORNERS_TOP(SCM(*ps), 1); + S_SET_HAS_ROUNDED_CORNERS_TOP(SCC(*ps), 1); + S_SET_HAS_ROUNDED_CORNERS_BOTTOM(SCF(*ps), on); + S_SET_HAS_ROUNDED_CORNERS_BOTTOM(SCM(*ps), 1); + S_SET_HAS_ROUNDED_CORNERS_BOTTOM(SCC(*ps), 1); + } + else if (StrEquals(token, "RoundedCornersTop")) + { + S_SET_HAS_ROUNDED_CORNERS_TOP(SCF(*ps), on); + S_SET_HAS_ROUNDED_CORNERS_TOP(SCM(*ps), 1); + S_SET_HAS_ROUNDED_CORNERS_TOP(SCC(*ps), 1); + } + else if (StrEquals(token, "RoundedCornersBottom")) + { + S_SET_HAS_ROUNDED_CORNERS_BOTTOM(SCF(*ps), on); + S_SET_HAS_ROUNDED_CORNERS_BOTTOM(SCM(*ps), 1); + S_SET_HAS_ROUNDED_CORNERS_BOTTOM(SCC(*ps), 1); + } else { found = False; @@ -3537,6 +3623,12 @@ S_SET_IS_STICKY_ACROSS_DESKS(SCM(*ps), 1); S_SET_IS_STICKY_ACROSS_DESKS(SCC(*ps), 1); } + else if (StrEquals(token, "SlightlyRoundedCorners")) + { + S_SET_HAS_SLIGHTLY_ROUNDED_CORNERS(SCF(*ps), on); + S_SET_HAS_SLIGHTLY_ROUNDED_CORNERS(SCM(*ps), 1); + S_SET_HAS_SLIGHTLY_ROUNDED_CORNERS(SCC(*ps), 1); + } else if (StrEquals(token, "Slippery")) { S_SET_IS_STICKY_ACROSS_PAGES(SCF(*ps), !on); @@ -3776,6 +3868,12 @@ S_SET_TITLE_DIR(SCM(*ps), DIR_MAJOR_MASK); S_SET_TITLE_DIR(SCC(*ps), DIR_MAJOR_MASK); } + else if (StrEquals(token, "TopBorder")) + { + S_SET_HAS_NO_TOP_BORDER(SCF(*ps), !on); + S_SET_HAS_NO_TOP_BORDER(SCM(*ps), 1); + S_SET_HAS_NO_TOP_BORDER(SCC(*ps), 1); + } else if (StrEquals(token, "TopTitleRotated")) { S_SET_IS_TOP_TITLE_ROTATED(SCF(*ps), on); @@ -4391,6 +4489,30 @@ flags->do_update_rotated_title = 1; } + /* has_fluxbox_handles */ + if (S_HAS_FLUXBOX_HANDLES(SCC(*ret_style))) + { + flags->do_redecorate = True; + } + + /* has_rounded_corners */ + if (S_HAS_ROUNDED_CORNERS_TOP(SCC(*ret_style)) + || S_HAS_ROUNDED_CORNERS_BOTTOM(SCC(*ret_style)) + || S_HAS_SLIGHTLY_ROUNDED_CORNERS(SCC(*ret_style))) + { + flags->do_redecorate = True; + } + + if (S_HAS_BORDER_UNDER_TITLE(SCC(*ret_style))) + { + flags->do_redecorate = True; + } + + if (S_HAS_NO_TOP_BORDER(SCC(*ret_style))) + { + flags->do_redecorate = True; + } + /* has_mwm_border * has_mwm_buttons */ if (S_HAS_MWM_BORDER(SCC(*ret_style)) || @@ -4411,6 +4533,12 @@ flags->do_update_window_font = 1; } + /* has_inactive_window_font */ + if (S_HAS_INACTIVE_WINDOW_FONT(SCC(*ret_style))) + { + flags->do_update_window_font = True; + } + /* has_stippled_title */ if (S_HAS_STIPPLED_TITLE(SCC(*ret_style))) { @@ -4626,6 +4754,12 @@ flags->do_update_modules_flags = 1; } + /* has_corner_width */ + if (ret_style->change_mask.has_corner_width) + { + flags->do_redecorate = True; + } + if (ret_style->change_mask.do_save_under || ret_style->change_mask.use_backing_store || ret_style->change_mask.use_parent_relative) Index: fvwm/conditional.c =================================================================== --- fvwm/conditional.c (revision 1) +++ fvwm/conditional.c (revision 14) @@ -581,6 +581,36 @@ SET_HAS_HANDLES(mask, on); SETM_HAS_HANDLES(mask, 1); } + else if (StrEquals(cond, "HasTitle")) + { + SET_HAS_TITLE(mask, on); + SETM_HAS_TITLE(mask, 1); + } + else if (StrEquals(cond, "HasBorders")) + { + SET_HAS_NO_BORDER(mask, !on); + SETM_HAS_NO_BORDER(mask, 1); + } + else if (StrEquals(cond, "TitleAtBottom")) + { + SET_TITLE_DIR(mask, DIR_S); + SETM_TITLE_DIR(mask, 1); + } + else if (StrEquals(cond, "TitleAtTop")) + { + SET_TITLE_DIR(mask, DIR_N); + SETM_TITLE_DIR(mask, 1); + } + else if (StrEquals(cond, "TitleAtLeft")) + { + SET_TITLE_DIR(mask, DIR_W); + SETM_TITLE_DIR(mask, 1); + } + else if (StrEquals(cond, "TitleAtRight")) + { + SET_TITLE_DIR(mask, DIR_E); + SETM_TITLE_DIR(mask, 1); + } else if (StrEquals(cond,"Iconifiable")) { SET_IS_UNICONIFIABLE(mask, !on); Index: fvwm/style.h =================================================================== --- fvwm/style.h (revision 1) +++ fvwm/style.h (revision 14) @@ -19,6 +19,8 @@ ((sf)->has_color_fore) #define SHAS_HANDLE_WIDTH(sf) \ ((sf)->has_handle_width) +#define SHAS_CORNER_WIDTH(sf) \ + ((sf)->has_corner_width) #define SHAS_ICON(sf) \ ((sf)->has_icon) #define SHAS_ICON_BOXES(sf) \ @@ -191,6 +193,10 @@ ((c).s.do_resize_opaque) #define S_SET_DO_RESIZE_OPAQUE(c,x) \ ((c).s.do_resize_opaque = !!(x)) +#define S_DO_RESIZE_OUTLINE_THIN(c) \ + ((c).s.do_resize_outline_thin) +#define S_SET_DO_RESIZE_OUTLINE_THIN(c,x) \ + ((c).s.do_resize_outline_thin = !!(x)) #define S_DO_SHRINK_WINDOWSHADE(c) \ ((c).s.do_shrink_windowshade) #define S_SET_DO_SHRINK_WINDOWSHADE(c,x) \ @@ -243,6 +249,10 @@ ((c).has_window_font) #define S_SET_HAS_WINDOW_FONT(c,x) \ ((c).has_window_font = !!(x)) +#define S_HAS_INACTIVE_WINDOW_FONT(c) \ + ((c).has_inactive_window_font) +#define S_SET_HAS_INACTIVE_WINDOW_FONT(c,x) \ + ((c).has_inactive_window_font = !!(x)) #define S_ICON_OVERRIDE(c) \ ((c).s.icon_override) #define S_SET_ICON_OVERRIDE(c,x) \ @@ -319,6 +329,31 @@ ((c).s.use_title_decor_rotation) #define S_SET_USE_TITLE_DECOR_ROTATION(c,x) \ ((c).s.use_title_decor_rotation = !!(x)) +#define S_HAS_BORDER_UNDER_TITLE(c) \ + ((c).s.has_border_under_title) +#define S_SET_HAS_BORDER_UNDER_TITLE(c,x) \ + ((c).s.has_border_under_title = !!(x)) +#define S_HAS_FLUXBOX_HANDLES(c) \ + ((c).s.has_fluxbox_handles) +#define S_SET_HAS_FLUXBOX_HANDLES(c,x) \ + ((c).s.has_fluxbox_handles = !!(x)) +#define S_HAS_ROUNDED_CORNERS_TOP(c) \ + ((c).s.has_rounded_corners_top) +#define S_SET_HAS_ROUNDED_CORNERS_TOP(c,x) \ + ((c).s.has_rounded_corners_top = !!(x)) +#define S_HAS_ROUNDED_CORNERS_BOTTOM(c) \ + ((c).s.has_rounded_corners_bottom) +#define S_SET_HAS_ROUNDED_CORNERS_BOTTOM(c,x) \ + ((c).s.has_rounded_corners_bottom = !!(x)) +#define S_HAS_SLIGHTLY_ROUNDED_CORNERS(c) \ + ((c).s.has_slightly_rounded_corners) +#define S_SET_HAS_SLIGHTLY_ROUNDED_CORNERS(c,x) \ + ((c).s.has_slightly_rounded_corners = !!(x)) +#define S_HAS_NO_TOP_BORDER(c) \ + ((c).s.has_no_top_border) +#define S_SET_HAS_NO_TOP_BORDER(c,x) \ + ((c).s.has_no_top_border = !!(x)) + #define S_DO_EWMH_MINI_ICON_OVERRIDE(c) \ ((c).s.do_ewmh_mini_icon_override) #define S_SET_DO_EWMH_MINI_ICON_OVERRIDE(c,x) \ @@ -435,6 +470,10 @@ ((s).window_font) #define SSET_WINDOW_FONT(s,x) \ ((s).window_font = (x)) +#define SGET_INACTIVE_WINDOW_FONT(s) \ + ((s).inactive_window_font) +#define SSET_INACTIVE_WINDOW_FONT(s,x) \ + ((s).inactive_window_font = (x)) #define SGET_COLORSET(s) \ ((s).colorset) #define SSET_COLORSET(s,x) \ @@ -473,6 +512,10 @@ ((s).handle_width) #define SSET_HANDLE_WIDTH(s,x) \ ((s).handle_width = (x)) +#define SGET_CORNER_WIDTH(s) \ + ((s).corner_width) +#define SSET_CORNER_WIDTH(s,x) \ + ((s).corner_width = (x)) #define SGET_LAYER(s) \ ((s).layer) #define SSET_LAYER(s,x) \ Index: fvwm/menuitem.c =================================================================== --- fvwm/menuitem.c (revision 1) +++ fvwm/menuitem.c (revision 14) @@ -82,10 +82,18 @@ * */ static void draw_separator( - Window w, GC TopGC, GC BottomGC, int x1, int y, int x2) + Window w, GC TopGC, GC BottomGC, GC ForeGC, int x1, int y, int x2, + Bool do_flat_separators) { - XDrawLine(dpy, w, TopGC , x1, y, x2, y); - XDrawLine(dpy, w, BottomGC, x1-1, y+1, x2+1, y+1); + if (do_flat_separators) + { + XDrawLine(dpy, w, ForeGC, x1, y, x2, y); + } + else + { + XDrawLine(dpy, w, TopGC , x1, y, x2, y); + XDrawLine(dpy, w, BottomGC, x1-1, y+1, x2+1, y+1); + } return; } @@ -326,6 +334,7 @@ /*Pixel fg, fgsh;*/ short relief_thickness = ST_RELIEF_THICKNESS(ms); Bool is_item_selected; + Bool do_flat_separators; Bool item_cleared = False; Bool xft_clear = False; Bool empty_inter = False; @@ -536,6 +545,8 @@ * Draw the item itself. */ + do_flat_separators = ST_DO_FLAT_SEPARATOR(ms); + /* Calculate the separator offsets. */ if (ST_HAS_LONG_SEPARATORS(ms)) { @@ -556,9 +567,9 @@ { /* It's a separator. */ draw_separator( - mpip->w, gcs.shadow_gc, gcs.hilight_gc, sx1, - y_offset + y_height - MENU_SEPARATOR_HEIGHT, - sx2); + mpip->w, gcs.shadow_gc, gcs.hilight_gc, gcs.fore_gc, + sx1, y_offset + y_height - MENU_SEPARATOR_HEIGHT, + sx2, do_flat_separators); /* Nothing else to do. */ } return; @@ -598,8 +609,8 @@ if (sx1 < sx2) { draw_separator( - mpip->w, gcs.shadow_gc, gcs.hilight_gc, - sx1, y, sx2); + mpip->w, gcs.shadow_gc, gcs.hilight_gc, gcs.fore_gc, + sx1, y, sx2, do_flat_separators); } } /* Underline the title. */ @@ -612,8 +623,8 @@ { y = y_offset + y_height - MENU_SEPARATOR_HEIGHT; draw_separator( - mpip->w, gcs.shadow_gc, gcs.hilight_gc, - sx1, y, sx2); + mpip->w, gcs.shadow_gc, gcs.hilight_gc, gcs.fore_gc, + sx1, y, sx2, do_flat_separators); } break; default: Index: fvwm/move_resize.c =================================================================== --- fvwm/move_resize.c (revision 1) +++ fvwm/move_resize.c (revision 14) @@ -107,7 +107,7 @@ extern Window PressedW; -static void draw_move_resize_grid(int x, int y, int width, int height); +static void draw_move_resize_grid(int x, int y, int width, int height, Bool thin); /* ----- end of resize globals ----- */ @@ -125,27 +125,34 @@ * */ static int get_outline_rects( - XRectangle *rects, int x, int y, int width, int height) + XRectangle *rects, int x, int y, int width, int height, Bool do_outline_thin) { int i; int n; int m; - n = 3; - m = (width - 5) / 2; - if (m < n) + if (do_outline_thin) { - n = m; + n = 1; } - m = (height - 5) / 2; - if (m < n) + else { - n = m; + n = 3; + m = (width - 5) / 2; + if (m < n) + { + n = m; + } + m = (height - 5) / 2; + if (m < n) + { + n = m; + } + if (n < 1) + { + n = 1; + } } - if (n < 1) - { - n = 1; - } for (i = 0; i < n; i++) { @@ -154,26 +161,29 @@ rects[i].width = width - (i << 1); rects[i].height = height - (i << 1); } - if (width - (n << 1) >= 5 && height - (n << 1) >= 5) + if (!do_outline_thin) { - if (width - (n << 1) >= 10) + if (width - (n << 1) >= 5 && height - (n << 1) >= 5) { - int off = (width - (n << 1)) / 3 + n; - rects[i].x = x + off; - rects[i].y = y + n; - rects[i].width = width - (off << 1); - rects[i].height = height - (n << 1); - i++; + if (width - (n << 1) >= 10) + { + int off = (width - (n << 1)) / 3 + n; + rects[i].x = x + off; + rects[i].y = y + n; + rects[i].width = width - (off << 1); + rects[i].height = height - (n << 1); + i++; + } + if (height - (n << 1) >= 10) + { + int off = (height - (n << 1)) / 3 + n; + rects[i].x = x + n; + rects[i].y = y + off; + rects[i].width = width - (n << 1); + rects[i].height = height - (off << 1); + i++; + } } - if (height - (n << 1) >= 10) - { - int off = (height - (n << 1)) / 3 + n; - rects[i].x = x + n; - rects[i].y = y + off; - rects[i].width = width - (n << 1); - rects[i].height = height - (off << 1); - i++; - } } return i; @@ -185,14 +195,15 @@ struct { unsigned is_enabled : 1; + unsigned do_outline_thin : 1; } flags; } move_resize_grid = { { 0, 0, 0, 0 }, - { 0 } + { 0, 0 } }; -static void draw_move_resize_grid(int x, int y, int width, int height) +static void draw_move_resize_grid(int x, int y, int width, int height, Bool do_outline_thin) { int nrects = 0; XRectangle rects[10]; @@ -201,7 +212,8 @@ x == move_resize_grid.geom.x && y == move_resize_grid.geom.y && width == move_resize_grid.geom.width && - height == move_resize_grid.geom.height) + height == move_resize_grid.geom.height && + do_outline_thin == move_resize_grid.flags.do_outline_thin) { return; } @@ -219,7 +231,8 @@ &(rects[0]), move_resize_grid.geom.x, move_resize_grid.geom.y, move_resize_grid.geom.width, - move_resize_grid.geom.height); + move_resize_grid.geom.height, + move_resize_grid.flags.do_outline_thin); } if (width && height) { @@ -228,8 +241,9 @@ move_resize_grid.geom.y = y; move_resize_grid.geom.width = width; move_resize_grid.geom.height = height; + move_resize_grid.flags.do_outline_thin = do_outline_thin; nrects += get_outline_rects( - &(rects[nrects]), x, y, width, height); + &(rects[nrects]), x, y, width, height, do_outline_thin); } if (nrects > 0) { @@ -246,7 +260,7 @@ { if (move_resize_grid.flags.is_enabled) { - draw_move_resize_grid(0, 0, 0, 0); + draw_move_resize_grid(0, 0, 0, 0, 0); } else { @@ -254,6 +268,7 @@ move_resize_grid.geom.y = 0; move_resize_grid.geom.width = 0; move_resize_grid.geom.height = 0; + move_resize_grid.flags.do_outline_thin = 0; } } else if (!move_resize_grid.flags.is_enabled) @@ -265,7 +280,8 @@ move_resize_grid.geom.x, move_resize_grid.geom.y, move_resize_grid.geom.width, - move_resize_grid.geom.height); + move_resize_grid.geom.height, + move_resize_grid.flags.do_outline_thin); } } @@ -1216,22 +1232,27 @@ { /* don't waste time in the same spot */ continue; - } - XMoveWindow(dpy,w,currentX,currentY); + } if (pmrtp != NULL) { repaint_transparent_menu( pmrtp, first, currentX, currentY, endX, endY); + XMoveWindow(dpy,w,currentX,currentY); } else if (draw_parts != PART_NONE) - { + { + XMoveWindow(dpy,w,currentX,currentY); border_draw_decorations( fw, draw_parts, ((fw == get_focus_window())) ? True : False, True, CLEAR_ALL, NULL, NULL); } + else + { + XMoveWindow(dpy,w,currentX,currentY); + } if (fw && pmrtp == NULL && IS_TEAR_OFF_MENU(fw)) { menu_redraw_transparent_tear_off_menu(fw, False); @@ -1295,13 +1316,14 @@ &evdummy)) { /* finish the move immediately */ - XMoveWindow(dpy,w,endX,endY); + if (pmrtp != NULL) { repaint_transparent_menu( pmrtp, first, endX, endY, endX, endY); } + XMoveWindow(dpy,w,endX,endY); break; } lastX = currentX; @@ -2154,6 +2176,7 @@ /* Must not set placed by button if the event is a modified KeyEvent */ Bool is_fake_event; FvwmWindow *fw = exc->w.fw; + Bool do_outline_thin = DO_RESIZE_OUTLINE_THIN(fw); unsigned int draw_parts = PART_NONE; XEvent e; @@ -2224,7 +2247,7 @@ if (!IS_ICONIFIED(fw) && ((!do_move_opaque && !Scr.gs.do_emulate_mwm) || !IS_MAPPED(fw))) { - draw_move_resize_grid(xl, yt, Width - 1, Height - 1); + draw_move_resize_grid(xl, yt, Width - 1, Height - 1, do_outline_thin); } if (move_w == FW_W_FRAME(fw) && do_move_opaque) @@ -2551,7 +2574,7 @@ if (!do_move_opaque) { draw_move_resize_grid( - xl, yt, Width - 1, Height - 1); + xl, yt, Width - 1, Height - 1, do_outline_thin); } else { @@ -2622,7 +2645,7 @@ if (!do_move_opaque) { draw_move_resize_grid( - xl, yt, Width - 1, Height - 1); + xl, yt, Width - 1, Height - 1, do_outline_thin); } break; @@ -3155,7 +3178,7 @@ static void __resize_step( const exec_context_t *exc, int x_root, int y_root, int *x_off, int *y_off, rectangle *drag, rectangle *orig, int *xmotionp, - int *ymotionp, Bool do_resize_opaque, Bool is_direction_fixed) + int *ymotionp, Bool do_resize_opaque, Bool is_direction_fixed, Bool do_outline_thin) { int action = 0; XEvent e; @@ -3279,7 +3302,7 @@ { draw_move_resize_grid( drag->x, drag->y, drag->width - 1, - drag->height - 1); + drag->height - 1, do_outline_thin); } else { @@ -3335,6 +3358,7 @@ int x_off; int y_off; direction_t dir; + Bool do_outline_thin = DO_RESIZE_OUTLINE_THIN(fw); bad_window = False; ResizeWindow = FW_W_FRAME(fw); @@ -3616,7 +3640,7 @@ if (!do_resize_opaque) { draw_move_resize_grid( - drag->x, drag->y, drag->width - 1, drag->height - 1); + drag->x, drag->y, drag->width - 1, drag->height - 1, do_outline_thin); } /* kick off resizing without requiring any motion if invoked with a key * press */ @@ -3637,7 +3661,7 @@ yo = 0; __resize_step( exc, stashed_x, stashed_y, &xo, &yo, drag, orig, - &xmotion, &ymotion, do_resize_opaque, True); + &xmotion, &ymotion, do_resize_opaque, True, do_outline_thin); } else { @@ -3805,7 +3829,7 @@ exc, start_g.x, start_g.y, &xo, &yo, &start_g, orig, &xmotion, &ymotion, - do_resize_opaque, True); + do_resize_opaque, True, do_outline_thin); } } is_done = True; @@ -3830,7 +3854,7 @@ __resize_step( exc, x, y, &x_off, &y_off, drag, orig, &xmotion, &ymotion, do_resize_opaque, - is_direction_fixed); + is_direction_fixed, do_outline_thin); /* need to move the viewport */ HandlePaging( &ev, Scr.EdgeScrollX, Scr.EdgeScrollY, @@ -3848,7 +3872,7 @@ __resize_step( exc, x, y, &x_off, &y_off, drag, orig, &xmotion, &ymotion, do_resize_opaque, - is_direction_fixed); + is_direction_fixed, do_outline_thin); } fForceRedraw = False; is_done = True; @@ -3883,7 +3907,7 @@ { draw_move_resize_grid( drag->x, drag->y, drag->width - 1, - drag->height - 1); + drag->height - 1, do_outline_thin); } } else Index: fvwm/geometry.c =================================================================== --- fvwm/geometry.c (revision 1) +++ fvwm/geometry.c (revision 14) @@ -46,6 +46,9 @@ /* ---------------------------- forward declarations ----------------------- */ +static void __get_window_borders( + const FvwmWindow *fw, size_borders *borders, Bool is_shaded); + /* ---------------------------- local variables ---------------------------- */ /* ---------------------------- exported variables (globals) --------------- */ @@ -342,7 +345,8 @@ int big_height = big_g->height; int d; - get_window_borders(fw, &b); + __get_window_borders(fw, &b, 1); + *small_g = *big_g; d = 0; switch (SHADED_DIR(fw)) @@ -468,25 +472,50 @@ void get_window_borders( const FvwmWindow *fw, size_borders *borders) { - borders->top_left.width = fw->boundary_width; - borders->bottom_right.width = fw->boundary_width; - borders->top_left.height = fw->boundary_width; + __get_window_borders(fw, borders, 0); +} + +static void __get_window_borders( + const FvwmWindow *fw, size_borders *borders, Bool is_shaded) +{ + int title_thickness; + int *title_border = NULL; + int bw; + + bw = HAS_FLUXBOX_HANDLES(fw) ? 1 : fw->boundary_width; + borders->top_left.width = bw; + borders->bottom_right.width = bw; + borders->top_left.height = bw; borders->bottom_right.height = fw->boundary_width; + + title_thickness = fw->title_thickness; + if (HAS_TITLE(fw) && HAS_BORDER_UNDER_TITLE(fw) && !is_shaded) + { + title_thickness += bw; + } + switch (GET_TITLE_DIR(fw)) { case DIR_N: - borders->top_left.height += fw->title_thickness; + title_border = &borders->top_left.height; break; case DIR_S: - borders->bottom_right.height += fw->title_thickness; + title_border = &borders->bottom_right.height; break; case DIR_W: - borders->top_left.width += fw->title_thickness; + title_border = &borders->top_left.width; break; case DIR_E: - borders->bottom_right.width += fw->title_thickness; + title_border = &borders->bottom_right.width; break; } + + if (HAS_NO_TOP_BORDER(fw)) + { + *title_border = 1; + } + *title_border += title_thickness; + borders->total_size.width = borders->top_left.width + borders->bottom_right.width; borders->total_size.height = @@ -499,10 +528,34 @@ void get_window_borders_no_title( const FvwmWindow *fw, size_borders *borders) { - borders->top_left.width = fw->boundary_width; - borders->bottom_right.width = fw->boundary_width; - borders->top_left.height = fw->boundary_width; + int bw; + + bw = HAS_FLUXBOX_HANDLES(fw) ? 1 : fw->boundary_width; + + borders->top_left.width = bw; + borders->bottom_right.width = bw; + borders->top_left.height = bw; borders->bottom_right.height = fw->boundary_width; + + if (HAS_NO_TOP_BORDER(fw)) + { + switch (GET_TITLE_DIR(fw)) + { + case DIR_N: + borders->top_left.height = 1; + break; + case DIR_S: + borders->bottom_right.height = 1; + break; + case DIR_W: + borders->top_left.width = 1; + break; + case DIR_E: + borders->bottom_right.width = 1; + break; + } + } + borders->total_size.width = borders->top_left.width + borders->bottom_right.width; borders->total_size.height = Index: fvwm/geometry.h =================================================================== --- fvwm/geometry.h (revision 1) +++ fvwm/geometry.h (revision 14) @@ -44,6 +44,8 @@ FvwmWindow *fw, rectangle *ret_g); void get_window_borders( const FvwmWindow *fw, size_borders *borders); +void get_window_borders_shaded( + const FvwmWindow *fw, size_borders *borders); void get_window_borders_no_title( const FvwmWindow *fw, size_borders *borders); void set_window_border_size( Index: fvwm/events.c =================================================================== --- fvwm/events.c (revision 1) +++ fvwm/events.c (revision 14) @@ -158,6 +158,7 @@ int last_event_type = 0; Window PressedW = None; +Window HoverW = None; fd_set init_fdset; /* ---------------------------- local functions ---------------------------- */ @@ -1672,6 +1673,23 @@ return; } +static void redraw_hover(FvwmWindow* fw, Window w) +{ + if (fw) + { + int i; + for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; i++) + { + if (w == FW_W_BUTTON(fw, i)) + { + HoverW = FW_W_BUTTON(fw, i); + } + } + border_redraw_decorations(fw); + HoverW = None; + } +} + /* ---------------------------- event handlers ----------------------------- */ void HandleButtonPress(const evh_args_t *ea) @@ -1877,6 +1895,8 @@ ewp = &te->xcrossing; ENTER_DBG((stderr, "++++++++ en (%d): fw 0x%08x w 0x%08x sw 0x%08xmode 0x%x detail 0x%x '%s'\n", ++ecount, (int)fw, (int)ewp->window, (int)ewp->subwindow, ewp->mode, ewp->detail, fw?fw->visible_name:"(none)")); + redraw_hover(fw, ewp->window); + if (ewp->window == Scr.Root && ewp->subwindow == None && ewp->detail == NotifyInferior && ewp->mode == NotifyNormal) { @@ -2516,6 +2536,8 @@ DBUG("HandleLeaveNotify", "Routine Entered"); + redraw_hover(fw, te->xcrossing.window); + ENTER_DBG((stderr, "-------- ln (%d): fw 0x%08x w 0x%08x sw 0x%08x mode 0x%x detail 0x%x '%s'\n", ++ecount, (int)fw, (int)te->xcrossing.window, (int)te->xcrossing.subwindow, te->xcrossing.mode, te->xcrossing.detail, fw?fw->visible_name:"(none)")); /* Ignore LeaveNotify events while a window is resized or moved as a * wire frame; otherwise the window list may be screwed up. */ Index: fvwm/fvwm.h =================================================================== --- fvwm/fvwm.h (revision 1) +++ fvwm/fvwm.h (revision 14) @@ -198,6 +198,7 @@ unsigned has_icon_font : 1; unsigned has_no_border : 1; unsigned has_window_font : 1; + unsigned has_inactive_window_font : 1; unsigned title_dir : 2; unsigned user_states : 32; /* static flags that do not change dynamically after the window has @@ -223,6 +224,7 @@ unsigned do_not_show_on_map : 1; unsigned do_raise_transient : 1; unsigned do_resize_opaque : 1; + unsigned do_resize_outline_thin : 1; unsigned do_shrink_windowshade : 1; unsigned do_stack_transient_parent : 1; unsigned do_window_list_skip : 1; @@ -264,6 +266,12 @@ #define WINDOWSHADE_LAZY_MASK 0x3 unsigned windowshade_laziness : 2; unsigned use_title_decor_rotation : 1; + unsigned has_border_under_title : 1; + unsigned has_fluxbox_handles : 1; + unsigned has_rounded_corners_top : 1; + unsigned has_rounded_corners_bottom : 1; + unsigned has_slightly_rounded_corners : 1; + unsigned has_no_top_border : 1; focus_policy_t focus_policy; } s; } common_flags_t; @@ -355,11 +363,13 @@ unsigned is_viewport_moved : 1; unsigned is_window_being_moved_opaque : 1; unsigned is_window_font_loaded : 1; + unsigned is_inactive_window_font_loaded : 1; unsigned is_window_shaded : 1; unsigned used_title_dir_for_shading : 1; unsigned shaded_dir : 3; unsigned using_default_icon_font : 1; unsigned using_default_window_font : 1; + unsigned using_default_inactive_window_font : 1; #define ICON_HINT_NEVER 0 #define ICON_HINT_ONCE 1 #define ICON_HINT_MULTIPLE 2 @@ -517,6 +527,7 @@ unsigned has_color_fore_hi : 1; unsigned has_decor : 1; unsigned has_handle_width : 1; + unsigned has_corner_width : 1; unsigned has_icon : 1; unsigned has_icon_boxes : 1; unsigned has_icon_size_limits : 1; @@ -605,6 +616,7 @@ char icon_title_relief; char *icon_font; char *window_font; + char *inactive_window_font; char *fore_color_name; char *back_color_name; char *fore_color_name_hi; @@ -619,6 +631,7 @@ short border_width; /* resize handle width */ short handle_width; + short corner_width; int layer; int start_desk; int start_page_x; @@ -693,6 +706,7 @@ unsigned buttons_lit : NUMBER_OF_TITLE_BUTTONS; unsigned buttons_inverted : NUMBER_OF_TITLE_BUTTONS; unsigned buttons_toggled : NUMBER_OF_TITLE_BUTTONS; + unsigned buttons_hover : NUMBER_OF_TITLE_BUTTONS; unsigned parts_drawn : 12; unsigned parts_lit : 12; unsigned parts_inverted : 12; @@ -717,6 +731,7 @@ /* title font */ FlocaleFont *title_font; + FlocaleFont *inactive_title_font; /* /Y coordinate to draw the title name */ short title_text_offset; short title_length; Index: fvwm/frame.c =================================================================== --- fvwm/frame.c (revision 1) +++ fvwm/frame.c (revision 14) @@ -434,6 +434,8 @@ /* inform the modules of the change */ BroadcastConfig(M_CONFIGURE_WINDOW,fw); + frame_make_rounded_corners(fw); + return; } @@ -1367,7 +1369,14 @@ tb_thick = fw->title_thickness; nbuttons = fw->nr_left_buttons + fw->nr_right_buttons; nbuttons_big = 0; - b_length = tb_thick; + if (fw->decor->button_width == 0) + { + b_length = tb_thick; + } + else + { + b_length = fw->decor->button_width; + } t_length = tb_length - nbuttons * b_length; if (nbuttons > 0 && t_length < MIN_WINDOW_TITLE_LENGTH) { @@ -1957,6 +1966,7 @@ FShapeSet); } frame_setup_shape(fw, mra->end_g.width, mra->end_g.height, fw->wShaped); + frame_make_rounded_corners(fw); if (mra->flags.do_restore_gravity) { mra->grav.client_grav = fw->hints.win_gravity; @@ -2078,6 +2088,210 @@ return; } +void draw_rounded_mask(Window win, int width, int height, Bool slightlyrounded, window_parts draw_parts, int col) +{ + Pixmap pm; + GC gc; + rectangle rect; + int w,h; + unsigned long valuemask; + int x; + int lstart, lend; + int l0[] = { 0, 1, 0, 1, 2, 3 }; + int l1[] = { 1, 2, 1, 2, 3, 5 }; + int l2[] = { 2, 1, 5, 3, 2, 1 }; + int l3[] = { 1, 1, 1, 1, 1, 2 }; + + if (slightlyrounded) + { + lstart = 0; + lend = 2; + } + else + { + lstart = 2; + lend = 6; + } + + XGetGeometry( + dpy, win, &JunkRoot, &rect.x, &rect.y, + &rect.width, &rect.height, &JunkBW, &JunkDepth); + + w = rect.width; + h = rect.height; + pm = XCreatePixmap(dpy, win, width, height, 1); + gc = Scr.MonoGC; + XSetForeground(dpy, gc, !col); + XFillRectangle(dpy, pm, gc, 0, 0, w, h); + XSetForeground(dpy, gc, col); + + /* Draw a rounded shape on the corners of the pixmap */ + for (x = lstart; x < lend; x++) + { + if (draw_parts & PART_BORDER_NW) + { + XFillRectangle(dpy, pm, gc, 0, l0[x], l2[x], l3[x]); + } + if (draw_parts & PART_BORDER_NE) + { + XFillRectangle(dpy, pm, gc, w-l2[x], l0[x], l2[x], l3[x]); + } + if (draw_parts & PART_BORDER_SW) + { + XFillRectangle(dpy, pm, gc, 0, h-l1[x], l2[x], l3[x]); + } + if (draw_parts & PART_BORDER_SE) + { + XFillRectangle(dpy, pm, gc, w-l2[x], h-l1[x], l2[x], l3[x]); + } + } + + FShapeCombineMask(dpy, win, ShapeBounding, 0, 0, pm, col==1 ? ShapeSubtract : ShapeSet); + + XFreePixmap(dpy, pm); +} + +static void frame_draw_rounded_mask(FvwmWindow *fw, Window win, window_parts draw_parts, int col) +{ + draw_rounded_mask(win, fw->frame_g.width, fw->frame_g.height, + HAS_SLIGHTLY_ROUNDED_CORNERS(fw), IS_MAXIMIZED(fw)?0:draw_parts, col); +} + +/* Returns a corner corrected for rotation of the titlebar (ie button 1 is always NW) */ +#define SWAP_CORNER(PART) corner = corner & (PART) ? corner ^ (PART) : corner +static window_parts __get_corner(window_parts corner, FvwmWindow *fw) +{ + int dir; + + dir = GET_TITLE_DIR(fw); + + /* Flip horizontally (relative to tb) if the titlebar is rotated */ + if ((dir == DIR_N && IS_TOP_TITLE_ROTATED(fw)) + || (dir == DIR_S && !IS_BOTTOM_TITLE_ROTATED(fw)) + || (dir == DIR_W && IS_LEFT_TITLE_ROTATED_CW(fw)) + || (dir == DIR_E && !IS_RIGHT_TITLE_ROTATED_CW(fw))) + { + SWAP_CORNER(PART_BORDER_NE | PART_BORDER_NW); + } + + /* Swap SE/SW so that shift left goes in a clockwise order */ + SWAP_CORNER(PART_BORDER_SW | PART_BORDER_SE); + + /* Rotate clockwise depending on dir */ + corner <<= dir; + if (corner > PART_BORDER_SE) + { + corner = corner >> 4; + } + + /* Swap SE/SW back */ + SWAP_CORNER(PART_BORDER_SW | PART_BORDER_SE); + + return corner; +} + +void frame_make_rounded_corners(FvwmWindow *fw) +{ + rectangle rect; + window_parts draw_parts; + window_parts mask; + int x; + FvwmWindow *left_button = 0; + FvwmWindow *right_button = 0; + + if (!fw || !FShapesSupported) + { + return; + } + + window_parts corner_nw = __get_corner(PART_BORDER_NW, fw); + window_parts corner_ne = __get_corner(PART_BORDER_NE, fw); + window_parts corner_se = __get_corner(PART_BORDER_SE, fw); + window_parts corner_sw = __get_corner(PART_BORDER_SW, fw); + + for (x = 9;x>=0;x--) + { + if (FW_W_BUTTON(fw, x) != None) + { + if (x%2 == 0) + { + left_button = FW_W_BUTTON(fw, x); + } + else + { + right_button = FW_W_BUTTON(fw, x); + } + } + } + + mask = 0; + if (HAS_ROUNDED_CORNERS_TOP(fw)) + { + mask |= corner_ne | corner_nw; + } + if (HAS_ROUNDED_CORNERS_BOTTOM(fw)) + { + mask |= corner_se | corner_sw; + } + + /* Draw mask on each corner of the window. This involves the frame, title, + * buttons and parent wins depending on the window configuration */ + rect = fw->frame_g; + frame_draw_rounded_mask(fw, FW_W_FRAME(fw), mask, 1); + if (HAS_TITLE(fw)) + { + draw_parts = 0; + if (!left_button) + { + draw_parts |= corner_nw; + } + if (!right_button) + { + draw_parts |= corner_ne; + } + if (IS_SHADED(fw)) + { + if (!left_button) + { + draw_parts |= corner_sw; + } + else + { + frame_draw_rounded_mask(fw, left_button, mask & (corner_nw|corner_sw), 0); + } + if (!right_button) + { + draw_parts |= corner_se; + } + else + { + frame_draw_rounded_mask(fw, right_button, mask & (corner_ne|corner_se), 0); + } + } + frame_draw_rounded_mask(fw, FW_W_TITLE(fw), mask & draw_parts, 0); + + if (!IS_SHADED(fw)) + { + frame_draw_rounded_mask(fw, FW_W_PARENT(fw), mask & (corner_sw|corner_se), 0); + + if (left_button) + { + frame_draw_rounded_mask(fw, left_button, mask & corner_nw, 0); + } + if (right_button) + { + frame_draw_rounded_mask(fw, right_button, mask & corner_ne, 0); + } + } + } + else + { + frame_draw_rounded_mask(fw, FW_W_PARENT(fw), mask & PART_CORNERS, 0); + } + + XFlush(dpy); +} + /**************************************************************************** * * Sets up the shaped window borders Index: fvwm/frame.h =================================================================== --- fvwm/frame.h (revision 1) +++ fvwm/frame.h (revision 14) @@ -5,6 +5,8 @@ /* ---------------------------- included header files ---------------------- */ +#include "borders.h" + /* ---------------------------- global definitions ------------------------- */ /* ---------------------------- global macros ------------------------------ */ @@ -71,5 +73,7 @@ Bool do_send_configure_notify); void frame_setup_shape( FvwmWindow *fw, int w, int h, int shape_mode); +void frame_make_rounded_corners(FvwmWindow *fw); +void draw_rounded_mask(Window win, int width, int height, Bool slightlyrounded, window_parts draw_parts, int col); #endif /* FRAME_H */ Index: fvwm/add_window.c =================================================================== --- fvwm/add_window.c (revision 1) +++ fvwm/add_window.c (revision 14) @@ -632,6 +632,18 @@ fw->title_font = Scr.DefaultFont; SET_USING_DEFAULT_WINDOW_FONT(fw, 1); + if (IS_INACTIVE_WINDOW_FONT_LOADED(fw) && !USING_DEFAULT_INACTIVE_WINDOW_FONT(fw) && + fw->title_font != Scr.DefaultFont) + { + FlocaleUnloadFont(dpy, fw->title_font); + } + SET_INACTIVE_WINDOW_FONT_LOADED(fw, 0); + /* Fall back to default font. There are some race conditions when a + * window is destroyed and recaptured where an invalid font might be + * accessed otherwise. */ + fw->title_font = Scr.DefaultFont; + SET_USING_DEFAULT_INACTIVE_WINDOW_FONT(fw, 1); + return; } @@ -1626,6 +1638,7 @@ { int width; int offset; + style_flags *sflags = &(pstyle->flags); get_title_font_size_and_offset( fw, S_TITLE_DIR(SCF(*pstyle)), @@ -1637,6 +1650,10 @@ fw->title_thickness = width; fw->title_text_offset = offset; fw->corner_width = fw->title_thickness + fw->boundary_width; + if (SHAS_CORNER_WIDTH(sflags)) + { + fw->corner_width = SGET_CORNER_WIDTH(*pstyle); + } if (!HAS_TITLE(fw)) { fw->title_thickness = 0; @@ -1673,6 +1690,25 @@ } SET_WINDOW_FONT_LOADED(fw, 1); } + /* load inactive font */ + if (!IS_INACTIVE_WINDOW_FONT_LOADED(fw)) + { + if (S_HAS_INACTIVE_WINDOW_FONT(SCF(*pstyle)) && + SGET_INACTIVE_WINDOW_FONT(*pstyle) && + (fw->inactive_title_font = + FlocaleLoadFont(dpy, SGET_INACTIVE_WINDOW_FONT(*pstyle), "FVWM"))) + { + SET_USING_DEFAULT_INACTIVE_WINDOW_FONT(fw, 0); + } + else + { + /* no explicit font or failed to load, use active title font + * instead */ + fw->inactive_title_font = fw->title_font; + SET_USING_DEFAULT_INACTIVE_WINDOW_FONT(fw, 1); + } + SET_INACTIVE_WINDOW_FONT_LOADED(fw, 1); + } setup_title_geometry(fw, pstyle); return; @@ -2529,6 +2565,8 @@ fw = NULL; } + frame_make_rounded_corners(fw); + return fw; } Index: fvwm/screen.h =================================================================== --- fvwm/screen.h (revision 1) +++ fvwm/screen.h (revision 14) @@ -99,6 +99,7 @@ AdjustedPixmapButton, ShrunkPixmapButton, MultiPixmap, + MultiBorder, MiniIconButton, SolidButton, ColorsetButton @@ -158,6 +159,9 @@ Pixel *pixels; unsigned short solid_flags; } mp; + struct { + FvwmPicture *pixmaps[8]; + } mb; struct { int cs; @@ -198,12 +202,16 @@ BS_All = -1, BS_ActiveUp, BS_ActiveDown, + BS_ActiveHover, BS_InactiveUp, BS_InactiveDown, + BS_InactiveHover, BS_ToggledActiveUp, BS_ToggledActiveDown, + BS_ToggledActiveHover, BS_ToggledInactiveUp, BS_ToggledInactiveDown, + BS_ToggledInactiveHover, BS_MaxButtonState, BS_MaxButtonStateMask = BS_MaxButtonState - 1, BS_Active, @@ -216,10 +224,13 @@ BS_AllInactive, BS_AllUp, BS_AllDown, + BS_AllHover, BS_AllActiveUp, BS_AllActiveDown, + BS_AllActiveHover, BS_AllInactiveUp, BS_AllInactiveDown, + BS_AllInactiveHover, BS_MaxButtonStateName } ButtonState; @@ -286,6 +297,8 @@ #endif int title_height; /* explicitly specified title bar height */ int min_title_height; + int button_width; + int title_text_offset; /* titlebar buttons */ TitleButton buttons[NUMBER_OF_TITLE_BUTTONS]; TitleButton titlebar; @@ -505,6 +518,7 @@ unsigned use_active_down_buttons : 1; unsigned use_inactive_buttons : 1; unsigned use_inactive_down_buttons : 1; + unsigned use_hover_buttons : 1; } gs; /* global style structure */ struct { Index: fvwm/window_flags.h =================================================================== --- fvwm/window_flags.h (revision 1) +++ fvwm/window_flags.h (revision 14) @@ -21,6 +21,8 @@ ((fw)->flags.common.s.do_raise_transient) #define DO_RESIZE_OPAQUE(fw) \ ((fw)->flags.common.s.do_resize_opaque) +#define DO_RESIZE_OUTLINE_THIN(fw) \ + ((fw)->flags.common.s.do_resize_outline_thin) #define DO_SHRINK_WINDOWSHADE(fw) \ ((fw)->flags.common.s.do_shrink_windowshade) #define SET_DO_SHRINK_WINDOWSHADE(fw,x) \ @@ -296,18 +298,54 @@ (fw)->flags.common.s.is_bottom_title_rotated = !!(x) #define SETM_IS_BOTTOM_TITLE_ROTATED(fw,x) \ (fw)->flag_mask.common.s.is_bottom_title_rotated = !!(x) -#define IS_BOTTOM_TITLE_ROTATED(fw) \ - ((fw)->flags.common.s.is_bottom_title_rotated) -#define SET_IS_BOTTOM_TITLE_ROTATED(fw,x) \ - (fw)->flags.common.s.is_bottom_title_rotated = !!(x) -#define SETM_IS_BOTTOM_TITLE_ROTATED(fw,x) \ - (fw)->flag_mask.common.s.is_bottom_title_rotated = !!(x) +#define IS_TOP_TITLE_ROTATED(fw) \ + ((fw)->flags.common.s.is_top_title_rotated) +#define SET_IS_TOP_TITLE_ROTATED(fw,x) \ + (fw)->flags.common.s.is_top_title_rotated = !!(x) +#define SETM_IS_TOP_TITLE_ROTATED(fw,x) \ + (fw)->flag_mask.common.s.is_top_title_rotated = !!(x) #define USE_TITLE_DECOR_ROTATION(fw) \ ((fw)->flags.common.s.use_title_decor_rotation) #define SET_USE_TITLE_DECOR_ROTATION(fw,x) \ (fw)->flags.common.s.use_title_decor_rotation = !!(x) #define SETM_USE_TITLE_DECOR_ROTATION(fw,x) \ (fw)->flag_mask.common.s.use_title_decor_rotation = !!(x) +#define HAS_BORDER_UNDER_TITLE(fw) \ + ((fw)->flags.common.s.has_border_under_title) +#define SET_HAS_BORDER_UNDER_TITLE(fw,x) \ + (fw)->flags.common.s.has_border_under_title = !!(x) +#define SETM_HAS_BORDER_UNDER_TITLE(fw,x) \ + (fw)->flag_mask.common.s.has_border_under_title = !!(x) +#define HAS_FLUXBOX_HANDLES(fw) \ + ((fw)->flags.common.s.has_fluxbox_handles && (fw->flags.has_handles)) +#define SET_HAS_FLUXBOX_HANDLES(fw,x) \ + (fw)->flags.common.s.has_fluxbox_handles = !!(x) +#define SETM_HAS_FLUXBOX_HANDLES(fw,x) \ + (fw)->flag_mask.common.s.has_fluxbox_handles = !!(x) +#define HAS_ROUNDED_CORNERS_TOP(fw) \ + ((fw)->flags.common.s.has_rounded_corners_top) +#define SET_HAS_ROUNDED_CORNERS_TOP(fw,x) \ + (fw)->flags.common.s.has_rounded_corners_top = !!(x) +#define SETM_HAS_ROUNDED_CORNERS_TOP(fw,x) \ + (fw)->flag_mask.common.s.has_rounded_corners_top = !!(x) +#define HAS_ROUNDED_CORNERS_BOTTOM(fw) \ + ((fw)->flags.common.s.has_rounded_corners_bottom) +#define SET_HAS_ROUNDED_CORNERS_BOTTOM(fw,x) \ + (fw)->flags.common.s.has_rounded_corners_bottom = !!(x) +#define SETM_HAS_ROUNDED_CORNERS_BOTTOM(fw,x) \ + (fw)->flag_mask.common.s.has_rounded_corners_bottom = !!(x) +#define HAS_SLIGHTLY_ROUNDED_CORNERS(fw) \ + ((fw)->flags.common.s.has_slightly_rounded_corners) +#define SET_HAS_SLIGHTLY_ROUNDED_CORNERS_BOTTOM(fw,x) \ + (fw)->flags.common.s.has_slightly_rounded_corners = !!(x) +#define SETM_HAS_SLIGHTLY_ROUNDED_CORNERS(fw,x) \ + (fw)->flag_mask.common.s.has_slightly_rounded_corners = !!(x) +#define HAS_NO_TOP_BORDER(fw) \ + ((fw)->flags.common.s.has_no_top_border) +#define SET_HAS_NO_TOP_BORDER(fw,x) \ + (fw)->flags.common.s.has_no_top_border = !!(x) +#define SETM_HAS_NO_TOP_BORDER(fw,x) \ + (fw)->flag_mask.common.s.has_no_top_border = !!(x) /* access to the special flags of a window */ #define DO_REUSE_DESTROYED(fw) \ @@ -554,6 +592,12 @@ (fw)->flags.is_window_font_loaded = !!(x) #define SETM_WINDOW_FONT_LOADED(fw,x) \ (fw)->flag_mask.is_window_font_loaded = !!(x) +#define IS_INACTIVE_WINDOW_FONT_LOADED(fw) \ + ((fw)->flags.is_inactive_window_font_loaded) +#define SET_INACTIVE_WINDOW_FONT_LOADED(fw,x) \ + (fw)->flags.is_inactive_window_font_loaded = !!(x) +#define SETM_INACTIVE_WINDOW_FONT_LOADED(fw,x) \ + (fw)->flag_mask.is_inactive_window_font_loaded = !!(x) #define CR_MOTION_METHOD(fw) \ ((fw)->flags.cr_motion_method) #define SET_CR_MOTION_METHOD(fw,x) \ @@ -596,6 +640,12 @@ (fw)->flags.using_default_window_font = !!(x) #define SETM_USING_DEFAULT_WINDOW_FONT(fw,x) \ (fw)->flag_mask.using_default_window_font = !!(x) +#define USING_DEFAULT_INACTIVE_WINDOW_FONT(fw) \ + ((fw)->flags.using_default_inactive_window_font) +#define SET_USING_DEFAULT_INACTIVE_WINDOW_FONT(fw,x) \ + (fw)->flags.using_default_inactive_window_font = !!(x) +#define SETM_USING_DEFAULT_INACTIVE_WINDOW_FONT(fw,x) \ + (fw)->flag_mask.using_default_inactive_window_font = !!(x) #define USING_DEFAULT_ICON_FONT(fw) \ ((fw)->flags.using_default_icon_font) #define SET_USING_DEFAULT_ICON_FONT(fw,x) \ Index: fvwm/builtins.c =================================================================== --- fvwm/builtins.c (revision 1) +++ fvwm/builtins.c (revision 14) @@ -93,12 +93,16 @@ { "ActiveUp", "ActiveDown", + "ActiveHover", "InactiveUp", "InactiveDown", + "InactiveHover", "ToggledActiveUp", "ToggledActiveDown", + "ToggledActiveHover", "ToggledInactiveUp", "ToggledInactiveDown", + "ToggledInactiveHover", "Active", "Inactive", "ToggledActive", @@ -109,10 +113,13 @@ "AllInactive", "AllUp", "AllDown", + "AllHover", "AllActiveUp", "AllActiveDown", + "AllActiveHover", "AllInactiveUp", "AllInactiveDown", + "AllInactiveHover", NULL }; @@ -485,6 +492,36 @@ if (action) action += next; } + else if (!do_add && StrEquals(parm,"buttonwidth")) + { + int width = 0; + int next = 0; + + sscanf(action, "%d%n", &width, &next); + + if (decor->button_width != width) + { + decor->button_width = width; + decor->flags.has_changed = 1; + } + if (action) + action += next; + } + else if (!do_add && StrEquals(parm,"TextOffset")) + { + int offset = 0; + int next = 0; + + sscanf(action, "%d%n", &offset, &next); + + if (decor->title_text_offset != offset) + { + decor->title_text_offset = offset; + decor->flags.has_changed = 1; + } + if (action) + action += next; + } else if (!do_add && StrEquals(parm,"MinHeight")) { int height = 0; @@ -755,6 +792,50 @@ return s; } +static char *ReadMultiBorderDecor(char *s, DecorFace *df) +{ + FvwmPictureAttributes fpa; + FvwmPicture **pm; + char *token; + int x; + int y; + + pm = df->u.mb.pixmaps; + df->style.face_type = MultiBorder; + + for (x = 0; x < 8; x++) + { + s = DoPeekToken(s, &token, " ", NULL, NULL); + if (s == NULL) + { + break; + } + if (pm[x]) + { + PDestroyFvwmPicture(dpy, pm[x]); + } + pm[x] = PCacheFvwmPicture(dpy, Scr.NoFocusWin, NULL, + token, fpa); + + if (!pm[x]) + { + fvwm_msg(ERR, "ReadMultiBorderDecor", + "Pixmap '%s' could not be loaded", + token); + for(y = 0; y < x; y++) + { + if (pm[y]) + { + PDestroyFvwmPicture(dpy, pm[y]); + } + } + return NULL; + } + } + + return s; +} + /* * * DestroyFvwmDecor -- frees all memory assocated with an FvwmDecor @@ -1464,6 +1545,14 @@ free(df->u.mp.pixels); } break; + case MultiBorder: + for (i = 0; i < 8; i++) + { + if (df->u.mb.pixmaps[i]) + { + PDestroyFvwmPicture(dpy, df->u.mb.pixmaps[i]); + } + } case VectorButton: case DefaultVectorButton: if (df->u.vector.x) @@ -1828,6 +1917,15 @@ return False; } } + else if (strncasecmp(style,"MultiBorder",11)==0) + { + s = ReadMultiBorderDecor(s, df); + if (!s) + { + return False; + } + DFS_FACE_TYPE(df->style) = MultiBorder; + } else if (FMiniIconsSupported && strncasecmp (style, "MiniIcon", 8) == 0) { Index: fvwm/borders.c =================================================================== --- fvwm/borders.c (revision 1) +++ fvwm/borders.c (revision 14) @@ -65,6 +65,7 @@ /* ---------------------------- imports ------------------------------------ */ extern Window PressedW; +extern Window HoverW; /* ---------------------------- included code files ------------------------ */ @@ -185,6 +186,7 @@ unsigned clear_bmask : NUMBER_OF_TITLE_BUTTONS; unsigned draw_bmask : NUMBER_OF_TITLE_BUTTONS; unsigned max_bmask : NUMBER_OF_TITLE_BUTTONS; + unsigned hover_bmask : NUMBER_OF_TITLE_BUTTONS; ButtonState bstate[NUMBER_OF_TITLE_BUTTONS]; unsigned is_title_pressed : 1; unsigned is_title_lit : 1; @@ -290,8 +292,12 @@ /* rules to get button state */ static ButtonState border_flags_to_button_state( - int is_pressed, int is_lit, int is_toggled) + int is_pressed, int is_lit, int is_toggled, int is_hover) { + if (is_lit && is_hover && Scr.gs.use_hover_buttons) + { + return BS_ActiveHover; + } if (!is_lit && Scr.gs.use_inactive_buttons) { if (is_pressed && Scr.gs.use_inactive_down_buttons) @@ -589,7 +595,7 @@ /* check if state changed */ old_state = border_flags_to_button_state( (fw->decor_state.parts_inverted & PART_TITLE), - (fw->decor_state.parts_lit & PART_TITLE), 0); + (fw->decor_state.parts_lit & PART_TITLE), 0, 0); if (old_state != td->tbstate.tstate) { draw_parts |= PART_TITLE; @@ -613,7 +619,8 @@ old_state = border_flags_to_button_state( (fw->decor_state.buttons_inverted & mask), (fw->decor_state.buttons_lit & mask), - (fw->decor_state.buttons_toggled & mask)); + (fw->decor_state.buttons_toggled & mask), + (fw->decor_state.buttons_hover & mask)); if (old_state != td->tbstate.bstate[i]) { draw_parts |= PART_BUTTONS; @@ -1279,6 +1286,7 @@ True : False; xgcv.fill_style = FillSolid; valuemask = GCFillStyle; + if (!bg->flags.use_pixmap) { /* solid pixel */ @@ -1384,6 +1392,24 @@ return; } +static void border_fill_fluxbox_handle( + Pixmap dest_pix, rectangle *dest_g, common_decorations_type *cd, Bool corner) +{ + corner = False; + /* Main Color */ + XFillRectangle( + dpy, dest_pix, corner ? cd->shadow_gc : cd->relief_gc, dest_g->x + 1, dest_g->y + 1, + dest_g->width - dest_g->x - 3, dest_g->height - dest_g->y - 2); + /* Right Shadow */ + XFillRectangle( + dpy, dest_pix, cd->shadow_gc, dest_g->x + dest_g->width - 2, dest_g->y + 1, + 1, dest_g->height - dest_g->y - 2); + /* Bottom Shadow */ + XFillRectangle( + dpy, dest_pix, cd->shadow_gc, dest_g->x + 1, dest_g->height - 2, + dest_g->width - dest_g->x - 3, 1); +} + /* create a root transparent colorset bg, we take in account a possible * drawing rotation */ static Pixmap border_create_root_transparent_pixmap( @@ -1451,13 +1477,26 @@ static void border_get_border_background( pixmap_background_type *bg, common_decorations_type *cd, - rectangle *part_g, rectangle *relative_g, int *free_bg_pixmap, Window w) + rectangle *part_g, rectangle *relative_g, int *free_bg_pixmap, Window w, FvwmPicture *fp) { *free_bg_pixmap = False; - if (cd->texture_pixmap) + if (fp) { bg->flags.use_pixmap = 1; + bg->pixmap.p = fp->picture; + bg->pixmap.g.width = fp->width; + bg->pixmap.g.height = fp->height; + bg->pixmap.shape = None; + bg->pixmap.alpha = None; + bg->pixmap.depth = fp->depth; + bg->pixmap.flags.is_tiled = 1; + bg->pixmap.flags.is_stretched = 0; + bg->pixmap.fra.mask = 0; + } + else if (cd->texture_pixmap) + { + bg->flags.use_pixmap = 1; bg->pixmap.p = cd->texture_pixmap; bg->pixmap.g.width = cd->texture_pixmap_width; bg->pixmap.g.height = cd->texture_pixmap_height; @@ -1523,6 +1562,18 @@ return; } +#define DRAWBORDER(PIX,X,Y,WIDTH,HEIGHT) \ + { \ + fp = df->u.mb.pixmaps[PIX]; \ + border_get_border_background( \ + &bg, cd, &part_g, &relative_g, &free_bg_pixmap, w, fp); \ + r.x = X; \ + r.y = Y; \ + r.width = WIDTH; \ + r.height = HEIGHT; \ + border_fill_pixmap_background(p, &r, &bg, cd); \ + } + static void border_draw_one_border_part( common_decorations_type *cd, FvwmWindow *fw, rectangle *sidebar_g, rectangle *frame_g, border_relief_descr *br, window_parts part, @@ -1535,6 +1586,7 @@ Pixmap p; Window w; Bool free_bg_pixmap = False; + int x, y, width, height; /* make a pixmap */ border_get_part_geometry(fw, part, sidebar_g, &part_g, &w); @@ -1548,8 +1600,28 @@ relative_g.height = fw->frame_g.height; relative_g.x = part_g.x; relative_g.y = part_g.y; - border_get_border_background( - &bg, cd, &part_g, &relative_g, &free_bg_pixmap, w); + + DecorFace* df; + FvwmPicture* fp = 0; + df = border_get_border_style(fw, (Scr.Hilite == fw)); + if (DFS_FACE_TYPE(df->style) == MultiBorder) + { + int id = -1; + if (part==PART_BORDER_NW) id = 0; + if (part==PART_BORDER_N) id = 1; + if (part==PART_BORDER_NE) id = 2; + if (part==PART_BORDER_E) id = 3; + if (part==PART_BORDER_SE) id = 4; + if (part==PART_BORDER_S) id = 5; + if (part==PART_BORDER_SW) id = 6; + if (part==PART_BORDER_W) id = 7; + + if (id>=0 && df->u.mb.pixmaps[id]) + { + fp = df->u.mb.pixmaps[id]; + } + } + if (cd->texture_pixmap) { switch (part) @@ -1592,19 +1664,123 @@ bg.pixmap.g.x = 0; bg.pixmap.g.y = 0; } - /* set the geometry for drawing the Tiled pixmap; maybe add the relief - * as offset? */ - pix_g.x = 0; - pix_g.y = 0; - pix_g.width = part_g.width; - pix_g.height = part_g.height; - border_fill_pixmap_background(p, &pix_g, &bg, cd); + + border_get_border_background( + &bg, cd, &part_g, &relative_g, &free_bg_pixmap, w, fp); + + int px = bg.pixmap.g.x; + int py = bg.pixmap.g.y; + + if (fp) + { + /* Position pixmap so that it's aligned to the edge of the window */ + if (part & PART_BOTTOM) + { + bg.pixmap.g.y = fp->height - sidebar_g->y; + if (part == PART_BORDER_S) + { + bg.pixmap.g.y = fp->height - fw->boundary_width; + } + } + if (part & PART_RIGHT) + { + bg.pixmap.g.x = fp->width - sidebar_g->x; + if (part == PART_BORDER_E) + { + bg.pixmap.g.x = fp->width - fw->boundary_width; + } + } + } + + /* set the geometry for drawing the Tiled pixmap; maybe add the relief + * as offset? */ + pix_g.x = 0; + pix_g.y = 0; + pix_g.width = part_g.width; + pix_g.height = part_g.height; + + border_fill_pixmap_background(p, &pix_g, &bg, cd); + + bg.pixmap.g.x = px; + bg.pixmap.g.y = py; + + if (fp) + { + height = pix_g.height; + width = pix_g.width; + x = pix_g.x; + y = pix_g.y; + + /* draw parts from other borders that overflow because of their width/height */ + rectangle r; + if (part==PART_BORDER_S) + { + DRAWBORDER(6, pix_g.x - sidebar_g->x, fw->boundary_width - fp->height, fp->width - sidebar_g->x, height); + DRAWBORDER(4, pix_g.width - fp->width + sidebar_g->x, fw->boundary_width - fp->height, fp->width - sidebar_g->x, height); + } + if (!IS_SHADED(fw)) + { + if (part==PART_BORDER_E) + { + DRAWBORDER(2, fw->boundary_width - fp->width, pix_g.y - sidebar_g->y, width, fp->height - sidebar_g->y); + DRAWBORDER(4, fw->boundary_width - fp->width, pix_g.height - fp->height + sidebar_g->y, width, fp->height - sidebar_g->y); + } + if (part==PART_BORDER_W) + { + DRAWBORDER(0, x, pix_g.y - sidebar_g->y, width, fp->height - sidebar_g->y); + DRAWBORDER(6, x, pix_g.height - fp->height + sidebar_g->y, width, fp->height - sidebar_g->y); + } + } + if (part==PART_BORDER_N) + { + DRAWBORDER(2, pix_g.width - fp->width + sidebar_g->x, y, fp->width - sidebar_g->x, height); + DRAWBORDER(0, pix_g.x - fp->width + sidebar_g->x, y, fp->width - sidebar_g->x, height); + } + /* Use the top border for the whole height of the titlebar, overflowing to parts of the bottom corners if shaded */ + if (IS_SHADED(fw)) + { + if (part==PART_BORDER_W) + { + DRAWBORDER(0, 0, -height, width, height); + } + if (part==PART_BORDER_SW) + { + DRAWBORDER(0, 0, -height*2, width, height - fw->boundary_width); + } + if (part==PART_BORDER_E) + { + DRAWBORDER(2, fw->boundary_width - fp->width, -height, width, height); + } + if (part==PART_BORDER_SE) + { + DRAWBORDER(2, width - fp->width, -height*2, width, height - fw->boundary_width); + } + } + } + + if (HAS_FLUXBOX_HANDLES(fw) && (part & PART_BOTTOM)) + { + pix_g.y = part_g.height - fw->boundary_width; + if (part != PART_BORDER_S) + { + pix_g.width++; + } + if (part == PART_BORDER_SE) + { + pix_g.x--; + } + if (fw->boundary_width > 2) + { + border_fill_fluxbox_handle(p, &pix_g, cd, !(part & PART_BORDER_S)); + } + } + if (free_bg_pixmap && bg.pixmap.p) { XFreePixmap(dpy, bg.pixmap.p); } /* draw the relief over the background */ - if (!br->relief.is_flat) + if (!br->relief.is_flat && !HAS_FLUXBOX_HANDLES(fw)) { border_draw_part_relief(br, frame_g, &part_g, p, is_inverted); /* draw the handle marks */ @@ -1652,7 +1828,7 @@ do_clear); } } - + return; } @@ -3279,7 +3455,7 @@ relative_g.x = button_g->x; relative_g.y = button_g->y; border_get_border_background( - &bg, td->cd, button_g, &relative_g, &free_bg_pixmap, w); + &bg, td->cd, button_g, &relative_g, &free_bg_pixmap, w, 0); bg.pixmap.g.x = 0; bg.pixmap.g.y = 0; /* set the geometry for drawing the Tiled pixmap; @@ -3510,7 +3686,7 @@ static void border_draw_title_mono( FvwmWindow *fw, titlebar_descr *td, title_draw_descr *tdd, - FlocaleWinString *fstr, Pixmap dest_pix) + FlocaleWinString *fstr, Pixmap dest_pix, Bool do_hilight) { int has_vt; @@ -3520,7 +3696,8 @@ td->offset - 2, 0, td->length+4, fw->title_thickness); if (fw->visible_name != (char *)NULL) { - FlocaleDrawString(dpy, fw->title_font, fstr, 0); + FlocaleDrawString(dpy, do_hilight ? fw->title_font : + fw->inactive_title_font, fstr, 0); } /* for mono, we clear an area in the title bar where the window * title goes, so that its more legible. For color, no need */ @@ -3584,7 +3761,7 @@ static void border_draw_title_deep( FvwmWindow *fw, titlebar_descr *td, title_draw_descr *tdd, - FlocaleWinString *fstr, Pixmap dest_pix, Window w) + FlocaleWinString *fstr, Pixmap dest_pix, Window w, Bool do_hilight) { DecorFace *df; pixmap_background_type bg; @@ -3606,14 +3783,15 @@ 1); } } - FlocaleDrawString(dpy, fw->title_font, &tdd->fstr, 0); + FlocaleDrawString(dpy, do_hilight ? fw->title_font : + fw->inactive_title_font, &tdd->fstr, 0); return; } static void border_get_titlebar_draw_descr( FvwmWindow *fw, titlebar_descr *td, title_draw_descr *tdd, - Pixmap dest_pix) + Pixmap dest_pix, Bool do_hilight) { rectangle *title_g; @@ -3629,7 +3807,8 @@ tdd->rgc = td->cd->relief_gc; tdd->sgc = td->cd->shadow_gc; } - NewFontAndColor(fw->title_font, td->cd->fore_color, td->cd->back_color); + NewFontAndColor(do_hilight ? fw->title_font : fw->inactive_title_font, + td->cd->fore_color, td->cd->back_color); title_g = &td->layout.title_g; tdd->tstyle = &TB_STATE( GetDecor(fw, titlebar))[td->tbstate.tstate].style; @@ -3649,12 +3828,12 @@ if (td->has_vt) { tdd->fstr.y = td->offset; - tdd->fstr.x = fw->title_text_offset + 1; + tdd->fstr.x = fw->title_text_offset + 1 + fw->decor->title_text_offset; } else { tdd->fstr.x = td->offset; - tdd->fstr.y = fw->title_text_offset + 1; + tdd->fstr.y = fw->title_text_offset + 1 + fw->decor->title_text_offset; } if (td->cd->cs >= 0) { @@ -3667,7 +3846,7 @@ } static void border_set_title_pixmap( - FvwmWindow *fw, titlebar_descr *td, Pixmap *dest_pix, Window w) + FvwmWindow *fw, titlebar_descr *td, Pixmap *dest_pix, Window w, Bool do_hilight) { pixmap_background_type bg; title_draw_descr tdd; @@ -3675,7 +3854,7 @@ Bool free_bg_pixmap = False; rectangle pix_g; - border_get_titlebar_draw_descr(fw, td, &tdd, *dest_pix); + border_get_titlebar_draw_descr(fw, td, &tdd, *dest_pix, do_hilight); /* prepare background, either from the window colour or from the * border style */ if (!DFS_USE_BORDER_STYLE(*tdd.tstyle)) @@ -3713,7 +3892,7 @@ relative_g.y = td->layout.title_g.y; border_get_border_background( &bg, td->cd, &td->layout.title_g, &relative_g, - &free_bg_pixmap, w); + &free_bg_pixmap, w, 0); bg.pixmap.g.x = 0; bg.pixmap.g.y = 0; /* set the geometry for drawing the Tiled pixmap; @@ -3743,11 +3922,11 @@ if (Pdepth < 2) { - border_draw_title_mono(fw, td, &tdd, &fstr, *dest_pix); + border_draw_title_mono(fw, td, &tdd, &fstr, *dest_pix, do_hilight); } else { - border_draw_title_deep(fw, td, &tdd, &fstr, *dest_pix, w); + border_draw_title_deep(fw, td, &tdd, &fstr, *dest_pix, w, do_hilight); } border_draw_title_relief(fw, td, &tdd, *dest_pix); border_draw_title_stick_lines(fw, td, &tdd, *dest_pix); @@ -3756,7 +3935,7 @@ } static void border_draw_title( - FvwmWindow *fw, titlebar_descr *td) + FvwmWindow *fw, titlebar_descr *td, Bool do_hilight) { Pixmap p; @@ -3774,7 +3953,7 @@ #if 0 fprintf(stderr,"drawing title\n"); #endif - border_set_title_pixmap(fw, td, &p, FW_W_TITLE(fw)); + border_set_title_pixmap(fw, td, &p, FW_W_TITLE(fw), do_hilight); if (td->draw_rotation != ROTATION_0) { Pixmap tmp; @@ -3821,6 +4000,7 @@ fw->decor_state.buttons_inverted = td->tbstate.pressed_bmask; fw->decor_state.buttons_lit = td->tbstate.lit_bmask; fw->decor_state.buttons_toggled = td->tbstate.toggled_bmask; + fw->decor_state.buttons_hover = td->tbstate.hover_bmask; return; } @@ -4087,13 +4267,18 @@ { tbstate->toggled_bmask |= mask; } + if (FW_W_BUTTON(fw, i) == HoverW) + { + tbstate->hover_bmask |= mask; + } tbstate->bstate[i] = border_flags_to_button_state( tbstate->pressed_bmask & mask, tbstate->lit_bmask & mask, - tbstate->toggled_bmask & mask); + tbstate->toggled_bmask & mask, + tbstate->hover_bmask & mask); } tbstate->tstate = border_flags_to_button_state( - tbstate->is_title_pressed, tbstate->is_title_lit, 0); + tbstate->is_title_pressed, tbstate->is_title_lit, 0, 0); } static window_parts border_get_titlebar_descr( @@ -4110,7 +4295,10 @@ JustificationType just; int lbl = 0; int rbl = 0; + int bw; + bw = HAS_FLUXBOX_HANDLES(fw) ? 1 : fw->boundary_width; + ret_td->cd = cd; ret_td->frame_g = *new_g; if (old_g == NULL) @@ -4147,16 +4335,16 @@ /* geometry of the title bar title + buttons */ if (!ret_td->has_vt) { - ret_td->bar_g.width = new_g->width - 2 * fw->boundary_width; + ret_td->bar_g.width = new_g->width - 2 * bw; ret_td->bar_g.height = ret_td->layout.title_g.height; - ret_td->bar_g.x = fw->boundary_width; + ret_td->bar_g.x = bw; ret_td->bar_g.y = ret_td->layout.title_g.y; } else { ret_td->bar_g.width = ret_td->layout.title_g.width; - ret_td->bar_g.height = new_g->height - 2 * fw->boundary_width; - ret_td->bar_g.y = fw->boundary_width; + ret_td->bar_g.height = new_g->height - 2 * bw; + ret_td->bar_g.y = bw; ret_td->bar_g.x = ret_td->layout.title_g.x; } @@ -4208,7 +4396,7 @@ { ret_td->left_buttons_g.height = rbl; ret_td->right_buttons_g.height = lbl; - ret_td->right_buttons_g.y = fw->boundary_width; + ret_td->right_buttons_g.y = bw; ret_td->right_buttons_g.x = ret_td->bar_g.x; ret_td->left_buttons_g.y = ret_td->layout.title_g.y + ret_td->layout.title_g.height; @@ -4218,7 +4406,7 @@ { ret_td->left_buttons_g.width = rbl; ret_td->right_buttons_g.width = lbl; - ret_td->right_buttons_g.x = fw->boundary_width; + ret_td->right_buttons_g.x = bw; ret_td->right_buttons_g.y = ret_td->bar_g.y; ret_td->left_buttons_g.x = ret_td->layout.title_g.x + ret_td->layout.title_g.width; @@ -4231,7 +4419,7 @@ { ret_td->left_buttons_g.height = lbl; ret_td->right_buttons_g.height = rbl; - ret_td->left_buttons_g.y = fw->boundary_width; + ret_td->left_buttons_g.y = bw; ret_td->left_buttons_g.x = ret_td->bar_g.x; ret_td->right_buttons_g.y = ret_td->layout.title_g.y + ret_td->layout.title_g.height; @@ -4241,7 +4429,7 @@ { ret_td->left_buttons_g.width = lbl; ret_td->right_buttons_g.width = rbl; - ret_td->left_buttons_g.x = fw->boundary_width; + ret_td->left_buttons_g.x = bw; ret_td->left_buttons_g.y = ret_td->bar_g.y; ret_td->right_buttons_g.x = ret_td->layout.title_g.x + ret_td->layout.title_g.width; @@ -4261,9 +4449,9 @@ if (fw->visible_name != (char *)NULL) { ret_td->length = FlocaleTextWidth( - fw->title_font, fw->visible_name, - (ret_td->has_vt) ? -strlen(fw->visible_name) : - strlen(fw->visible_name)); + do_hilight ? fw->title_font : fw->inactive_title_font, + fw->visible_name, (ret_td->has_vt) ? + -strlen(fw->visible_name) : strlen(fw->visible_name)); if (ret_td->length > fw->title_length - 2*MIN_WINDOW_TITLE_TEXT_OFFSET) { @@ -4355,6 +4543,7 @@ fw->decor_state.buttons_lit = 0; fw->decor_state.buttons_inverted = 0; fw->decor_state.buttons_toggled = 0; + fw->decor_state.buttons_hover = 0; return; } memset(&td, 0, sizeof(td)); @@ -4369,7 +4558,7 @@ } if ((draw_parts & PART_TITLE) != PART_NONE) { - border_draw_title(fw, &td); + border_draw_title(fw, &td, do_hilight); } if ((draw_parts & PART_BUTTONS) != PART_NONE) { @@ -4406,6 +4595,7 @@ fw->decor_state.buttons_toggled = (fw->decor_state.buttons_toggled & ~td.tbstate.max_bmask) | td.tbstate.toggled_bmask; + fw->decor_state.buttons_hover &= td.tbstate.hover_bmask; } return; @@ -4496,11 +4686,12 @@ ButtonState bs; int is_pressed; int is_toggled; + int is_hover; int i; /* title */ is_pressed = (FW_W_TITLE(fw) == PressedW); - bs = border_flags_to_button_state(is_pressed, has_focus, 0); + bs = border_flags_to_button_state(is_pressed, has_focus, 0, 0); if (DFS_USE_BORDER_STYLE(TB_STATE(GetDecor(fw, titlebar))[bs].style)) { return 1; @@ -4513,8 +4704,9 @@ } is_pressed = (FW_W_BUTTON(fw, i) == PressedW); is_toggled = (is_button_toggled(fw, i) == True); + is_hover = (FW_W_BUTTON(fw, i) == HoverW); bs = border_flags_to_button_state( - is_pressed, (has_focus == True), is_toggled); + is_pressed, (has_focus == True), is_toggled, is_hover); if (DFS_USE_BORDER_STYLE( TB_STATE(GetDecor(fw, buttons[i]))[bs].style)) { @@ -4582,8 +4774,9 @@ rectangle *ret_g, Window *ret_w) { int bw; + Bool title; - bw = fw->boundary_width; + bw = HAS_FLUXBOX_HANDLES(fw) ? 1 : fw->boundary_width; /* ret_g->x and ret->y is just an offset relatively to the w, * maybe we can take the relief in account? */ switch (part) @@ -4601,6 +4794,10 @@ case PART_BORDER_S: ret_g->x = sidebar_g->x; ret_g->y = 2 * sidebar_g->y + sidebar_g->height - bw; + if (HAS_FLUXBOX_HANDLES(fw)) + { + ret_g->y = 2 * sidebar_g->y + sidebar_g->height - fw->boundary_width; + } *ret_w = FW_W_SIDE(fw, 2); break; case PART_BORDER_W: @@ -4635,9 +4832,14 @@ switch (part) { case PART_BORDER_N: + ret_g->height = bw; case PART_BORDER_S: ret_g->width = sidebar_g->width; ret_g->height = bw; + if (HAS_FLUXBOX_HANDLES(fw)) + { + ret_g->height = fw->boundary_width; + } break; case PART_BORDER_E: case PART_BORDER_W: @@ -4652,9 +4854,34 @@ ret_g->height = sidebar_g->y; break; default: - return; + break; } + if (HAS_BORDER_UNDER_TITLE(fw)) + { + title = False; + switch (GET_TITLE_DIR(fw)) + { + case DIR_N: title = part & PART_TOP; break; + case DIR_E: title = part & PART_RIGHT; break; + case DIR_S: title = part & PART_BOTTOM; break; + case DIR_W: title = part & PART_LEFT; break; + } + if (title) + { + ret_g->width = max(ret_g->width, 2 * bw + fw->title_thickness); + ret_g->height = max(ret_g->height, 2 * bw + fw->title_thickness); + if (part & PART_RIGHT) + { + ret_g->x = 2 * sidebar_g->x + sidebar_g->width - ret_g->width; + } + if (part & PART_BOTTOM) + { + ret_g->y = 2 * sidebar_g->y + sidebar_g->height - ret_g->height; + } + } + } + return; } @@ -4940,6 +5167,7 @@ DEFAULT_USE_INACTIVE_BUTTONS; Scr.gs.use_inactive_down_buttons = DEFAULT_USE_INACTIVE_DOWN_BUTTONS; + Scr.gs.use_hover_buttons = 0; return; } first = False; @@ -4961,6 +5189,12 @@ action, &action, DEFAULT_USE_INACTIVE_DOWN_BUTTONS, True); } + else if (StrEquals("hover", token)) + { + Scr.gs.use_hover_buttons = ParseToggleArgument( + action, &action, + 0, True); + } else { Scr.gs.use_active_down_buttons = @@ -4969,6 +5203,7 @@ DEFAULT_USE_INACTIVE_BUTTONS; Scr.gs.use_inactive_down_buttons = DEFAULT_USE_INACTIVE_DOWN_BUTTONS; + Scr.gs.use_hover_buttons = 0; fvwm_msg(ERR, "cmd_button_state", "Unknown button state %s", token); return; Index: fvwm/borders.h =================================================================== --- fvwm/borders.h (revision 1) +++ fvwm/borders.h (revision 14) @@ -36,7 +36,12 @@ PART_FRAME = 0xff, PART_TITLEBAR = 0x300, PART_HANDLES = 0xc00, - PART_ALL = 0xfff + PART_ALL = 0xfff, + + PART_TOP = 0x31, + PART_BOTTOM = 0xc2, + PART_LEFT = 0x58, + PART_RIGHT = 0xa4 } window_parts; typedef enum Index: fvwm/windowshade.c =================================================================== --- fvwm/windowshade.c (revision 1) +++ fvwm/windowshade.c (revision 14) @@ -210,6 +210,7 @@ border_draw_decorations( fw, PART_TITLEBAR, (fw == get_focus_window()) ? True : False, 0, CLEAR_BUTTONS, NULL, NULL); + frame_make_rounded_corners(fw); /* update hints and inform modules */ BroadcastConfig(M_CONFIGURE_WINDOW, fw); BroadcastPacket( Index: fvwm/menustyle.c =================================================================== --- fvwm/menustyle.c (revision 1) +++ fvwm/menustyle.c (revision 14) @@ -153,6 +153,8 @@ char *token; char *action = s; FvwmPictureAttributes fpa; + FvwmPicture **pm; + int x, y; s = GetNextToken(s, &style); if (style && strncasecmp(style, "--", 2) == 0) @@ -264,6 +266,58 @@ return False; } } + else if (StrEquals(style,"MultiBorder")) + { + fpa.mask = (Pdepth <= 8)? FPAM_DITHER:0; + pm = mf->u.mb.pixmaps; + + for (x = 0; x < 8; x++) + { + if (pm[x]) + { + PDestroyFvwmPicture(dpy, pm[x]); + } + s = GetNextToken(s, &token); + if (token) + { + pm[x] = PCacheFvwmPicture(dpy, Scr.NoFocusWin, NULL, token, fpa); + if (!pm[x]) + { + fvwm_msg(ERR, + "menustyle_parse_face", "Pixmap '%s' could not be loaded", + token); + } + } + else + { + fvwm_msg(ERR, + "menustyle_parse_face", "Too few parameters for MultiBorder"); + } + if (!token || !pm[x]) + { + for (y = 0; y < x; y++) + { + if (pm[y]) + { + PDestroyFvwmPicture(dpy, pm[y]); + pm[y] = 0; + } + } + free(style); + if (token) + { + free(token); + } + if (mf->type == MultiPixmapMenu) + { + mf->type = SimpleMenu; + } + return False; + } + mf->type = MultiPixmapMenu; + free(token); + } + } else { if (verbose) @@ -398,7 +452,8 @@ "MouseWheel", "ScrollOffPage", "TrianglesUseFore", "TitleColorset", "HilightTitleBack", - "TitleFont", + "TitleFont", "FlatSeparators", + "RoundedCorners", "SlightlyRoundedCorners", NULL }; @@ -842,6 +897,7 @@ ST_SCROLL_OFF_PAGE(tmpms) = 1; ST_DO_HILIGHT_TITLE_BACK(tmpms) = 0; ST_USING_DEFAULT_TITLEFONT(tmpms) = True; + ST_DO_FLAT_SEPARATOR(tmpms) = 0; has_gc_changed = True; option = "fvwm"; } @@ -1529,8 +1585,16 @@ } has_gc_changed = True; break; + case 62: /* FlatSeparators */ + ST_DO_FLAT_SEPARATOR(tmpms) = on; + break; + case 63: /* RoundedCorners */ + ST_HAS_ROUNDED_CORNERS(tmpms) = on; + break; + case 64: /* SlightlyRoundedCorners */ + ST_HAS_SLIGHTLY_ROUNDED_CORNERS(tmpms) = on; + break; - #if 0 case 99: /* PositionHints */ /* to be implemented */ @@ -1783,6 +1847,8 @@ ST_TRIANGLES_USE_FORE(destms) = ST_TRIANGLES_USE_FORE(origms); /* Title */ ST_DO_HILIGHT_TITLE_BACK(destms) = ST_DO_HILIGHT_TITLE_BACK(origms); + /* FlatSeparators */ + ST_DO_FLAT_SEPARATOR(destms) = ST_DO_FLAT_SEPARATOR(origms); menustyle_update(destms); Index: fvwm/menus.c =================================================================== --- fvwm/menus.c (revision 1) +++ fvwm/menus.c (revision 14) @@ -55,6 +55,7 @@ #include "colormaps.h" #include "geometry.h" #include "menus.h" +#include "frame.h" /* ---------------------------- local definitions -------------------------- */ @@ -63,6 +64,19 @@ /* ---------------------------- local macros ------------------------------- */ +#define MENU_IS_TRANSLUCENT(mr,cs) \ + (!MR_IS_TEAR_OFF_MENU(mr) && CSET_IS_TRANSLUCENT(cs)) +#define MENU_IS_TRANSPARENT(mr,cs) \ + (MENU_IS_TRANSLUCENT(mr,cs) || CSET_IS_TRANSPARENT(cs)) +#define MR_IS_TRANSLUCENT_MENU(mr) \ + (!MR_IS_TEAR_OFF_MENU(mr) && MR_STYLE(mr) && \ + ST_HAS_MENU_CSET(MR_STYLE(mr)) && CSET_IS_TRANSLUCENT( \ + ST_CSET_MENU(MR_STYLE(mr)))) +#define MR_IS_TRANSPARENT_MENU(mr) \ + (MR_IS_TRANSLUCENT_MENU(mr) || (MR_STYLE(mr) && \ + ST_HAS_MENU_CSET(MR_STYLE(mr)) && CSET_IS_TRANSPARENT( \ + ST_CSET_MENU(MR_STYLE(mr))))) + /* ---------------------------- imports ------------------------------------ */ /* This external is safe. It's written only during startup. */ @@ -198,7 +212,9 @@ } mloop_static_info_t; /* ---------------------------- forward declarations ----------------------- */ - +static MenuRoot *seek_submenu_instance( + MenuRoot *parent_menu, MenuItem *parent_item); +static void pop_menu_down(MenuRoot **pmr, MenuParameters *pmp); /* ---------------------------- local variables ---------------------------- */ /* This global is saved and restored every time a function is called that @@ -428,8 +444,7 @@ Bool transparent_bg = False; /* move it back */ - if (ST_HAS_MENU_CSET(MR_STYLE(mr)) && - CSET_IS_TRANSPARENT(ST_CSET_MENU(MR_STYLE(mr)))) + if (MR_IS_TRANSPARENT_MENU(mr)) { transparent_bg = True; get_menu_repaint_transparent_parameters( @@ -461,16 +476,26 @@ } else { - XMoveWindow(dpy, MR_WINDOW(mr), endX, endY); - if (ST_HAS_MENU_CSET(MR_STYLE(mr)) && - CSET_IS_TRANSPARENT(ST_CSET_MENU(MR_STYLE(mr)))) - { + if (MR_IS_TRANSPARENT_MENU(mr)) + { + if (MR_IS_TRANSLUCENT_MENU(mr) && MR_SUBMENU_ITEM(mr)) + { + MenuRoot *smr; + smr = seek_submenu_instance( + mr, MR_SUBMENU_ITEM(mr)); + if (smr) + { + pop_menu_down(&smr,pmp); + MR_SUBMENU_ITEM(mr) = NULL; + } + } MenuRepaintTransparentParameters mrtp; get_menu_repaint_transparent_parameters( &mrtp, mr, (*pmp->pexc)->w.fw); repaint_transparent_menu( &mrtp,False, endX,endY, endX, endY); } + XMoveWindow(dpy, MR_WINDOW(mr), endX, endY); } } @@ -2339,6 +2364,10 @@ separator_height = (last_item_has_relief) ? MENU_SEPARATOR_HEIGHT + relief_thickness : MENU_SEPARATOR_TOTAL_HEIGHT; + if (MST_DO_FLAT_SEPARATOR(msp->menu)) + { + separator_height += 1; + } MI_Y_OFFSET(mi) = y; if (MI_IS_TITLE(mi)) { @@ -2631,6 +2660,7 @@ /* Doh. Use the standard display instead. */ MR_CREATE_DPY(mr) = dpy; } + MR_IS_TEAR_OFF_MENU(mr) = 1; } else { @@ -3299,6 +3329,41 @@ return do_clear; } +#define DRAWMENUBORDER(NUM, XX, YY) XCopyArea(\ + dpy, pm[(NUM)]->picture, MR_WINDOW(mr), Scr.TransMaskGC,\ + 0, 0, pm[(NUM)]->width, pm[(NUM)]->height, (XX), (YY)) +static Bool paint_menu_multipixmap_background( + MenuRoot *mr, XEvent *pevent) +{ + MenuStyle *ms = MR_STYLE(mr); + int width, height, x, y; + int bw = MST_BORDER_WIDTH(mr); + FvwmPicture **pm; + pm = ST_FACE(ms).u.mb.pixmaps; + + width = MR_WIDTH(mr); + height = MR_HEIGHT(mr); + + FvwmPicture *p = pm[0]; + + width = MR_WIDTH(mr); + height = MR_HEIGHT(mr); + + x = 0; + + for (x = 0; x < width; x+=pm[1]->width) DRAWMENUBORDER(1, x, 0); + for (x = 0; x < width; x+=pm[5]->width) DRAWMENUBORDER(5, x, height-pm[5]->height); + for (x = 0; x < height; x+=pm[3]->height) DRAWMENUBORDER(3, width-pm[3]->height, x); + for (x = 0; x < height; x+=pm[7]->height) DRAWMENUBORDER(7, 0, x); + + DRAWMENUBORDER(0, 0, 0); + DRAWMENUBORDER(2, width-pm[2]->width, 0); + DRAWMENUBORDER(4, width-pm[4]->width, height-pm[4]->height); + DRAWMENUBORDER(6, 0, height-pm[6]->height); + + return False; +} + static Bool paint_menu_pixmap_background( MenuRoot *mr, XEvent *pevent) { @@ -3403,22 +3468,57 @@ { /* Only the border was obscured. Redraw it centrally instead of * redrawing several menu items. */ - RelieveRectangle( - dpy, MR_WINDOW(mr), 0, 0, MR_WIDTH(mr) - 1, - MR_HEIGHT(mr) - 1, (Pdepth < 2) ? - SHADOW_GC(MST_MENU_INACTIVE_GCS(mr)) : - HILIGHT_GC(MST_MENU_INACTIVE_GCS(mr)), - SHADOW_GC(MST_MENU_INACTIVE_GCS(mr)), bw); + if (ms && ST_FACE(ms).type == MultiPixmapMenu) { - return; + paint_menu_multipixmap_background(mr, pevent); } + else + { + RelieveRectangle( + dpy, MR_WINDOW(mr), 0, 0, MR_WIDTH(mr) - 1, + MR_HEIGHT(mr) - 1, (Pdepth < 2) ? + SHADOW_GC(MST_MENU_INACTIVE_GCS(mr)) : + HILIGHT_GC(MST_MENU_INACTIVE_GCS(mr)), + SHADOW_GC(MST_MENU_INACTIVE_GCS(mr)), bw); + } + return; } MR_IS_PAINTED(mr) = 1; /* paint the menu background */ - if (ms && ST_HAS_MENU_CSET(ms)) + if (MR_IS_TRANSLUCENT_MENU(mr)) { + Pixmap trans = None; + FvwmRenderAttributes fra; + colorset_t *colorset = &Colorset[ST_CSET_MENU(ms)]; + + fra.mask = 0; + if (colorset->translucent_tint_percent > 0) + { + fra.mask = FRAM_HAVE_TINT; + fra.tint = colorset->translucent_tint; + fra.tint_percent = colorset->translucent_tint_percent; + } if (MR_IS_BACKGROUND_SET(mr) == False) { + trans = PGraphicsCreateTranslucent( + dpy, MR_WINDOW(mr), &fra, + BACK_GC(ST_MENU_INACTIVE_GCS(ms)), + MR_X(mr), MR_Y(mr), MR_WIDTH(mr), MR_HEIGHT(mr)); + XMapRaised(dpy, MR_WINDOW(mr)); + if (trans != None) + { + XSetWindowBackgroundPixmap( + dpy, MR_WINDOW(mr), trans); + MR_IS_BACKGROUND_SET(mr) = True; + clear_expose_menu_area(MR_WINDOW(mr), pevent); + XFreePixmap(dpy, trans); + } + } + } + else if (ms && ST_HAS_MENU_CSET(ms)) + { + if (MR_IS_BACKGROUND_SET(mr) == False) + { SetWindowBackground( dpy, MR_WINDOW(mr), MR_WIDTH(mr), MR_HEIGHT(mr), &Colorset[ST_CSET_MENU(ms)], Pdepth, @@ -3445,6 +3545,9 @@ case PixmapMenu: do_clear = paint_menu_pixmap_background(mr, pevent); break; + case MultiPixmapMenu: + do_clear = paint_menu_multipixmap_background(mr, pevent); + break; case TiledPixmapMenu: XSetWindowBackgroundPixmap( dpy, MR_WINDOW(mr), ST_FACE(ms).u.p->picture); @@ -3457,11 +3560,14 @@ } } /* if (ms) */ /* draw the relief */ - RelieveRectangle(dpy, MR_WINDOW(mr), 0, 0, MR_WIDTH(mr) - 1, - MR_HEIGHT(mr) - 1, (Pdepth < 2) ? - SHADOW_GC(MST_MENU_INACTIVE_GCS(mr)) : - HILIGHT_GC(MST_MENU_INACTIVE_GCS(mr)), - SHADOW_GC(MST_MENU_INACTIVE_GCS(mr)), bw); + if (!(ms && ST_FACE(ms).type == MultiPixmapMenu)) + { + RelieveRectangle(dpy, MR_WINDOW(mr), 0, 0, MR_WIDTH(mr) - 1, + MR_HEIGHT(mr) - 1, (Pdepth < 2) ? + SHADOW_GC(MST_MENU_INACTIVE_GCS(mr)) : + HILIGHT_GC(MST_MENU_INACTIVE_GCS(mr)), + SHADOW_GC(MST_MENU_INACTIVE_GCS(mr)), bw); + } /* paint the menu items */ for (mi = MR_FIRST_ITEM(mr); mi != NULL; mi = MI_NEXT_ITEM(mi)) { @@ -3821,6 +3927,13 @@ return x_overlap; } +static void menu_make_rounded_corners(MenuRoot *mr) +{ + draw_rounded_mask(MR_WINDOW(mr), MR_WIDTH(mr), MR_HEIGHT(mr), + MST_HAS_SLIGHTLY_ROUNDED_CORNERS(mr), + MST_HAS_ROUNDED_CORNERS(mr)?PART_CORNERS:0, 0); +} + /* * * Procedure: @@ -4214,10 +4327,7 @@ MR_HAS_POPPED_UP_RIGHT(mr) = 0; } MR_XANIMATION(parent_menu) += end_x - prev_x; - if (ST_HAS_MENU_CSET(MR_STYLE(parent_menu)) && - CSET_IS_TRANSPARENT( - ST_CSET_MENU( - MR_STYLE(parent_menu)))) + if (MR_IS_TRANSPARENT_MENU(parent_menu)) { transparent_bg = True; get_menu_repaint_transparent_parameters( @@ -4391,11 +4501,24 @@ * Pop up the menu */ + menu_make_rounded_corners(mr); + XMoveWindow(dpy, MR_WINDOW(mr), x, y); + MR_X(mr) = x; + MR_Y(mr) = y; XSelectInput(dpy, MR_WINDOW(mr), event_mask); - XMapRaised(dpy, MR_WINDOW(mr)); - if (popdown_window) - XUnmapWindow(dpy, popdown_window); + if (MR_IS_TRANSLUCENT_MENU(mr)) + { + if (popdown_window) + XUnmapWindow(dpy, popdown_window); + paint_menu(mr, NULL, fw); + } + else + { + XMapRaised(dpy, MR_WINDOW(mr)); + if (popdown_window) + XUnmapWindow(dpy, popdown_window); + } XFlush(dpy); MR_MAPPED_COPIES(mr)++; MST_USAGE_COUNT(mr)++; @@ -4856,7 +4979,7 @@ { MenuRoot *tmrMi; Bool rc; - + pmret->rc = MENU_NOP; switch ((*pmp->pexc)->x.elast->type) { @@ -4898,6 +5021,11 @@ { /* umpost the menu if posted */ pmret->flags.is_menu_posted = 0; + if (MR_SUBMENU_ITEM(pmp->menu) == NULL) + { + /* really ugly hack */ + in->mrPopup = in->mrPopdown = NULL; + } } break; } @@ -6874,15 +7002,119 @@ { last = True; } - if (!last && CSET_IS_TRANSPARENT_PR_TINT(ST_CSET_MENU(ms))) + if (!last && + (CSET_IS_TRANSPARENT_PR_TINT(ST_CSET_MENU(ms)) || + MR_IS_TRANSLUCENT_MENU(mr))) { /* too slow ... */ return; } - SetWindowBackground( - dpy, MR_WINDOW(mr), MR_WIDTH(mr), MR_HEIGHT(mr), - &Colorset[ST_CSET_MENU(ms)], Pdepth, - FORE_GC(MST_MENU_INACTIVE_GCS(mr)), False); + if (MR_IS_TRANSLUCENT_MENU(mr)) + { + Pixmap trans, tmp; + FvwmRenderAttributes fra; + colorset_t *colorset = &Colorset[ST_CSET_MENU(ms)]; + + fra.mask = 0; + if (colorset->translucent_tint_percent > 0) + { + fra.mask = FRAM_HAVE_TINT; + fra.tint = colorset->translucent_tint; + fra.tint_percent = colorset->translucent_tint_percent; + } + if (MR_X(mr) == x) + { + /* Reuse the old pixmap for the part of the menu + * that has not moved. (This can be extended to get + * two new rectangles, one in each direction) + * + * It saves the unmapping of the window and makes + * Things less flickering. + */ + GC my_gc; + unsigned long valuemask = GCSubwindowMode; + XGCValues values; + int out_y=0; + values.subwindow_mode = IncludeInferiors; + if (y < 0) + { + out_y = -y; + } + trans = XCreatePixmap(dpy, MR_WINDOW(mr), MR_WIDTH(mr), + MR_HEIGHT(mr), Pdepth); + my_gc = fvwmlib_XCreateGC(dpy, MR_WINDOW(mr), 0, NULL); + XChangeGC(dpy, my_gc, valuemask, &values); + + XClearWindow(dpy, MR_WINDOW(mr)); + + if (MR_Y(mr) < y) + { + XCopyArea(dpy, MR_WINDOW(mr), trans, my_gc, 0, + y-MR_Y(mr), MR_WIDTH(mr), + MR_HEIGHT(mr)-(y-MR_Y(mr)),0,0); + tmp = PGraphicsCreateTranslucent( + dpy, MR_WINDOW(mr), &fra, + BACK_GC(ST_MENU_INACTIVE_GCS(ms)), + MR_X(mr),MR_Y(mr)+MR_HEIGHT(mr), + MR_WIDTH(mr), y-MR_Y(mr)); + + XCopyArea(dpy, tmp, trans, my_gc, 0, 0, + MR_WIDTH(mr), y-MR_Y(mr),0, + MR_HEIGHT(mr)-(y-MR_Y(mr))); + } + else + { + XCopyArea(dpy, MR_WINDOW(mr), trans, my_gc, 0, + 0, MR_WIDTH(mr), + MR_HEIGHT(mr)-(MR_Y(mr)-y), 0, + MR_Y(mr)-y); + tmp = PGraphicsCreateTranslucent( + dpy, MR_WINDOW(mr), &fra, + BACK_GC(ST_MENU_INACTIVE_GCS(ms)), + MR_X(mr),y, MR_WIDTH(mr), + MR_Y(mr)-y); + XCopyArea(dpy, tmp, trans, my_gc, 0, 0, + MR_WIDTH(mr), MR_Y(mr)-y,0, out_y); + } + MR_X(mr) = x; + MR_Y(mr) = y; + XFreePixmap(dpy, tmp); + XFreeGC(dpy,my_gc); + } + else + { + XUnmapWindow(dpy, MR_WINDOW(mr)); + MR_X(mr) = x; + MR_Y(mr) = y; + trans = PGraphicsCreateTranslucent( + dpy, MR_WINDOW(mr), &fra, + BACK_GC(ST_MENU_INACTIVE_GCS(ms)), + MR_X(mr), MR_Y(mr), MR_WIDTH(mr), + MR_HEIGHT(mr)); + XMapRaised(dpy, MR_WINDOW(mr)); + } + XSetWindowBackgroundPixmap( + dpy, MR_WINDOW(mr), trans); + XFreePixmap(dpy, trans); + if (MR_X(mr) == x) + { + /* Redraw the border */ + RelieveRectangle( + dpy, MR_WINDOW(mr), 0, 0, MR_WIDTH(mr) - 1, + MR_HEIGHT(mr) - 1, (Pdepth < 2) ? + SHADOW_GC(MST_MENU_INACTIVE_GCS(mr)) : + HILIGHT_GC(MST_MENU_INACTIVE_GCS(mr)), + SHADOW_GC(MST_MENU_INACTIVE_GCS(mr)), + MST_BORDER_WIDTH(mr)); + } + } + else + { + SetWindowBackground( + dpy, MR_WINDOW(mr), MR_WIDTH(mr), MR_HEIGHT(mr), + &Colorset[ST_CSET_MENU(ms)], Pdepth, + FORE_GC(MST_MENU_INACTIVE_GCS(mr)), False); + } /* redraw the background of non active item */ for (mi = MR_FIRST_ITEM(mr); mi != NULL; mi = MI_NEXT_ITEM(mi)) { @@ -7518,10 +7750,12 @@ SetWindowBackground( dpy, MR_WINDOW(mr), MR_WIDTH(mr), MR_HEIGHT(mr), - &Colorset[ST_CSET_MENU(ms)], - Pdepth, + &Colorset[ST_CSET_MENU(ms)], Pdepth, FORE_GC(MST_MENU_INACTIVE_GCS(mr)), - True); + False); + XClearArea( + dpy, MR_WINDOW(mr), 0, 0, MR_WIDTH(mr), + MR_HEIGHT(mr), True); } else if ((ST_HAS_ACTIVE_CSET(ms) && ST_CSET_ACTIVE(ms) == cset) || Index: fvwm/colorset.c =================================================================== --- fvwm/colorset.c (revision 1) +++ fvwm/colorset.c (revision 14) @@ -160,6 +160,8 @@ "NoIconTint", "IconAlpha", + "Translucent", + "NoTranslucent", NULL }; @@ -620,6 +622,7 @@ char *fg_tint = NULL; char *bg_tint = NULL; char *icon_tint = NULL; + char *translucent_tint = NULL; Bool have_pixels_changed = False; Bool has_icon_pixels_changed = False; Bool has_fg_changed = False; @@ -632,6 +635,7 @@ Bool has_fg_tint_changed = False; Bool has_bg_tint_changed = False; Bool has_icon_tint_changed = False; + Bool has_translucent_tint_changed = False; Bool has_pixmap_changed = False; Bool has_shape_changed = False; Bool has_image_alpha_changed = False; @@ -758,6 +762,10 @@ case 21: /* Plain */ has_pixmap_changed = True; free_colorset_background(cs, True); + cs->is_translucent = False; + cs->translucent_tint_percent = 0; + cs->color_flags &= ~TRANSLUCENT_TINT_SUPPLIED; + has_translucent_tint_changed = True; break; case 22: /* NoShape */ has_shape_changed = True; @@ -924,6 +932,24 @@ cs->icon_alpha_percent = tmp; } break; + case 42: /* Translucent */ + cs->is_translucent = True; + parse_simple_tint( + cs, args, &translucent_tint, + TRANSLUCENT_TINT_SUPPLIED, + &has_translucent_tint_changed, &percent, + "Translucent"); + if (has_translucent_tint_changed) + { + cs->translucent_tint_percent = percent; + } + break; + case 43: /* NoTranslucent */ + cs->is_translucent = False; + cs->translucent_tint_percent = 0; + cs->color_flags &= ~TRANSLUCENT_TINT_SUPPLIED; + has_translucent_tint_changed = True; + break; default: /* test for ?Gradient */ if (option[0] && StrEquals(&option[1], "Gradient")) @@ -1609,6 +1635,27 @@ } /* + * ---------- change the translucent tint colour ---------- + */ + if (has_translucent_tint_changed) + { + /* user specified colour */ + if (translucent_tint != NULL) + { + PictureFreeColors( + dpy, Pcmap, &cs->translucent_tint, 1, 0, True); + cs->translucent_tint = GetColor(translucent_tint); + } + else + { + /* default */ + PictureFreeColors( + dpy, Pcmap, &cs->translucent_tint, 1, 0, True); + cs->translucent_tint = GetColor(black); + } + } + + /* * ---------- send new colorset to fvwm and clean up ---------- */ /* make sure the server has this to avoid races */ @@ -1704,6 +1751,7 @@ ncs->fgsh = GetColor(white); ncs->tint = GetColor(black); ncs->icon_tint = GetColor(black); + ncs->translucent_tint = GetColor(black); ncs->pixmap = XCreatePixmapFromBitmapData( dpy, Scr.NoFocusWin, &g_bits[4 * (nColorsets % 3)], 4, 4, @@ -1721,6 +1769,7 @@ ncs->fgsh = GetForeShadow(ncs->fg, ncs->bg); ncs->tint = GetColor(black); ncs->icon_tint = GetColor(black); + ncs->translucent_tint = GetColor(black); } ncs->fg_tint = ncs->bg_tint = GetColor(black); /* set flags for fg contrast, bg average */ @@ -1732,6 +1781,7 @@ ncs->icon_alpha_percent = 100; ncs->tint_percent = 0; ncs->icon_tint_percent = 0; + ncs->translucent_tint_percent = 0; ncs->fg_tint_percent = ncs->bg_tint_percent = 0; ncs->dither = (PictureDitherByDefault())? True:False; nColorsets++; Index: fvwm/menustyle.h =================================================================== --- fvwm/menustyle.h (revision 1) +++ fvwm/menustyle.h (revision 14) @@ -23,6 +23,12 @@ #define MST_FACE(m) ((m)->s->ms->look.face) #define ST_DO_HILIGHT_BACK(s) ((s)->look.flags.do_hilight_back) #define MST_DO_HILIGHT_BACK(m) ((m)->s->ms->look.flags.do_hilight_back) +#define ST_DO_FLAT_SEPARATOR(s) ((s)->look.flags.do_flat_separator) +#define MST_DO_FLAT_SEPARATOR(m) ((m)->s->ms->look.flags.do_flat_separator) +#define ST_HAS_ROUNDED_CORNERS(s) ((s)->look.flags.has_rounded_corners) +#define MST_HAS_ROUNDED_CORNERS(m) ((m)->s->ms->look.flags.has_rounded_corners) +#define ST_HAS_SLIGHTLY_ROUNDED_CORNERS(s) ((s)->look.flags.has_slightly_rounded_corners) +#define MST_HAS_SLIGHTLY_ROUNDED_CORNERS(m) ((m)->s->ms->look.flags.has_slightly_rounded_corners) #define ST_DO_HILIGHT_FORE(s) ((s)->look.flags.do_hilight_fore) #define MST_DO_HILIGHT_FORE(m) ((m)->s->ms->look.flags.do_hilight_fore) #define ST_DO_HILIGHT_TITLE_BACK(s) ((s)->look.flags.do_hilight_title_back) @@ -183,6 +189,7 @@ GradientMenu, PixmapMenu, TiledPixmapMenu, + MultiPixmapMenu, SolidMenu /* max button is 8 (0x8) */ } MenuFaceType; @@ -232,6 +239,9 @@ { union { + struct { + FvwmPicture *pixmaps[8]; + } mb; FvwmPicture *p; Pixel back; struct @@ -275,6 +285,9 @@ unsigned has_title_cset : 1; unsigned do_hilight_title_back : 1; unsigned using_default_titlefont : 1; + unsigned do_flat_separator : 1; + unsigned has_rounded_corners : 1; + unsigned has_slightly_rounded_corners : 1; } flags; unsigned char ReliefThickness; unsigned char TitleUnderlines; Index: fvwm/menus.h =================================================================== --- fvwm/menus.h (revision 1) +++ fvwm/menus.h (revision 14) @@ -143,6 +143,9 @@ MenuItem *submenu_item; /* x distance window was moved by animation */ int xanimation; + /* x,y XMapRaise */ + int x; + int y; /* dynamic temp flags */ struct { @@ -184,6 +187,8 @@ #define MR_SELECTED_ITEM(m) ((m)->d->selected_item) #define MR_SUBMENU_ITEM(m) ((m)->d->submenu_item) #define MR_XANIMATION(m) ((m)->d->xanimation) +#define MR_X(m) ((m)->d->x) +#define MR_Y(m) ((m)->d->y) #define MR_STORED_ITEM(m) ((m)->d->stored_item) #define MR_STORED_PIXELS(m) ((m)->d->stored_pixels) /* flags */ Index: libs/PictureGraphics.c =================================================================== --- libs/PictureGraphics.c (revision 1) +++ libs/PictureGraphics.c (revision 14) @@ -1338,7 +1338,7 @@ } } -#if 0 /* humm... maybe useful one day with menus */ +#if 1 /* humm... maybe useful one day with menus */ Pixmap PGraphicsCreateTranslucent( Display *dpy, Window win, FvwmRenderAttributes *fra, GC gc, int x, int y, int width, int height) Index: libs/PictureGraphics.h =================================================================== --- libs/PictureGraphics.h (revision 1) +++ libs/PictureGraphics.h (revision 14) @@ -122,7 +122,9 @@ Display *dpy, Window win, Pixel tint, int tint_percent, Drawable dest, Bool dest_is_a_window, GC gc, GC mono_gc, GC alpha_gc, int dest_x, int dest_y, int dest_w, int dest_h); - +Pixmap PGraphicsCreateTranslucent( + Display *dpy, Window win, FvwmRenderAttributes *fra, GC gc, + int x, int y, int width, int height); /* never used ! */ Pixmap PGraphicsCreateDitherPixmap( Display *dpy, Window win, Drawable src, Pixmap mask, int depth, GC gc, Index: libs/Colorset.h =================================================================== --- libs/Colorset.h (revision 1) +++ libs/Colorset.h (revision 14) @@ -51,6 +51,10 @@ Bool dither; Bool allows_buffered_transparency; Bool is_maybe_root_transparent; + /* only use by fvwm menu (non tear-off) */ + Bool is_translucent; + Pixel translucent_tint; + unsigned int translucent_tint_percent : 7; #endif } colorset_t; @@ -78,6 +82,7 @@ #define FG_TINT_SUPPLIED 0x100 #define BG_TINT_SUPPLIED 0x200 #define ICON_TINT_SUPPLIED 0x400 +#define TRANSLUCENT_TINT_SUPPLIED 0x800 #endif /* colorsets are stored as an array of structs to permit fast dereferencing */ @@ -153,6 +158,11 @@ (cset >= 0 && cset->pixmap == ParentRelative && \ cset->tint_percent > 0) +#define CSET_IS_TRANSLUCENT(cset) \ + (cset >= 0 && Colorset[cset].is_translucent) +#define CSETS_IS_TRANSLUCENT(cset) \ + (cset && cset->is_translucent) + #ifndef FVWM_COLORSET_PRIVATE /* Create n new colorsets, fvwm/colorset.c does its own thing (different size) */