ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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 
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: { 
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
00501                     break;  
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))   
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 
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 
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 
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 
01612 HWND StartMenuRoot::Create(HWND hwndOwner, int icon_size)
01613 {
01614     RECT rect;
01615 
01616     CalculateStartPos(hwndOwner, rect, icon_size);
01617 
01618     StartMenuRootCreateInfo create_info;
01619 
01620     create_info._icon_size = icon_size;
01621 
01622     return Window::Create(WINDOW_CREATOR_INFO(StartMenuRoot,StartMenuRootCreateInfo), &create_info, 0, GetWndClasss(), TITLE_STARTMENU,
01623                             WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN,
01624                             rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndOwner);
01625 }
01626 
01627 
01628 void StartMenuRoot::TrackStartmenu()
01629 {
01630     MSG msg;
01631     HWND hwnd = _hwnd;
01632 
01633 #ifdef _LIGHT_STARTMENU
01634     _selected_id = -1;
01635 #endif
01636 
01637 #ifdef _LIGHT_STARTMENU
01638      // recalculate start menu root position
01639     RECT rect;
01640 
01641     CalculateStartPos(_hwndStartButton, rect, _icon_size);
01642 
01643     SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0);
01644 
01645     ResizeToButtons();
01646 #endif
01647 
01648      // show previously hidden start menu
01649     ShowWindow(hwnd, SW_SHOW);
01650     SetForegroundWindow(hwnd);
01651 
01652     while(IsWindow(hwnd) && IsWindowVisible(hwnd)) {
01653         if (!GetMessage(&msg, 0, 0, 0)) {
01654             PostQuitMessage(msg.wParam);
01655             break;
01656         }
01657 
01658          // Check for a mouse click on any window, that is not part of the start menu
01659         if (msg.message==WM_LBUTTONDOWN || msg.message==WM_MBUTTONDOWN || msg.message==WM_RBUTTONDOWN) {
01660             StartMenu* menu_wnd = NULL;
01661 
01662             for(HWND hwnd=msg.hwnd; hwnd; hwnd=GetParent(hwnd)) {
01663                 menu_wnd = WINDOW_DYNAMIC_CAST(StartMenu, hwnd);
01664 
01665                 if (menu_wnd)
01666                     break;
01667             }
01668 
01669             if (!menu_wnd) {
01670                 CloseStartMenu();
01671                 break;
01672             }
01673         }
01674 
01675         try {
01676             if (pretranslate_msg(&msg))
01677                 continue;
01678 
01679             if (dispatch_dialog_msg(&msg))
01680                 continue;
01681 
01682             TranslateMessage(&msg);
01683 
01684             try {
01685                 DispatchMessage(&msg);
01686             } catch(COMException& e) {
01687                 HandleException(e, _hwnd);
01688             }
01689         } catch(COMException& e) {
01690             HandleException(e, _hwnd);
01691         }
01692     }
01693 }
01694 
01695 int StartMenuRoot::Command(int id, int code)
01696 {
01697     return super::Command(id, code);
01698 }
01699 
01700 LRESULT StartMenuRoot::Init(LPCREATESTRUCT pcs)
01701 {
01702      // add buttons for entries in _entries
01703     if (super::Init(pcs))
01704         return 1;
01705 
01706     AddSeparator();
01707 
01708 
01709 #ifdef __MINGW32__
01710     HKEY hkey, hkeyAdv;
01711     DWORD value, len;
01712 
01713     if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), &hkey))
01714         hkey = 0;
01715 
01716     if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hkeyAdv))
01717         hkeyAdv = 0;
01718 
01719 #define IS_VALUE_ZERO(hk, name) \
01720     (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || !value))
01721 
01722 #define IS_VALUE_NOT_ZERO(hk, name) \
01723     (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || value>0))
01724 #endif
01725 
01726 
01727      // insert hard coded start entries
01728     AddButton(ResString(IDS_PROGRAMS),      ICID_APPS, true, IDC_PROGRAMS);
01729 
01730     AddButton(ResString(IDS_DOCUMENTS),     ICID_DOCUMENTS, true, IDC_DOCUMENTS);
01731 
01732 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01733     if (!g_Globals._SHRestricted || !SHRestricted(REST_NORECENTDOCSMENU))
01734 #else
01735     if (IS_VALUE_ZERO(hkey, _T("NoRecentDocsMenu")))
01736 #endif
01737         AddButton(ResString(IDS_RECENT),    ICID_RECENT, true, IDC_RECENT);
01738 
01739     AddButton(ResString(IDS_FAVORITES),     ICID_FAVORITES, true, IDC_FAVORITES);
01740 
01741     AddButton(ResString(IDS_SETTINGS),      ICID_CONFIG, true, IDC_SETTINGS);
01742 
01743     AddButton(ResString(IDS_BROWSE),        ICID_FOLDER, true, IDC_BROWSE);
01744 
01745 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01746     if (!g_Globals._SHRestricted || !SHRestricted(REST_NOFIND))
01747 #else
01748     if (IS_VALUE_ZERO(hkey, _T("NoFind")))
01749 #endif
01750         AddButton(ResString(IDS_SEARCH),    ICID_SEARCH, true, IDC_SEARCH);
01751 
01752     AddButton(ResString(IDS_START_HELP),    ICID_INFO, false, IDC_START_HELP);
01753 
01754 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01755     if (!g_Globals._SHRestricted || !SHRestricted(REST_NORUN))
01756 #else
01757     if (IS_VALUE_ZERO(hkey, _T("NoRun")))
01758 #endif
01759         AddButton(ResString(IDS_LAUNCH),    ICID_ACTION, false, IDC_LAUNCH);
01760 
01761 
01762     AddSeparator();
01763 
01764 
01765 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01766     if (!g_Globals._SHRestricted || SHRestricted(REST_STARTMENULOGOFF) != 1)
01767 #else
01768     if (IS_VALUE_NOT_ZERO(hkeyAdv, _T("StartMenuLogoff")))
01769 #endif
01770         AddButton(ResString(IDS_LOGOFF),    ICID_LOGOFF, false, IDC_LOGOFF);
01771 
01772 #ifdef __REACTOS__
01773         AddButton(ResString(IDS_RESTART), ICID_RESTART, false, IDC_RESTART);
01774 #endif
01775 
01776 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
01777     if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCLOSE))
01778 #else
01779     if (IS_VALUE_ZERO(hkey, _T("NoClose")))
01780 #endif
01781         AddButton(ResString(IDS_SHUTDOWN),  ICID_SHUTDOWN, false, IDC_SHUTDOWN);
01782 
01783 #ifndef __REACTOS__
01784     AddButton(ResString(IDS_TERMINATE), ICID_LOGOFF, false, IDC_TERMINATE);
01785 #endif
01786 
01787 
01788 #ifdef __MINGW32__
01789     RegCloseKey(hkeyAdv);
01790     RegCloseKey(hkey);
01791 #endif
01792 
01793 
01794 #ifdef _LIGHT_STARTMENU
01795      // set the window size to fit all buttons
01796     ResizeToButtons();
01797 #endif
01798 
01799     return 0;
01800 }
01801 
01802 
01803 void StartMenuRoot::AddEntries()
01804 {
01805     super::AddEntries();
01806 
01807     AddButton(ResString(IDS_EXPLORE),   ICID_EXPLORER, false, IDC_EXPLORE);
01808 }
01809 
01810 
01811 LRESULT StartMenuRoot::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
01812 {
01813     switch(nmsg) {
01814       case WM_PAINT: {
01815         PaintCanvas canvas(_hwnd);
01816         Paint(canvas);
01817         break;}
01818 
01819       case WM_DISPLAYCHANGE:
01820          // re-evaluate logo size using the correct color depth
01821         ReadLogoSize();
01822         break;
01823 
01824       default:
01825         return super::WndProc(nmsg, wparam, lparam);
01826     }
01827 
01828     return 0;
01829 }
01830 
01831 void StartMenuRoot::Paint(PaintCanvas& canvas)
01832 {
01833     MemCanvas mem_dc;
01834     ResBitmap bmp(GetLogoResId());
01835     BitmapSelection sel(mem_dc, bmp);
01836 
01837     ClientRect clnt(_hwnd);
01838     int h = min(_logo_size.cy, clnt.bottom);
01839 
01840     RECT rect = {0, 0, _logo_size.cx, clnt.bottom-h};
01841     HBRUSH hbr = CreateSolidBrush(GetPixel(mem_dc, 0, 0));
01842     FillRect(canvas, &rect, hbr);
01843     DeleteObject(hbr);
01844 
01845     PatBlt(canvas, _logo_size.cx, 0, 1, clnt.bottom, WHITENESS);
01846 
01847     BitBlt(canvas, 0, clnt.bottom-h, _logo_size.cx, h, mem_dc, 0, ( h<_logo_size.cy ? _logo_size.cy-h : 0) , SRCCOPY);
01848 
01849     super::Paint(canvas);
01850 }
01851 
01852 UINT StartMenuRoot::GetLogoResId()
01853 {
01854     WindowCanvas dc(_hwnd);
01855 
01856     int clr_bits = GetDeviceCaps(dc, BITSPIXEL);
01857 
01858     if (clr_bits > 8)
01859         return IDB_LOGOV;
01860     else if (clr_bits > 4)
01861         return IDB_LOGOV256;
01862     else
01863         return IDB_LOGOV16;
01864 }
01865 
01866 
01867 void StartMenuRoot::CloseStartMenu(int id)
01868 {
01869     if (_submenu)
01870         CloseSubmenus();
01871 
01872     ShowWindow(_hwnd, SW_HIDE);
01873 }
01874 
01875 bool StartMenuRoot::IsStartMenuVisible() const
01876 {
01877     return IsWindowVisible(_hwnd);
01878 }
01879 
01880 void StartMenuRoot::ProcessKey(int vk)
01881 {
01882     switch(vk) {
01883       case VK_LEFT:
01884         if (_submenu)
01885             CloseOtherSubmenus();
01886         // don't close start menu root
01887         break;
01888 
01889       default:
01890         super::ProcessKey(vk);
01891     }
01892 }
01893 
01894 
01895 int StartMenuHandler::Command(int id, int code)
01896 {
01897     switch(id) {
01898 
01899     // start menu root
01900 
01901       case IDC_PROGRAMS:
01902         CreateSubmenu(id, CSIDL_COMMON_PROGRAMS, CSIDL_PROGRAMS, ResString(IDS_PROGRAMS));
01903         break;
01904 
01905       case IDC_EXPLORE:
01906         CloseStartMenu(id);
01907         explorer_show_frame(SW_SHOWNORMAL);
01908         break;
01909 
01910       case IDC_LAUNCH:
01911         CloseStartMenu(id);
01912         ShowLaunchDialog(g_Globals._hwndDesktopBar);
01913         break;
01914 
01915       case IDC_DOCUMENTS:
01916         CreateSubmenu(id, CSIDL_PERSONAL, ResString(IDS_DOCUMENTS));
01917         break;
01918 
01919       case IDC_RECENT:
01920         CreateSubmenu(id, CSIDL_RECENT, ResString(IDS_RECENT), STARTMENU_CREATOR(RecentStartMenu));
01921         break;
01922 
01923       case IDC_FAVORITES:
01924 #ifndef _SHELL32_FAVORITES
01925         CreateSubmenu(id, ResString(IDS_FAVORITES), STARTMENU_CREATOR(FavoritesMenu), &static_cast<BookmarkList&>(g_Globals._favorites));
01926 #else
01927         CreateSubmenu(id, CSIDL_COMMON_FAVORITES, CSIDL_FAVORITES, ResString(IDS_FAVORITES));
01928 #endif
01929         break;
01930 
01931       case IDC_BROWSE:
01932         CreateSubmenu(id, ResString(IDS_BROWSE), STARTMENU_CREATOR(BrowseMenu));
01933         break;
01934 
01935       case IDC_SETTINGS:
01936         CreateSubmenu(id, ResString(IDS_SETTINGS), STARTMENU_CREATOR(SettingsMenu));
01937         break;
01938 
01939       case IDC_SEARCH:
01940         CreateSubmenu(id, ResString(IDS_SEARCH), STARTMENU_CREATOR(SearchMenu));
01941         break;
01942 
01943       case IDC_START_HELP:
01944         CloseStartMenu(id);
01945         MessageBox(g_Globals._hwndDesktopBar, TEXT("Help not yet implemented"), ResString(IDS_TITLE), MB_OK);
01946         break;
01947 
01948       case IDC_LOGOFF:
01949         CloseStartMenu(id);
01950         ShowLogoffDialog(g_Globals._hwndDesktopBar);
01951         break;
01952 
01953 #ifndef __REACTOS__
01954       case IDC_TERMINATE:
01955         DestroyWindow(g_Globals._hwndDesktopBar);
01956         DestroyWindow(g_Globals._hwndDesktop);
01957         break;
01958 #endif
01959 
01960       case IDC_SHUTDOWN:
01961         CloseStartMenu(id);
01962         ShowExitWindowsDialog(g_Globals._hwndDesktop);
01963         break;
01964 
01965       case IDC_RESTART:
01966         CloseStartMenu(id);
01967         ShowRestartDialog(g_Globals._hwndDesktop, EWX_REBOOT);
01968         /* An alternative way to do restart without shell32 help */
01969         //launch_file(_hwnd, TEXT("shutdown.exe"), SW_HIDE, TEXT("-r"));
01970         break;
01971 
01972     // settings menu
01973 
01974       case ID_DESKTOPBAR_SETTINGS:
01975         CloseStartMenu(id);
01976         ExplorerPropertySheet(g_Globals._hwndDesktopBar);
01977         break;
01978 
01979       case IDC_CONTROL_PANEL: {
01980         CloseStartMenu(id);
01981 #ifndef ROSSHELL
01982 #ifndef _NO_MDI
01983         XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
01984         bool mdi = XMLBool(explorer_options, "mdi", true);
01985 
01986         if (mdi)
01987             MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
01988         else
01989 #endif
01990             SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
01991 #else
01992         launch_file(_hwnd, SHELLPATH_CONTROL_PANEL);
01993 #endif
01994         break;}
01995 
01996       case IDC_SETTINGS_MENU:
01997         CreateSubmenu(id, CSIDL_CONTROLS, ResString(IDS_SETTINGS_MENU));
01998         break;
01999 
02000       case IDC_PRINTERS: {
02001         CloseStartMenu(id);
02002 
02003 #ifndef ROSSHELL
02004 #ifndef _NO_MDI
02005         XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
02006         bool mdi = XMLBool(explorer_options, "mdi", true);
02007 
02008         if (mdi)
02009             MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0);
02010         else
02011 #endif
02012             SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0);
02013 #else
02014         launch_file(_hwnd, SHELLPATH_PRINTERS);
02015 #endif
02016         break;}
02017 
02018 #if 0   ///@todo use printer start menu folder per default and allow opening "printers" cabinet window using the context menu
02019       case IDC_PRINTERS_MENU:
02020         CreateSubmenu(id, CSIDL_PRINTERS, CSIDL_PRINTHOOD, ResString(IDS_PRINTERS));
02021 /*      StartMenuFolders new_folders;
02022 
02023         try {
02024             new_folders.push_back(ShellPath(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}")));
02025         } catch(COMException&) {
02026         }
02027 
02028         CreateSubmenu(id, new_folders, ResString(IDS_PRINTERS));*/
02029         break;
02030 #endif
02031 
02032       case IDC_ADMIN:
02033 #ifndef ROSSHELL
02034         CreateSubmenu(id, CSIDL_COMMON_ADMINTOOLS, CSIDL_ADMINTOOLS, ResString(IDS_ADMIN));
02035         //CloseStartMenu(id);
02036         //MainFrame::Create(SpecialFolderPath(CSIDL_COMMON_ADMINTOOLS, _hwnd), OWM_PIDL);
02037 #else
02038         launch_file(_hwnd, SpecialFolderFSPath(CSIDL_COMMON_ADMINTOOLS, _hwnd));
02039 #endif
02040         break;
02041 
02042         case IDC_CONNECTIONS:{
02043         CloseStartMenu(id);
02044 #ifndef ROSSHELL
02045 #ifndef _NO_MDI
02046         XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
02047         bool mdi = XMLBool(explorer_options, "mdi", true);
02048 
02049         if (mdi)
02050             MDIMainFrame::Create(SHELLPATH_NET_CONNECTIONS, 0);
02051         else
02052 #endif
02053             SDIMainFrame::Create(SHELLPATH_NET_CONNECTIONS, 0);
02054 #else
02055         launch_file(_hwnd, SHELLPATH_NET_CONNECTIONS);
02056 #endif
02057         break;}
02058 
02059 
02060     // browse menu
02061 
02062       case IDC_NETWORK:
02063 #ifdef __REACTOS__  ///@todo to be removed when network browsing will be implemented in shell namespace
02064         MessageBox(0, TEXT("not yet implemented"), ResString(IDS_TITLE), MB_OK);
02065 #else
02066         CreateSubmenu(id, CSIDL_NETWORK, ResString(IDS_NETWORK));
02067 #endif
02068         break;
02069 
02070       case IDC_DRIVES:
02072         CreateSubmenu(id, CSIDL_DRIVES, ResString(IDS_DRIVES));
02073         break;
02074 
02075 
02076     // search menu
02077 
02078       case IDC_SEARCH_PROGRAM:
02079         CloseStartMenu(id);
02080         Dialog::DoModal(IDD_SEARCH_PROGRAM, WINDOW_CREATOR(FindProgramDlg));
02081         break;
02082 
02083       case IDC_SEARCH_FILES:
02084         CloseStartMenu(id);
02085         ShowSearchDialog();
02086         break;
02087 
02088       case IDC_SEARCH_COMPUTER:
02089         CloseStartMenu(id);
02090         ShowSearchComputer();
02091         break;
02092 
02093 
02094       default:
02095         return super::Command(id, code);
02096     }
02097 
02098     return 0;
02099 }
02100 
02101 
02102 void StartMenuHandler::ShowSearchDialog()
02103 {
02104 #ifndef __REACTOS__ ///@todo to be removed when SHFindFiles() will be implemented in shell32.dll
02105     static DynamicFct<SHFINDFILES> SHFindFiles(TEXT("SHELL32"), 90);
02106 
02107     if (SHFindFiles)
02108         (*SHFindFiles)(NULL, NULL);
02109     else
02110 #endif
02111         MessageBox(0, TEXT("SHFindFiles() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02112 }
02113 
02114 void StartMenuHandler::ShowSearchComputer()
02115 {
02116 #ifndef __REACTOS__ ///@todo to be removed when SHFindComputer() will be implemented in shell32.dll
02117     static DynamicFct<SHFINDCOMPUTER> SHFindComputer(TEXT("SHELL32"), 91);
02118 
02119     if (SHFindComputer)
02120         (*SHFindComputer)(NULL, NULL);
02121     else
02122 #endif
02123         MessageBox(0, TEXT("SHFindComputer() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02124 }
02125 
02126 struct RunDialogThread : public Thread
02127 {
02128     int Run();
02129 };
02130 
02131 int RunDialogThread::Run()
02132 {
02133     static DynamicFct<RUNFILEDLG> RunFileDlg(TEXT("SHELL32"), 61);
02134 
02135     // RunFileDlg needs owner window to properly position dialog
02136     // that window will be disabled so we can't use DesktopBar
02137     RECT rect = {0};
02138 #ifndef TASKBAR_AT_TOP
02139     rect.top = GetSystemMetrics(SM_CYSCREEN) - DESKTOPBARBAR_HEIGHT;
02140 #endif
02141     rect.right = GetSystemMetrics(SM_CXSCREEN);
02142     rect.bottom = rect.top + DESKTOPBARBAR_HEIGHT;
02143     Static dlgOwner(0, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0, 0);
02144 
02145      // Show "Run..." dialog
02146     if (RunFileDlg) {
02147         (*RunFileDlg)(dlgOwner, 0, NULL, NULL, NULL, RFF_CALCDIRECTORY);
02148     }
02149     DestroyWindow(dlgOwner);
02150     return 0;
02151 }
02152 
02153 void StartMenuHandler::ShowLaunchDialog(HWND hwndOwner)
02154 {
02155     RunDialogThread * rdt = new RunDialogThread();
02156     rdt->Start();
02157 }
02158 
02159 void StartMenuHandler::ShowLogoffDialog(HWND hwndOwner)
02160 {
02161     static DynamicFct<LOGOFFWINDOWSDIALOG> LogoffWindowsDialog(TEXT("SHELL32"), 54);
02162 //  static DynamicFct<RESTARTWINDOWSDLG> RestartDialog(TEXT("SHELL32"), 59);
02163 
02164     if (LogoffWindowsDialog)
02165         (*LogoffWindowsDialog)(0);
02166 /* The RestartDialog function prompts about some system setting change. This is not what we want to display here.
02167     else if (RestartDialog)
02168         return (*RestartDialog)(hwndOwner, (LPWSTR)L"You selected <Log Off>.\n\n", EWX_LOGOFF) == 1;    ///@todo ANSI string conversion if needed
02169 */
02170     else
02171         MessageBox(hwndOwner, TEXT("LogoffWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02172 }
02173 
02174 void ShowExitWindowsDialog(HWND hwndOwner)
02175 {
02176     static DynamicFct<EXITWINDOWSDLG> ExitWindowsDialog(TEXT("SHELL32"), 60);
02177 
02178     if (ExitWindowsDialog)
02179         (*ExitWindowsDialog)(hwndOwner);
02180     else
02181         MessageBox(hwndOwner, TEXT("ExitWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02182 }
02183 
02184 void StartMenuHandler::ShowRestartDialog(HWND hwndOwner, UINT flags)
02185 {
02186     static DynamicFct<RESTARTWINDOWSDLG> RestartDlg(TEXT("SHELL32"), 59);
02187 
02188     if (RestartDlg)
02189         (*RestartDlg)(hwndOwner, (LPWSTR)L"You selected restart.\n\n", flags);
02190     else
02191         MessageBox(hwndOwner, TEXT("RestartDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
02192 }
02193 
02194 void SettingsMenu::AddEntries()
02195 {
02196     super::AddEntries();
02197 
02198 #if defined(ROSSHELL) || defined(__REACTOS__)   // __REACTOS__ to be removed when printers will be implemented
02199 //TODO  AddButton(ResString(IDS_PRINTERS),          ICID_PRINTER, false, IDC_PRINTERS_MENU);
02200 #else
02201 //TODO  AddButton(ResString(IDS_PRINTERS),          ICID_PRINTER, true, IDC_PRINTERS_MENU);
02202 #endif
02203 
02204     AddButton(ResString(IDS_CONNECTIONS),       ICID_NETCONNS, false, IDC_CONNECTIONS);
02205 
02206     AddButton(ResString(IDS_ADMIN),             ICID_ADMIN, true, IDC_ADMIN);
02207 
02208 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
02209     if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
02210 #endif
02211     AddButton(ResString(IDS_SETTINGS_MENU), ICID_CONFIG, true, IDC_SETTINGS_MENU);
02212 
02213     AddButton(ResString(IDS_DESKTOPBAR_SETTINGS), ICID_DESKSETTING, false, ID_DESKTOPBAR_SETTINGS);
02214 
02215     AddButton(ResString(IDS_PRINTERS),          ICID_PRINTER, false, IDC_PRINTERS);
02216 
02217 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
02218     if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
02219 #endif
02220         AddButton(ResString(IDS_CONTROL_PANEL), ICID_CONTROLPAN, false, IDC_CONTROL_PANEL);
02221 }
02222 
02223 void BrowseMenu::AddEntries()
02224 {
02225     super::AddEntries();
02226 
02227 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
02228     if (!g_Globals._SHRestricted || !SHRestricted(REST_NONETHOOD))  // or REST_NOENTIRENETWORK ?
02229 #endif
02230 #if defined(ROSSHELL) || defined(__REACTOS__)   // __REACTOS__ to be removed when printer/network will be implemented
02231         AddButton(ResString(IDS_NETWORK),       ICID_NETWORK, false, IDC_NETWORK);
02232 #else
02233         AddButton(ResString(IDS_NETWORK),       ICID_NETWORK, true, IDC_NETWORK);
02234 #endif
02235 
02236     AddButton(ResString(IDS_DRIVES),            ICID_FOLDER, true, IDC_DRIVES);
02237 }
02238 
02239 void SearchMenu::AddEntries()
02240 {
02241     super::AddEntries();
02242 
02243     AddButton(ResString(IDS_SEARCH_FILES),      ICID_SEARCH_DOC, false, IDC_SEARCH_FILES);
02244 
02245 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
02246     if (!g_Globals._SHRestricted || !SHRestricted(REST_HASFINDCOMPUTERS))
02247 #endif
02248         AddButton(ResString(IDS_SEARCH_COMPUTER),ICID_COMPUTER, false, IDC_SEARCH_COMPUTER);
02249 
02250     AddButton(ResString(IDS_SEARCH_PRG),        ICID_APPS, false, IDC_SEARCH_PROGRAM);
02251 }
02252 
02253 
02254 void RecentStartMenu::AddEntries()
02255 {
02256     for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
02257         StartMenuDirectory& smd = *it;
02258         ShellDirectory& dir = smd._dir;
02259 
02260         if (!dir._scanned) {
02261             WaitCursor wait;
02262 
02263 #ifdef _LAZY_ICONEXTRACT
02264             dir.smart_scan(SORT_NAME, SCAN_DONT_EXTRACT_ICONS);
02265 #else
02266             dir.smart_scan(SORT_NAME);
02267 #endif
02268         }
02269 
02270         dir.sort_directory(SORT_DATE);
02271         AddShellEntries(dir, RECENT_DOCS_COUNT, smd._ignore);   
02272     }
02273 }
02274 
02275 
02276 #ifndef _SHELL32_FAVORITES
02277 
02278 void FavoritesMenu::AddEntries()
02279 {
02280     super::AddEntries();
02281 
02282     String lwr_filter = _create_info._filter;
02283     lwr_filter.toLower();
02284 
02285     for(BookmarkList::iterator it=_bookmarks.begin(); it!=_bookmarks.end(); ++it) {
02286         BookmarkNode& node = *it;
02287 
02288         int id = ++_next_id;
02289 
02290         _entries[id] = node;
02291 
02292         if (node._type == BookmarkNode::BMNT_FOLDER) {
02293             BookmarkFolder& folder = *node._pfolder;
02294 
02295             AddButton(folder._name, ICID_FOLDER, true, id);
02296         } else if (node._type == BookmarkNode::BMNT_BOOKMARK) {
02297             Bookmark& bookmark = *node._pbookmark;
02298 
02299             ICON_ID icon = ICID_NONE;
02300 
02301             if (!bookmark._icon_path.empty())
02302                 icon = g_Globals._icon_cache.extract(bookmark._icon_path, bookmark._icon_idx);
02303 
02304              // filter non-directory entries
02305             if (!lwr_filter.empty()) {
02306                 String lwr_name = bookmark._name;
02307                 String lwr_desc = bookmark._description;
02308                 String lwr_url = bookmark._url;
02309 
02310                 lwr_name.toLower();
02311                 lwr_desc.toLower();
02312                 lwr_url.toLower();
02313 
02314                 if (!_tcsstr(lwr_name,lwr_filter) && !_tcsstr(lwr_desc,lwr_filter) && !_tcsstr(lwr_url,lwr_filter))
02315                     continue;
02316             }
02317 
02318             AddButton(bookmark._name, icon!=ICID_NONE?icon:ICID_BOOKMARK, false, id);
02319         }
02320     }
02321 }
02322 
02323 int FavoritesMenu::Command(int id, int code)
02324 {
02325     BookmarkMap::iterator found = _entries.find(id);
02326 
02327     if (found != _entries.end()) {
02328         BookmarkNode& node = found->second;
02329 
02330         if (node._type == BookmarkNode::BMNT_FOLDER) {
02331             BookmarkFolder& folder = *node._pfolder;
02332 
02333             if (CloseOtherSubmenus(id))
02334                 CreateSubmenu(id, folder._name, STARTMENU_CREATOR(FavoritesMenu), &static_cast<BookmarkList&>(folder._bookmarks));
02335         } else if (node._type == BookmarkNode::BMNT_BOOKMARK) {
02336             Bookmark& bookmark = *node._pbookmark;
02337 
02338             String url = bookmark._url;
02339             HWND hparent = GetParent(_hwnd);
02340 
02341             CloseStartMenu(id);
02342 
02343             launch_file(hparent, url, SW_SHOWNORMAL);
02344         }
02345 
02346         return 0;
02347     }
02348 
02349     return super::Command(id, code);
02350 }
02351 
02352 #endif

Generated on Mon May 28 2012 04:18:22 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.