static void button_paint_split(HWND win, button_t* button, HDC dc) { RECT rect; RECT rect_left, rect_right; int state_left, state_right; int text_offset = 0; HFONT font, old_font; int old_bk_mode; COLORREF old_text_color; HRGN old_clip; HICON glyph; int width_right = DROPDOWN_W; glyph = ImageList_GetIcon(mc_bmp_glyphs, MC_BMP_GLYPH_MORE_OPTIONS, ILD_TRANSPARENT); GetClientRect(win, &rect); font = (HFONT) MC_SEND(win, WM_GETFONT, 0, 0); if(font == NULL) font = GetStockObject(SYSTEM_FONT); old_font = SelectObject(dc, font); old_bk_mode = GetBkMode(dc); old_text_color = GetTextColor(dc); old_clip = get_clip(dc); /* Draw what's common for left and right parts background. */ if(!button->theme && (button->style & BS_DEFPUSHBUTTON)) { SelectObject(dc, GetSysColorBrush(COLOR_WINDOWFRAME)); Rectangle(dc, rect.left, rect.top, rect.right, rect.bottom); mc_rect_inflate(&rect, -1, -1); width_right--; } /* Setup subrectangles (mainpart 1 and push-down part 2) */ mc_rect_copy(&rect_left, &rect); rect_left.right -= width_right; mc_rect_copy(&rect_right, &rect); rect_right.left = rect_left.right; /* Draw background. */ if(button->theme) { UINT transparent; RECT tmp; /* Determine styles for left and right parts */ if(button->style & WS_DISABLED) { state_left = state_right = PBS_DISABLED; } else { LRESULT state; state = MC_SEND(win, BM_GETSTATE, 0, 0); if(state & MC_BST_DROPDOWNPUSHED) { state_left = PBS_NORMAL; state_right = PBS_PRESSED; } else { if(state & BST_PUSHED) state_left = state_right = PBS_PRESSED; else if(state & BST_HOT) state_left = state_right = PBS_HOT; else if(button->style & BS_DEFPUSHBUTTON) state_left = state_right = PBS_DEFAULTED; else state_left = state_right = PBS_NORMAL; } } /* Handle (semi-)transparent themes. */ transparent = 0; if(mcIsThemeBackgroundPartiallyTransparent(button->theme, BP_PUSHBUTTON, state_left)) transparent |= 0x1; if(mcIsThemeBackgroundPartiallyTransparent(button->theme, BP_PUSHBUTTON, state_right)) transparent |= 0x2; switch(transparent) { case 0x1: mcDrawThemeParentBackground(win, dc, &rect_left); break; case 0x2: mcDrawThemeParentBackground(win, dc, &rect_right); break; case 0x3: mcDrawThemeParentBackground(win, dc, &rect); break; } /* Draw backgrond. */ mcDrawThemeBackground(button->theme, dc, BP_PUSHBUTTON, state_left, &rect, &rect_left); mcDrawThemeBackground(button->theme, dc, BP_PUSHBUTTON, state_right, &rect, &rect_right); /* Deflate both rects to content rects only */ mcGetThemeBackgroundContentRect(button->theme, dc, BP_PUSHBUTTON, state_left, &rect_left, &tmp); rect_left.left = tmp.left; rect_left.top = tmp.top; rect_left.bottom = tmp.bottom; mcGetThemeBackgroundContentRect(button->theme, dc, BP_PUSHBUTTON, state_right, &rect_right, &tmp); rect_right.top = tmp.top; rect_right.right = tmp.right; rect_right.bottom = tmp.bottom; /* Draw delimiter of left and right parts. */ rect_right.top += 1; rect_right.bottom -= 1; mcDrawThemeEdge(button->theme, dc, BP_PUSHBUTTON, state_right, &rect_right, BDR_SUNKEN, BF_LEFT, NULL); rect_right.left = tmp.left; } else { /* Determine styles for left and right parts */ if(button->style & WS_DISABLED) { state_left = state_right = DFCS_INACTIVE; } else { LRESULT s = MC_SEND(win, BM_GETSTATE, 0, 0); if(s & MC_BST_DROPDOWNPUSHED) { state_left = 0; state_right = DFCS_PUSHED; } else { if(s & BST_PUSHED) { state_left = state_right = DFCS_PUSHED; } else { state_left = state_right = 0; } } } button_send_ctlcolorbtn(win, dc); /* Draw control edges */ IntersectClipRect(dc, rect_left.left, rect_left.top, rect_left.right, rect_left.bottom); DrawFrameControl(dc, &rect, DFC_BUTTON, DFCS_BUTTONPUSH | state_left); SelectClipRgn(dc, NULL); IntersectClipRect(dc, rect_right.left, rect_right.top, rect_right.right, rect_right.bottom); DrawFrameControl(dc, &rect, DFC_BUTTON, DFCS_BUTTONPUSH | state_right); /* Parts which are pushed, should have the contents moved a bit */ if(state_left == DFCS_PUSHED) mc_rect_offset(&rect_left, 1, 1); if(state_right == DFCS_PUSHED) mc_rect_offset(&rect_right, 1, 1); /* Draw delimiter */ if(state_left == state_right) { DrawEdge(dc, &rect_right, BDR_SUNKENOUTER | BDR_RAISEDINNER, BF_LEFT | BF_SOFT); } else { rect_right.left--; DrawEdge(dc, &rect_right, BDR_SUNKENOUTER, BF_LEFT | BF_SOFT); rect_right.left++; } /* Adjust for the outer control edges */ mc_rect_inflate(&rect_left, 0, -2); rect_left.left += 2; mc_rect_inflate(&rect_right, -2, -2); } /* Draw focus rectangle. */ if((MC_SEND(win, BM_GETSTATE, 0, 0) & BST_FOCUS) && !button->hide_focus) { SelectClipRgn(dc, NULL); if(button->theme) { mc_rect_set(&rect, rect_left.left, rect_left.top, rect_right.right - DROPDOWN_W, rect_right.bottom); DrawFocusRect(dc, &rect); } else { mc_rect_inflate(&rect_left, -1, -2); DrawFocusRect(dc, &rect_left); mc_rect_inflate(&rect_left, -1, -1); } } /* Draw glyph into the right part */ SelectClipRgn(dc, NULL); IntersectClipRect(dc, rect_right.left, rect_right.top, rect_right.right, rect_right.bottom); DrawIconEx(dc, (rect_right.right + rect_right.left - MC_BMP_GLYPH_W) / 2, (rect_right.bottom + rect_right.top - MC_BMP_GLYPH_H) / 2, glyph, MC_BMP_GLYPH_W, MC_BMP_GLYPH_H, 0, NULL, DI_NORMAL); /* Draw left part contents */ SelectClipRgn(dc, NULL); IntersectClipRect(dc, rect_left.left, rect_left.top, rect_left.right, rect_left.bottom); if(button->style & BS_ICON) { /* Paint (BS_SPLITBUTTON | BS_ICON). Note that this is used even on * Vista, as according to some my testing this style combination * is not supported there... */ HICON icon; icon = (HICON) MC_SEND(win, BM_GETIMAGE, (WPARAM) IMAGE_ICON, (LPARAM) 0); if(icon != NULL) { SIZE size; UINT flags; mc_icon_size(icon, &size); flags = DST_ICON; if(button->style & WS_DISABLED) flags |= DSS_DISABLED; DrawState(dc, NULL, NULL, (LPARAM) icon, 0, (rect_left.right + rect_left.left - size.cx) / 2, (rect_left.bottom + rect_left.top - size.cy) / 2, size.cx, size.cy, flags); } } else { /* Paint text label */ TCHAR buffer[256]; int n; UINT flags = 0; /* Setup flags for TextOut/mcDrawThemeText */ switch(button->style & (BS_LEFT | BS_CENTER | BS_RIGHT)) { case BS_LEFT: flags |= DT_LEFT; break; case BS_RIGHT: flags |= DT_RIGHT; break; default: if(GetWindowLong(win, GWL_EXSTYLE) & WS_EX_RIGHT) flags |= DT_RIGHT; else flags |= DT_CENTER; break; } switch(button->style & (BS_TOP | BS_VCENTER | BS_BOTTOM)) { case BS_TOP: flags |= DT_TOP; break; case BS_BOTTOM: flags |= DT_BOTTOM; break; default: flags |= DT_VCENTER; break; } if(button->style & BS_MULTILINE) flags |= DT_WORDBREAK; else flags |= DT_SINGLELINE; if(button->hide_accel) flags |= DT_HIDEPREFIX; n = MC_SEND(win, WM_GETTEXT, MC_ARRAY_SIZE(buffer), buffer); if(button->theme) { mcDrawThemeText(button->theme, dc, BP_PUSHBUTTON, state_left, buffer, n, flags, 0, &rect_left); } else { SetBkMode(dc, TRANSPARENT); SetTextColor(dc, GetSysColor(COLOR_BTNTEXT)); mc_rect_offset(&rect_left, text_offset, text_offset); DrawText(dc, buffer, n, &rect_left, flags); } } SelectObject(dc, old_font); SetBkMode(dc, old_bk_mode); SetTextColor(dc, old_text_color); SelectObject(dc, old_clip); }
static LRESULT CALLBACK button_proc(HWND win, UINT msg, WPARAM wp, LPARAM lp) { button_t* button = (button_t*) GetWindowLongPtr(win, extra_offset); /* Window procedure for our subclassed BUTTON does some logic if * either [1] the control is split button and system does not support it * (i.e. Windows is older then Vista). * or [2] the control is BS_ICON and theming is in use, as std. * control draws in the old unthemed style this kind of button. * In all other cases all messages are just forwarded to the standard * Microsoft button procedure. */ switch(msg) { case WM_PAINT: case WM_PRINTCLIENT: { BOOL fake_split; BOOL fake_icon; PAINTSTRUCT ps; HDC dc; fake_split = button_is_fake_split(button); fake_icon = button_is_fake_icon(button); if(!fake_split && !fake_icon) { /* Keep it on original proc */ break; } if(msg == WM_PAINT) dc = BeginPaint(win, &ps); else dc = (HDC) wp; if(!button->no_redraw) { if(fake_split) button_paint_split(win, button, dc); else button_paint_icon(win, button, dc); } if(msg == WM_PAINT) EndPaint(win, &ps); return 0; } case WM_LBUTTONDOWN: if(button_is_fake_split(button)) { POINT pt = { GET_X_LPARAM(lp), GET_Y_LPARAM(lp) }; RECT rect; SetFocus(win); GetClientRect(win, &rect); rect.left = rect.right - DROPDOWN_W; if(mc_rect_contains_pt(&rect, &pt)) { /* Handle the click in the drop-down part */ MC_NMBCDROPDOWN notify; button->is_dropdown_pushed = 1; InvalidateRect(win, &rect, TRUE); notify.hdr.hwndFrom = win; notify.hdr.idFrom = GetWindowLong(win, GWL_ID); notify.hdr.code = MC_BCN_DROPDOWN; mc_rect_copy(¬ify.rcButton, &rect); MC_SEND(GetAncestor(win, GA_PARENT), WM_NOTIFY, notify.hdr.idFrom, ¬ify); /* We unpush immediately after the parent handles the * notification. Usually it takes some time - parent * typically shows some popup-menu or other stuff * which includes a modal eventloop and/or mouse capture. */ button->is_dropdown_pushed = 0; InvalidateRect(win, NULL, TRUE); return 0; } } break; case WM_LBUTTONDBLCLK: if(button_is_fake_split(button)) { RECT rect; GetClientRect(win, &rect); rect.left = rect.right - DROPDOWN_W; if(mc_rect_contains_pos(&rect, lp)) { /* We ignore duble-click in the drop-down part. */ return 0; } } break; case WM_GETDLGCODE: /* Handling this message allows the dialogs to set the button * as default, as it is done for normal push buttons. Unfortunately * it causes other problems. See the comment in WM_STYLECHANGING. */ if(button_is_fake_split(button)) { if((button->style & BS_TYPEMASK) == MC_BS_DEFSPLITBUTTON) { BUTTON_TRACE("button_proc(WM_GETDLGCODE): -> DLGC_DEFPUSHBUTTON"); return DLGC_BUTTON | DLGC_DEFPUSHBUTTON; } if((button->style & BS_TYPEMASK) == MC_BS_SPLITBUTTON) { BUTTON_TRACE("button_proc(WM_GETDLGCODE): -> DLGC_UNDEFPUSHBUTTON"); return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON; } } break; case BM_SETSTATE: if(button_is_fake_split(button)) { CallWindowProc(orig_button_proc, win, msg, wp, lp); /* USER32.DLL does some painting in BM_SETSTATE. Repaint * the split button. */ InvalidateRect(win, NULL, TRUE); return 0; } break; case BM_GETSTATE: if(button_is_fake_split(button)) { DWORD s = CallWindowProc(orig_button_proc, win, msg, wp, lp); if(button->is_dropdown_pushed) s |= MC_BST_DROPDOWNPUSHED; return s; } break; case BM_SETSTYLE: if(button_is_fake_split(button)) { BUTTON_TRACE("button_proc(BM_SETSTYLE): split style fixup"); wp &= ~(BS_TYPEMASK & ~BS_DEFPUSHBUTTON); wp |= MC_BS_SPLITBUTTON; CallWindowProc(orig_button_proc, win, msg, wp, lp); button->style = GetWindowLong(win, GWL_STYLE); return 0; } break; case WM_SETREDRAW: button->no_redraw = !wp; break; case WM_STYLECHANGING: if(button_is_fake_split(button)) { STYLESTRUCT* ss = (STYLESTRUCT*) lp; if((ss->styleOld & BS_TYPEMASK) == MC_BS_SPLITBUTTON || (ss->styleOld & BS_TYPEMASK) == MC_BS_DEFSPLITBUTTON) { /* On older system which do not support split buttons * (i.e. 2000, XP), the dialog procedure does not handle * moving the default state correctly: It accidentaly * removes our split button button type. Hence we perform * this fixup. * * Unfortunately this means that app. cannot freely change * BS_SPLITBUTTON to BS_BUTTON with SetWindowLong(GWL_STYLE) * as this will prevent that too... */ BUTTON_TRACE("button_proc(WM_STYLECHANGING): split style fixup"); ss->styleNew &= ~(BS_TYPEMASK & ~BS_DEFPUSHBUTTON); ss->styleNew |= MC_BS_SPLITBUTTON; } } break; case WM_STYLECHANGED: if(wp == GWL_STYLE) { STYLESTRUCT* ss = (STYLESTRUCT*) lp; button->style = ss->styleNew; } break; case WM_THEMECHANGED: if(button->theme) mcCloseThemeData(button->theme); button->theme = mcOpenThemeData(win, button_tc); InvalidateRect(win, NULL, FALSE); break; case WM_SYSCOLORCHANGE: InvalidateRect(win, NULL, FALSE); break; case WM_UPDATEUISTATE: button_update_ui_state(button, LOWORD(wp), HIWORD(wp)); InvalidateRect(win, NULL, FALSE); break; case WM_NCCREATE: if(MC_ERR(!CallWindowProc(orig_button_proc, win, WM_NCCREATE, wp, lp))) { MC_TRACE_ERR("button_proc(WM_NCCREATE): orig_button_proc() failed"); return FALSE; } button = (button_t*) malloc(sizeof(button_t)); if(MC_ERR(button == NULL)) { MC_TRACE("button_proc(WM_CREATE): malloc() failed."); return FALSE; } memset(button, 0, sizeof(button_t)); button->style = ((CREATESTRUCT*)lp)->style; SetWindowLongPtr(win, extra_offset, (LONG_PTR) button); return TRUE; case WM_CREATE: if(MC_ERR(CallWindowProc(orig_button_proc, win, WM_CREATE, wp, lp) != 0)) { MC_TRACE_ERR("button_proc(WM_CREATE): orig_button_proc() failed"); return -1; } button->theme = mcOpenThemeData(win, button_tc); { WORD ui_state = MC_SEND(win, WM_QUERYUISTATE, 0, 0); button->hide_focus = (ui_state & UISF_HIDEFOCUS) ? 1 : 0; button->hide_accel = (ui_state & UISF_HIDEACCEL) ? 1 : 0; } return 0; case WM_DESTROY: if(button->theme) { mcCloseThemeData(button->theme); button->theme = NULL; } break; case WM_NCDESTROY: if(button) free(button); break; } return CallWindowProc(orig_button_proc, win, msg, wp, lp); }
BOOL MCTRL_API mcMenubar_HandleRebarChevronPushed(HWND hwndMenubar, NMREBARCHEVRON* lpRebarChevron) { REBARBANDINFO band_info; menubar_t* mb; RECT rect; HMENU menu; MENUITEMINFO mii; TCHAR buffer[MENUBAR_ITEM_LABEL_MAXSIZE]; int i, n; TPMPARAMS params; /* Verify lpRebarChevron is from notification we assume. */ if(MC_ERR(lpRebarChevron->hdr.code != RBN_CHEVRONPUSHED)) { MC_TRACE("mcMenubar_HandleRebarChevronPushed: Not RBN_CHEVRONPUSHED"); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } /* Verify/get the menubar handle */ band_info.cbSize = REBARBANDINFO_V3_SIZE; band_info.fMask = RBBIM_CHILD; MC_SEND(lpRebarChevron->hdr.hwndFrom, RB_GETBANDINFO, lpRebarChevron->uBand, &band_info); if(hwndMenubar != NULL) { if(MC_ERR(hwndMenubar != band_info.hwndChild)) { MC_TRACE("mcMenubar_HandleRebarChevronPushed: " "Notification not about band with the specified menubar."); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } } else { hwndMenubar = band_info.hwndChild; if(MC_ERR(hwndMenubar == NULL)) { MC_TRACE("mcMenubar_HandleRebarChevronPushed: " "The band does not host any child window"); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } } /* Create popup menu for not completely visible menu items */ mb = (menubar_t*) GetWindowLongPtr(hwndMenubar, extra_offset); GetClientRect(hwndMenubar, &rect); menu = CreatePopupMenu(); if(MC_ERR(menu == NULL)) { MC_TRACE_ERR("mcMenubar_HandleRebarChevronPushed: CreatePopupMenu() failed."); return FALSE; } mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_STRING | MIIM_STATE | MIIM_ID | MIIM_SUBMENU; mii.dwTypeData = buffer; mii.cch = MC_SIZEOF_ARRAY(buffer); n = MENUBAR_SENDMSG(hwndMenubar, TB_BUTTONCOUNT, 0, 0); for(i = n-1; i >= 0; i--) { RECT item_rect; MENUBAR_SENDMSG(hwndMenubar, TB_GETITEMRECT, i, &item_rect); if(item_rect.right < rect.right) break; mii.cch = MC_SIZEOF_ARRAY(buffer); GetMenuItemInfo(mb->menu, i, TRUE, &mii); InsertMenuItem(menu, 0, TRUE, &mii); } params.cbSize = sizeof(TPMPARAMS); mc_rect_copy(¶ms.rcExclude, &lpRebarChevron->rc); /* Run the menu */ MapWindowPoints(hwndMenubar, NULL, (POINT*) ¶ms.rcExclude, 2); TrackPopupMenuEx(menu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL, params.rcExclude.left, params.rcExclude.bottom, mb->win, ¶ms); /* Destroy the popup menu. Note submenus have to survive as they are shared * with the menubar itself. */ n = GetMenuItemCount(menu); for(i = 0; i < n; i++) RemoveMenu(menu, 0, MF_BYPOSITION); DestroyMenu(menu); return TRUE; }