merge ROS Shell without integrated explorer part into trunk
[reactos.git] / reactos / subsys / system / explorer / taskbar / startmenu.cpp
index 03a6326..2a00ded 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003 Martin Fuchs
+ * Copyright 2003, 2004 Martin Fuchs
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  //
 
 
-#include "../utility/utility.h"
+#include "precomp.h"
 
-#include "../explorer.h"
-#include "../globals.h"
-#include "../externals.h"
 #include "../explorer_intres.h"
 
 #include "desktopbar.h"
 #include "startmenu.h"
+
 #include "../dialogs/searchprogram.h"
+#include "../dialogs/settings.h"
+
+
+#define        SHELLPATH_CONTROL_PANEL         TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}")
+#define        SHELLPATH_PRINTERS                      TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}")
+#define        SHELLPATH_NET_CONNECTIONS       TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}")
 
 
 StartMenu::StartMenu(HWND hwnd)
@@ -47,9 +51,22 @@ 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;
+       _last_mouse_pos = 0;
+#endif
 }
 
 StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info)
@@ -58,13 +75,26 @@ StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info)
 {
        for(StartMenuFolders::const_iterator it=create_info._folders.begin(); it!=create_info._folders.end(); ++it)
                if (*it)
-                       _dirs.push_back(ShellDirectory(Desktop(), *it, _hwnd));
+                       _dirs.push_back(ShellDirectory(GetDesktopFolder(), *it, _hwnd));
 
        _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;
+       _last_mouse_pos = 0;
+#endif
 }
 
 StartMenu::~StartMenu()
@@ -83,9 +113,9 @@ BtnWindowClass& StartMenu::GetWndClasss()
 }
 
 
-Window::CREATORFUNC StartMenu::s_def_creator = STARTMENU_CREATOR(StartMenu);
+Window::CREATORFUNC_INFO StartMenu::s_def_creator = STARTMENU_CREATOR(StartMenu);
 
-HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndParent, LPCTSTR title, CREATORFUNC creator)
+HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndParent, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
 {
        UINT style, ex_style;
        int top_height;
@@ -100,7 +130,11 @@ HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndP
                top_height = 0;
        }
 
-       RECT rect = {x, y, x+STARTMENU_WIDTH_MIN, y+top_height};        // start height before adding an menu button
+       RECT rect = {x, y-STARTMENU_LINE_HEIGHT-top_height, x+STARTMENU_WIDTH_MIN, y};
+
+#ifndef _LIGHT_STARTMENU
+       rect.top += STARTMENU_LINE_HEIGHT;
+#endif
 
        AdjustWindowRectEx(&rect, style, FALSE, ex_style);
 
@@ -109,6 +143,7 @@ HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndP
        create_info._folders = folders;
        create_info._border_top = top_height;
        create_info._creator = creator;
+       create_info._info = info;
 
        if (title)
                create_info._title = title;
@@ -123,7 +158,7 @@ HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndP
 }
 
 
-LRESULT        StartMenu::Init(LPCREATESTRUCT pcs)
+LRESULT StartMenu::Init(LPCREATESTRUCT pcs)
 {
        try {
                AddEntries();
@@ -140,11 +175,27 @@ LRESULT   StartMenu::Init(LPCREATESTRUCT pcs)
                                if ((*it)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                                        hasSubmenu = true;
 
+#ifdef _LIGHT_STARTMENU
+                       _buttons.push_back(SMBtnInfo(sme, it->first, hasSubmenu));
+#else
                        AddButton(sme._title, sme._hIcon, hasSubmenu, it->first);
+#endif
                }
 
+#ifdef _LIGHT_STARTMENU
+               if (_buttons.empty())
+#else
                if (!GetWindow(_hwnd, GW_CHILD))
-                       AddButton(ResString(IDS_EMPTY), 0, false, (UINT)-1, WS_VISIBLE|WS_CHILD|BS_OWNERDRAW|WS_DISABLED);
+#endif
+                       AddButton(ResString(IDS_EMPTY), ICID_NONE, false, 0, false);
+
+#ifdef _LIGHT_STARTMENU
+               ResizeToButtons();
+#endif
+
+#ifdef _LAZY_ICONEXTRACT
+               PostMessage(_hwnd, PM_UPDATE_ICONS, 0, 0);
+#endif
        } catch(COMException& e) {
                HandleException(e, pcs->hwndParent);    // destroys the start menu window while switching focus
        }
@@ -161,7 +212,11 @@ void StartMenu::AddEntries()
                if (!dir._scanned) {
                        WaitCursor wait;
 
-                       dir.smart_scan();
+#ifdef _LAZY_ICONEXTRACT
+                       dir.smart_scan(SORT_NAME, SCAN_FILESYSTEM);     // lazy icon extraction, try to read directly from filesystem
+#else
+                       dir.smart_scan(SORT_NAME, SCAN_EXTRACT_ICONS|SCAN_FILESYSTEM);
+#endif
                }
 
                AddShellEntries(dir, -1, smd._subfolders);
@@ -173,7 +228,7 @@ void StartMenu::AddShellEntries(const ShellDirectory& dir, int max, bool subfold
 {
        int cnt = 0;
 
-       for(const Entry*entry=dir._down; entry; entry=entry->_next) {
+       for(Entry*entry=dir._down; entry; entry=entry->_next) {
                 // hide files like "desktop.ini"
                if (entry->_shell_attribs & SFGAO_HIDDEN)
                //not appropriate for drive roots: if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
@@ -188,9 +243,10 @@ void StartMenu::AddShellEntries(const ShellDirectory& dir, int max, bool subfold
                if (++cnt == max)
                        break;
 
-               const ShellEntry* shell_entry = static_cast<const ShellEntry*>(entry);
-
-               AddEntry(dir._folder, shell_entry);
+               if (entry->_etype == ET_SHELL)
+                       AddEntry(dir._folder, static_cast<ShellEntry*>(entry));
+               else
+                       AddEntry(dir._folder, entry);
        }
 }
 
@@ -198,9 +254,10 @@ void StartMenu::AddShellEntries(const ShellDirectory& dir, int max, bool subfold
 LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
 {
        switch(nmsg) {
-         case WM_PAINT:
-               DrawFloatingButton(PaintCanvas(_hwnd));
-               break;
+         case WM_PAINT: {
+               PaintCanvas canvas(_hwnd);
+               Paint(canvas);
+               break;}
 
          case WM_SIZE:
                ResizeButtons(LOWORD(lparam)-_border_left);
@@ -237,16 +294,23 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
                RECT rect;
 
                 // check mouse cursor for coordinates of floating button
-               GetFloatingButonRect(&rect);
+               GetFloatingButtonRect(&rect);
 
                if (PtInRect(&rect, Point(lparam))) {
                         // create a floating copy of the current start menu
                        WindowRect pos(_hwnd);
 
                        ///@todo do something similar to StartMenuRoot::TrackStartmenu() in order to automatically close submenus when clicking on the desktop background
-                       StartMenu::Create(pos.left+3, pos.bottom-3, _create_info._folders, 0, _create_info._title, _create_info._creator);
+                       StartMenu::Create(pos.left+3, pos.bottom-3, _create_info._folders, 0, _create_info._title, _create_info._creator, _create_info._info);
                        CloseStartMenu();
                }
+
+#ifdef _LIGHT_STARTMENU
+               int id = ButtonHitTest(Point(lparam));
+
+               if (id)
+                       Command(id, BN_CLICKED);
+#endif
                break;}
 
          case WM_SYSCOMMAND:
@@ -262,8 +326,74 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
 
          case WM_CANCELMODE:
                CloseStartMenu();
+
+#ifdef _LIGHT_STARTMENU
+               if (_scroll_mode != SCROLL_NOT) {
+                       ReleaseCapture();
+                       KillTimer(_hwnd, 0);
+               }
+#endif
+               break;
+
+#ifdef _LIGHT_STARTMENU
+         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
+                       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) {
+                                               ReleaseCapture();
+                                               KillTimer(_hwnd, 0);
+                                       } else {
+                                               CloseSubmenus();
+                                               SetTimer(_hwnd, 0, 150, NULL);  // 150 ms scroll interval
+                                               SetCapture(_hwnd);
+                                       }
+
+                                       _scroll_mode = scroll_mode;
+                               }
+                       }
+
+                       int new_id = ButtonHitTest(pt);
+
+                       if (new_id > 0 && new_id != _selected_id)
+                               SelectButton(new_id);
+
+                       _last_mouse_pos = 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;
+#else
          case PM_STARTENTRY_FOCUSED: { ///@todo use TrackMouseEvent() and WM_MOUSEHOVER to wait a bit before opening submenus
                BOOL hasSubmenu = wparam;
                HWND hctrl = (HWND)lparam;
@@ -275,9 +405,16 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
                        Command(GetDlgCtrlID(hctrl), BN_CLICKED);
                } else {
                         // close any open submenu
-                       CloseOtherSubmenus(0);
+                       CloseOtherSubmenus();
                }
                break;}
+#endif
+
+#ifdef _LAZY_ICONEXTRACT
+         case PM_UPDATE_ICONS:
+               UpdateIcons(/*wparam*/);
+               break;
+#endif
 
          case PM_STARTENTRY_LAUNCHED:
                if (GetWindowStyle(_hwnd) & WS_CAPTION) // don't automatically close floating menus
@@ -285,13 +422,39 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
 
                 // route message to the parent menu and close menus after launching an entry
                if (!SendParent(nmsg, wparam, lparam))
-                       DestroyWindow(_hwnd);
+                       CloseStartMenu();
                return 1;       // signal that we have received and processed the message
 
          case PM_STARTMENU_CLOSED:
                _submenu = 0;
                break;
 
+         case PM_SELECT_ENTRY:
+               SelectButtonIndex(0, wparam!=0);
+               break;
+
+#ifdef _LIGHT_STARTMENU
+         case WM_CONTEXTMENU: {
+               Point screen_pt(lparam), clnt_pt=screen_pt;
+               ScreenToClient(_hwnd, &clnt_pt);
+
+               int id = ButtonHitTest(clnt_pt);
+
+               if (id) {
+                       StartMenuEntry& sme = _entries[id];
+
+                       for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
+                               Entry* entry = *it;
+
+                               if (entry) {
+                                       CHECKERROR(entry->do_context_menu(_hwnd, screen_pt, _cm_ifs));  // may close start menu because of focus loss
+                                       break;  ///@todo handle context menu for more than one entry
+                               }
+                       }
+               }
+               break;}
+#endif
+
          default: def:
                return super::WndProc(nmsg, wparam, lparam);
        }
@@ -300,268 +463,819 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
 }
 
 
-void StartMenu::DrawFloatingButton(HDC hdc)
-{
-       static ResIconEx floatingIcon(IDI_FLOATING, 8, 4);
-
-       ClientRect clnt(_hwnd);
-
-       DrawIconEx(hdc, clnt.right-12, 0, floatingIcon, 8, 4, 0, 0, DI_NORMAL);
-}
+#ifdef _LIGHT_STARTMENU
 
-void StartMenu::GetFloatingButonRect(LPRECT prect)
+int StartMenu::ButtonHitTest(POINT pt)
 {
-       GetClientRect(_hwnd, prect);
+       ClientRect clnt(_hwnd);
+       RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
 
-       prect->right -= 4;
-       prect->left = prect->right - 8;
-       prect->bottom = 4;
-}
+       if (pt.x<rect.left || pt.x>rect.right)
+               return 0;
 
+       for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
+               const SMBtnInfo& info = *it;
 
- // resize child button controls to accomodate for new window size
-void StartMenu::ResizeButtons(int cx)
-{
-       HDWP hdwp = BeginDeferWindowPos(10);
+               if (rect.top > pt.y)
+                       break;
 
-       for(HWND ctrl=GetWindow(_hwnd,GW_CHILD); ctrl; ctrl=GetNextWindow(ctrl,GW_HWNDNEXT)) {
-               ClientRect rt(ctrl);
+               rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT: STARTMENU_LINE_HEIGHT);
 
-               if (rt.right != cx) {
-                       int height = rt.bottom - rt.top;
+               if (rect.bottom > _bottom_max)
+                       break;
 
-                        // special handling for separator controls
-                       if (!height && (GetWindowStyle(ctrl)&SS_TYPEMASK)==SS_ETCHEDHORZ)
-                               height = 2;
+               if (pt.y < rect.bottom) // PtInRect(&rect, pt)
+                       return info._id;
 
-                       hdwp = DeferWindowPos(hdwp, ctrl, 0, 0, 0, cx, height, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
-               }
+               rect.top = rect.bottom;
        }
 
-       EndDeferWindowPos(hdwp);
+       return 0;
 }
 
-
-int StartMenu::Command(int id, int code)
+void StartMenu::InvalidateSelection()
 {
-       switch(id) {
-         case IDCANCEL:
-               DestroyWindow(_hwnd);
-               break;
+       if (_selected_id <= 0)
+               return;
 
-         default: {
-               ShellEntryMap::iterator found = _entries.find(id);
+       ClientRect clnt(_hwnd);
+       RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
 
-               if (found != _entries.end()) {
-                       ActivateEntry(id, found->second._entries);
+       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);
+
+               if (info._id == _selected_id) {
+                       InvalidateRect(_hwnd, &rect, TRUE);
                        break;
                }
 
-               return super::Command(id, code);}
+               rect.top = rect.bottom;
        }
-
-       return 0;
 }
 
-
-StartMenuEntry& StartMenu::AddEntry(LPCTSTR title, HICON hIcon, UINT id)
+const SMBtnInfo* StartMenu::GetButtonInfo(int id) const
 {
-       if (id == (UINT)-1)
-               id = ++_next_id;
+       for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it)
+               if (it->_id == id)
+                       return &*it;
 
-       StartMenuEntry& sme = _entries[id];
-
-       sme._title = title;
-       sme._hIcon = hIcon;
-
-       return sme;
+       return NULL;
 }
 
-StartMenuEntry& StartMenu::AddEntry(const ShellFolder folder, const ShellEntry* entry)
+bool StartMenu::SelectButton(int id, bool open_sub)
 {
-       HICON hIcon = entry->_hIcon;
+       if (id == -1)
+               return false;
 
-       if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-               hIcon = SmallIcon(IDI_EXPLORER);
+       if (id == _selected_id)
+               return true;
 
-       const String& entry_name = folder.get_name(entry->_pidl);
+       InvalidateSelection();
 
-        // search for an already existing subdirectory entry with the same name
-       if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-               for(ShellEntryMap::iterator it=_entries.begin(); it!=_entries.end(); ++it) {
-                       StartMenuEntry& sme = it->second;
+       const SMBtnInfo* btn = GetButtonInfo(id);
 
-                       if (sme._title == entry_name)   ///@todo speed up by using a map indexed by name
-                               for(ShellEntrySet::iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
-                                       if ((*it2)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
-                                                // merge the new shell entry with the existing of the same name
-                                               sme._entries.insert(entry);
-                                               return sme;
-                                       }
-                               }
-               }
+       if (btn && btn->_enabled) {
+               _selected_id = id;
 
-       StartMenuEntry& sme = AddEntry(entry_name, hIcon);
+               InvalidateSelection();
 
-       sme._entries.insert(entry);
+                // automatically open submenus
+               if (btn->_hasSubmenu) {
+                       if (open_sub)
+                               OpenSubmenu();
+               } else
+                       CloseOtherSubmenus();   // close any open submenu
 
-       return sme;
+               return true;
+       } else {
+               _selected_id = -1;
+               return false;
+       }
 }
 
-
-void StartMenu::AddButton(LPCTSTR title, HICON hIcon, bool hasSubmenu, UINT id, DWORD style)
+bool StartMenu::OpenSubmenu(bool select_first)
 {
-       WindowRect rect(_hwnd);
-       ClientRect clnt(_hwnd);
-
-        // increase window height to make room for the new button
-       rect.top -= STARTMENU_LINE_HEIGHT;
+       if (_selected_id == -1)
+               return false;
 
-        // move down if we are too high now
-       if (rect.top < 0) {
-               rect.top += STARTMENU_LINE_HEIGHT;
-               rect.bottom += STARTMENU_LINE_HEIGHT;
-       }
+       InvalidateSelection();
 
-        // widen window, if it is too small
-       int text_width = StartMenuButton::GetTextWidth(title,_hwnd) + 16/*icon*/ + 10/*placeholder*/ + 16/*arrow*/;
+       const SMBtnInfo* btn = GetButtonInfo(_selected_id);
 
-       int cx = clnt.right - _border_left;
-       if (text_width > cx)
-               rect.right += text_width-cx;
+        // automatically open submenus
+       if (btn->_hasSubmenu) {
+               //@@ allows destroying of startmenu when processing PM_UPDATE_ICONS -> GPF
+               UpdateWindow(_hwnd);    // draw focused button before waiting on submenu creation
+               Command(_selected_id, BN_CLICKED);
 
-       MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
+               if (select_first && _submenu)
+                       SendMessage(_submenu, PM_SELECT_ENTRY, (WPARAM)false, 0);
 
-       StartMenuCtrl(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left,
-                                       title, id, hIcon, hasSubmenu, style);
+               return true;
+       } else
+               return false;
 }
 
-void StartMenu::AddSeparator()
-{
-       WindowRect rect(_hwnd);
-       ClientRect clnt(_hwnd);
-
-        // increase window height to make room for the new separator
-       rect.top -= STARTMENU_SEP_HEIGHT;
 
-        // move down if we are too high now
-       if (rect.top < 0) {
-               rect.top += STARTMENU_LINE_HEIGHT;
-               rect.bottom += STARTMENU_LINE_HEIGHT;
-       }
+int StartMenu::GetSelectionIndex()
+{
+       if (_selected_id == -1)
+               return -1;
 
-       MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
+       for(int i=0; i<(int)_buttons.size(); ++i)
+               if (_buttons[i]._id == _selected_id)
+                       return i;
 
-       StartMenuSeparator(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left);
+       return -1;
 }
 
+bool StartMenu::SelectButtonIndex(int idx, bool open_sub)
+{
+       if (idx>=0 && idx<(int)_buttons.size())
+               return SelectButton(_buttons[idx]._id, open_sub);
+       else
+               return false;
+}
 
-bool StartMenu::CloseOtherSubmenus(int id)
+void StartMenu::ProcessKey(int vk)
 {
-       if (_submenu) {
-               if (IsWindow(_submenu)) {
-                       if (_submenu_id == id)
-                               return false;
-                       else {
-                               DestroyWindow(_submenu);
-                               _submenu_id = 0;
-                               // _submenu should be reset automatically by PM_STARTMENU_CLOSED, but safety first...
-                       }
-               }
+       switch(vk) {
+         case VK_RETURN:
+               if (_selected_id)
+                       Command(_selected_id, BN_CLICKED);
+               break;
 
-               _submenu = 0;
-       }
+         case VK_UP:
+               Navigate(-1);
+               break;
 
-       return true;
-}
+         case VK_DOWN:
+               Navigate(+1);
+               break;
 
+         case VK_HOME:
+               SelectButtonIndex(0, false);
+               break;
 
-void StartMenu::CreateSubmenu(int id, LPCTSTR title, CREATORFUNC creator)
-{
-       CreateSubmenu(id, StartMenuFolders(), title, creator);
-}
+         case VK_END:
+               SelectButtonIndex(_buttons.size()-1, false);
+               break;
 
-void StartMenu::CreateSubmenu(int id, int folder_id, LPCTSTR title, CREATORFUNC creator)
-{
-       try {
-               SpecialFolderPath folder(folder_id, _hwnd);
+         case VK_LEFT:
+               if (_submenu)
+                       CloseOtherSubmenus();
+               else if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) // don't automatically close floating menus
+                       DestroyWindow(_hwnd);
+               break;
 
-               StartMenuFolders new_folders;
-               new_folders.push_back(folder);
+         case VK_RIGHT:
+               OpenSubmenu(true);
+               break;
 
-               CreateSubmenu(id, new_folders, title, creator);
-       } catch(COMException&) {
-               // ignore Exception and don't display anything
+         case VK_ESCAPE:
+               CloseStartMenu();
+               break;
+
+         default:
+               if (vk>='0' && vk<='Z')
+                       JumpToNextShortcut(vk);
        }
 }
 
-void StartMenu::CreateSubmenu(int id, int folder_id1, int folder_id2, LPCTSTR title, CREATORFUNC creator)
+bool StartMenu::Navigate(int step)
 {
-       StartMenuFolders new_folders;
+       int idx = GetSelectionIndex();
 
-       try {
-               new_folders.push_back(SpecialFolderPath(folder_id1, _hwnd));
-       } catch(COMException&) {
-       }
+       if (idx == -1)
+               if (step > 0)
+                       idx = 0 - step;
+               else
+                       idx = _buttons.size() - step;
 
-       try {
-               new_folders.push_back(SpecialFolderPath(folder_id2, _hwnd));
-       } catch(COMException&) {
+       for(;;) {
+               idx += step;
+
+               if (idx<0 || idx>(int)_buttons.size())
+                       break;
+
+               if (SelectButtonIndex(idx, false))
+                       return true;
        }
 
-       if (!new_folders.empty())
-               CreateSubmenu(id, new_folders, title, creator);
+       return false;
 }
 
-void StartMenu::CreateSubmenu(int id, const StartMenuFolders& new_folders, LPCTSTR title, CREATORFUNC creator)
+bool StartMenu::JumpToNextShortcut(char c)
 {
-        // Only open one submenu at a time.
-       if (!CloseOtherSubmenus(id))
-               return;
+       int cur_idx = GetSelectionIndex();
 
-       HWND btn = GetDlgItem(_hwnd, id);
-       int x, y;
+       if (cur_idx == -1)
+               cur_idx = 0;
 
-       if (btn) {
-               WindowRect pos(btn);
+       int first_found = 0;
+       int found_more = 0;
 
-               x = pos.right;  // Submenus should overlap their parent a bit.
-               y = pos.top+STARTMENU_LINE_HEIGHT-_border_top;
-       } else {
-               WindowRect pos(_hwnd);
+       SMBtnVector::const_iterator cur_it = _buttons.begin();
+       cur_it += cur_idx + 1;
 
-               x = pos.right;
-               y = pos.top;
+        // first search down from current item...
+       SMBtnVector::const_iterator it = cur_it;
+       for(; it!=_buttons.end(); ++it) {
+               const SMBtnInfo& btn = *it;
+
+               if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
+                       if (!first_found)
+                               first_found = btn._id;
+                       else
+                               ++found_more;
+               }
        }
 
-       _submenu_id = id;
-       _submenu = StartMenu::Create(x, y, new_folders, _hwnd, title, creator);
-}
+        // ...now search from top to the current item
+       it = _buttons.begin();
+       for(; it!=_buttons.end() && it!=cur_it; ++it) {
+               const SMBtnInfo& btn = *it;
 
+               if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
+                       if (!first_found)
+                               first_found = btn._id;
+                       else
+                               ++found_more;
+               }
+       }
 
-void StartMenu::ActivateEntry(int id, const ShellEntrySet& entries)
-{
-       StartMenuFolders new_folders;
-       String title;
+       if (first_found) {
+               SelectButton(first_found);
 
-       for(ShellEntrySet::const_iterator it=entries.begin(); it!=entries.end(); ++it) {
-               ShellEntry* entry = const_cast<ShellEntry*>(*it);
+               if (!found_more)
+                       Command(first_found, BN_CLICKED);
 
-               if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
-                       new_folders.push_back(entry->create_absolute_pidl());
+               return true;
+       } else
+               return false;
+}
 
-                       if (title.empty())
-                               title = entry->_display_name;
-               } else {
-                        // If the entry is no subdirectory, there can only be one shell entry.
-                       assert(entries.size()==1);
+#endif // _LIGHT_STARTMENU
 
-                       entry->launch_entry(_hwnd);     ///@todo launch in the background; specify correct HWND for error message box titles
 
-                        // close start menus after launching the selected entry
-                       CloseStartMenu(id);
+bool StartMenu::GetButtonRect(int id, PRECT prect) const
+{
+#ifdef _LIGHT_STARTMENU
+       ClientRect clnt(_hwnd);
+       RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
 
-                        // we deleted 'this' - ensure we leave loop and function
+       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);
+
+               if (info._id == id) {
+                       *prect = rect;
+                       return true;
+               }
+
+               rect.top = rect.bottom;
+       }
+
+       return false;
+#else
+       HWND btn = GetDlgItem(_hwnd, id);
+
+       if (btn) {
+               GetWindowRect(btn, prect);
+               ScreenToClient(_hwnd, prect);
+
+               return true;
+       } else
+               return false;
+#endif
+}
+
+
+void StartMenu::DrawFloatingButton(HDC hdc)
+{
+       static ResIconEx floatingIcon(IDI_FLOATING, 8, 4);
+
+       ClientRect clnt(_hwnd);
+
+       DrawIconEx(hdc, clnt.right-12, 0, floatingIcon, 8, 4, 0, 0, DI_NORMAL);
+}
+
+void StartMenu::GetFloatingButtonRect(LPRECT prect)
+{
+       GetClientRect(_hwnd, prect);
+
+       prect->right -= 4;
+       prect->left = prect->right - 8;
+       prect->bottom = 4;
+}
+
+
+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 (_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};
+
+       int sep_width = rect.right-rect.left - 4;
+
+       FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
+       BkMode bk_mode(canvas, TRANSPARENT);
+
+       for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
+               const SMBtnInfo& btn = *it;
+
+               if (rect.top > canvas.rcPaint.bottom)
+                       break;
+
+               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);
+
+                       SelectBrush(canvas, GetSysColorBrush(COLOR_BTNHIGHLIGHT));
+                       PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT/2, sep_width, 1, PATCOPY);
+               } 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);
+               }
+
+               rect.top = rect.bottom;
+       }
+#endif
+}
+
+#ifdef _LAZY_ICONEXTRACT
+void StartMenu::UpdateIcons(/*int idx*/)
+{
+       UpdateWindow(_hwnd);
+
+#ifdef _SINGLE_ICONEXTRACT
+
+       //if (idx >= 0)
+       int idx = _scroll_pos;
+
+       for(; idx<(int)_buttons.size(); ++idx) {
+               SMBtnInfo& btn = _buttons[idx];
+
+               if (btn._icon_id==ICID_UNKNOWN && btn._id>0) {
+                       StartMenuEntry& sme = _entries[btn._id];
+
+                       btn._icon_id = ICID_NONE;
+
+                       for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
+                               Entry* entry = *it;
+
+                               if (entry->_icon_id == ICID_UNKNOWN)
+                                       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;
+
+                                       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);
+
+                                       //InvalidateRect(_hwnd, &rect, FALSE);
+                                       //UpdateWindow(_hwnd);
+                                       //break;
+
+                                       break;
+                               }
+                       }
+               }
+       }
+
+//     if (++idx < (int)_buttons.size())
+//             PostMessage(_hwnd, PM_UPDATE_ICONS, idx, 0);
+
+#else
+
+       int icons_extracted = 0;
+       int icons_updated = 0;
+
+       for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
+               ShellDirectory& dir = it->_dir;
+
+               icons_extracted += dir.extract_icons();
+       }
+
+       if (icons_extracted) {
+               for(ShellEntryMap::iterator it1=_entries.begin(); it1!=_entries.end(); ++it1) {
+                       StartMenuEntry& sme = it1->second;
+
+                       if (!sme._hIcon) {
+                               sme._hIcon = (HICON)-1;
+
+                               for(ShellEntrySet::const_iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
+                                       const Entry* sm_entry = *it2;
+
+                                       if (sm_entry->_hIcon) {
+                                               sme._hIcon = sm_entry->_hIcon;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               for(SMBtnVector::iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
+                       SMBtnInfo& info = *it;
+
+                       if (info._id>0 && !info._hIcon) {
+                               info._hIcon = _entries[info._id]._hIcon;
+                               ++icons_updated;
+                       }
+               }
+       }
+
+       if (icons_updated) {
+               InvalidateRect(_hwnd, NULL, FALSE);
+               UpdateWindow(_hwnd);
+       }
+#endif
+}
+#endif
+
+
+ // resize child button controls to accomodate for new window size
+void StartMenu::ResizeButtons(int cx)
+{
+       HDWP hdwp = BeginDeferWindowPos(10);
+
+       for(HWND ctrl=GetWindow(_hwnd,GW_CHILD); ctrl; ctrl=GetNextWindow(ctrl,GW_HWNDNEXT)) {
+               ClientRect rt(ctrl);
+
+               if (rt.right != cx) {
+                       int height = rt.bottom - rt.top;
+
+                        // special handling for separator controls
+                       if (!height && (GetWindowStyle(ctrl)&SS_TYPEMASK)==SS_ETCHEDHORZ)
+                               height = 2;
+
+                       hdwp = DeferWindowPos(hdwp, ctrl, 0, 0, 0, cx, height, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
+               }
+       }
+
+       EndDeferWindowPos(hdwp);
+}
+
+
+int StartMenu::Command(int id, int code)
+{
+#ifndef _LIGHT_STARTMENU
+       switch(id) {
+         case IDCANCEL:
+               CloseStartMenu(id);
+               break;
+
+         default: {
+#endif
+               ShellEntryMap::iterator found = _entries.find(id);
+
+               if (found != _entries.end()) {
+                       ActivateEntry(id, found->second._entries);
+                       return 0;
+               }
+
+               return super::Command(id, code);
+#ifndef _LIGHT_STARTMENU
+         }
+       }
+
+       return 0;
+#endif
+}
+
+
+ShellEntryMap::iterator StartMenu::AddEntry(const String& title, ICON_ID icon_id, Entry* entry)
+{
+        // search for an already existing subdirectory entry with the same name
+       if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+               for(ShellEntryMap::iterator it=_entries.begin(); it!=_entries.end(); ++it) {
+                       StartMenuEntry& sme = it->second;
+
+                       if (!_tcsicmp(sme._title, title))       ///@todo speed up by using a map indexed by name
+                               for(ShellEntrySet::iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
+                                       if ((*it2)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+                                                // merge the new shell entry with the existing of the same name
+                                               sme._entries.insert(entry);
+
+                                               return it;
+                                       }
+                               }
+               }
+
+       ShellEntryMap::iterator sme = AddEntry(title, icon_id);
+
+       sme->second._entries.insert(entry);
+
+       return sme;
+}
+
+ShellEntryMap::iterator StartMenu::AddEntry(const String& title, ICON_ID icon_id, int id)
+{
+       if (id == -1)
+               id = ++_next_id;
+
+       StartMenuEntry sme;
+
+       sme._title = title;
+       sme._icon_id = icon_id;
+
+       ShellEntryMap::iterator it = _entries.insert(make_pair(id, sme)).first;
+
+       return it;
+}
+
+ShellEntryMap::iterator StartMenu::AddEntry(const ShellFolder folder, ShellEntry* entry)
+{
+       ICON_ID icon_id;
+
+       if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+               icon_id = ICID_FOLDER;
+       else
+               icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
+
+       return AddEntry(folder.get_name(entry->_pidl), icon_id, entry);
+}
+
+ShellEntryMap::iterator StartMenu::AddEntry(const ShellFolder folder, Entry* entry)
+{
+       ICON_ID icon_id;
+
+       if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+               icon_id = ICID_FOLDER;
+       else
+               icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
+
+       return AddEntry(entry->_display_name, icon_id, entry);
+}
+
+
+void StartMenu::AddButton(LPCTSTR title, ICON_ID icon_id, bool hasSubmenu, int id, bool enabled)
+{
+#ifdef _LIGHT_STARTMENU
+       _buttons.push_back(SMBtnInfo(title, icon_id, id, hasSubmenu, enabled));
+#else
+       DWORD style = enabled? WS_VISIBLE|WS_CHILD|BS_OWNERDRAW: WS_VISIBLE|WS_CHILD|BS_OWNERDRAW|WS_DISABLED;
+
+       WindowRect rect(_hwnd);
+       ClientRect clnt(_hwnd);
+
+        // increase window height to make room for the new button
+       rect.top -= STARTMENU_LINE_HEIGHT;
+
+        // move down if we are too high now
+       if (rect.top < 0) {
+               rect.top += STARTMENU_LINE_HEIGHT;
+               rect.bottom += STARTMENU_LINE_HEIGHT;
+       }
+
+       WindowCanvas canvas(_hwnd);
+       FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
+
+        // widen window, if it is too small
+       int text_width = GetStartMenuBtnTextWidth(canvas, title, _hwnd) + 16/*icon*/ + 10/*placeholder*/ + 16/*arrow*/;
+
+       int cx = clnt.right - _border_left;
+       if (text_width > cx)
+               rect.right += text_width-cx;
+
+       MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
+
+       StartMenuCtrl(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left,
+                                       title, id, g_Globals._icon_cache.get_icon(icon_id)._hIcon, hasSubmenu, style);
+#endif
+}
+
+void StartMenu::AddSeparator()
+{
+#ifdef _LIGHT_STARTMENU
+       _buttons.push_back(SMBtnInfo(NULL, ICID_NONE, -1, false));
+#else
+       WindowRect rect(_hwnd);
+       ClientRect clnt(_hwnd);
+
+        // increase window height to make room for the new separator
+       rect.top -= STARTMENU_SEP_HEIGHT;
+
+        // move down if we are too high now
+       if (rect.top < 0) {
+               rect.top += STARTMENU_LINE_HEIGHT;
+               rect.bottom += STARTMENU_LINE_HEIGHT;
+       }
+
+       MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
+
+       StartMenuSeparator(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left);
+#endif
+}
+
+
+bool StartMenu::CloseOtherSubmenus(int id)
+{
+       if (_submenu) {
+               if (IsWindow(_submenu)) {
+                       if (_submenu_id == id)
+                               return false;
+                       else {
+                               _submenu_id = 0;
+                               DestroyWindow(_submenu);
+                               // _submenu should be reset automatically by PM_STARTMENU_CLOSED, but safety first...
+                       }
+               }
+
+               _submenu = 0;
+       }
+
+       return true;
+}
+
+
+void StartMenu::CreateSubmenu(int id, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
+{
+       CreateSubmenu(id, StartMenuFolders(), title, creator, info);
+}
+
+bool StartMenu::CreateSubmenu(int id, int folder_id, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
+{
+       try {
+               SpecialFolderPath folder(folder_id, _hwnd);
+
+               StartMenuFolders new_folders;
+               new_folders.push_back(folder);
+
+               CreateSubmenu(id, new_folders, title, creator, info);
+
+               return true;
+       } catch(COMException&) {
+               // ignore Exception and don't display anything
+               CloseOtherSubmenus(id);
+               _buttons[GetSelectionIndex()]._enabled = false; // disable entries for non-existing folders
+               return false;
+       }
+}
+
+bool StartMenu::CreateSubmenu(int id, int folder_id1, int folder_id2, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
+{
+       StartMenuFolders new_folders;
+
+       try {
+               new_folders.push_back(SpecialFolderPath(folder_id1, _hwnd));
+       } catch(COMException&) {
+       }
+
+       try {
+               new_folders.push_back(SpecialFolderPath(folder_id2, _hwnd));
+       } catch(COMException&) {
+       }
+
+       if (!new_folders.empty()) {
+               CreateSubmenu(id, new_folders, title, creator, info);
+               return true;
+       } else {
+               CloseOtherSubmenus(id);
+               _buttons[GetSelectionIndex()]._enabled = false; // disable entries for non-existing folders
+               return false;
+       }
+}
+
+void StartMenu::CreateSubmenu(int id, const StartMenuFolders& new_folders, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
+{
+        // Only open one submenu at a time.
+       if (!CloseOtherSubmenus(id))
+               return;
+
+       RECT rect;
+       int x, y;
+
+       if (GetButtonRect(id, &rect)) {
+               ClientToScreen(_hwnd, &rect);
+
+               x = rect.right; // Submenus should overlap their parent a bit.
+               y = rect.top+STARTMENU_LINE_HEIGHT +_border_top/*own border*/ -STARTMENU_TOP_BTN_SPACE/*border of new submenu*/;
+       } else {
+               WindowRect pos(_hwnd);
+
+               x = pos.right;
+               y = pos.top;
+       }
+
+       _submenu_id = id;
+       _submenu = StartMenu::Create(x, y, new_folders, _hwnd, title, creator, info);
+}
+
+
+void StartMenu::ActivateEntry(int id, const ShellEntrySet& entries)
+{
+       StartMenuFolders new_folders;
+       String title;
+
+       for(ShellEntrySet::const_iterator it=entries.begin(); it!=entries.end(); ++it) {
+               Entry* entry = const_cast<Entry*>(*it);
+
+               if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+
+                       ///@todo If the user explicitly clicked on a submenu, display this folder as floating start menu.
+
+                       if (entry->_etype == ET_SHELL)
+                               new_folders.push_back(entry->create_absolute_pidl());
+                       else {
+                               TCHAR path[MAX_PATH];
+
+                               if (entry->get_path(path))
+                                       new_folders.push_back(path);
+                       }
+
+                       if (title.empty())
+                               title = entry->_display_name;
+               } else {
+                        // The entry is no subdirectory, so there can only be one shell entry.
+                       assert(entries.size()==1);
+
+                       HWND hparent = GetParent(_hwnd);
+                       ShellPath shell_path = entry->create_absolute_pidl();
+
+                        // close start menus when launching the selected entry
+                       CloseStartMenu(id);
+
+                       ///@todo launch in the background; specify correct HWND for error message box titles
+                       SHELLEXECUTEINFO shexinfo;
+
+                       shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
+                       shexinfo.fMask = SEE_MASK_IDLIST;       // SEE_MASK_INVOKEIDLIST is also possible.
+                       shexinfo.hwnd = hparent;
+                       shexinfo.lpVerb = NULL;
+                       shexinfo.lpFile = NULL;
+                       shexinfo.lpParameters = NULL;
+                       shexinfo.lpDirectory = NULL;
+                       shexinfo.nShow = SW_SHOWNORMAL;
+
+                       shexinfo.lpIDList = &*shell_path;
+
+                        // add PIDL to the recent file list
+                       SHAddToRecentDocs(SHARD_PIDL, shexinfo.lpIDList);
+
+                       if (!ShellExecuteEx(&shexinfo))
+                               display_error(hparent, GetLastError());
+
+                        // we may have deleted 'this' - ensure we leave the loop and function
                        return;
                }
        }
@@ -587,23 +1301,155 @@ void StartMenu::CloseStartMenu(int id)
 }
 
 
-int StartMenuButton::GetTextWidth(LPCTSTR title, HWND hwnd)
+int GetStartMenuBtnTextWidth(HDC hdc, LPCTSTR title, HWND hwnd)
 {
-       WindowCanvas canvas(hwnd);
-       FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
-
        RECT rect = {0, 0, 0, 0};
-       DrawText(canvas, title, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
+       DrawText(hdc, title, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
 
        return rect.right-rect.left;
 }
 
+#ifdef _LIGHT_STARTMENU
+void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, const SMBtnInfo& btn, bool has_focus, bool pushed)
+#else
+void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, HICON hIcon,
+                                                               bool hasSubmenu, bool enabled, bool has_focus, bool pushed);
+#endif
+{
+       UINT style = DFCS_BUTTONPUSH;
+
+       if (!btn._enabled)
+               style |= DFCS_INACTIVE;
+
+       POINT iconPos = {rect.left+2, (rect.top+rect.bottom-16)/2};
+       RECT textRect = {rect.left+16+4, rect.top+2, rect.right-4, rect.bottom-4};
+
+       if (pushed) {
+               style |= DFCS_PUSHED;
+               ++iconPos.x;            ++iconPos.y;
+               ++textRect.left;        ++textRect.top;
+               ++textRect.right;       ++textRect.bottom;
+       }
+
+       int bk_color_idx = COLOR_BTNFACE;
+       int text_color_idx = COLOR_BTNTEXT;
+
+       if (has_focus) {
+               bk_color_idx = COLOR_HIGHLIGHT;
+               text_color_idx = COLOR_HIGHLIGHTTEXT;
+       }
+
+       COLORREF bk_color = GetSysColor(bk_color_idx);
+       HBRUSH bk_brush = GetSysColorBrush(bk_color_idx);
+
+       if (title)
+               FillRect(hdc, &rect, bk_brush);
+
+       if (btn._icon_id > ICID_NONE)
+               g_Globals._icon_cache.get_icon(btn._icon_id).draw(hdc, iconPos.x, iconPos.y, 16, 16, bk_color, bk_brush);
+
+        // draw submenu arrow at the right
+       if (btn._hasSubmenu) {
+               static SmallIcon arrowIcon(IDI_ARROW);
+               static SmallIcon selArrowIcon(IDI_ARROW_SELECTED);
+
+               DrawIconEx(hdc, rect.right-16, iconPos.y,
+                                       has_focus? selArrowIcon: arrowIcon,
+                                       16, 16, 0, bk_brush, DI_NORMAL);
+       }
+
+       if (title) {
+               BkMode bk_mode(hdc, TRANSPARENT);
+
+               if (!btn._enabled)      // dis->itemState & (ODS_DISABLED|ODS_GRAYED)
+                       DrawGrayText(hdc, &textRect, title, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
+               else {
+                       TextColor lcColor(hdc, GetSysColor(text_color_idx));
+                       DrawText(hdc, title, -1, &textRect, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
+               }
+       }
+}
+
+
+#ifdef _LIGHT_STARTMENU
+
+void StartMenu::ResizeToButtons()
+{
+       WindowRect rect(_hwnd);
+
+       WindowCanvas canvas(_hwnd);
+       FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
+
+       int max_width = STARTMENU_WIDTH_MIN;
+       int height = 0;
+
+       for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
+               int w = GetStartMenuBtnTextWidth(canvas, it->_title, _hwnd);
+
+               if (w > max_width)
+                       max_width = w;
+
+               if (it->_id == -1)
+                       height += STARTMENU_SEP_HEIGHT;
+               else
+                       height += STARTMENU_LINE_HEIGHT;
+       }
+
+        // calculate new window size
+       int text_width = max_width + 16/*icon*/ + 10/*placeholder*/ + 16/*arrow*/;
+
+       RECT rt_hgt = {rect.left, rect.bottom-_border_top-height, rect.left+_border_left+text_width, rect.bottom};
+       AdjustWindowRectEx(&rt_hgt, GetWindowStyle(_hwnd), FALSE, GetWindowExStyle(_hwnd));
+
+        // ignore movement, only look at the size change
+       rect.right = rect.left + (rt_hgt.right-rt_hgt.left);
+       rect.top = rect.bottom - (rt_hgt.bottom-rt_hgt.top);
+
+        // move down if we are too high
+       if (rect.top < 0) {
+               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
 
 LRESULT StartMenuButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
 {
        switch(nmsg) {
          case WM_MOUSEMOVE:
-                // automatically set the focus to startmenu entries when moving the mouse over them            
+                // automatically set the focus to startmenu entries when moving the mouse over them
                if (GetFocus()!=_hwnd && !(GetWindowStyle(_hwnd)&WS_DISABLED))
                        SetFocus(_hwnd);
                break;
@@ -625,56 +1471,19 @@ LRESULT StartMenuButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
 
 void StartMenuButton::DrawItem(LPDRAWITEMSTRUCT dis)
 {
-       UINT style = DFCS_BUTTONPUSH;
-
-       if (dis->itemState & ODS_DISABLED)
-               style |= DFCS_INACTIVE;
-
-       POINT iconPos = {dis->rcItem.left+2, (dis->rcItem.top+dis->rcItem.bottom-16)/2};
-       RECT textRect = {dis->rcItem.left+16+4, dis->rcItem.top+2, dis->rcItem.right-4, dis->rcItem.bottom-4};
-
-       if (dis->itemState & ODS_SELECTED) {
-               style |= DFCS_PUSHED;
-               ++iconPos.x;            ++iconPos.y;
-               ++textRect.left;        ++textRect.top;
-               ++textRect.right;       ++textRect.bottom;
-       }
-
-       int bk_color = COLOR_BTNFACE;
-       int text_color = COLOR_BTNTEXT;
-
-       if (dis->itemState & ODS_FOCUS) {
-               bk_color = COLOR_HIGHLIGHT;
-               text_color = COLOR_HIGHLIGHTTEXT;
-       }
-
-       HBRUSH bk_brush = GetSysColorBrush(bk_color);
-
-       FillRect(dis->hDC, &dis->rcItem, bk_brush);
-       DrawIconEx(dis->hDC, iconPos.x, iconPos.y, _hIcon, 16, 16, 0, bk_brush, DI_NORMAL);
-
-        // draw submenu arrow at the right
-       if (_hasSubmenu) {
-               static SmallIcon arrowIcon(IDI_ARROW);
-               static SmallIcon selArrowIcon(IDI_ARROW_SELECTED);
-
-               DrawIconEx(dis->hDC, dis->rcItem.right-16, iconPos.y,
-                                       dis->itemState&ODS_FOCUS?selArrowIcon:arrowIcon, 16, 16, 0, bk_brush, DI_NORMAL);
-       }
-
        TCHAR title[BUFFER_LEN];
-       GetWindowText(_hwnd, title, BUFFER_LEN);
 
-       BkMode bk_mode(dis->hDC, TRANSPARENT);
+       GetWindowText(_hwnd, title, BUFFER_LEN);
 
-       if (dis->itemState & (ODS_DISABLED|ODS_GRAYED))
-               DrawGrayText(dis, &textRect, title, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
-       else {
-               TextColor lcColor(dis->hDC, GetSysColor(text_color));
-               DrawText(dis->hDC, title, -1, &textRect, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
-       }
+       DrawStartMenuButton(dis->hDC, dis->rcItem, title, _hIcon,
+                                               _hasSubmenu,
+                                               !(dis->itemState & ODS_DISABLED),
+                                               dis->itemState&ODS_FOCUS? true: false,
+                                               dis->itemState&ODS_SELECTED? true: false);
 }
 
+#endif
+
 
 StartMenuRoot::StartMenuRoot(HWND hwnd)
  :     super(hwnd)
@@ -684,7 +1493,7 @@ StartMenuRoot::StartMenuRoot(HWND hwnd)
 #endif
                try {
                         // insert directory "All Users\Start Menu"
-                       ShellDirectory cmn_startmenu(Desktop(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
+                       ShellDirectory cmn_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
                        _dirs.push_back(StartMenuDirectory(cmn_startmenu, false));      // don't add subfolders
                } catch(COMException&) {
                        // ignore exception and don't show additional shortcuts
@@ -692,7 +1501,8 @@ StartMenuRoot::StartMenuRoot(HWND hwnd)
 
        try {
                 // insert directory "<user name>\Start Menu"
-               ShellDirectory usr_startmenu(Desktop(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
+
+               ShellDirectory usr_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
                _dirs.push_back(StartMenuDirectory(usr_startmenu, false));      // don't add subfolders
        } catch(COMException&) {
                // ignore exception and don't show additional shortcuts
@@ -704,16 +1514,35 @@ StartMenuRoot::StartMenuRoot(HWND hwnd)
        _logo_size.cx = bmp_hdr.bmWidth;
        _logo_size.cy = bmp_hdr.bmHeight;
 
-       _border_left = _logo_size.cx;
+       _border_left = _logo_size.cx + 1;
 }
 
 
-HWND StartMenuRoot::Create(HWND hwndDesktopBar)
+static void CalculateStartPos(HWND hwndOwner, RECT& rect)
+{
+       WindowRect pos(hwndOwner);
+
+       rect.left = pos.left;
+       rect.top = pos.top-STARTMENU_LINE_HEIGHT-4;
+       rect.right = pos.left+STARTMENU_WIDTH_MIN;
+       rect.bottom = pos.top;
+
+#ifndef _LIGHT_STARTMENU
+       rect.top += STARTMENU_LINE_HEIGHT;
+#endif
+
+       AdjustWindowRectEx(&rect, WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE, FALSE, 0);
+}
+
+HWND StartMenuRoot::Create(HWND hwndOwner)
 {
-       WindowRect pos(hwndDesktopBar);
+       RECT rect;
+
+       CalculateStartPos(hwndOwner, rect);
 
        return Window::Create(WINDOW_CREATOR(StartMenuRoot), 0, GetWndClasss(), TITLE_STARTMENU,
-                                                       WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE, pos.left, pos.top-4, STARTMENU_WIDTH_MIN, 4, hwndDesktopBar);
+                                                       WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN,
+                                                       rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndOwner);
 }
 
 
@@ -722,6 +1551,25 @@ void StartMenuRoot::TrackStartmenu()
        MSG msg;
        HWND hwnd = _hwnd;
 
+#ifdef _LIGHT_STARTMENU
+       _selected_id = -1;
+#endif
+
+#ifdef _LIGHT_STARTMENU
+        // recalculate start menu root position
+       RECT rect;
+
+       CalculateStartPos(GetParent(hwnd), rect);
+
+       SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0);
+
+       ResizeToButtons();
+#endif
+
+        // show previously hidden start menu
+       ShowWindow(hwnd, SW_SHOW);
+       SetForegroundWindow(hwnd);
+
        while(IsWindow(hwnd)) {
                if (!GetMessage(&msg, 0, 0, 0)) {
                        PostQuitMessage(msg.wParam);
@@ -740,7 +1588,7 @@ void StartMenuRoot::TrackStartmenu()
                        }
 
                        if (!menu_wnd) {
-                               DestroyWindow(_hwnd);
+                               CloseStartMenu();
                                break;
                        }
                }
@@ -794,38 +1642,38 @@ LRESULT  StartMenuRoot::Init(LPCREATESTRUCT pcs)
 
 
         // insert hard coded start entries
-       AddButton(ResString(IDS_PROGRAMS),              SmallIcon(IDI_APPS), true, IDC_PROGRAMS);
+       AddButton(ResString(IDS_PROGRAMS),              ICID_APPS, true, IDC_PROGRAMS);
 
-       AddButton(ResString(IDS_DOCUMENTS),             SmallIcon(IDI_DOCUMENTS), true, IDC_DOCUMENTS);
+       AddButton(ResString(IDS_DOCUMENTS),             ICID_DOCUMENTS, true, IDC_DOCUMENTS);
 
 #ifndef __MINGW32__    // SHRestricted() missing in MinGW (as of 29.10.2003)
        if (!g_Globals._SHRestricted || !SHRestricted(REST_NORECENTDOCSMENU))
 #else
        if (IS_VALUE_ZERO(hkey, _T("NoRecentDocsMenu")))
 #endif
-               AddButton(ResString(IDS_RECENT),        SmallIcon(IDI_DOCUMENTS), true, IDC_RECENT);
+               AddButton(ResString(IDS_RECENT),        ICID_DOCUMENTS, true, IDC_RECENT);
 
-       AddButton(ResString(IDS_FAVORITES),             SmallIcon(IDI_FAVORITES), true, IDC_FAVORITES);
+       AddButton(ResString(IDS_FAVORITES),             ICID_FAVORITES, true, IDC_FAVORITES);
 
-       AddButton(ResString(IDS_SETTINGS),              SmallIcon(IDI_CONFIG), true, IDC_SETTINGS);
+       AddButton(ResString(IDS_SETTINGS),              ICID_CONFIG, true, IDC_SETTINGS);
 
-       AddButton(ResString(IDS_BROWSE),                SmallIcon(IDI_FOLDER), true, IDC_BROWSE);
+       AddButton(ResString(IDS_BROWSE),                ICID_FOLDER, true, IDC_BROWSE);
 
 #ifndef __MINGW32__    // SHRestricted() missing in MinGW (as of 29.10.2003)
        if (!g_Globals._SHRestricted || !SHRestricted(REST_NOFIND))
 #else
        if (IS_VALUE_ZERO(hkey, _T("NoFind")))
 #endif
-               AddButton(ResString(IDS_SEARCH),        SmallIcon(IDI_SEARCH), true, IDC_SEARCH);
+               AddButton(ResString(IDS_SEARCH),        ICID_SEARCH, true, IDC_SEARCH);
 
-       AddButton(ResString(IDS_START_HELP),    SmallIcon(IDI_INFO), false, IDC_START_HELP);
+       AddButton(ResString(IDS_START_HELP),    ICID_INFO, false, IDC_START_HELP);
 
 #ifndef __MINGW32__    // SHRestricted() missing in MinGW (as of 29.10.2003)
        if (!g_Globals._SHRestricted || !SHRestricted(REST_NORUN))
 #else
        if (IS_VALUE_ZERO(hkey, _T("NoRun")))
 #endif
-               AddButton(ResString(IDS_LAUNCH),        SmallIcon(IDI_ACTION), false, IDC_LAUNCH);
+               AddButton(ResString(IDS_LAUNCH),        ICID_ACTION, false, IDC_LAUNCH);
 
 
        AddSeparator();
@@ -836,7 +1684,7 @@ LRESULT    StartMenuRoot::Init(LPCREATESTRUCT pcs)
 #else
        if (IS_VALUE_NOT_ZERO(hkeyAdv, _T("StartMenuLogoff")))
 #endif
-               AddButton(ResString(IDS_LOGOFF),        SmallIcon(IDI_LOGOFF), false, IDC_LOGOFF);
+               AddButton(ResString(IDS_LOGOFF),        ICID_LOGOFF, false, IDC_LOGOFF);
 
 
 #ifndef __MINGW32__    // SHRestricted() missing in MinGW (as of 29.10.2003)
@@ -844,7 +1692,10 @@ LRESULT   StartMenuRoot::Init(LPCREATESTRUCT pcs)
 #else
        if (IS_VALUE_ZERO(hkey, _T("NoClose")))
 #endif
-               AddButton(ResString(IDS_SHUTDOWN),      SmallIcon(IDI_LOGOFF), false, IDC_SHUTDOWN);
+               AddButton(ResString(IDS_SHUTDOWN),      ICID_LOGOFF, false, IDC_SHUTDOWN);
+
+
+       AddButton(ResString(IDS_TERMINATE),     ICID_LOGOFF, false, IDC_TERMINATE);
 
 
 #ifdef __MINGW32__
@@ -852,6 +1703,12 @@ LRESULT   StartMenuRoot::Init(LPCREATESTRUCT pcs)
        RegCloseKey(hkey);
 #endif
 
+
+#ifdef _LIGHT_STARTMENU
+        // set the window size to fit all buttons
+       ResizeToButtons();
+#endif
+
        return 0;
 }
 
@@ -860,7 +1717,7 @@ void StartMenuRoot::AddEntries()
 {
        super::AddEntries();
 
-       AddButton(ResString(IDS_EXPLORE),       SmallIcon(IDI_EXPLORER), false, IDC_EXPLORE);
+       AddButton(ResString(IDS_EXPLORE),       ICID_EXPLORER, false, IDC_EXPLORE);
 }
 
 
@@ -868,62 +1725,84 @@ LRESULT  StartMenuRoot::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
 {
        switch(nmsg) {
          case WM_PAINT: {
-               int clr_bits;
-               {WindowCanvas dc(_hwnd); clr_bits=GetDeviceCaps(dc, BITSPIXEL);}
-               bool logo256 = clr_bits<=8;
-
                PaintCanvas canvas(_hwnd);
+               Paint(canvas);
+               break;}
+
+         default:
+               return super::WndProc(nmsg, wparam, lparam);
+       }
+
+       return 0;
+}
 
-               MemCanvas mem_dc;
-               ResBitmap bmp(logo256? IDB_LOGOV256: IDB_LOGOV);
-               BitmapSelection sel(mem_dc, bmp);
+void StartMenuRoot::Paint(PaintCanvas& canvas)
+{
+       int clr_bits;
+       {WindowCanvas dc(_hwnd); clr_bits=GetDeviceCaps(dc, BITSPIXEL);}
 
-               ClientRect clnt(_hwnd);
-               int h = min(_logo_size.cy, clnt.bottom);
+       MemCanvas mem_dc;
+       ResBitmap bmp(clr_bits<=8? clr_bits<=4? IDB_LOGOV16: IDB_LOGOV256: IDB_LOGOV);
+       BitmapSelection sel(mem_dc, bmp);
 
-               RECT rect = {0, 0, _logo_size.cx-1, clnt.bottom-h};
-               HBRUSH hbr = CreateSolidBrush(logo256? RGB(166,202,240): RGB(255,255,255));     // same color as the background color in the logo bitmap
-               FillRect(canvas, &rect, hbr);
-               DeleteObject(hbr);
-               //PatBlt(canvas, _logo_size.cx-1, 0, 1, clnt.bottom-h, WHITENESS);
-               PatBlt(canvas, _logo_size.cx-1, 0, 1, clnt.bottom-h, WHITENESS);
+       ClientRect clnt(_hwnd);
+       int h = min(_logo_size.cy, clnt.bottom);
 
-               BitBlt(canvas, 0, clnt.bottom-h, _logo_size.cx, h, mem_dc, 0, 0, SRCCOPY);
+       RECT rect = {0, 0, _logo_size.cx, clnt.bottom-h};
+       HBRUSH hbr = CreateSolidBrush(GetPixel(mem_dc, 0, 0));
+       FillRect(canvas, &rect, hbr);
+       DeleteObject(hbr);
 
-               if (!logo256) {
-                       rect.left = rect.right++;
-                       rect.bottom = clnt.bottom;
-                       HBRUSH hbr_border = GetStockBrush(GRAY_BRUSH);  //CreateSolidBrush(RGB(71,88,85));
-                       FillRect(canvas, &rect, hbr_border);
-                       //DeleteObject(hbr_border);
-               }
-               break;}
+       PatBlt(canvas, _logo_size.cx, 0, 1, clnt.bottom, WHITENESS);
+
+       BitBlt(canvas, 0, clnt.bottom-h, _logo_size.cx, h, mem_dc, 0, 0, SRCCOPY);
+
+       super::Paint(canvas);
+}
+
+
+void StartMenuRoot::CloseStartMenu(int id)
+{
+       if (_submenu)
+               CloseSubmenus();
+
+       ShowWindow(_hwnd, SW_HIDE);
+}
+
+void StartMenuRoot::ProcessKey(int vk)
+{
+       switch(vk) {
+         case VK_LEFT:
+               if (_submenu)
+                       CloseOtherSubmenus();
+               // don't close start menu root
+               break;
 
          default:
-               return super::WndProc(nmsg, wparam, lparam);
+               super::ProcessKey(vk);
        }
-
-       return 0;
 }
 
 
-int StartMenuRoot::Command(int id, int code)
+int StartMenuHandler::Command(int id, int code)
 {
        switch(id) {
+
+       // start menu root
+
          case IDC_PROGRAMS:
                CreateSubmenu(id, CSIDL_COMMON_PROGRAMS, CSIDL_PROGRAMS, ResString(IDS_PROGRAMS));
                break;
 
          case IDC_EXPLORE:
                CloseStartMenu(id);
-               explorer_show_frame(_hwnd, SW_SHOWNORMAL);
+               explorer_show_frame(SW_SHOWNORMAL);
                break;
 
-         case IDC_LAUNCH: {
-               HWND hwndDesktopBar = GetWindow(_hwnd, GW_OWNER);
+         case IDC_LAUNCH:
                CloseStartMenu(id);
-               ShowLaunchDialog(hwndDesktopBar);
-               break;}
+               ShowLaunchDialog(g_Globals._hwndDesktopBar);
+               break;
 
          case IDC_DOCUMENTS:
                CreateSubmenu(id, CSIDL_PERSONAL, ResString(IDS_DOCUMENTS));
@@ -934,7 +1813,11 @@ int StartMenuRoot::Command(int id, int code)
                break;
 
          case IDC_FAVORITES:
-               CreateSubmenu(id, CSIDL_FAVORITES, ResString(IDS_FAVORITES));
+#ifndef _SHELL32_FAVORITES
+               CreateSubmenu(id, ResString(IDS_FAVORITES), STARTMENU_CREATOR(FavoritesMenu), &static_cast<BookmarkList&>(g_Globals._favorites));
+#else
+               CreateSubmenu(id, CSIDL_COMMON_FAVORITES, CSIDL_FAVORITES, ResString(IDS_FAVORITES));
+#endif
                break;
 
          case IDC_BROWSE:
@@ -949,20 +1832,147 @@ int StartMenuRoot::Command(int id, int code)
                CreateSubmenu(id, ResString(IDS_SEARCH), STARTMENU_CREATOR(SearchMenu));
                break;
 
+         case IDC_START_HELP:
+               CloseStartMenu(id);
+               MessageBox(g_Globals._hwndDesktopBar, TEXT("Help not yet implemented"), ResString(IDS_TITLE), MB_OK);
+               break;
+
          case IDC_LOGOFF:
-               /* The shell32 Dialog prompts about some system setting change. This is not what we want to display here.
-               HWND hwndDesktopBar = GetWindow(_hwnd, GW_OWNER);
                CloseStartMenu(id);
-               ShowRestartDialog(hwndDesktopBar, EWX_LOGOFF);*/
+               ShowLogoffDialog(g_Globals._hwndDesktopBar);
+               break;
+
+         case IDC_TERMINATE:
                DestroyWindow(GetParent(_hwnd));
                break;
 
-         case IDC_SHUTDOWN: {
-               HWND hwndDesktopBar = GetWindow(_hwnd, GW_OWNER);
+         case IDC_SHUTDOWN:
+               CloseStartMenu(id);
+               ShowExitWindowsDialog(g_Globals._hwndDesktopBar);
+               break;
+
+
+       // settings menu
+
+         case ID_DESKTOPBAR_SETTINGS:
+               CloseStartMenu(id);
+               ExplorerPropertySheet(g_Globals._hwndDesktopBar);
+               break;
+
+         case IDC_CONTROL_PANEL: {
+               CloseStartMenu(id);
+#ifndef ROSSHELL
+#ifndef _NO_MDI
+               XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
+               bool mdi = XMLBool(explorer_options, "mdi", true);
+
+               if (mdi)
+                       MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
+               else
+#endif
+                       SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
+#else
+               launch_file(_hwnd, SHELLPATH_CONTROL_PANEL);
+#endif
+               break;}
+
+         case IDC_SETTINGS_MENU:
+               CreateSubmenu(id, CSIDL_CONTROLS, ResString(IDS_SETTINGS_MENU));
+               break;
+
+         case IDC_PRINTERS: {
                CloseStartMenu(id);
-               ShowExitWindowsDialog(hwndDesktopBar);
+
+#ifndef ROSSHELL
+#ifdef _ROS_   // to be removed when printer folder will be implemented
+               MessageBox(0, TEXT("printer folder not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
+#else
+#ifndef _NO_MDI
+               XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
+               bool mdi = XMLBool(explorer_options, "mdi", true);
+
+               if (mdi)
+                       MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0);
+               else
+#endif
+                       SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0);
+#endif
+#else
+               launch_file(_hwnd, SHELLPATH_PRINTERS);
+#endif
                break;}
 
+         case IDC_PRINTERS_MENU:
+               CreateSubmenu(id, CSIDL_PRINTERS, CSIDL_PRINTHOOD, ResString(IDS_PRINTERS));
+/*             StartMenuFolders new_folders;
+
+               try {
+                       new_folders.push_back(ShellPath(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}")));
+               } catch(COMException&) {
+               }
+
+               CreateSubmenu(id, new_folders, ResString(IDS_PRINTERS));*/
+               break;
+
+         case IDC_ADMIN:
+#ifndef ROSSHELL
+               CreateSubmenu(id, CSIDL_COMMON_ADMINTOOLS, CSIDL_ADMINTOOLS, ResString(IDS_ADMIN));
+               //CloseStartMenu(id);
+               //MainFrame::Create(SpecialFolderPath(CSIDL_COMMON_ADMINTOOLS, _hwnd), OWM_PIDL);
+#else
+               launch_file(_hwnd, SpecialFolderFSPath(CSIDL_COMMON_ADMINTOOLS, _hwnd));
+#endif
+               break;
+
+         case IDC_CONNECTIONS:
+#ifndef ROSSHELL
+#ifdef _ROS_   // to be removed when RAS will be implemented
+               MessageBox(0, TEXT("RAS folder not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
+#else
+               CreateSubmenu(id, CSIDL_CONNECTIONS, ResString(IDS_CONNECTIONS));
+               //CloseStartMenu(id);
+               //MainFrame::Create(SpecialFolderPath(CSIDL_CONNECTIONS, _hwnd), OWM_PIDL);
+#endif
+#else
+               launch_file(_hwnd, SHELLPATH_NET_CONNECTIONS);
+#endif
+               break;
+
+
+       // browse menu
+
+         case IDC_NETWORK:
+#ifdef _ROS_   // to be removed when network will be implemented
+               MessageBox(0, TEXT("network not yet implemented"), ResString(IDS_TITLE), MB_OK);
+#else
+               CreateSubmenu(id, CSIDL_NETWORK, ResString(IDS_NETWORK));
+#endif
+               break;
+
+         case IDC_DRIVES:
+               ///@todo exclude removable drives
+               CreateSubmenu(id, CSIDL_DRIVES, ResString(IDS_DRIVES));
+               break;
+
+
+       // search menu
+
+         case IDC_SEARCH_PROGRAM:
+               CloseStartMenu(id);
+               Dialog::DoModal(IDD_SEARCH_PROGRAM, WINDOW_CREATOR(FindProgramDlg));
+               break;
+
+         case IDC_SEARCH_FILES:
+               CloseStartMenu(id);
+               ShowSearchDialog();
+               break;
+
+         case IDC_SEARCH_COMPUTER:
+               CloseStartMenu(id);
+               ShowSearchComputer();
+               break;
+
+
          default:
                return super::Command(id, code);
        }
@@ -971,44 +1981,74 @@ int StartMenuRoot::Command(int id, int code)
 }
 
 
-void StartMenuRoot::ShowLaunchDialog(HWND hwndDesktopBar)
+void StartMenuHandler::ShowSearchDialog()
+{
+       static DynamicFct<SHFINDFILES> SHFindFiles(TEXT("SHELL32"), 90);
+
+       if (SHFindFiles)
+               (*SHFindFiles)(NULL, NULL);
+       else
+               MessageBox(0, TEXT("SHFindFiles() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
+}
+
+void StartMenuHandler::ShowSearchComputer()
+{
+       static DynamicFct<SHFINDCOMPUTER> SHFindComputer(TEXT("SHELL32"), 91);
+
+       if (SHFindComputer)
+               (*SHFindComputer)(NULL, NULL);
+       else
+               MessageBox(0, TEXT("SHFindComputer() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
+}
+
+void StartMenuHandler::ShowLaunchDialog(HWND hwndOwner)
 {
         ///@todo All text phrases should be put into the resources.
-       static LPCSTR szTitle = "Create New Task";
-       static LPCSTR szText = "Type the name of a program, folder, document, or Internet resource, and Task Manager will open it for you.";
+       static LPCSTR szTitle = "Run";
+       static LPCSTR szText = "Type the name of a program, folder, document, or Internet resource, and Explorer will open it for you.";
 
        static DynamicFct<RUNFILEDLG> RunFileDlg(TEXT("SHELL32"), 61);
 
         // Show "Run..." dialog
        if (RunFileDlg) {
-#define        W_VER_NT 0
+#ifndef _ROS_ /* FIXME: our shell32 always expects Ansi strings */
                if ((HIWORD(GetVersion())>>14) == W_VER_NT) {
                        WCHAR wTitle[40], wText[256];
 
                        MultiByteToWideChar(CP_ACP, 0, szTitle, -1, wTitle, 40);
                        MultiByteToWideChar(CP_ACP, 0, szText, -1, wText, 256);
 
-                       (*RunFileDlg)(hwndDesktopBar, 0, NULL, (LPCSTR)wTitle, (LPCSTR)wText, RFF_CALCDIRECTORY);
+                       (*RunFileDlg)(hwndOwner, 0, NULL, (LPCSTR)wTitle, (LPCSTR)wText, RFF_CALCDIRECTORY);
                }
                else
-                       (*RunFileDlg)(hwndDesktopBar, 0, NULL, szTitle, szText, RFF_CALCDIRECTORY);
+#endif
+                       (*RunFileDlg)(hwndOwner, 0, NULL, szTitle, szText, RFF_CALCDIRECTORY);
        }
 }
 
-void StartMenuRoot::ShowExitWindowsDialog(HWND hwndOwner)
+void StartMenuHandler::ShowLogoffDialog(HWND hwndOwner)
 {
-       static DynamicFct<EXITWINDOWSDLG> ExitWindowsDlg(TEXT("SHELL32"), 60);
-
-       if (ExitWindowsDlg)
-               (*ExitWindowsDlg)(hwndOwner);
+       static DynamicFct<LOGOFFWINDOWSDIALOG> LogoffWindowsDialog(TEXT("SHELL32"), 54);
+//     static DynamicFct<RESTARTWINDOWSDLG> RestartDialog(TEXT("SHELL32"), 59);
+
+       if (LogoffWindowsDialog)
+               (*LogoffWindowsDialog)(0);
+/* The RestartDialog function prompts about some system setting change. This is not what we want to display here.
+       else if (RestartDialog)
+               return (*RestartDialog)(hwndOwner, (LPWSTR)L"You selected <Log Off>.\n\n", EWX_LOGOFF) == 1;    ///@todo ANSI string conversion if needed
+*/
+       else
+               MessageBox(hwndOwner, TEXT("LogoffWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
 }
 
-void StartMenuRoot::ShowRestartDialog(HWND hwndOwner, UINT flags)
+void ShowExitWindowsDialog(HWND hwndOwner)
 {
-       static DynamicFct<RESTARTWINDOWSDLG> RestartDlg(TEXT("SHELL32"), 59);
+       static DynamicFct<EXITWINDOWSDLG> ExitWindowsDialog(TEXT("SHELL32"), 60);
 
-       if (RestartDlg)
-               (*RestartDlg)(hwndOwner, (LPWSTR)L"You selected <Log Off>.\n\n", flags);        ///@todo ANSI string conversion if needed
+       if (ExitWindowsDialog)
+               (*ExitWindowsDialog)(hwndOwner);
+       else
+               MessageBox(hwndOwner, TEXT("ExitWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
 }
 
 
@@ -1016,53 +2056,30 @@ void SettingsMenu::AddEntries()
 {
        super::AddEntries();
 
-#ifndef __MINGW32__    // SHRestricted() missing in MinGW (as of 29.10.2003)
-       if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
+#ifdef _ROS_   // to be removed when printer/network will be implemented
+       AddButton(ResString(IDS_PRINTERS),                      ICID_PRINTER, false, IDC_PRINTERS_MENU);
+       AddButton(ResString(IDS_CONNECTIONS),           ICID_NETWORK, false, IDC_CONNECTIONS);
+#else
+       AddButton(ResString(IDS_PRINTERS),                      ICID_PRINTER, true, IDC_PRINTERS_MENU);
+       AddButton(ResString(IDS_CONNECTIONS),           ICID_NETWORK, true, IDC_CONNECTIONS);
 #endif
-               AddButton(ResString(IDS_CONTROL_PANEL), SmallIcon(IDI_CONFIG), false, IDC_CONTROL_PANEL);
-
-       AddButton(ResString(IDS_PRINTERS),              SmallIcon(IDI_PRINTER), true, IDC_PRINTERS);
-       AddButton(ResString(IDS_CONNECTIONS),   SmallIcon(IDI_NETWORK), true, IDC_CONNECTIONS);
-       AddButton(ResString(IDS_ADMIN),                 SmallIcon(IDI_CONFIG), true, IDC_ADMIN);
+       AddButton(ResString(IDS_ADMIN),                         ICID_CONFIG, true, IDC_ADMIN);
 
 #ifndef __MINGW32__    // SHRestricted() missing in MinGW (as of 29.10.2003)
        if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
 #endif
-               AddButton(ResString(IDS_SETTINGS_MENU), SmallIcon(IDI_CONFIG), true, IDC_SETTINGS_MENU);
-}
-
-int SettingsMenu::Command(int id, int code)
-{
-       switch(id) {
-         case IDC_SETTINGS_MENU:
-               CreateSubmenu(id, CSIDL_CONTROLS, ResString(IDS_SETTINGS_MENU));
-               break;
-
-         case IDC_PRINTERS:
-               CreateSubmenu(id, CSIDL_PRINTERS, CSIDL_PRINTHOOD, ResString(IDS_PRINTERS));
-               break;
-
-         case IDC_CONTROL_PANEL:
-               CloseStartMenu(id);
-               MainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), FALSE);
-               break;
+               AddButton(ResString(IDS_SETTINGS_MENU), ICID_CONFIG, true, IDC_SETTINGS_MENU);
 
-         case IDC_ADMIN:
-               CreateSubmenu(id, CSIDL_COMMON_ADMINTOOLS, CSIDL_ADMINTOOLS, ResString(IDS_ADMIN));
-               break;
-
-         case IDC_CONNECTIONS:
-               CreateSubmenu(id, CSIDL_CONNECTIONS, ResString(IDS_CONNECTIONS));
-               break;
+       AddButton(ResString(IDS_DESKTOPBAR_SETTINGS), ICID_CONFIG, false, ID_DESKTOPBAR_SETTINGS);
 
-         default:
-               return super::Command(id, code);
-       }
+       AddButton(ResString(IDS_PRINTERS),                      ICID_PRINTER, false, IDC_PRINTERS);
 
-       return 0;
+#ifndef __MINGW32__    // SHRestricted() missing in MinGW (as of 29.10.2003)
+       if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
+#endif
+               AddButton(ResString(IDS_CONTROL_PANEL), ICID_CONFIG, false, IDC_CONTROL_PANEL);
 }
 
-
 void BrowseMenu::AddEntries()
 {
        super::AddEntries();
@@ -1070,105 +2087,109 @@ void BrowseMenu::AddEntries()
 #ifndef __MINGW32__    // SHRestricted() missing in MinGW (as of 29.10.2003)
        if (!g_Globals._SHRestricted || !SHRestricted(REST_NONETHOOD))  // or REST_NOENTIRENETWORK ?
 #endif
-               AddButton(ResString(IDS_NETWORK),       SmallIcon(IDI_NETWORK), true, IDC_NETWORK);
+#ifdef _ROS_   // to be removed when printer/network will be implemented
+               AddButton(ResString(IDS_NETWORK),               ICID_NETWORK, false, IDC_NETWORK);
+#else
+               AddButton(ResString(IDS_NETWORK),               ICID_NETWORK, true, IDC_NETWORK);
+#endif
 
-       AddButton(ResString(IDS_DRIVES),        SmallIcon(IDI_FOLDER), true, IDC_DRIVES);
+       AddButton(ResString(IDS_DRIVES),                        ICID_FOLDER, true, IDC_DRIVES);
 }
 
-int BrowseMenu::Command(int id, int code)
+void SearchMenu::AddEntries()
 {
-       switch(id) {
-         case IDC_NETWORK:
-               CreateSubmenu(id, CSIDL_NETWORK, ResString(IDS_NETWORK));
-               break;
+       super::AddEntries();
 
-         case IDC_DRIVES:
-               ///@todo exclude removeable drives
-               CreateSubmenu(id, CSIDL_DRIVES, ResString(IDS_DRIVES));
-               break;
+       AddButton(ResString(IDS_SEARCH_FILES),          ICID_SEARCH_DOC, false, IDC_SEARCH_FILES);
 
-         default:
-               return super::Command(id, code);
-       }
+#ifndef __MINGW32__    // SHRestricted() missing in MinGW (as of 29.10.2003)
+       if (!g_Globals._SHRestricted || !SHRestricted(REST_HASFINDCOMPUTERS))
+#endif
+               AddButton(ResString(IDS_SEARCH_COMPUTER),ICID_COMPUTER, false, IDC_SEARCH_COMPUTER);
 
-       return 0;
+       AddButton(ResString(IDS_SEARCH_PRG),            ICID_APPS, false, IDC_SEARCH_PROGRAM);
 }
 
 
-void SearchMenu::AddEntries()
+void RecentStartMenu::AddEntries()
 {
-       super::AddEntries();
-
-       AddButton(ResString(IDS_SEARCH_PRG),    SmallIcon(IDI_APPS), false, IDC_SEARCH_PROGRAM);
+       for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
+               StartMenuDirectory& smd = *it;
+               ShellDirectory& dir = smd._dir;
 
-       AddButton(ResString(IDS_SEARCH_FILES),  SmallIcon(IDI_SEARCH_DOC), false, IDC_SEARCH_FILES);
+               if (!dir._scanned) {
+                       WaitCursor wait;
 
-#ifndef __MINGW32__    // SHRestricted() missing in MinGW (as of 29.10.2003)
-       if (!g_Globals._SHRestricted || !SHRestricted(REST_HASFINDCOMPUTERS))
+#ifdef _LAZY_ICONEXTRACT
+                       dir.smart_scan(SORT_NAME, SCAN_FILESYSTEM);
+#else
+                       dir.smart_scan(SORT_NAME, SCAN_EXTRACT_ICONS|SCAN_FILESYSTEM);
 #endif
-               AddButton(ResString(IDS_SEARCH_COMPUTER),       SmallIcon(IDI_COMPUTER), false, IDC_SEARCH_COMPUTER);
+               }
+
+               dir.sort_directory(SORT_DATE);
+               AddShellEntries(dir, RECENT_DOCS_COUNT, smd._subfolders);
+       }
 }
 
-int SearchMenu::Command(int id, int code)
+
+#ifndef _SHELL32_FAVORITES
+
+void FavoritesMenu::AddEntries()
 {
-       switch(id) {
-         case IDC_SEARCH_PROGRAM:
-               CloseStartMenu(id);
-               Dialog::DoModal(IDD_SEARCH_PROGRAM, WINDOW_CREATOR(FindProgramDlg));
-               break;
+       super::AddEntries();
 
-         case IDC_SEARCH_FILES:
-               CloseStartMenu(id);
-               ShowSearchDialog();
-               break;
+       for(BookmarkList::iterator it=_bookmarks.begin(); it!=_bookmarks.end(); ++it) {
+               BookmarkNode& node = *it;
 
-         case IDC_SEARCH_COMPUTER:
-               CloseStartMenu(id);
-               ShowSearchComputer();
-               break;
+               int id = ++_next_id;
 
-         default:
-               return super::Command(id, code);
-       }
+               _entries[id] = node;
 
-       return 0;
-}
+               if (node._type == BookmarkNode::BMNT_FOLDER) {
+                       BookmarkFolder& folder = *node._pfolder;
 
-void SearchMenu::ShowSearchDialog()
-{
-       static DynamicFct<SHFINDFILES> SHFindFiles(TEXT("SHELL32"), 90);
+                       AddButton(folder._name, ICID_FOLDER, true, id);
+               } else if (node._type == BookmarkNode::BMNT_BOOKMARK) {
+                       Bookmark& bookmark = *node._pbookmark;
 
-       if (SHFindFiles)
-               (*SHFindFiles)(NULL, NULL);
+                       ICON_ID icon = ICID_NONE;
+
+                       if (!bookmark._icon_path.empty())
+                               icon = g_Globals._icon_cache.extract(bookmark._icon_path, bookmark._icon_idx);
+
+                       AddButton(bookmark._name, icon!=ICID_NONE?icon:ICID_BOOKMARK, false, id);
+               }
+       }
 }
 
-void SearchMenu::ShowSearchComputer()
+int FavoritesMenu::Command(int id, int code)
 {
-       static DynamicFct<SHFINDCOMPUTER> SHFindComputer(TEXT("SHELL32"), 91);
+       BookmarkMap::iterator found = _entries.find(id);
 
-       if (SHFindComputer)
-               (*SHFindComputer)(NULL, NULL);
-}
+       if (found != _entries.end()) {
+               BookmarkNode& node = found->second;
 
+               if (node._type == BookmarkNode::BMNT_FOLDER) {
+                       BookmarkFolder& folder = *node._pfolder;
 
-RecentStartMenu::RecentStartMenu(HWND hwnd, const StartMenuCreateInfo& create_info)
- :     super(hwnd, create_info)
-{
-}
+                       if (CloseOtherSubmenus(id))
+                               CreateSubmenu(id, folder._name, STARTMENU_CREATOR(FavoritesMenu), &static_cast<BookmarkList&>(folder._bookmarks));
+               } else if (node._type == BookmarkNode::BMNT_BOOKMARK) {
+                       Bookmark& bookmark = *node._pbookmark;
 
-void RecentStartMenu::AddEntries()
-{
-       for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
-               StartMenuDirectory& smd = *it;
-               ShellDirectory& dir = smd._dir;
+                       String url = bookmark._url;
+                       HWND hparent = GetParent(_hwnd);
 
-               if (!dir._scanned) {
-                       WaitCursor wait;
+                       CloseStartMenu(id);
 
-                       dir.smart_scan();
+                       launch_file(hparent, url, SW_SHOWNORMAL);
                }
 
-               dir.sort_directory(SORT_DATE);
-               AddShellEntries(dir, 16, smd._subfolders);      ///@todo read max. count of entries from registry
+               return 0;
        }
+
+       return super::Command(id, code);
 }
+
+#endif