startmenu.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright 2003, 2004 Martin Fuchs
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2.1 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 
00020  //
00021  // Explorer clone
00022  //
00023  // startmenu.cpp
00024  //
00025  // Explorer start menu
00026  //
00027  // Martin Fuchs, 19.08.2003
00028  //
00029  // Credits: Thanks to Everaldo (http://www.everaldo.com) for his nice looking icons.
00030  //
00031 
00032 
00033 #include <precomp.h>
00034 
00035 #include "../resource.h"
00036 
00037 #include "desktopbar.h"
00038 #include "startmenu.h"
00039 
00040 #include "../dialogs/searchprogram.h"
00041 #include "../dialogs/settings.h"
00042 
00043 
00044 #define SHELLPATH_CONTROL_PANEL     TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}")
00045 #define SHELLPATH_PRINTERS          TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}")
00046 #define SHELLPATH_NET_CONNECTIONS   TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}")
00047 
00048 
00049 StartMenu::StartMenu(HWND hwnd, int icon_size)
00050  :  super(hwnd),
00051     _icon_size(icon_size)
00052 {
00053     _next_id = IDC_FIRST_MENU;
00054     _submenu_id = 0;
00055 
00056     _border_left = 0;
00057     _border_top = 0;
00058     _bottom_max = INT_MAX;
00059 
00060     _floating_btn = false;
00061     _arrow_btns = false;
00062     _scroll_mode = SCROLL_NOT;
00063     _scroll_pos = 0;
00064     _invisible_lines = 0;
00065 
00066     _last_pos = WindowRect(hwnd).pos();
00067 #ifdef _LIGHT_STARTMENU
00068     _selected_id = -1;
00069     _last_mouse_pos = 0;
00070 #endif
00071 }
00072 
00073 StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info, int icon_size)
00074  :  super(hwnd),
00075     _create_info(create_info),
00076     _icon_size(icon_size)
00077 {
00078     for(StartMenuFolders::const_iterator it=create_info._folders.begin(); it!=create_info._folders.end(); ++it)
00079         if (*it)
00080             _dirs.push_back(ShellDirectory(GetDesktopFolder(), *it, _hwnd));
00081 
00082     _next_id = IDC_FIRST_MENU;
00083     _submenu_id = 0;
00084 
00085     _border_left = 0;
00086     _border_top = create_info._border_top;
00087     _bottom_max = INT_MAX;
00088 
00089     _floating_btn = create_info._border_top? true: false;
00090     _arrow_btns = false;
00091     _scroll_mode = SCROLL_NOT;
00092     _scroll_pos = 0;
00093     _invisible_lines = 0;
00094 
00095     _last_pos = WindowRect(hwnd).pos();
00096 #ifdef _LIGHT_STARTMENU
00097     _selected_id = -1;
00098     _last_mouse_pos = 0;
00099 #endif
00100 }
00101 
00102 StartMenu::~StartMenu()
00103 {
00104     SendParent(PM_STARTMENU_CLOSED);
00105 }
00106 
00107 
00108  // We need this wrapper function for s_wcStartMenu, it calls the WIN32 API,
00109  // though static C++ initializers are not allowed for Winelib applications.
00110 BtnWindowClass& StartMenu::GetWndClasss()
00111 {
00112     static BtnWindowClass s_wcStartMenu(CLASSNAME_STARTMENU);
00113 
00114     return s_wcStartMenu;
00115 }
00116 
00117 
00118 Window::CREATORFUNC_INFO StartMenu::s_def_creator = STARTMENU_CREATOR(StartMenu);
00119 
00120 HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndParent, LPCTSTR title,
00121                         CREATORFUNC_INFO creator, void* info, const String& filter)
00122 {
00123     UINT style, ex_style;
00124     int top_height;
00125 
00126     if (hwndParent) {
00127         style = WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE;
00128         ex_style = 0;
00129         top_height = STARTMENU_TOP_BTN_SPACE;
00130     } else {
00131         style = WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPCHILDREN|WS_VISIBLE;
00132         ex_style = WS_EX_TOOLWINDOW;
00133         top_height = 0;
00134     }
00135 
00136     int icon_size = ICON_SIZE_SMALL;
00137     RECT rect = {x, y-STARTMENU_LINE_HEIGHT(icon_size)-top_height, x+STARTMENU_WIDTH_MIN, y};
00138 
00139 #ifndef _LIGHT_STARTMENU
00140     rect.top += STARTMENU_LINE_HEIGHT(icon_size);
00141 #endif
00142 
00143     AdjustWindowRectEx(&rect, style, FALSE, ex_style);
00144 
00145     StartMenuCreateInfo create_info;
00146 
00147     create_info._folders = folders;
00148     create_info._border_top = top_height;
00149     create_info._creator = creator;
00150     create_info._info = info;
00151     create_info._filter = filter;
00152 
00153     if (title)
00154         create_info._title = title;
00155 
00156     HWND hwnd = Window::Create(creator, &create_info, ex_style, GetWndClasss(), title,
00157                                 style, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndParent);
00158 
00159      // make sure the window is not off the screen
00160     MoveVisible(hwnd);
00161 
00162     return hwnd;
00163 }
00164 
00165 
00166 LRESULT StartMenu::Init(LPCREATESTRUCT pcs)
00167 {
00168     try {
00169         AddEntries();
00170 
00171         if (super::Init(pcs))
00172             return 1;
00173 
00174          // create buttons for registered entries in _entries
00175         for(ShellEntryMap::const_iterator it=_entries.begin(); it!=_entries.end(); ++it) {
00176             const StartMenuEntry& sme = it->second;
00177             bool hasSubmenu = false;
00178 
00179             for(ShellEntrySet::const_iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it)
00180                 if ((*it)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
00181                     hasSubmenu = true;
00182 
00183 #ifdef _LIGHT_STARTMENU
00184             _buttons.push_back(SMBtnInfo(sme, it->first, hasSubmenu));
00185 #else
00186             AddButton(sme._title, sme._hIcon, hasSubmenu, it->first);
00187 #endif
00188         }
00189 
00190 #ifdef _LIGHT_STARTMENU
00191         if (_buttons.empty())
00192 #else
00193         if (!GetWindow(_hwnd, GW_CHILD))
00194 #endif
00195             AddButton(ResString(IDS_EMPTY), ICID_NONE, false, 0, false);
00196 
00197 #ifdef _LIGHT_STARTMENU
00198         ResizeToButtons();
00199 #endif
00200 
00201 #ifdef _LAZY_ICONEXTRACT
00202         PostMessage(_hwnd, PM_UPDATE_ICONS, 0, 0);
00203 #endif
00204     } catch(COMException& e) {
00205         HandleException(e, pcs->hwndParent);    // destroys the start menu window while switching focus
00206     }
00207 
00208     return 0;
00209 }
00210 
00211 void StartMenu::AddEntries()
00212 {
00213     for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
00214         StartMenuDirectory& smd = *it;
00215         ShellDirectory& dir = smd._dir;
00216 
00217         if (!dir._scanned) {
00218             WaitCursor wait;
00219 
00220 #ifdef _LAZY_ICONEXTRACT
00221             dir.smart_scan(SORT_NAME, SCAN_DONT_EXTRACT_ICONS); // lazy icon extraction, try to read directly from filesystem
00222 #else
00223             dir.smart_scan(SORT_NAME);
00224 #endif
00225         }
00226 
00227         AddShellEntries(dir, -1, smd._ignore);
00228     }
00229 }
00230 
00231 
00232 static LPTSTR trim_path_slash(LPTSTR path)
00233 {
00234     LPTSTR p = path;
00235 
00236     while(*p)
00237         ++p;
00238 
00239     if (p>path && (p[-1]=='\\' || p[-1]=='/'))
00240         *--p = '\0';
00241 
00242     return path;
00243 }
00244 
00245 void StartMenu::AddShellEntries(const ShellDirectory& dir, int max, const String& ignore)
00246 {
00247     TCHAR ignore_path[MAX_PATH], ignore_dir[MAX_PATH], ignore_name[_MAX_FNAME], ignore_ext[_MAX_EXT];
00248     TCHAR dir_path[MAX_PATH];
00249 
00250     if (!ignore.empty()) {
00251         _tsplitpath_s(ignore, ignore_path, COUNTOF(ignore_path), ignore_dir, COUNTOF(ignore_dir), ignore_name, COUNTOF(ignore_name), ignore_ext, COUNTOF(ignore_ext));
00252 
00253         _tcscat(ignore_path, ignore_dir);
00254         _tcscat(ignore_name, ignore_ext);
00255 
00256         dir.get_path(dir_path, COUNTOF(dir_path));
00257 
00258         if (_tcsicmp(trim_path_slash(dir_path), trim_path_slash(ignore_path)))
00259             *ignore_name = '\0';
00260     } else
00261         *ignore_name = '\0';
00262 
00263     String lwr_filter = _create_info._filter;
00264     lwr_filter.toLower();
00265 
00266     int cnt = 0;
00267     for(Entry*entry=dir._down; entry; entry=entry->_next) {
00268          // hide files like "desktop.ini"
00269         if (entry->_shell_attribs & SFGAO_HIDDEN)
00270         //not appropriate for drive roots: if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
00271             continue;
00272 
00273          // hide "Programs" subfolders if requested
00274         if (*ignore_name && !_tcsicmp(entry->_data.cFileName, ignore_name))
00275             continue;
00276 
00277          // only 'max' entries shall be added.
00278         if (++cnt == max)
00279             break;
00280 
00281          // filter only non-directory entries
00282         if (!(entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) && !lwr_filter.empty()) {
00283             String lwr_name = entry->_data.cFileName;
00284             String lwr_disp = entry->_display_name;
00285 
00286             lwr_name.toLower();
00287             lwr_disp.toLower();
00288 
00289             if (!_tcsstr(lwr_name,lwr_filter) && !_tcsstr(lwr_disp,lwr_filter))
00290                 continue;
00291         }
00292 
00293         if (entry->_etype == ET_SHELL)
00294             AddEntry(dir._folder, static_cast<ShellEntry*>(entry));
00295         else
00296             AddEntry(dir._folder, entry);
00297     }
00298 }
00299 
00300 
00301 LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
00302 {
00303     switch(nmsg) {
00304       case WM_PAINT: {
00305         PaintCanvas canvas(_hwnd);
00306         Paint(canvas);
00307         break;}
00308 
00309       case WM_SIZE:
00310         ResizeButtons(LOWORD(lparam)-_border_left);
00311         break;
00312 
00313       case WM_MOVE: {
00314         POINTS pos;
00315         pos.x = LOWORD(lparam);
00316         pos.y = HIWORD(lparam);
00317 
00318          // move open submenus of floating menus
00319         if (_submenu) {
00320             int dx = pos.x - _last_pos.x;
00321             int dy = pos.y - _last_pos.y;
00322 
00323             if (dx || dy) {
00324                 WindowRect rt(_submenu);
00325                 SetWindowPos(_submenu, 0, rt.left+dx, rt.top+dy, 0, 0, SWP_NOSIZE|SWP_NOACTIVATE);
00326                 //MoveVisible(_submenu);
00327             }
00328         }
00329 
00330         _last_pos.x = pos.x;
00331         _last_pos.y = pos.y;
00332         goto def;}
00333 
00334       case WM_NCHITTEST: {
00335         LRESULT res = super::WndProc(nmsg, wparam, lparam);
00336 
00337         if (res>=HTSIZEFIRST && res<=HTSIZELAST)
00338             return HTCLIENT;    // disable window resizing
00339 
00340         return res;}
00341 
00342       case WM_LBUTTONDOWN: {
00343         RECT rect;
00344 
00345          // check mouse cursor for coordinates of floating button
00346         GetFloatingButtonRect(&rect);
00347 
00348         if (PtInRect(&rect, Point(lparam))) {
00349              // create a floating copy of the current start menu
00350             WindowRect pos(_hwnd);
00351 
00352             ///@todo do something similar to StartMenuRoot::TrackStartmenu() in order to automatically close submenus when clicking on the desktop background
00353             StartMenu::Create(pos.left+3, pos.bottom-3, _create_info._folders, 0, _create_info._title, _create_info._creator, _create_info._info);
00354             CloseStartMenu();
00355         }
00356 
00357 #ifdef _LIGHT_STARTMENU
00358         int id = ButtonHitTest(Point(lparam));
00359 
00360         if (id)
00361             Command(id, BN_CLICKED);
00362 #endif
00363         break;}
00364 
00365       case WM_SYSCOMMAND:
00366         if ((wparam&0xFFF0) == SC_SIZE)
00367             return 0;           // disable window resizing
00368         goto def;
00369 
00370       case WM_ACTIVATEAPP:
00371          // close start menu when activating another application
00372         if (!wparam)
00373             CloseStartMenu();
00374         break;  // don't call super::WndProc in case "this" has been deleted
00375 
00376       case WM_CANCELMODE:
00377         CloseStartMenu();
00378 
00379 #ifdef _LIGHT_STARTMENU
00380         if (_scroll_mode != SCROLL_NOT) {
00381             ReleaseCapture();
00382             KillTimer(_hwnd, 0);
00383         }
00384 #endif
00385         break;
00386 
00387 #ifdef _LIGHT_STARTMENU
00388       case WM_MOUSEMOVE: {
00389          // automatically set the focus to startmenu entries when moving the mouse over them
00390         if (lparam != _last_mouse_pos) { // don't process WM_MOUSEMOVE when opening submenus using keyboard navigation
00391             Point pt(lparam);
00392 
00393             if (_arrow_btns) {
00394                 RECT rect_up, rect_down;
00395 
00396                 GetArrowButtonRects(&rect_up, &rect_down, _icon_size);
00397 
00398                 SCROLL_MODE scroll_mode = SCROLL_NOT;
00399 
00400                 if (PtInRect(&rect_up, pt))
00401                     scroll_mode = SCROLL_UP;
00402                 else if (PtInRect(&rect_down, pt))
00403                     scroll_mode = SCROLL_DOWN;
00404 
00405                 if (scroll_mode != _scroll_mode) {
00406                     if (scroll_mode == SCROLL_NOT) {
00407                         ReleaseCapture();
00408                         KillTimer(_hwnd, 0);
00409                     } else {
00410                         CloseSubmenus();
00411                         SetTimer(_hwnd, 0, 150, NULL);  // 150 ms scroll interval
00412                         SetCapture(_hwnd);
00413                     }
00414 
00415                     _scroll_mode = scroll_mode;
00416                 }
00417             }
00418 
00419             int new_id = ButtonHitTest(pt);
00420 
00421             if (new_id>0 && new_id!=_selected_id)
00422                 SelectButton(new_id);
00423 
00424             _last_mouse_pos = lparam;
00425         }
00426         break;}
00427 
00428       case WM_TIMER:
00429         if (_scroll_mode == SCROLL_UP) {
00430             if (_scroll_pos > 0) {
00431                 --_scroll_pos;
00432                 InvalidateRect(_hwnd, NULL, TRUE);
00433             }
00434         } else {
00435             if (_scroll_pos <= _invisible_lines) {
00436                 ++_scroll_pos;
00437                 InvalidateRect(_hwnd, NULL, TRUE);
00438             }
00439         }
00440         break;
00441 
00442       case WM_KEYDOWN:
00443         ProcessKey(wparam);
00444         break;
00445 #else
00446       case PM_STARTENTRY_FOCUSED: { ///@todo use TrackMouseEvent() and WM_MOUSEHOVER to wait a bit before opening submenus
00447         BOOL hasSubmenu = wparam;
00448         HWND hctrl = (HWND)lparam;
00449 
00450          // automatically open submenus
00451         if (hasSubmenu) {
00452             UpdateWindow(_hwnd);    // draw focused button before waiting on submenu creation
00453             //SendMessage(_hwnd, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hctrl),BN_CLICKED), (LPARAM)hctrl);
00454             Command(GetDlgCtrlID(hctrl), BN_CLICKED);
00455         } else {
00456              // close any open submenu
00457             CloseOtherSubmenus();
00458         }
00459         break;}
00460 #endif
00461 
00462 #ifdef _LAZY_ICONEXTRACT
00463       case PM_UPDATE_ICONS:
00464         UpdateIcons(/*wparam*/);
00465         break;
00466 #endif
00467 
00468       case PM_STARTENTRY_LAUNCHED:
00469         if (GetWindowStyle(_hwnd) & WS_CAPTION) // don't automatically close floating menus
00470             return 0;
00471 
00472          // route message to the parent menu and close menus after launching an entry
00473         if (!SendParent(nmsg, wparam, lparam))
00474             CloseStartMenu();
00475         return 1;   // signal that we have received and processed the message
00476 
00477       case PM_STARTMENU_CLOSED:
00478         _submenu = 0;
00479         break;
00480 
00481       case PM_SELECT_ENTRY:
00482         SelectButtonIndex(0, wparam!=0);
00483         break;
00484 
00485 #ifdef _LIGHT_STARTMENU
00486       case WM_CONTEXTMENU: {
00487         Point screen_pt(lparam), clnt_pt=screen_pt;
00488         ScreenToClient(_hwnd, &clnt_pt);
00489 
00490         int id = ButtonHitTest(clnt_pt);
00491 
00492         if (id) {
00493             StartMenuEntry& sme = _entries[id];
00494 
00495             for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
00496                 Entry* entry = *it;
00497 
00498                 if (entry) {
00499                     CHECKERROR(entry->do_context_menu(_hwnd, screen_pt, _cm_ifs));  // may close start menu because of focus loss
00500                     ///@todo refresh on successfull context menu execution?
00501                     break;  ///@todo handle context menu for more than one entry
00502                 }
00503             }
00504         }
00505         break;}
00506 #endif
00507 
00508       default: def:
00509         return super::WndProc(nmsg, wparam, lparam);
00510     }
00511 
00512     return 0;
00513 }
00514 
00515 
00516 #ifdef _LIGHT_STARTMENU
00517 
00518 int StartMenu::ButtonHitTest(POINT pt)
00519 {
00520     ClientRect clnt(_hwnd);
00521     const int icon_size = _icon_size;
00522     RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
00523 
00524     if (pt.x<rect.left || pt.x>rect.right)
00525         return 0;
00526 
00527     for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
00528         const SMBtnInfo& info = *it;
00529 
00530         if (rect.top > pt.y)
00531             break;
00532 
00533         rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
00534 
00535         if (rect.bottom > _bottom_max)
00536             break;
00537 
00538         if (pt.y < rect.bottom) // PtInRect(&rect, pt)
00539             return info._id;
00540 
00541         rect.top = rect.bottom;
00542     }
00543 
00544     return 0;
00545 }
00546 
00547 void StartMenu::InvalidateSelection()
00548 {
00549     if (_selected_id <= 0)
00550         return;
00551 
00552     ClientRect clnt(_hwnd);
00553     const int icon_size = _icon_size;
00554     RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
00555 
00556     for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
00557         const SMBtnInfo& info = *it;
00558 
00559         rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
00560 
00561         if (info._id == _selected_id) {
00562             InvalidateRect(_hwnd, &rect, TRUE);
00563             break;
00564         }
00565 
00566         rect.top = rect.bottom;
00567     }
00568 }
00569 
00570 const SMBtnInfo* StartMenu::GetButtonInfo(int id) const
00571 {
00572     for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it)
00573         if (it->_id == id)
00574             return &*it;
00575 
00576     return NULL;
00577 }
00578 
00579 bool StartMenu::SelectButton(int id, bool open_sub)
00580 {
00581     if (id == -1)
00582         return false;
00583 
00584     if (id == _selected_id)
00585         return true;
00586 
00587     InvalidateSelection();
00588 
00589     const SMBtnInfo* btn = GetButtonInfo(id);
00590 
00591     if (btn && btn->_enabled) {
00592         _selected_id = id;
00593 
00594         InvalidateSelection();
00595 
00596          // automatically open submenus
00597         if (btn->_hasSubmenu) {
00598             if (open_sub)
00599                 OpenSubmenu();
00600         } else
00601             CloseOtherSubmenus();   // close any open submenu
00602 
00603         return true;
00604     } else {
00605         _selected_id = -1;
00606         return false;
00607     }
00608 }
00609 
00610 bool StartMenu::OpenSubmenu(bool select_first)
00611 {
00612     if (_selected_id == -1)
00613         return false;
00614 
00615     InvalidateSelection();
00616 
00617     const SMBtnInfo* btn = GetButtonInfo(_selected_id);
00618 
00619      // automatically open submenus
00620     if (btn->_hasSubmenu) {
00621         //@@ allows destroying of startmenu when processing PM_UPDATE_ICONS -> GPF
00622         UpdateWindow(_hwnd);    // draw focused button before waiting on submenu creation
00623         Command(_selected_id, BN_CLICKED);
00624 
00625         if (select_first && _submenu)
00626             SendMessage(_submenu, PM_SELECT_ENTRY, (WPARAM)false, 0);
00627 
00628         return true;
00629     } else
00630         return false;
00631 }
00632 
00633 
00634 int StartMenu::GetSelectionIndex()
00635 {
00636     if (_selected_id == -1)
00637         return -1;
00638 
00639     for(int i=0; i<(int)_buttons.size(); ++i)
00640         if (_buttons[i]._id == _selected_id)
00641             return i;
00642 
00643     return -1;
00644 }
00645 
00646 bool StartMenu::SelectButtonIndex(int idx, bool open_sub)
00647 {
00648     if (idx>=0 && idx<(int)_buttons.size())
00649         return SelectButton(_buttons[idx]._id, open_sub);
00650     else
00651         return false;
00652 }
00653 
00654 void StartMenu::ProcessKey(int vk)
00655 {
00656     switch(vk) {
00657       case VK_RETURN:
00658         if (_selected_id)
00659             Command(_selected_id, BN_CLICKED);
00660         break;
00661 
00662       case VK_UP:
00663         Navigate(-1);
00664         break;
00665 
00666       case VK_DOWN:
00667         Navigate(+1);
00668         break;
00669 
00670       case VK_HOME:
00671         SelectButtonIndex(0, false);
00672         break;
00673 
00674       case VK_END:
00675         SelectButtonIndex(_buttons.size()-1, false);
00676         break;
00677 
00678       case VK_LEFT:
00679         if (_submenu)
00680             CloseOtherSubmenus();
00681         else if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) // don't automatically close floating menus
00682             DestroyWindow(_hwnd);
00683         break;
00684 
00685       case VK_RIGHT:
00686         OpenSubmenu(true);
00687         break;
00688 
00689       case VK_ESCAPE:
00690         CloseStartMenu();
00691         break;
00692 
00693       default:
00694         if (vk>='0' && vk<='Z')
00695             JumpToNextShortcut(vk);
00696     }
00697 }
00698 
00699 bool StartMenu::Navigate(int step)
00700 {
00701     int idx = GetSelectionIndex();
00702 
00703     if (idx == -1)
00704     {
00705         if (step > 0)
00706             idx = 0 - step;
00707         else
00708             idx = _buttons.size() - step;
00709     }
00710 
00711     for(;;) {
00712         idx += step;
00713 
00714         if (_buttons.size()<=1 && (idx<0 || idx>(int)_buttons.size()))
00715             break;
00716 
00717         if (idx < 0)
00718             idx += _buttons.size();
00719 
00720         if (idx > (int)_buttons.size())
00721             idx -= _buttons.size()+1;
00722 
00723         if (SelectButtonIndex(idx, false))
00724             return true;
00725     }
00726 
00727     return false;
00728 }
00729 
00730 bool StartMenu::JumpToNextShortcut(char c)
00731 {
00732     int cur_idx = GetSelectionIndex();
00733 
00734     if (cur_idx == -1)
00735         cur_idx = 0;
00736 
00737     int first_found = 0;
00738     int found_more = 0;
00739 
00740     SMBtnVector::const_iterator cur_it = _buttons.begin();
00741     cur_it += cur_idx + 1;
00742 
00743      // first search down from current item...
00744     SMBtnVector::const_iterator it = cur_it;
00745     for(; it!=_buttons.end(); ++it) {
00746         const SMBtnInfo& btn = *it;
00747 
00748         if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
00749             if (!first_found)
00750                 first_found = btn._id;
00751             else
00752                 ++found_more;
00753         }
00754     }
00755 
00756      // ...now search from top to the current item
00757     it = _buttons.begin();
00758     for(; it!=_buttons.end() && it!=cur_it; ++it) {
00759         const SMBtnInfo& btn = *it;
00760 
00761         if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
00762             if (!first_found)
00763                 first_found = btn._id;
00764             else
00765                 ++found_more;
00766         }
00767     }
00768 
00769     if (first_found) {
00770         SelectButton(first_found);
00771 
00772         if (!found_more)
00773             Command(first_found, BN_CLICKED);
00774 
00775         return true;
00776     } else
00777         return false;
00778 }
00779 
00780 #endif // _LIGHT_STARTMENU
00781 
00782 
00783 bool StartMenu::GetButtonRect(int id, PRECT prect) const
00784 {
00785 #ifdef _LIGHT_STARTMENU
00786     ClientRect clnt(_hwnd);
00787     const int icon_size = _icon_size;
00788     RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
00789 
00790     for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
00791         const SMBtnInfo& info = *it;
00792 
00793         rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
00794 
00795         if (info._id == id) {
00796             *prect = rect;
00797             return true;
00798         }
00799 
00800         rect.top = rect.bottom;
00801     }
00802 
00803     return false;
00804 #else
00805     HWND btn = GetDlgItem(_hwnd, id);
00806 
00807     if (btn) {
00808         GetWindowRect(btn, prect);
00809         ScreenToClient(_hwnd, prect);
00810 
00811         return true;
00812     } else
00813         return false;
00814 #endif
00815 }
00816 
00817 
00818 void StartMenu::DrawFloatingButton(HDC hdc)
00819 {
00820     static ResIconEx floatingIcon(IDI_FLOATING, 8, 4);
00821 
00822     ClientRect clnt(_hwnd);
00823 
00824     DrawIconEx(hdc, clnt.right-12, 0, floatingIcon, 8, 4, 0, 0, DI_NORMAL);
00825 }
00826 
00827 void StartMenu::GetFloatingButtonRect(LPRECT prect)
00828 {
00829     GetClientRect(_hwnd, prect);
00830 
00831     prect->right -= 4;
00832     prect->left = prect->right - 8;
00833     prect->bottom = 4;
00834 }
00835 
00836 
00837 void StartMenu::DrawArrows(HDC hdc, int icon_size)
00838 {
00839     int cx = icon_size / 2;
00840     int cy = icon_size / 4;
00841 
00842     ResIconEx arrowUpIcon(IDI_ARROW_UP, cx, cy);
00843     ResIconEx arrowDownIcon(IDI_ARROW_DOWN, cx, cy);
00844 
00845     ClientRect clnt(_hwnd);
00846 
00847     DrawIconEx(hdc, clnt.right/2-cx/2, _floating_btn?3:1, arrowUpIcon, cx, cy, 0, 0, DI_NORMAL);
00848     DrawIconEx(hdc, clnt.right/2-cx/2, clnt.bottom-cy-1, arrowDownIcon, cx, cy, 0, 0, DI_NORMAL);
00849 }
00850 
00851 void StartMenu::GetArrowButtonRects(LPRECT prect_up, LPRECT prect_down, int icon_size)
00852 {
00853     int cx = icon_size / 2;
00854     int cy = icon_size / 4;
00855 
00856     GetClientRect(_hwnd, prect_up);
00857     *prect_down = *prect_up;
00858 
00859 //  prect_up->left = prect_up->right/2 - cx/2;
00860 //  prect_up->right = prect_up->left + cy;
00861     prect_up->right -= cx;
00862     prect_up->top = _floating_btn? cy-1: 1;
00863     prect_up->bottom = prect_up->top + cy;
00864 
00865 //  prect_down->left = prect_down->right/2 - cx/2;
00866 //  prect_down->right = prect_down->left + cy;
00867     prect_down->right -= cx;
00868     prect_down->top = prect_down->bottom - cy - 1;
00869 }
00870 
00871 
00872 void StartMenu::Paint(PaintCanvas& canvas)
00873 {
00874     if (_floating_btn)
00875         DrawFloatingButton(canvas);
00876 
00877 #ifdef _LIGHT_STARTMENU
00878     if (_arrow_btns)
00879         DrawArrows(canvas, _icon_size);
00880 
00881     ClientRect clnt(_hwnd);
00882     const int icon_size = _icon_size;
00883     RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
00884 
00885     int sep_width = rect.right-rect.left - 4;
00886 
00887     FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
00888     BkMode bk_mode(canvas, TRANSPARENT);
00889 
00890     for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
00891         const SMBtnInfo& btn = *it;
00892 
00893         if (rect.top > canvas.rcPaint.bottom)
00894             break;
00895 
00896         if (btn._id == -1) {    // a separator?
00897             rect.bottom = rect.top + STARTMENU_SEP_HEIGHT(icon_size);
00898 
00899             if (rect.bottom > _bottom_max)
00900                 break;
00901 
00902             BrushSelection brush_sel(canvas, GetSysColorBrush(COLOR_BTNSHADOW));
00903             PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT(icon_size)/2-1, sep_width, 1, PATCOPY);
00904 
00905             SelectBrush(canvas, GetSysColorBrush(COLOR_BTNHIGHLIGHT));
00906             PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT(icon_size)/2, sep_width, 1, PATCOPY);
00907         } else {
00908             rect.bottom = rect.top + STARTMENU_LINE_HEIGHT(icon_size);
00909 
00910             if (rect.bottom > _bottom_max)
00911                 break;
00912 
00913             if (rect.top >= canvas.rcPaint.top)
00914                 DrawStartMenuButton(canvas, rect, btn._title, btn, btn._id==_selected_id, false, _icon_size);
00915         }
00916 
00917         rect.top = rect.bottom;
00918     }
00919 #endif
00920 }
00921 
00922 #ifdef _LAZY_ICONEXTRACT
00923 void StartMenu::UpdateIcons(/*int idx*/)
00924 {
00925     UpdateWindow(_hwnd);
00926 
00927 #ifdef _SINGLE_ICONEXTRACT
00928 
00929     //if (idx >= 0)
00930     int idx = _scroll_pos;
00931 
00932     for(; idx<(int)_buttons.size(); ++idx) {
00933         SMBtnInfo& btn = _buttons[idx];
00934 
00935         if (btn._icon_id==ICID_UNKNOWN && btn._id>0) {
00936             StartMenuEntry& sme = _entries[btn._id];
00937 
00938             btn._icon_id = ICID_NONE;
00939 
00940             for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
00941                 Entry* entry = *it;
00942 
00943                 if (entry->_icon_id == ICID_UNKNOWN)
00944                     entry->_icon_id = entry->safe_extract_icon(ICF_FROM_ICON_SIZE(_icon_size));
00945 
00946                 if (entry->_icon_id > ICID_NONE) {
00947                     btn._icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
00948 
00949                     RECT rect;
00950 
00951                     GetButtonRect(btn._id, &rect);
00952 
00953                     if (rect.bottom > _bottom_max)
00954                         break;
00955 
00956                     WindowCanvas canvas(_hwnd);
00957                     DrawStartMenuButton(canvas, rect, NULL, btn, btn._id==_selected_id, false, _icon_size);
00958 
00959                     //InvalidateRect(_hwnd, &rect, FALSE);
00960                     //UpdateWindow(_hwnd);
00961                     //break;
00962 
00963                     break;
00964                 }
00965             }
00966         }
00967     }
00968 
00969 //  if (++idx < (int)_buttons.size())
00970 //      PostMessage(_hwnd, PM_UPDATE_ICONS, idx, 0);
00971 
00972 #else
00973 
00974     int icons_extracted = 0;
00975     int icons_updated = 0;
00976 
00977     for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
00978         ShellDirectory& dir = it->_dir;
00979 
00980         icons_extracted += dir.extract_icons(icon_size);
00981     }
00982 
00983     if (icons_extracted) {
00984         for(ShellEntryMap::iterator it1=_entries.begin(); it1!=_entries.end(); ++it1) {
00985             StartMenuEntry& sme = it1->second;
00986 
00987             if (!sme._hIcon) {
00988                 sme._hIcon = (HICON)-1;
00989 
00990                 for(ShellEntrySet::const_iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
00991                     const Entry* sm_entry = *it2;
00992 
00993                     if (sm_entry->_hIcon) {
00994                         sme._hIcon = sm_entry->_hIcon;
00995                         break;
00996                     }
00997                 }
00998             }
00999         }
01000 
01001         for(SMBtnVector::iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
01002             SMBtnInfo& info = *it;
01003 
01004             if (info._id>0 && !info._hIcon) {
01005                 info._hIcon = _entries[info._id]._hIcon;
01006                 ++icons_updated;
01007             }
01008         }
01009     }
01010 
01011     if (icons_updated) {
01012         InvalidateRect(_hwnd, NULL, FALSE);
01013         UpdateWindow(_hwnd);
01014     }
01015 #endif
01016 }
01017 #endif
01018 
01019 
01020  // resize child button controls to accomodate for new window size
01021 void StartMenu::ResizeButtons(int cx)
01022 {
01023     HDWP hdwp = BeginDeferWindowPos(10);
01024 
01025     for(HWND ctrl=GetWindow(_hwnd,GW_CHILD); ctrl; ctrl=GetNextWindow(ctrl,GW_HWNDNEXT)) {
01026         ClientRect rt(ctrl);
01027 
01028         if (rt.right != cx) {
01029             int height = rt.bottom - rt.top;
01030 
01031              // special handling for separator controls
01032             if (!height && (GetWindowStyle(ctrl)&SS_TYPEMASK)==SS_ETCHEDHORZ)
01033                 height = 2;
01034 
01035             hdwp = DeferWindowPos(hdwp, ctrl, 0, 0, 0, cx, height, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
01036         }
01037     }
01038 
01039     EndDeferWindowPos(hdwp);
01040 }
01041 
01042 
01043 int StartMenu::Command(int id, int code)
01044 {
01045 #ifndef _LIGHT_STARTMENU
01046     switch(id) {
01047       case IDCANCEL:
01048         CloseStartMenu(id);
01049         break;
01050 
01051       default: {
01052 #endif
01053         ShellEntryMap::iterator found = _entries.find(id);
01054 
01055         if (found != _entries.end()) {
01056             ActivateEntry(id, found->second._entries);
01057             return 0;
01058         }
01059 
01060         return super::Command(id, code);
01061 #ifndef _LIGHT_STARTMENU
01062       }
01063     }
01064 
01065     return 0;
01066 #endif
01067 }
01068 
01069 
01070 ShellEntryMap::iterator StartMenu::AddEntry(const String& title, ICON_ID icon_id, Entry* entry)
01071 {
01072      // search for an already existing subdirectory entry with the same name
01073     if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
01074     {
01075         for(ShellEntryMap::iterator it=_entries.begin(); it!=_entries.end(); ++it) {
01076             StartMenuEntry& sme = it->second;
01077 
01078             if (!_tcsicmp(sme._title, title))   ///@todo speed up by using a map indexed by name
01079             {
01080                 for(ShellEntrySet::iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
01081                     if ((*it2)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
01082                          // merge the new shell entry with the existing of the same name
01083                         sme._entries.insert(entry);
01084 
01085                         return it;
01086                     }
01087                 }
01088             }
01089         }
01090     }
01091 
01092     ShellEntryMap::iterator sme = AddEntry(title, icon_id);
01093 
01094     sme->second._entries.insert(entry);
01095 
01096     return sme;
01097 }
01098 
01099 ShellEntryMap::iterator StartMenu::AddEntry(const String& title, ICON_ID icon_id, int id)
01100 {
01101     if (id == -1)
01102         id = ++_next_id;
01103 
01104     StartMenuEntry sme;
01105 
01106     sme._title = title;
01107     sme._icon_id = icon_id;
01108 
01109     ShellEntryMap::iterator it = _entries.insert(make_pair(id, sme)).first;
01110 
01111     return it;
01112 }
01113 
01114 ShellEntryMap::iterator StartMenu::AddEntry(const ShellFolder folder, ShellEntry* entry)
01115 {
01116     ICON_ID icon_id;
01117 
01118     if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
01119         icon_id = ICID_APPS;
01120     else
01121         icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
01122 
01123     return AddEntry(folder.get_name(entry->_pidl), icon_id, entry);
01124 }
01125 
01126 ShellEntryMap::iterator StartMenu::AddEntry(const ShellFolder folder, Entry* entry)
01127 {
01128     ICON_ID icon_id;
01129 
01130     if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
01131         icon_id = ICID_APPS;
01132     else
01133         icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
01134 
01135     return AddEntry(entry->_display_name, icon_id, entry);
01136 }
01137 
01138 
01139 void StartMenu::AddButton(LPCTSTR title, ICON_ID icon_id, bool hasSubmenu, int id, bool enabled)
01140 {
01141 #ifdef _LIGHT_STARTMENU
01142     _buttons.push_back(SMBtnInfo(title, icon_id, id, hasSubmenu, enabled));
01143 #else
01144     DWORD style = enabled? WS_VISIBLE|WS_CHILD|BS_OWNERDRAW: WS_VISIBLE|WS_CHILD|BS_OWNERDRAW|WS_DISABLED;
01145 
01146     WindowRect rect(_hwnd);
01147     ClientRect clnt(_hwnd);
01148 
01149      // increase window height to make room for the new button
01150     rect.top -= STARTMENU_LINE_HEIGHT(icon_size);
01151 
01152      // move down if we are too high now
01153     if (rect.top < 0) {
01154         rect.top += STARTMENU_LINE_HEIGHT(icon_size);
01155         rect.bottom += STARTMENU_LINE_HEIGHT(icon_size);
01156     }
01157 
01158     WindowCanvas canvas(_hwnd);
01159     FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
01160 
01161      // widen window, if it is too small
01162     int text_width = GetStartMenuBtnTextWidth(canvas, title, _hwnd) + icon_size + 10/*placeholder*/ + 16/*arrow*/;
01163 
01164     int cx = clnt.right - _border_left;
01165     if (text_width > cx)
01166         rect.right += text_width-cx;
01167 
01168     MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
01169 
01170     StartMenuCtrl(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left,
01171                     title, id, g_Globals._icon_cache.get_icon(icon_id)._hIcon, hasSubmenu, style);
01172 #endif
01173 }
01174 
01175 void StartMenu::AddSeparator()
01176 {
01177 #ifdef _LIGHT_STARTMENU
01178     _buttons.push_back(SMBtnInfo(NULL, ICID_NONE, -1, false));
01179 #else
01180     WindowRect rect(_hwnd);
01181     ClientRect clnt(_hwnd);
01182 
01183      // increase window height to make room for the new separator
01184     rect.top -= STARTMENU_SEP_HEIGHT(icon_size);
01185 
01186      // move down if we are too high now
01187     if (rect.top < 0) {
01188         rect.top += STARTMENU_LINE_HEIGHT(icon_size);
01189         rect.bottom += STARTMENU_LINE_HEIGHT(icon_size);
01190     }
01191 
01192     MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
01193 
01194     StartMenuSeparator(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left);
01195 #endif
01196 }
01197 
01198 
01199 bool StartMenu::CloseOtherSubmenus(int id)
01200 {
01201     if (_submenu) {
01202         if (IsWindow(_submenu)) {
01203             if (_submenu_id == id)
01204                 return false;
01205             else {
01206                 _submenu_id = 0;
01207                 DestroyWindow(_submenu);
01208                 // _submenu should be reset automatically by PM_STARTMENU_CLOSED, but safety first...
01209             }
01210         }
01211 
01212         _submenu = 0;
01213     }
01214 
01215     return true;
01216 }
01217 
01218 
01219 void StartMenu::CreateSubmenu(int id, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
01220 {
01221     CreateSubmenu(id, StartMenuFolders(), title, creator, info);
01222 }
01223 
01224 bool StartMenu::CreateSubmenu(int id, int folder_id, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
01225 {
01226     try {
01227         SpecialFolderPath folder(folder_id, _hwnd);
01228 
01229         StartMenuFolders new_folders;
01230         new_folders.push_back(folder);
01231 
01232         CreateSubmenu(id, new_folders, title, creator, info);
01233 
01234         return true;
01235     } catch(COMException&) {
01236         // ignore Exception and don't display anything
01237         CloseOtherSubmenus(id);
01238         _buttons[GetSelectionIndex()]._enabled = false; // disable entries for non-existing folders
01239         return false;
01240     }
01241 }
01242 
01243 bool StartMenu::CreateSubmenu(int id, int folder_id1, int folder_id2, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
01244 {
01245     StartMenuFolders new_folders;
01246 
01247     try {
01248         new_folders.push_back(SpecialFolderPath(folder_id1, _hwnd));
01249     } catch(COMException&) {
01250     }
01251 
01252     try {
01253         new_folders.push_back(SpecialFolderPath(folder_id2, _hwnd));
01254     } catch(COMException&) {
01255     }
01256 
01257     if (!new_folders.empty()) {
01258         CreateSubmenu(id, new_folders, title, creator, info);
01259         return true;
01260     } else {
01261         CloseOtherSubmenus(id);
01262         _buttons[GetSelectionIndex()]._enabled = false; // disable entries for non-existing folders
01263         return false;
01264     }
01265 }
01266 
01267 void StartMenu::CreateSubmenu(int id, const StartMenuFolders& new_folders, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
01268 {
01269      // Only open one submenu at a time.
01270     if (!CloseOtherSubmenus(id))
01271         return;
01272 
01273     RECT rect;
01274     int x, y;
01275 
01276     if (GetButtonRect(id, &rect)) {
01277         ClientToScreen(_hwnd, &rect);
01278 
01279         x = rect.right; // Submenus should overlap their parent a bit.
01280         const int icon_size = _icon_size;
01281         y = rect.top+STARTMENU_LINE_HEIGHT(icon_size) +_border_top/*own border*/ -STARTMENU_TOP_BTN_SPACE/*border of new submenu*/;
01282     } else {
01283         WindowRect pos(_hwnd);
01284 
01285         x = pos.right;
01286         y = pos.top;
01287     }
01288 
01289     _submenu_id = id;
01290     _submenu = StartMenu::Create(x, y, new_folders, _hwnd, title, creator, info, _create_info._filter);
01291 }
01292 
01293 
01294 void StartMenu::ActivateEntry(int id, const ShellEntrySet& entries)
01295 {
01296     StartMenuFolders new_folders;
01297     String title;
01298 
01299     for(ShellEntrySet::const_iterator it=entries.begin(); it!=entries.end(); ++it) {
01300         Entry* entry = const_cast<Entry*>(*it);
01301 
01302         if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
01303 
01304             ///@todo If the user explicitly clicked on a submenu, display this folder as floating start menu.
01305 
01306             if (entry->_etype == ET_SHELL)
01307                 new_folders.push_back(entry->create_absolute_pidl());
01308             else {
01309                 TCHAR path[MAX_PATH];
01310 
01311                 if (entry->get_path(path, COUNTOF(path)))
01312                     new_folders.push_back(path);
01313             }
01314 
01315             if (title.empty())
01316                 title = entry->_display_name;
01317         } else {
01318              // The entry is no subdirectory, so there can only be one shell entry.
01319             assert(entries.size()==1);
01320 
01321             HWND hparent = GetParent(_hwnd);
01322             ShellPath shell_path = entry->create_absolute_pidl();
01323 
01324              // close start menus when launching the selected entry
01325             CloseStartMenu(id);
01326 
01327             ///@todo launch in the background; specify correct HWND for error message box titles
01328             SHELLEXECUTEINFO shexinfo;
01329 
01330             shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
01331             shexinfo.fMask = SEE_MASK_IDLIST;   // SEE_MASK_INVOKEIDLIST is also possible.
01332             shexinfo.hwnd = hparent;
01333             shexinfo.lpVerb = NULL;
01334             shexinfo.lpFile = NULL;
01335             shexinfo.lpParameters = NULL;
01336             shexinfo.lpDirectory = NULL;
01337             shexinfo.nShow = SW_SHOWNORMAL;
01338 
01339             shexinfo.lpIDList = &*shell_path;
01340 
01341              // add PIDL to the recent file list
01342             SHAddToRecentDocs(SHARD_PIDL, shexinfo.lpIDList);
01343 
01344             if (!ShellExecuteEx(&shexinfo))
01345                 display_error(hparent, GetLastError());
01346 
01347              // we may have deleted 'this' - ensure we leave the loop and function
01348             return;
01349         }
01350     }
01351 
01352     if (!new_folders.empty()) {
01353          // Only open one submenu at a time.
01354         if (!CloseOtherSubmenus(id))
01355             return;
01356 
01357         CreateSubmenu(id, new_folders, title);
01358     }
01359 }
01360 
01361 
01362  /// close all windows of the start menu popup
01363 void StartMenu::CloseStartMenu(int id)
01364 {
01365     if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) {    // don't automatically close floating menus
01366         if (!SendParent(PM_STARTENTRY_LAUNCHED, id, (LPARAM)_hwnd))
01367             DestroyWindow(_hwnd);
01368     } else if (_submenu)    // instead close submenus of floating parent menus
01369         CloseSubmenus();
01370 }
01371 
01372 
01373 int GetStartMenuBtnTextWidth(HDC hdc, LPCTSTR title, HWND hwnd)
01374 {
01375     RECT rect = {0, 0, 0, 0};
01376     DrawText(hdc, title, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
01377 
01378     return rect.right-rect.left;
01379 }
01380 
01381 #ifdef _LIGHT_STARTMENU
01382 void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, const SMBtnInfo& btn, bool has_focus, bool pushed, int icon_size)
01383 #else
01384 void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, HICON hIcon,
01385                                 bool hasSubmenu, bool enabled, bool has_focus, bool pushed, int icon_size);
01386 #endif
01387 {
01388     UINT style = DFCS_BUTTONPUSH;
01389 
01390     if (!btn._enabled)
01391         style |= DFCS_INACTIVE;
01392 
01393     POINT iconPos = {rect.left+2, (rect.top+rect.bottom-icon_size)/2};
01394     RECT textRect = {rect.left+icon_size+4, rect.top+2, rect.right-4, rect.bottom-4};
01395 
01396     if (pushed) {
01397         style |= DFCS_PUSHED;
01398         ++iconPos.x;        ++iconPos.y;
01399         ++textRect.left;    ++textRect.top;
01400         ++textRect.right;   ++textRect.bottom;
01401     }
01402 
01403     int bk_color_idx = COLOR_BTNFACE;
01404     int text_color_idx = COLOR_BTNTEXT;
01405 
01406     if (has_focus) {
01407         bk_color_idx = COLOR_HIGHLIGHT;
01408         text_color_idx = COLOR_HIGHLIGHTTEXT;
01409     }
01410 
01411     COLORREF bk_color = GetSysColor(bk_color_idx);
01412     HBRUSH bk_brush = GetSysColorBrush(bk_color_idx);
01413 
01414     if (title)
01415         FillRect(hdc, &rect, bk_brush);
01416 
01417     if (btn._icon_id > ICID_NONE)
01418         g_Globals._icon_cache.get_icon(btn._icon_id).draw(hdc, iconPos.x, iconPos.y, icon_size, icon_size, bk_color, bk_brush/*, icon_size*/);
01419 
01420      // draw submenu arrow at the right
01421     if (btn._hasSubmenu) {
01422         ResIconEx arrowIcon(IDI_ARROW, icon_size, icon_size);
01423         ResIconEx selArrowIcon(IDI_ARROW_SELECTED, icon_size, icon_size);
01424 
01425         DrawIconEx(hdc, rect.right-icon_size, iconPos.y,
01426                     has_focus? selArrowIcon: arrowIcon,
01427                     icon_size, icon_size, 0, bk_brush, DI_NORMAL);
01428     }
01429 
01430     if (title) {
01431         BkMode bk_mode(hdc, TRANSPARENT);
01432 
01433         if (!btn._enabled)  // dis->itemState & (ODS_DISABLED|ODS_GRAYED)
01434             DrawGrayText(hdc, &textRect, title, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
01435         else {
01436             TextColor lcColor(hdc, GetSysColor(text_color_idx));
01437             DrawText(hdc, title, -1, &textRect, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
01438         }
01439     }
01440 }
01441 
01442 
01443 #ifdef _LIGHT_STARTMENU
01444 
01445 void StartMenu::ResizeToButtons()
01446 {
01447     WindowRect rect(_hwnd);
01448 
01449     WindowCanvas canvas(_hwnd);
01450     FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
01451 
01452     const int icon_size = _icon_size;
01453 
01454     int max_width = STARTMENU_WIDTH_MIN;
01455     int height = 0;
01456 
01457     for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
01458         int w = GetStartMenuBtnTextWidth(canvas, it->_title, _hwnd);
01459 
01460         if (w > max_width)
01461             max_width = w;
01462 
01463         if (it->_id == -1)
01464             height += STARTMENU_SEP_HEIGHT(icon_size);
01465         else
01466             height += STARTMENU_LINE_HEIGHT(icon_size);
01467     }
01468 
01469      // calculate new window size
01470     int text_width = max_width + icon_size + 10/*placeholder*/ + 16/*arrow*/;
01471 
01472     RECT rt_hgt = {rect.left, rect.bottom-_border_top-height, rect.left+_border_left+text_width, rect.bottom};
01473     AdjustWindowRectEx(&rt_hgt, GetWindowStyle(_hwnd), FALSE, GetWindowExStyle(_hwnd));
01474 
01475      // ignore movement, only look at the size change
01476     rect.right = rect.left + (rt_hgt.right-rt_hgt.left);
01477     rect.top = rect.bottom - (rt_hgt.bottom-rt_hgt.top);
01478 
01479      // move down if we are too high
01480     if (rect.top < 0) {
01481         int dy = -rect.top;
01482         rect.top += dy;
01483         rect.bottom += dy;
01484     }
01485 
01486      // enable scroll mode for long start menus, which span more than the whole screen height
01487     int cyscreen = GetSystemMetrics(SM_CYSCREEN);
01488     int bottom_max = 0;
01489 
01490     if (rect.bottom > cyscreen) {
01491         _arrow_btns = true;
01492 
01493         _invisible_lines = (rect.bottom-cyscreen+(STARTMENU_LINE_HEIGHT(icon_size)+1))/STARTMENU_LINE_HEIGHT(icon_size)+1;
01494         rect.bottom -= _invisible_lines * STARTMENU_LINE_HEIGHT(icon_size);
01495 
01496         bottom_max = rect.bottom;
01497 
01498         if (_floating_btn)
01499             rect.bottom += 6;   // lower scroll arrow
01500         else {
01501             _border_top += 6;   // upper scroll arrow
01502             rect.bottom += 2*6; // upper+lower scroll arrow
01503         }
01504     }
01505 
01506     MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
01507 
01508     if (bottom_max) {
01509         POINT pt = {0, bottom_max};
01510 
01511         ScreenToClient(_hwnd, &pt);
01512 
01513         _bottom_max = pt.y;
01514     }
01515 }
01516 
01517 #else // _LIGHT_STARTMENU
01518 
01519 LRESULT StartMenuButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
01520 {
01521     switch(nmsg) {
01522       case WM_MOUSEMOVE:
01523          // automatically set the focus to startmenu entries when moving the mouse over them
01524         if (GetFocus()!=_hwnd && !(GetWindowStyle(_hwnd)&WS_DISABLED))
01525             SetFocus(_hwnd);
01526         break;
01527 
01528       case WM_SETFOCUS:
01529         PostParent(PM_STARTENTRY_FOCUSED, _hasSubmenu, (LPARAM)_hwnd);
01530         goto def;
01531 
01532       case WM_CANCELMODE:
01533          // route WM_CANCELMODE to the startmenu window
01534         return SendParent(nmsg, wparam, lparam);
01535 
01536       default: def:
01537         return super::WndProc(nmsg, wparam, lparam);
01538     }
01539 
01540     return 0;
01541 }
01542 
01543 void StartMenuButton::DrawItem(LPDRAWITEMSTRUCT dis)
01544 {
01545     TCHAR title[BUFFER_LEN];
01546 
01547     GetWindowText(_hwnd, title, BUFFER_LEN);
01548 
01549     DrawStartMenuButton(dis->hDC, dis->rcItem, title, _hIcon,
01550                         _hasSubmenu,
01551                         !(dis->itemState & ODS_DISABLED),
01552                         dis->itemState&ODS_FOCUS? true: false,
01553                         dis->itemState&ODS_SELECTED? true: false);
01554 }
01555 
01556 #endif
01557 
01558 
01559 StartMenuRoot::StartMenuRoot(HWND hwnd, const StartMenuRootCreateInfo& info)
01560  :  super(hwnd, info._icon_size),
01561     _hwndStartButton(0)
01562 {
01563 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01564     if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCOMMONGROUPS))
01565 #endif
01566         try {
01567              // insert directory "All Users\Start Menu"
01568             ShellDirectory cmn_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
01569             _dirs.push_back(StartMenuDirectory(cmn_startmenu, (LPCTSTR)SpecialFolderFSPath(CSIDL_COMMON_PROGRAMS, _hwnd)));
01570         } catch(COMException&) {
01571             // ignore exception and don't show additional shortcuts
01572         }
01573 
01574     try {
01575          // insert directory "<user name>\Start Menu"
01576         ShellDirectory usr_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
01577         _dirs.push_back(StartMenuDirectory(usr_startmenu, (LPCTSTR)SpecialFolderFSPath(CSIDL_PROGRAMS, _hwnd)));
01578     } catch(COMException&) {
01579         // ignore exception and don't show additional shortcuts
01580     }
01581 
01582     ReadLogoSize();
01583 }
01584 
01585 void StartMenuRoot::ReadLogoSize()
01586 {
01587      // read size of logo bitmap
01588     BITMAP bmp_hdr;
01589     GetObject(ResBitmap(GetLogoResId()), sizeof(BITMAP), &bmp_hdr);
01590     _logo_size.cx = bmp_hdr.bmWidth;
01591     _logo_size.cy = bmp_hdr.bmHeight;
01592 
01593      // cache logo width
01594     _border_left = _logo_size.cx + 1;
01595 }
01596 
01597 
01598 static void CalculateStartPos(HWND hwndOwner, RECT& rect, int icon_size)
01599 {
01600     WindowRect pos(hwndOwner);
01601 
01602     rect.left = pos.left;
01603     rect.top = pos.top-STARTMENU_LINE_HEIGHT(icon_size)-4;
01604     rect.right = pos.left+STARTMENU_WIDTH_MIN;
01605     rect.bottom = pos.top;
01606 
01607 #ifndef _LIGHT_STARTMENU
01608     rect.top += STARTMENU_LINE_HEIGHT(icon_size);
01609 #endif
01610 
01611     AdjustWindowRectEx(&rect, WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE, FALSE, 0);
01612 }
01613 
01614 HWND StartMenuRoot::Create(HWND hwndOwner, int icon_size)
01615 {
01616     RECT rect;
01617 
01618     CalculateStartPos(hwndOwner, rect, icon_size);
01619 
01620     StartMenuRootCreateInfo create_info;
01621 
01622     create_info._icon_size = icon_size;
01623 
01624     return Window::Create(WINDOW_CREATOR_INFO(StartMenuRoot,StartMenuRootCreateInfo), &create_info, 0, GetWndClasss(), TITLE_STARTMENU,
01625                             WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN,
01626                             rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndOwner);
01627 }
01628 
01629 
01630 void StartMenuRoot::TrackStartmenu()
01631 {
01632     MSG msg;
01633     HWND hwnd = _hwnd;
01634 
01635 #ifdef _LIGHT_STARTMENU
01636     _selected_id = -1;
01637 #endif
01638 
01639 #ifdef _LIGHT_STARTMENU
01640      // recalculate start menu root position
01641     RECT rect;
01642 
01643     CalculateStartPos(_hwndStartButton, rect, _icon_size);
01644 
01645     SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0);
01646 
01647     ResizeToButtons();
01648 #endif
01649 
01650      // show previously hidden start menu
01651     ShowWindow(hwnd, SW_SHOW);
01652     SetForegroundWindow(hwnd);
01653 
01654     while(IsWindow(hwnd) && IsWindowVisible(hwnd)) {
01655         if (!GetMessage(&msg, 0, 0, 0)) {
01656             PostQuitMessage(msg.wParam);
01657             break;
01658         }
01659 
01660          // Check for a mouse click on any window, that is not part of the start menu
01661         if (msg.message==WM_LBUTTONDOWN || msg.message==WM_MBUTTONDOWN || msg.message==WM_RBUTTONDOWN) {
01662             StartMenu* menu_wnd = NULL;
01663 
01664             for(HWND hwnd=msg.hwnd; hwnd; hwnd=GetParent(hwnd)) {
01665                 menu_wnd = WINDOW_DYNAMIC_CAST(StartMenu, hwnd);
01666 
01667                 if (menu_wnd)
01668                     break;
01669             }
01670 
01671             if (!menu_wnd) {
01672                 CloseStartMenu();
01673                 break;
01674             }
01675         }
01676 
01677         try {
01678             if (pretranslate_msg(&msg))
01679                 continue;
01680 
01681             if (dispatch_dialog_msg(&msg))
01682                 continue;
01683 
01684             TranslateMessage(&msg);
01685 
01686             try {
01687                 DispatchMessage(&msg);
01688             } catch(COMException& e) {
01689                 HandleException(e, _hwnd);
01690             }
01691         } catch(COMException& e) {
01692             HandleException(e, _hwnd);
01693         }
01694     }
01695 }
01696 
01697 
01698 LRESULT StartMenuRoot::Init(LPCREATESTRUCT pcs)
01699 {
01700      // add buttons for entries in _entries
01701     if (super::Init(pcs))
01702         return 1;
01703 
01704     AddSeparator();
01705 
01706 
01707 #ifdef __MINGW32__
01708     HKEY hkey, hkeyAdv;
01709     DWORD value, len;
01710 
01711     if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), &hkey))
01712         hkey = 0;
01713 
01714     if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hkeyAdv))
01715         hkeyAdv = 0;
01716 
01717 #define IS_VALUE_ZERO(hk, name) \
01718     (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || !value))
01719 
01720 #define IS_VALUE_NOT_ZERO(hk, name) \
01721     (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || value>0))
01722 #endif
01723 
01724 
01725      // insert hard coded start entries
01726     AddButton(ResString(IDS_PROGRAMS),      ICID_APPS, true, IDC_PROGRAMS);
01727 
01728     AddButton(ResString(IDS_DOCUMENTS),     ICID_DOCUMENTS, true, IDC_DOCUMENTS);
01729 
01730 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01731     if (!g_Globals._SHRestricted || !SHRestricted(REST_NORECENTDOCSMENU))
01732 #else
01733     if (IS_VALUE_ZERO(hkey, _T("NoRecentDocsMenu")))
01734 #endif
01735         AddButton(ResString(IDS_RECENT),    ICID_RECENT, true, IDC_RECENT);
01736 
01737     AddButton(ResString(IDS_FAVORITES),     ICID_FAVORITES, true, IDC_FAVORITES);
01738 
01739     AddButton(ResString(IDS_SETTINGS),      ICID_CONFIG, true, IDC_SETTINGS);
01740 
01741     AddButton(ResString(IDS_BROWSE),        ICID_FOLDER, true, IDC_BROWSE);
01742 
01743 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01744     if (!g_Globals._SHRestricted || !SHRestricted(REST_NOFIND))
01745 #else
01746     if (IS_VALUE_ZERO(hkey, _T("NoFind")))
01747 #endif
01748         AddButton(ResString(IDS_SEARCH),    ICID_SEARCH, true, IDC_SEARCH);
01749 
01750     AddButton(ResString(IDS_START_HELP),    ICID_INFO, false, IDC_START_HELP);
01751 
01752 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01753     if (!g_Globals._SHRestricted || !SHRestricted(REST_NORUN))
01754 #else
01755     if (IS_VALUE_ZERO(hkey, _T("NoRun")))
01756 #endif
01757         AddButton(ResString(IDS_LAUNCH),    ICID_ACTION, false, IDC_LAUNCH);
01758 
01759 
01760     AddSeparator();
01761 
01762 
01763 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01764     if (!g_Globals._SHRestricted || SHRestricted(REST_STARTMENULOGOFF) != 1)
01765 #else
01766     if (IS_VALUE_NOT_ZERO(hkeyAdv, _T("StartMenuLogoff")))
01767 #endif
01768         AddButton(ResString(IDS_LOGOFF),    ICID_LOGOFF, false, IDC_LOGOFF);
01769 
01770 #ifdef __REACTOS__
01771         AddButton(ResString(IDS_RESTART), ICID_RESTART, false, IDC_RESTART);
01772 #endif
01773 
01774 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01775     if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCLOSE))
01776 #else
01777     if (IS_VALUE_ZERO(hkey, _T("NoClose")))
01778 #endif
01779         AddButton(ResString(IDS_SHUTDOWN),  ICID_SHUTDOWN, false, IDC_SHUTDOWN);
01780 
01781 #ifndef __REACTOS__
01782     AddButton(ResString(IDS_TERMINATE), ICID_LOGOFF, false, IDC_TERMINATE);
01783 #endif
01784 
01785 
01786 #ifdef __MINGW32__
01787     RegCloseKey(hkeyAdv);
01788     RegCloseKey(hkey);
01789 #endif
01790 
01791 
01792 #ifdef _LIGHT_STARTMENU
01793      // set the window size to fit all buttons
01794     ResizeToButtons();
01795 #endif
01796 
01797     return 0;
01798 }
01799 
01800 
01801 void StartMenuRoot::AddEntries()
01802 {
01803     super::AddEntries();
01804 
01805     AddButton(ResString(IDS_EXPLORE),   ICID_EXPLORER, false, IDC_EXPLORE);
01806 }
01807 
01808 
01809 LRESULT StartMenuRoot::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
01810 {
01811     switch(nmsg) {
01812       case WM_PAINT: {
01813         PaintCanvas canvas(_hwnd);
01814         Paint(canvas);
01815         break;}
01816 
01817       case WM_DISPLAYCHANGE:
01818          // re-evaluate logo size using the correct color depth
01819         ReadLogoSize();
01820         break;
01821 
01822       default:
01823         return super::WndProc(nmsg, wparam, lparam);
01824     }
01825 
01826     return 0;
01827 }
01828 
01829 void StartMenuRoot::Paint(PaintCanvas& canvas)
01830 {
01831     MemCanvas mem_dc;
01832     ResBitmap bmp(GetLogoResId());
01833     BitmapSelection sel(mem_dc, bmp);
01834 
01835     ClientRect clnt(_hwnd);
01836     int h = min(_logo_size.cy, clnt.bottom);
01837 
01838     RECT rect = {0, 0, _logo_size.cx, clnt.bottom-h};
01839     HBRUSH hbr = CreateSolidBrush(GetPixel(mem_dc, 0, 0));
01840     FillRect(canvas, &rect, hbr);
01841     DeleteObject(hbr);
01842 
01843     PatBlt(canvas, _logo_size.cx, 0, 1, clnt.bottom, WHITENESS);
01844 
01845     BitBlt(canvas, 0, clnt.bottom-h, _logo_size.cx, h, mem_dc, 0, ( h<_logo_size.cy ? _logo_size.cy-h : 0) , SRCCOPY);
01846 
01847     super::Paint(canvas);
01848 }
01849 
01850 UINT StartMenuRoot::GetLogoResId()
01851 {
01852     WindowCanvas dc(_hwnd);
01853 
01854     int clr_bits = GetDeviceCaps(dc, BITSPIXEL);
01855 
01856     if (clr_bits > 8)
01857         return IDB_LOGOV;
01858     else if (clr_bits > 4)
01859         return IDB_LOGOV256;
01860     else
01861         return IDB_LOGOV16;
01862 }
01863 
01864 
01865 void StartMenuRoot::CloseStartMenu(int id)
01866 {
01867     if (_submenu)
01868         CloseSubmenus();
01869 
01870     ShowWindow(_hwnd, SW_HIDE);
01871 }
01872 
01873 void StartMenuRoot::ProcessKey(int vk)
01874 {
01875     switch(vk) {
01876       case VK_LEFT:
01877         if (_submenu)
01878             CloseOtherSubmenus();
01879         // don't close start menu root
01880         break;
01881 
01882       default:
01883         super::ProcessKey(vk);
01884     }
01885 }
01886 
01887 
01888 int StartMenuHandler::Command(int id, int code)
01889 {
01890     switch(id) {
01891 
01892     // start menu root
01893 
01894       case IDC_PROGRAMS:
01895         CreateSubmenu(id, CSIDL_COMMON_PROGRAMS, CSIDL_PROGRAMS, ResString(IDS_PROGRAMS));
01896         break;
01897 
01898       case IDC_EXPLORE:
01899         CloseStartMenu(id);
01900         explorer_show_frame(SW_SHOWNORMAL);
01901         break;
01902 
01903       case IDC_LAUNCH:
01904         CloseStartMenu(id);
01905         ShowLaunchDialog(g_Globals._hwndDesktopBar);
01906         break;
01907 
01908       case IDC_DOCUMENTS:
01909         CreateSubmenu(id, CSIDL_PERSONAL, ResString(IDS_DOCUMENTS));
01910         break;
01911 
01912       case IDC_RECENT:
01913         CreateSubmenu(id, CSIDL_RECENT, ResString(IDS_RECENT), STARTMENU_CREATOR(RecentStartMenu));
01914         break;
01915 
01916       case IDC_FAVORITES:
01917 #ifndef _SHELL32_FAVORITES
01918         CreateSubmenu(id, ResString(IDS_FAVORITES), STARTMENU_CREATOR(FavoritesMenu), &static_cast<BookmarkList&>(g_Globals._favorites));
01919 #else
01920         CreateSubmenu(id, CSIDL_COMMON_FAVORITES, CSIDL_FAVORITES, ResString(IDS_FAVORITES));
01921 #endif
01922         break;
01923 
01924       case IDC_BROWSE:
01925         CreateSubmenu(id, ResString(IDS_BROWSE), STARTMENU_CREATOR(BrowseMenu));
01926         break;
01927 
01928       case IDC_SETTINGS:
01929         CreateSubmenu(id, ResString(IDS_SETTINGS), STARTMENU_CREATOR(SettingsMenu));
01930         break;
01931 
01932       case IDC_SEARCH:
01933         CreateSubmenu(id, ResString(IDS_SEARCH), STARTMENU_CREATOR(SearchMenu));
01934         break;
01935 
01936       case IDC_START_HELP:
01937         CloseStartMenu(id);
01938         MessageBox(g_Globals._hwndDesktopBar, TEXT("Help not yet implemented"), ResString(IDS_TITLE), MB_OK);
01939         break;
01940 
01941       case IDC_LOGOFF:
01942         CloseStartMenu(id);
01943         ShowLogoffDialog(g_Globals._hwndDesktopBar);
01944         break;
01945 
01946 #ifndef __REACTOS__
01947       case IDC_TERMINATE:
01948         DestroyWindow(GetParent(_hwnd));
01949         break;
01950 #endif
01951 
01952       case IDC_SHUTDOWN:
01953         CloseStartMenu(id);
01954         ShowExitWindowsDialog(g_Globals._hwndDesktop);
01955         break;
01956 
01957       case IDC_RESTART:
01958         CloseStartMenu(id);
01959         ShowRestartDialog(g_Globals._hwndDesktop, EWX_REBOOT);
01960         /* An alternative way to do restart without shell32 help */
01961         //launch_file(_hwnd, TEXT("shutdown.exe"), SW_HIDE, TEXT("-r"));
01962         break;
01963 
01964     // settings menu
01965 
01966       case ID_DESKTOPBAR_SETTINGS:
01967         CloseStartMenu(id);
01968         ExplorerPropertySheet(g_Globals._hwndDesktopBar);
01969         break;
01970 
01971       case IDC_CONTROL_PANEL: {
01972         CloseStartMenu(id);
01973 #ifndef ROSSHELL
01974 #ifndef _NO_MDI
01975         XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
01976         bool mdi = XMLBool(explorer_options, "mdi", true);
01977 
01978         if (mdi)
01979             MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
01980         else
01981 #endif
01982             SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
01983 #else
01984         launch_file(_hwnd, SHELLPATH_CONTROL_PANEL);
01985 #endif
01986         break;}
01987 
01988       case IDC_SETTINGS_MENU:
01989         CreateSubmenu(id, CSIDL_CONTROLS, ResString(IDS_SETTINGS_MENU));
01990         break;
01991 
01992       case IDC_PRINTERS: {
01993         CloseStartMenu(id);
01994 
01995 #ifndef ROSSHELL
01996 #ifndef _NO_MDI
01997         XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
01998         bool mdi = XMLBool(explorer_options, "mdi", true);
01999 
02000         if (mdi)
02001             MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0);
02002         else
02003 #endif
02004             SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0);
02005 #else
02006         launch_file(_hwnd, SHELLPATH_PRINTERS);
02007 #endif
02008         break;}
02009 
02010 #if 0   ///@todo use printer start menu folder per default and allow opening "printers" cabinet window using the context menu
02011       case IDC_PRINTERS_MENU:
02012         CreateSubmenu(id, CSIDL_PRINTERS, CSIDL_PRINTHOOD, ResString(IDS_PRINTERS));
02013 /*      StartMenuFolders new_folders;
02014 
02015         try {
02016             new_folders.push_back(ShellPath(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}")));
02017         } catch(COMException&) {
02018         }
02019 
02020         CreateSubmenu(id, new_folders, ResString(IDS_PRINTERS));*/
02021         break;
02022 #endif
02023 
02024       case IDC_ADMIN:
02025 #ifndef ROSSHELL
02026         CreateSubmenu(id, CSIDL_COMMON_ADMINTOOLS, CSIDL_ADMINTOOLS, ResString(IDS_ADMIN));
02027         //CloseStartMenu(id);
02028         //MainFrame::Create(SpecialFolderPath(CSIDL_COMMON_ADMINTOOLS, _hwnd), OWM_PIDL);
02029 #else
02030         launch_file(_hwnd, SpecialFolderFSPath(CSIDL_COMMON_ADMINTOOLS, _hwnd));
02031 #endif
02032         break;
02033 
02034       case IDC_CONNECTIONS:
02035 #ifndef ROSSHELL
02036 #ifdef __REACTOS__  // to be removed when RAS will be implemented
02037         MessageBox(0, TEXT("RAS folder not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02038 #else
02039         CreateSubmenu(id, CSIDL_CONNECTIONS, ResString(IDS_CONNECTIONS));
02040         //CloseStartMenu(id);
02041         //MainFrame::Create(SpecialFolderPath(CSIDL_CONNECTIONS, _hwnd), OWM_PIDL);
02042 #endif
02043 #else
02044         launch_file(_hwnd, SHELLPATH_NET_CONNECTIONS);
02045 #endif
02046         break;
02047 
02048 
02049     // browse menu
02050 
02051       case IDC_NETWORK:
02052 #ifdef __REACTOS__  ///@todo to be removed when network browsing will be implemented in shell namespace
02053         MessageBox(0, TEXT("network not yet implemented"), ResString(IDS_TITLE), MB_OK);
02054 #else
02055         CreateSubmenu(id, CSIDL_NETWORK, ResString(IDS_NETWORK));
02056 #endif
02057         break;
02058 
02059       case IDC_DRIVES:
02060         ///@todo exclude removable drives
02061         CreateSubmenu(id, CSIDL_DRIVES, ResString(IDS_DRIVES));
02062         break;
02063 
02064 
02065     // search menu
02066 
02067       case IDC_SEARCH_PROGRAM:
02068         CloseStartMenu(id);
02069         Dialog::DoModal(IDD_SEARCH_PROGRAM, WINDOW_CREATOR(FindProgramDlg));
02070         break;
02071 
02072       case IDC_SEARCH_FILES:
02073         CloseStartMenu(id);
02074         ShowSearchDialog();
02075         break;
02076 
02077       case IDC_SEARCH_COMPUTER:
02078         CloseStartMenu(id);
02079         ShowSearchComputer();
02080         break;
02081 
02082 
02083       default:
02084         return super::Command(id, code);
02085     }
02086 
02087     return 0;
02088 }
02089 
02090 
02091 void StartMenuHandler::ShowSearchDialog()
02092 {
02093 #ifndef __REACTOS__ ///@todo to be removed when SHFindFiles() will be implemented in shell32.dll
02094     static DynamicFct<SHFINDFILES> SHFindFiles(TEXT("SHELL32"), 90);
02095 
02096     if (SHFindFiles)
02097         (*SHFindFiles)(NULL, NULL);
02098     else
02099 #endif
02100         MessageBox(0, TEXT("SHFindFiles() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02101 }
02102 
02103 void StartMenuHandler::ShowSearchComputer()
02104 {
02105 #ifndef __REACTOS__ ///@todo to be removed when SHFindComputer() will be implemented in shell32.dll
02106     static DynamicFct<SHFINDCOMPUTER> SHFindComputer(TEXT("SHELL32"), 91);
02107 
02108     if (SHFindComputer)
02109         (*SHFindComputer)(NULL, NULL);
02110     else
02111 #endif
02112         MessageBox(0, TEXT("SHFindComputer() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02113 }
02114 
02115 void StartMenuHandler::ShowLaunchDialog(HWND hwndOwner)
02116 {
02117     static DynamicFct<RUNFILEDLG> RunFileDlg(TEXT("SHELL32"), 61);
02118 
02119      // Show "Run..." dialog
02120     if (RunFileDlg) {
02121         (*RunFileDlg)(hwndOwner, 0, NULL, NULL, NULL, RFF_CALCDIRECTORY);
02122     }
02123 }
02124 
02125 void StartMenuHandler::ShowLogoffDialog(HWND hwndOwner)
02126 {
02127     static DynamicFct<LOGOFFWINDOWSDIALOG> LogoffWindowsDialog(TEXT("SHELL32"), 54);
02128 //  static DynamicFct<RESTARTWINDOWSDLG> RestartDialog(TEXT("SHELL32"), 59);
02129 
02130     if (LogoffWindowsDialog)
02131         (*LogoffWindowsDialog)(0);
02132 /* The RestartDialog function prompts about some system setting change. This is not what we want to display here.
02133     else if (RestartDialog)
02134         return (*RestartDialog)(hwndOwner, (LPWSTR)L"You selected <Log Off>.\n\n", EWX_LOGOFF) == 1;    ///@todo ANSI string conversion if needed
02135 */
02136     else
02137         MessageBox(hwndOwner, TEXT("LogoffWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02138 }
02139 
02140 void ShowExitWindowsDialog(HWND hwndOwner)
02141 {
02142     static DynamicFct<EXITWINDOWSDLG> ExitWindowsDialog(TEXT("SHELL32"), 60);
02143 
02144     if (ExitWindowsDialog)
02145         (*ExitWindowsDialog)(hwndOwner);
02146     else
02147         MessageBox(hwndOwner, TEXT("ExitWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02148 }
02149 
02150 void StartMenuHandler::ShowRestartDialog(HWND hwndOwner, UINT flags)
02151 {
02152     static DynamicFct<RESTARTWINDOWSDLG> RestartDlg(TEXT("SHELL32"), 59);
02153 
02154     if (RestartDlg)
02155         (*RestartDlg)(hwndOwner, (LPWSTR)L"You selected restart.\n\n", flags);
02156     else
02157         MessageBox(hwndOwner, TEXT("RestartDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02158 }
02159 
02160 void SettingsMenu::AddEntries()
02161 {
02162     super::AddEntries();
02163 
02164 #if defined(ROSSHELL) || defined(__REACTOS__)   // __REACTOS__ to be removed when printer/network will be implemented
02165 //TODO  AddButton(ResString(IDS_PRINTERS),          ICID_PRINTER, false, IDC_PRINTERS_MENU);
02166     AddButton(ResString(IDS_CONNECTIONS),       ICID_NETWORK, false, IDC_CONNECTIONS);
02167 #else
02168 //TODO  AddButton(ResString(IDS_PRINTERS),          ICID_PRINTER, true, IDC_PRINTERS_MENU);
02169     AddButton(ResString(IDS_CONNECTIONS),       ICID_NETCONNS, false, IDC_CONNECTIONS);
02170 #endif
02171     AddButton(ResString(IDS_ADMIN),             ICID_ADMIN, true, IDC_ADMIN);
02172 
02173 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
02174     if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
02175 #endif
02176     AddButton(ResString(IDS_SETTINGS_MENU), ICID_CONFIG, true, IDC_SETTINGS_MENU);
02177 
02178     AddButton(ResString(IDS_DESKTOPBAR_SETTINGS), ICID_DESKSETTING, false, ID_DESKTOPBAR_SETTINGS);
02179 
02180     AddButton(ResString(IDS_PRINTERS),          ICID_PRINTER, false, IDC_PRINTERS);
02181 
02182 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
02183     if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
02184 #endif
02185         AddButton(ResString(IDS_CONTROL_PANEL), ICID_CONTROLPAN, false, IDC_CONTROL_PANEL);
02186 }
02187 
02188 void BrowseMenu::AddEntries()
02189 {
02190     super::AddEntries();
02191 
02192 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
02193     if (!g_Globals._SHRestricted || !SHRestricted(REST_NONETHOOD))  // or REST_NOENTIRENETWORK ?
02194 #endif
02195 #if defined(ROSSHELL) || defined(__REACTOS__)   // __REACTOS__ to be removed when printer/network will be implemented
02196         AddButton(ResString(IDS_NETWORK),       ICID_NETWORK, false, IDC_NETWORK);
02197 #else
02198         AddButton(ResString(IDS_NETWORK),       ICID_NETWORK, true, IDC_NETWORK);
02199 #endif
02200 
02201     AddButton(ResString(IDS_DRIVES),            ICID_FOLDER, true, IDC_DRIVES);
02202 }
02203 
02204 void SearchMenu::AddEntries()
02205 {
02206     super::AddEntries();
02207 
02208     AddButton(ResString(IDS_SEARCH_FILES),      ICID_SEARCH_DOC, false, IDC_SEARCH_FILES);
02209 
02210 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
02211     if (!g_Globals._SHRestricted || !SHRestricted(REST_HASFINDCOMPUTERS))
02212 #endif
02213         AddButton(ResString(IDS_SEARCH_COMPUTER),ICID_COMPUTER, false, IDC_SEARCH_COMPUTER);
02214 
02215     AddButton(ResString(IDS_SEARCH_PRG),        ICID_APPS, false, IDC_SEARCH_PROGRAM);
02216 }
02217 
02218 
02219 void RecentStartMenu::AddEntries()
02220 {
02221     for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
02222         StartMenuDirectory& smd = *it;
02223         ShellDirectory& dir = smd._dir;
02224 
02225         if (!dir._scanned) {
02226             WaitCursor wait;
02227 
02228 #ifdef _LAZY_ICONEXTRACT
02229             dir.smart_scan(SORT_NAME, SCAN_DONT_EXTRACT_ICONS);
02230 #else
02231             dir.smart_scan(SORT_NAME);
02232 #endif
02233         }
02234 
02235         dir.sort_directory(SORT_DATE);
02236         AddShellEntries(dir, RECENT_DOCS_COUNT, smd._ignore);   ///@todo read max. count of entries from registry
02237     }
02238 }
02239 
02240 
02241 #ifndef _SHELL32_FAVORITES
02242 
02243 void FavoritesMenu::AddEntries()
02244 {
02245     super::AddEntries();
02246 
02247     String lwr_filter = _create_info._filter;
02248     lwr_filter.toLower();
02249 
02250     for(BookmarkList::iterator it=_bookmarks.begin(); it!=_bookmarks.end(); ++it) {
02251         BookmarkNode& node = *it;
02252 
02253         int id = ++_next_id;
02254 
02255         _entries[id] = node;
02256 
02257         if (node._type == BookmarkNode::BMNT_FOLDER) {
02258             BookmarkFolder& folder = *node._pfolder;
02259 
02260             AddButton(folder._name, ICID_FOLDER, true, id);
02261         } else if (node._type == BookmarkNode::BMNT_BOOKMARK) {
02262             Bookmark& bookmark = *node._pbookmark;
02263 
02264             ICON_ID icon = ICID_NONE;
02265 
02266             if (!bookmark._icon_path.empty())
02267                 icon = g_Globals._icon_cache.extract(bookmark._icon_path, bookmark._icon_idx);
02268 
02269              // filter non-directory entries
02270             if (!lwr_filter.empty()) {
02271                 String lwr_name = bookmark._name;
02272                 String lwr_desc = bookmark._description;
02273                 String lwr_url = bookmark._url;
02274 
02275                 lwr_name.toLower();
02276                 lwr_desc.toLower();
02277                 lwr_url.toLower();
02278 
02279                 if (!_tcsstr(lwr_name,lwr_filter) && !_tcsstr(lwr_desc,lwr_filter) && !_tcsstr(lwr_url,lwr_filter))
02280                     continue;
02281             }
02282 
02283             AddButton(bookmark._name, icon!=ICID_NONE?icon:ICID_BOOKMARK, false, id);
02284         }
02285     }
02286 }
02287 
02288 int FavoritesMenu::Command(int id, int code)
02289 {
02290     BookmarkMap::iterator found = _entries.find(id);
02291 
02292     if (found != _entries.end()) {
02293         BookmarkNode& node = found->second;
02294 
02295         if (node._type == BookmarkNode::BMNT_FOLDER) {
02296             BookmarkFolder& folder = *node._pfolder;
02297 
02298             if (CloseOtherSubmenus(id))
02299                 CreateSubmenu(id, folder._name, STARTMENU_CREATOR(FavoritesMenu), &static_cast<BookmarkList&>(folder._bookmarks));
02300         } else if (node._type == BookmarkNode::BMNT_BOOKMARK) {
02301             Bookmark& bookmark = *node._pbookmark;
02302 
02303             String url = bookmark._url;
02304             HWND hparent = GetParent(_hwnd);
02305 
02306             CloseStartMenu(id);
02307 
02308             launch_file(hparent, url, SW_SHOWNORMAL);
02309         }
02310 
02311         return 0;
02312     }
02313 
02314     return super::Command(id, code);
02315 }
02316 
02317 #endif

ROS Explorer Source Code Documentation
generated on 02.01.2013 by doxygen

ROS Explorer Homepage