diff --git a/reactos/subsys/system/explorer/doc/TODO.txt b/reactos/subsys/system/explorer/doc/TODO.txt index 3dbafff6367..982daa9c2b7 100644 --- a/reactos/subsys/system/explorer/doc/TODO.txt +++ b/reactos/subsys/system/explorer/doc/TODO.txt @@ -18,14 +18,11 @@ - close start start menu when resizing explorer bar - adjust already open windows of all running applications when resizing explorer bar - implement blink effect in task bar for SetForegoundWindow() requests -- implement more than one column or scrolling in start menus for very crowded start menus - Execute selected programs with additional command line options ("Run" dialog with pre-filled edit field) - switch background images with desktop switcher - Desktop Manager: Desktop Names; Speicherung der Verteilung von Applikations-Fenstern auf die verschiedenen Desktops -> config file - autostart doesn't work on NT4 ? - hide desktop bar when showing full screen applications - new start menu entry "Filemanager" close to "Explore" -> display C: and D: drive in MDI window - -- Startmenu: You can't close the start menu with pressing Win-key You can open the start menu - with pressing Win-key, but can't close with another hit of Win-key. +- Startmenu: You can open the start menu by pressing Win-key, but can't close with another hit of Win-key. diff --git a/reactos/subsys/system/explorer/explorer.dsp b/reactos/subsys/system/explorer/explorer.dsp index 075c4f81b3d..62facffc7f3 100644 --- a/reactos/subsys/system/explorer/explorer.dsp +++ b/reactos/subsys/system/explorer/explorer.dsp @@ -361,6 +361,18 @@ SOURCE=.\res\arrow.ico # End Source File # Begin Source File +SOURCE=.\res\arrow_do.ico +# End Source File +# Begin Source File + +SOURCE=.\res\arrow_dwn.ico +# End Source File +# Begin Source File + +SOURCE=.\res\arrow_up.ico +# End Source File +# Begin Source File + SOURCE=.\res\arrowsel.ico # End Source File # Begin Source File diff --git a/reactos/subsys/system/explorer/explorer_intres.h b/reactos/subsys/system/explorer/explorer_intres.h index 1e9a4e0b1d1..caef1ea3d68 100644 --- a/reactos/subsys/system/explorer/explorer_intres.h +++ b/reactos/subsys/system/explorer/explorer_intres.h @@ -106,6 +106,8 @@ #define IDB_LOGOV16 164 #define IDI_DOT_TRANS 164 #define IDI_DOT_RED 165 +#define IDI_ARROW_UP 166 +#define IDI_ARROW_DOWN 167 #define ID_VIEW_NAME 401 #define ID_VIEW_ALL_ATTRIBUTES 402 #define ID_VIEW_SELECTED_ATTRIBUTES 403 diff --git a/reactos/subsys/system/explorer/explorer_intres.rc b/reactos/subsys/system/explorer/explorer_intres.rc index 52c550ed81e..5a6241bb229 100644 --- a/reactos/subsys/system/explorer/explorer_intres.rc +++ b/reactos/subsys/system/explorer/explorer_intres.rc @@ -947,6 +947,8 @@ IDI_SPEAKER ICON DISCARDABLE "res/speaker.ico" IDI_DOT ICON DISCARDABLE "res/dot.ico" IDI_DOT_TRANS ICON DISCARDABLE "res/dot_trans.ico" IDI_DOT_RED ICON DISCARDABLE "res/dot_red.ico" +IDI_ARROW_UP ICON DISCARDABLE "res/arrow_up.ico" +IDI_ARROW_DOWN ICON DISCARDABLE "res/arrow_dwn.ico" ///////////////////////////////////////////////////////////////////////////// // diff --git a/reactos/subsys/system/explorer/res/arrow_dwn.ico b/reactos/subsys/system/explorer/res/arrow_dwn.ico new file mode 100644 index 00000000000..e6a982ddad1 Binary files /dev/null and b/reactos/subsys/system/explorer/res/arrow_dwn.ico differ diff --git a/reactos/subsys/system/explorer/res/arrow_up.ico b/reactos/subsys/system/explorer/res/arrow_up.ico new file mode 100644 index 00000000000..d5244d6a659 Binary files /dev/null and b/reactos/subsys/system/explorer/res/arrow_up.ico differ diff --git a/reactos/subsys/system/explorer/taskbar/startmenu.cpp b/reactos/subsys/system/explorer/taskbar/startmenu.cpp index ec326a4f564..a96e0c33626 100644 --- a/reactos/subsys/system/explorer/taskbar/startmenu.cpp +++ b/reactos/subsys/system/explorer/taskbar/startmenu.cpp @@ -46,8 +46,17 @@ StartMenu::StartMenu(HWND hwnd) { _next_id = IDC_FIRST_MENU; _submenu_id = 0; + _border_left = 0; _border_top = 0; + _bottom_max = INT_MAX; + + _floating_btn = false; + _arrow_btns = false; + _scroll_mode = SCROLL_NOT; + _scroll_pos = 0; + _invisible_lines = 0; + _last_pos = WindowRect(hwnd).pos(); #ifdef _LIGHT_STARTMENU _selected_id = -1; @@ -65,8 +74,17 @@ StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info) _next_id = IDC_FIRST_MENU; _submenu_id = 0; + _border_left = 0; _border_top = create_info._border_top; + _bottom_max = INT_MAX; + + _floating_btn = create_info._border_top? true: false; + _arrow_btns = false; + _scroll_mode = SCROLL_NOT; + _scroll_pos = 0; + _invisible_lines = 0; + _last_pos = WindowRect(hwnd).pos(); #ifdef _LIGHT_STARTMENU _selected_id = -1; @@ -309,7 +327,33 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) case WM_MOUSEMOVE: { // automatically set the focus to startmenu entries when moving the mouse over them if (lparam != _last_mouse_pos) { // don't process WM_MOUSEMOVE when opening submenus using keyboard navigation - int new_id = ButtonHitTest(Point(lparam)); + Point pt(lparam); + + if (_arrow_btns) { + RECT rect_up, rect_down; + + GetArrowButtonRects(&rect_up, &rect_down); + + SCROLL_MODE scroll_mode = SCROLL_NOT; + + if (PtInRect(&rect_up, pt)) + scroll_mode = SCROLL_UP; + else if (PtInRect(&rect_down, pt)) + scroll_mode = SCROLL_DOWN; + + if (scroll_mode != _scroll_mode) { + if (scroll_mode == SCROLL_NOT) + KillTimer(_hwnd, 0); + else { + CloseSubmenus(); + SetTimer(_hwnd, 0, 150, NULL); // 150 ms scroll interval + } + + _scroll_mode = scroll_mode; + } + } + + int new_id = ButtonHitTest(pt); if (new_id != _selected_id) SelectButton(new_id); @@ -318,6 +362,20 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) } break;} + case WM_TIMER: + if (_scroll_mode == SCROLL_UP) { + if (_scroll_pos > 0) { + --_scroll_pos; + InvalidateRect(_hwnd, NULL, TRUE); + } + } else { + if (_scroll_pos <= _invisible_lines) { + ++_scroll_pos; + InvalidateRect(_hwnd, NULL, TRUE); + } + } + break; + case WM_KEYDOWN: ProcessKey(wparam); break; @@ -379,7 +437,7 @@ int StartMenu::ButtonHitTest(POINT pt) if (pt.xrect.right) return 0; - for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) { + for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) { const SMBtnInfo& info = *it; if (rect.top > pt.y) @@ -387,6 +445,9 @@ int StartMenu::ButtonHitTest(POINT pt) rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT: STARTMENU_LINE_HEIGHT); + if (rect.bottom > _bottom_max) + break; + if (pt.y < rect.bottom) // PtInRect(&rect, pt) return info._id; @@ -404,7 +465,7 @@ void StartMenu::InvalidateSelection() ClientRect clnt(_hwnd); RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT}; - for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) { + for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) { const SMBtnInfo& info = *it; rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT: STARTMENU_LINE_HEIGHT); @@ -629,7 +690,7 @@ bool StartMenu::GetButtonRect(int id, PRECT prect) const ClientRect clnt(_hwnd); RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT}; - for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) { + for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) { const SMBtnInfo& info = *it; rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT: STARTMENU_LINE_HEIGHT); @@ -676,12 +737,44 @@ void StartMenu::GetFloatingButtonRect(LPRECT prect) } +void StartMenu::DrawArrows(HDC hdc) +{ + static ResIconEx arrowUpIcon(IDI_ARROW_UP, 8, 4); + static ResIconEx arrowDownIcon(IDI_ARROW_DOWN, 8, 4); + + ClientRect clnt(_hwnd); + + DrawIconEx(hdc, clnt.right/2-4, _floating_btn?3:1, arrowUpIcon, 8, 4, 0, 0, DI_NORMAL); + DrawIconEx(hdc, clnt.right/2-4, clnt.bottom-5, arrowDownIcon, 8, 4, 0, 0, DI_NORMAL); +} + +void StartMenu::GetArrowButtonRects(LPRECT prect_up, LPRECT prect_down) +{ + GetClientRect(_hwnd, prect_up); + *prect_down = *prect_up; + +// prect_up->left = prect_up->right/2 - 4; +// prect_up->right = prect_up->left + 8; + prect_up->right -= 8; + prect_up->top = _floating_btn? 3: 1; + prect_up->bottom = prect_up->top + 4; + +// prect_down->left = prect_down->right/2 - 4; +// prect_down->right = prect_down->left + 8; + prect_down->right -= 8; + prect_down->top = prect_down->bottom - 5; +} + + void StartMenu::Paint(PaintCanvas& canvas) { - if (_border_top) + if (_floating_btn) DrawFloatingButton(canvas); #ifdef _LIGHT_STARTMENU + if (_arrow_btns) + DrawArrows(canvas); + ClientRect clnt(_hwnd); RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT}; @@ -690,7 +783,7 @@ void StartMenu::Paint(PaintCanvas& canvas) FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT)); BkMode bk_mode(canvas, TRANSPARENT); - for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) { + for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) { const SMBtnInfo& btn = *it; if (rect.top > canvas.rcPaint.bottom) @@ -699,6 +792,9 @@ void StartMenu::Paint(PaintCanvas& canvas) if (btn._id == -1) { // a separator? rect.bottom = rect.top + STARTMENU_SEP_HEIGHT; + if (rect.bottom > _bottom_max) + break; + BrushSelection brush_sel(canvas, GetSysColorBrush(COLOR_BTNSHADOW)); PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT/2-1, sep_width, 1, PATCOPY); @@ -707,6 +803,9 @@ void StartMenu::Paint(PaintCanvas& canvas) } else { rect.bottom = rect.top + STARTMENU_LINE_HEIGHT; + if (rect.bottom > _bottom_max) + break; + if (rect.top >= canvas.rcPaint.top) DrawStartMenuButton(canvas, rect, btn._title, btn, btn._id==_selected_id, false); } @@ -724,7 +823,7 @@ void StartMenu::UpdateIcons(/*int idx*/) #ifdef _SINGLE_ICONEXTRACT //if (idx >= 0) - int idx = 0; + int idx = _scroll_pos; for(; idx<(int)_buttons.size(); ++idx) { SMBtnInfo& btn = _buttons[idx]; @@ -738,7 +837,11 @@ void StartMenu::UpdateIcons(/*int idx*/) Entry* entry = *it; if (entry->_icon_id == ICID_UNKNOWN) - entry->extract_icon(); + try { + entry->extract_icon(); + } catch(COMException&) { + // ignore unexpected exceptions while extracting icons + } if (entry->_icon_id > ICID_NONE) { btn._icon_id = (ICON_ID)/*@@*/ entry->_icon_id; @@ -746,6 +849,10 @@ void StartMenu::UpdateIcons(/*int idx*/) RECT rect; GetButtonRect(btn._id, &rect); + + if (rect.bottom > _bottom_max) + break; + WindowCanvas canvas(_hwnd); DrawStartMenuButton(canvas, rect, NULL, btn, btn._id==_selected_id, false); @@ -1066,7 +1173,7 @@ void StartMenu::CreateSubmenu(int id, const StartMenuFolders& new_folders, LPCTS ClientToScreen(_hwnd, &rect); x = rect.right; // Submenus should overlap their parent a bit. - y = rect.top+STARTMENU_LINE_HEIGHT-_border_top; + y = rect.top+STARTMENU_LINE_HEIGHT +_border_top/*own border*/ -STARTMENU_TOP_BTN_SPACE/*border of new submenu*/; } else { WindowRect pos(_hwnd); @@ -1103,7 +1210,7 @@ void StartMenu::ActivateEntry(int id, const ShellEntrySet& entries) if (title.empty()) title = entry->_display_name; } else { - // If the entry is no subdirectory, there can only be one shell entry. + // The entry is no subdirectory, so there can only be one shell entry. assert(entries.size()==1); HWND hparent = GetParent(_hwnd); @@ -1264,11 +1371,40 @@ void StartMenu::ResizeToButtons() // move down if we are too high if (rect.top < 0) { - rect.top += STARTMENU_LINE_HEIGHT; - rect.bottom += STARTMENU_LINE_HEIGHT; + int dy = -rect.top; + rect.top += dy; + rect.bottom += dy; + } + + // enable scroll mode for long start menus, which span more than the whole screen height + int cyscreen = GetSystemMetrics(SM_CYSCREEN); + int bottom_max = 0; + + if (rect.bottom > cyscreen) { + _arrow_btns = true; + + _invisible_lines = (rect.bottom-cyscreen+(STARTMENU_LINE_HEIGHT-1))/STARTMENU_LINE_HEIGHT + 1; + rect.bottom -= _invisible_lines * STARTMENU_LINE_HEIGHT; + + bottom_max = rect.bottom; + + if (_floating_btn) + rect.bottom += 6; // lower scroll arrow + else { + _border_top += 6; // upper scroll arrow + rect.bottom += 2*6; // upper+lower scroll arrow + } } MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE); + + if (bottom_max) { + POINT pt = {0, bottom_max}; + + ScreenToClient(_hwnd, &pt); + + _bottom_max = pt.y; + } } #else // _LIGHT_STARTMENU diff --git a/reactos/subsys/system/explorer/taskbar/startmenu.h b/reactos/subsys/system/explorer/taskbar/startmenu.h index 6402029e224..c5b42487cab 100644 --- a/reactos/subsys/system/explorer/taskbar/startmenu.h +++ b/reactos/subsys/system/explorer/taskbar/startmenu.h @@ -240,8 +240,15 @@ protected: int _border_left; // left border in pixels int _border_top; // top border in pixels + int _bottom_max; // limit display area for long start menus + + bool _floating_btn; + bool _arrow_btns; POINT _last_pos; + enum SCROLL_MODE {SCROLL_NOT, SCROLL_UP, SCROLL_DOWN} _scroll_mode; + int _scroll_pos; + int _invisible_lines; StartMenuCreateInfo _create_info; // copy of the original create info @@ -291,6 +298,9 @@ protected: void DrawFloatingButton(HDC hdc); void GetFloatingButtonRect(LPRECT prect); + void GetArrowButtonRects(LPRECT prect_up, LPRECT prect_down); + + void DrawArrows(HDC hdc); void Paint(PaintCanvas& canvas); void UpdateIcons(/*int idx*/);