mirror of
https://github.com/reactos/reactos.git
synced 2025-01-02 12:32:47 +00:00
desktop switching
svn path=/trunk/; revision=8095
This commit is contained in:
parent
0dc86c7a3e
commit
59cd294e28
25 changed files with 388 additions and 91 deletions
|
@ -45,6 +45,157 @@ static BOOL (WINAPI*SetShellWindow)(HWND);
|
||||||
static BOOL (WINAPI*SetShellWindowEx)(HWND, HWND);
|
static BOOL (WINAPI*SetShellWindowEx)(HWND, HWND);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _USE_HDESK
|
||||||
|
|
||||||
|
Desktop::Desktop(HDESK hdesktop/*, HWINSTA hwinsta*/)
|
||||||
|
: _hdesktop(hdesktop)
|
||||||
|
// _hwinsta(hwinsta)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Desktop::~Desktop()
|
||||||
|
{
|
||||||
|
if (_hdesktop)
|
||||||
|
CloseDesktop(_hdesktop);
|
||||||
|
|
||||||
|
// if (_hwinsta)
|
||||||
|
// CloseWindowStation(_hwinsta);
|
||||||
|
|
||||||
|
if (_pThread.get()) {
|
||||||
|
_pThread->Stop();
|
||||||
|
_pThread.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
Desktops::Desktops()
|
||||||
|
{
|
||||||
|
_current_desktop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Desktops::init()
|
||||||
|
{
|
||||||
|
resize(DESKTOP_COUNT);
|
||||||
|
|
||||||
|
#ifdef _USE_HDESK
|
||||||
|
DesktopPtr& desktop = (*this)[0];
|
||||||
|
|
||||||
|
desktop = DesktopPtr(new Desktop(OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _USE_HDESK
|
||||||
|
|
||||||
|
void Desktops::SwitchToDesktop(int idx)
|
||||||
|
{
|
||||||
|
if (_current_desktop == idx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DesktopPtr& desktop = (*this)[idx];
|
||||||
|
|
||||||
|
DesktopThread* pThread = NULL;
|
||||||
|
|
||||||
|
if (desktop.get()) {
|
||||||
|
if (desktop->_hdesktop)
|
||||||
|
if (!SwitchDesktop(desktop->_hdesktop))
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
FmtString desktop_name(TEXT("Desktop %d"), idx);
|
||||||
|
|
||||||
|
SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
|
||||||
|
/*
|
||||||
|
HWINSTA hwinsta = CreateWindowStation(TEXT("ExplorerWinStation"), 0, GENERIC_ALL, &saAttr);
|
||||||
|
|
||||||
|
if (!SetProcessWindowStation(hwinsta))
|
||||||
|
return;
|
||||||
|
*/
|
||||||
|
HDESK hdesktop = CreateDesktop(desktop_name, NULL, NULL, 0, GENERIC_ALL, &saAttr);
|
||||||
|
if (!hdesktop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
desktop = DesktopPtr(new Desktop(hdesktop/*, hwinsta*/));
|
||||||
|
|
||||||
|
pThread = new DesktopThread(*desktop);
|
||||||
|
}
|
||||||
|
|
||||||
|
_current_desktop = idx;
|
||||||
|
|
||||||
|
if (pThread) {
|
||||||
|
desktop->_pThread = DesktopThreadPtr(pThread);
|
||||||
|
pThread->Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int DesktopThread::Run()
|
||||||
|
{
|
||||||
|
if (!SetThreadDesktop(_desktop._hdesktop))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
HDESK hDesk_old = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP);
|
||||||
|
|
||||||
|
if (!SwitchDesktop(_desktop._hdesktop))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!_desktop._hwndDesktop)
|
||||||
|
_desktop._hwndDesktop = DesktopWindow::Create();
|
||||||
|
|
||||||
|
int ret = Window::MessageLoop();
|
||||||
|
|
||||||
|
SwitchDesktop(hDesk_old);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // _USE_HDESK
|
||||||
|
|
||||||
|
static BOOL CALLBACK DesktopEnumFct(HWND hwnd, LPARAM lparam)
|
||||||
|
{
|
||||||
|
WindowSet& windows = *(WindowSet*)lparam;
|
||||||
|
|
||||||
|
if (IsWindowVisible(hwnd)) {
|
||||||
|
DWORD pid;
|
||||||
|
|
||||||
|
GetWindowThreadProcessId(hwnd, &pid);
|
||||||
|
|
||||||
|
if (pid != GetCurrentProcessId())
|
||||||
|
windows.insert(hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Desktops::SwitchToDesktop(int idx)
|
||||||
|
{
|
||||||
|
if (_current_desktop == idx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Desktop& desktop = (*this)[idx];
|
||||||
|
|
||||||
|
// save currently visible application windows
|
||||||
|
Desktop& old_desktop = (*this)[_current_desktop];
|
||||||
|
WindowSet& windows = old_desktop._windows;
|
||||||
|
|
||||||
|
windows.clear();
|
||||||
|
EnumWindows(DesktopEnumFct, (LPARAM)&windows);
|
||||||
|
|
||||||
|
// hide all windows we found
|
||||||
|
for(WindowSet::iterator it=windows.begin(); it!=windows.end(); ++it)
|
||||||
|
ShowWindowAsync(*it, SW_HIDE);
|
||||||
|
|
||||||
|
// show all windows of the new desktop
|
||||||
|
for(WindowSet::iterator it=desktop._windows.begin(); it!=desktop._windows.end(); ++it)
|
||||||
|
ShowWindowAsync(*it, SW_SHOW);
|
||||||
|
|
||||||
|
desktop._windows.clear();
|
||||||
|
|
||||||
|
_current_desktop = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _USE_HDESK
|
||||||
|
|
||||||
|
|
||||||
BOOL IsAnyDesktopRunning()
|
BOOL IsAnyDesktopRunning()
|
||||||
{
|
{
|
||||||
HINSTANCE hUser32 = GetModuleHandle(TEXT("user32"));
|
HINSTANCE hUser32 = GetModuleHandle(TEXT("user32"));
|
||||||
|
@ -95,7 +246,7 @@ LRESULT BackgroundWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case WM_MBUTTONDBLCLK:
|
case WM_MBUTTONDBLCLK:
|
||||||
explorer_show_frame(_hwnd, SW_SHOWNORMAL);
|
explorer_show_frame(SW_SHOWNORMAL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -121,7 +272,7 @@ DesktopWindow::~DesktopWindow()
|
||||||
|
|
||||||
HWND DesktopWindow::Create()
|
HWND DesktopWindow::Create()
|
||||||
{
|
{
|
||||||
IconWindowClass wcDesktop(TEXT("Progman"), IDI_REACTOS, CS_DBLCLKS);
|
static IconWindowClass wcDesktop(TEXT("Progman"), IDI_REACTOS, CS_DBLCLKS);
|
||||||
wcDesktop.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1);
|
wcDesktop.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1);
|
||||||
|
|
||||||
int width = GetSystemMetrics(SM_CXSCREEN);
|
int width = GetSystemMetrics(SM_CXSCREEN);
|
||||||
|
@ -147,12 +298,12 @@ LRESULT DesktopWindow::Init(LPCREATESTRUCT pcs)
|
||||||
if (super::Init(pcs))
|
if (super::Init(pcs))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
HRESULT hr = Desktop()->CreateViewObject(_hwnd, IID_IShellView, (void**)&_pShellView);
|
HRESULT hr = GetDesktopFolder()->CreateViewObject(_hwnd, IID_IShellView, (void**)&_pShellView);
|
||||||
/* also possible:
|
/* also possible:
|
||||||
SFV_CREATE sfv_create;
|
SFV_CREATE sfv_create;
|
||||||
|
|
||||||
sfv_create.cbSize = sizeof(SFV_CREATE);
|
sfv_create.cbSize = sizeof(SFV_CREATE);
|
||||||
sfv_create.pshf = Desktop();
|
sfv_create.pshf = GetDesktopFolder();
|
||||||
sfv_create.psvOuter = NULL;
|
sfv_create.psvOuter = NULL;
|
||||||
sfv_create.psfvcb = NULL;
|
sfv_create.psfvcb = NULL;
|
||||||
|
|
||||||
|
@ -233,7 +384,7 @@ LRESULT DesktopWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
|
||||||
case WM_LBUTTONDBLCLK:
|
case WM_LBUTTONDBLCLK:
|
||||||
case WM_RBUTTONDBLCLK:
|
case WM_RBUTTONDBLCLK:
|
||||||
case WM_MBUTTONDBLCLK:
|
case WM_MBUTTONDBLCLK:
|
||||||
explorer_show_frame(_hwnd, SW_SHOWNORMAL);
|
explorer_show_frame(SW_SHOWNORMAL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_GETISHELLBROWSER:
|
case WM_GETISHELLBROWSER:
|
||||||
|
|
|
@ -57,7 +57,7 @@ int CollectProgramsThread::Run()
|
||||||
|
|
||||||
void CollectProgramsThread::collect_programs(const ShellPath& path)
|
void CollectProgramsThread::collect_programs(const ShellPath& path)
|
||||||
{
|
{
|
||||||
ShellDirectory* dir = new ShellDirectory(Desktop(), path, 0);
|
ShellDirectory* dir = new ShellDirectory(GetDesktopFolder(), path, 0);
|
||||||
_dirs.push(dir);
|
_dirs.push(dir);
|
||||||
|
|
||||||
dir->smart_scan(/*SCAN_EXTRACT_ICONS|*/SCAN_FILESYSTEM);
|
dir->smart_scan(/*SCAN_EXTRACT_ICONS|*/SCAN_FILESYSTEM);
|
||||||
|
@ -263,7 +263,7 @@ void FindProgramDlg::add_entry(const FPDEntry& cache_entry)
|
||||||
|
|
||||||
int FindProgramDlg::Command(int id, int code)
|
int FindProgramDlg::Command(int id, int code)
|
||||||
{
|
{
|
||||||
if (code == BN_CLICKED)
|
if (code == BN_CLICKED) {
|
||||||
switch(id) {
|
switch(id) {
|
||||||
case ID_REFRESH:
|
case ID_REFRESH:
|
||||||
Refresh(true);
|
Refresh(true);
|
||||||
|
@ -276,15 +276,22 @@ int FindProgramDlg::Command(int id, int code)
|
||||||
default:
|
default:
|
||||||
return super::Command(id, code);
|
return super::Command(id, code);
|
||||||
}
|
}
|
||||||
else if (code == EN_CHANGE)
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else if (code == EN_CHANGE) {
|
||||||
switch(id) {
|
switch(id) {
|
||||||
case IDC_TOPIC:
|
case IDC_TOPIC:
|
||||||
Refresh();
|
Refresh();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
void FindProgramDlg::LaunchSelected()
|
void FindProgramDlg::LaunchSelected()
|
||||||
{
|
{
|
||||||
Lock lock(_thread._crit_sect);
|
Lock lock(_thread._crit_sect);
|
||||||
|
|
|
@ -135,9 +135,11 @@ int DesktopSettingsDlg::Command(int id, int code)
|
||||||
|
|
||||||
SendMessage(g_Globals._hwndShellView, PM_SET_ICON_ALGORITHM, alignment, 0);
|
SendMessage(g_Globals._hwndShellView, PM_SET_ICON_ALGORITHM, alignment, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
- command line parameters like "/e,/root,c:\" and "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\::{21EC2020-3AEA-1069-A2DD-08002B30309D}" (launch of control panel)
|
- command line parameters like "/e,/root,c:\" and "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\::{21EC2020-3AEA-1069-A2DD-08002B30309D}" (launch of control panel)
|
||||||
- Windows-key combos
|
- Windows-key combos
|
||||||
- Application Desktop Toolbars
|
- Application Desktop Toolbars
|
||||||
- desktop switching
|
|
||||||
- command line in explorer windows
|
- command line in explorer windows
|
||||||
- hide CVS subdirectories, may be even implement a CVS managment plugin
|
- hide CVS subdirectories, may be even implement a CVS managment plugin
|
||||||
- printer and RAS connection icons in desktop notification area
|
- printer and RAS connection icons in desktop notification area
|
||||||
|
|
|
@ -61,3 +61,4 @@ If you search for more information, look into the CVS repository.
|
||||||
31.01.2004 m. fuchs included Registry as virtual file system
|
31.01.2004 m. fuchs included Registry as virtual file system
|
||||||
02.02.2004 m. fuchs reading of FAT image files
|
02.02.2004 m. fuchs reading of FAT image files
|
||||||
07.02.2004 m. fuchs included IE/Mozilla as Active X control
|
07.02.2004 m. fuchs included IE/Mozilla as Active X control
|
||||||
|
08.02.3004 m. fuchs desktop switching
|
||||||
|
|
|
@ -427,7 +427,7 @@ ResBitmap::ResBitmap(UINT nid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void explorer_show_frame(HWND hwndDesktop, int cmdshow, LPTSTR lpCmdLine)
|
void explorer_show_frame(int cmdshow, LPTSTR lpCmdLine)
|
||||||
{
|
{
|
||||||
if (g_Globals._hMainWnd) {
|
if (g_Globals._hMainWnd) {
|
||||||
if (IsIconic(g_Globals._hMainWnd))
|
if (IsIconic(g_Globals._hMainWnd))
|
||||||
|
@ -558,7 +558,7 @@ static void InitInstance(HINSTANCE hInstance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int explorer_main(HINSTANCE hInstance, HWND hwndDesktop, LPTSTR lpCmdLine, int cmdshow)
|
int explorer_main(HINSTANCE hInstance, LPTSTR lpCmdLine, int cmdshow)
|
||||||
{
|
{
|
||||||
CONTEXT("explorer_main");
|
CONTEXT("explorer_main");
|
||||||
|
|
||||||
|
@ -568,13 +568,10 @@ int explorer_main(HINSTANCE hInstance, HWND hwndDesktop, LPTSTR lpCmdLine, int c
|
||||||
try {
|
try {
|
||||||
InitInstance(hInstance);
|
InitInstance(hInstance);
|
||||||
} catch(COMException& e) {
|
} catch(COMException& e) {
|
||||||
HandleException(e, hwndDesktop);
|
HandleException(e, GetDesktopWindow());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hwndDesktop)
|
|
||||||
g_Globals._desktop_mode = true;
|
|
||||||
|
|
||||||
if (cmdshow != SW_HIDE) {
|
if (cmdshow != SW_HIDE) {
|
||||||
/* // don't maximize if being called from the ROS desktop
|
/* // don't maximize if being called from the ROS desktop
|
||||||
if (cmdshow == SW_SHOWNORMAL)
|
if (cmdshow == SW_SHOWNORMAL)
|
||||||
|
@ -582,7 +579,7 @@ int explorer_main(HINSTANCE hInstance, HWND hwndDesktop, LPTSTR lpCmdLine, int c
|
||||||
cmdshow = SW_MAXIMIZE;
|
cmdshow = SW_MAXIMIZE;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
explorer_show_frame(hwndDesktop, cmdshow, lpCmdLine);
|
explorer_show_frame(cmdshow, lpCmdLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Window::MessageLoop();
|
return Window::MessageLoop();
|
||||||
|
@ -688,12 +685,15 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL
|
||||||
|
|
||||||
HWND hwndDesktop = 0;
|
HWND hwndDesktop = 0;
|
||||||
|
|
||||||
if (startup_desktop)
|
if (startup_desktop) {
|
||||||
{
|
g_Globals._desktops.init();
|
||||||
hwndDesktop = DesktopWindow::Create();
|
|
||||||
|
|
||||||
if (autostart)
|
hwndDesktop = DesktopWindow::Create();
|
||||||
{
|
#ifdef _USE_HDESK
|
||||||
|
g_Globals._desktops.get_current_Desktop()->_hwndDesktop = hwndDesktop;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (autostart) {
|
||||||
char* argv[] = {"", "s"}; // call startup routine in SESSION_START mode
|
char* argv[] = {"", "s"}; // call startup routine in SESSION_START mode
|
||||||
startup(2, argv);
|
startup(2, argv);
|
||||||
}
|
}
|
||||||
|
@ -705,7 +705,10 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL
|
||||||
lpCmdLine[_tcslen(lpCmdLine)-1] = '\0';
|
lpCmdLine[_tcslen(lpCmdLine)-1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = explorer_main(hInstance, hwndDesktop, lpCmdLine, nShowCmd);
|
if (hwndDesktop)
|
||||||
|
g_Globals._desktop_mode = true;
|
||||||
|
|
||||||
|
int ret = explorer_main(hInstance, lpCmdLine, nShowCmd);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#define IDS_TASKBAR 31
|
#define IDS_TASKBAR 31
|
||||||
#define IDS_STARTMENU 32
|
#define IDS_STARTMENU 32
|
||||||
#define IDS_MINIMIZE_ALL 33
|
#define IDS_MINIMIZE_ALL 33
|
||||||
|
#define IDS_DESKTOP_NUM 34
|
||||||
#define IDI_REACTOS 100
|
#define IDI_REACTOS 100
|
||||||
#define IDI_EXPLORER 101
|
#define IDI_EXPLORER 101
|
||||||
#define IDI_STARTMENU 102
|
#define IDI_STARTMENU 102
|
||||||
|
@ -133,6 +134,9 @@
|
||||||
#define ID_BROWSE_HOME 40007
|
#define ID_BROWSE_HOME 40007
|
||||||
#define ID_BROWSE_SEARCH 40008
|
#define ID_BROWSE_SEARCH 40008
|
||||||
#define ID_STOP 40009
|
#define ID_STOP 40009
|
||||||
|
#define ID_MINIMIZE_ALL 40010
|
||||||
|
#define ID_EXPLORE 40011
|
||||||
|
#define ID_SWITCH_DESKTOP_1 40012
|
||||||
#define ID_WINDOW_NEW 0xE130
|
#define ID_WINDOW_NEW 0xE130
|
||||||
#define ID_WINDOW_ARRANGE 0xE131
|
#define ID_WINDOW_ARRANGE 0xE131
|
||||||
#define ID_WINDOW_CASCADE 0xE132
|
#define ID_WINDOW_CASCADE 0xE132
|
||||||
|
@ -150,7 +154,7 @@
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
#define _APS_NEXT_RESOURCE_VALUE 161
|
#define _APS_NEXT_RESOURCE_VALUE 161
|
||||||
#define _APS_NEXT_COMMAND_VALUE 40006
|
#define _APS_NEXT_COMMAND_VALUE 40015
|
||||||
#define _APS_NEXT_CONTROL_VALUE 1003
|
#define _APS_NEXT_CONTROL_VALUE 1003
|
||||||
#define _APS_NEXT_SYMED_VALUE 101
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -127,6 +127,7 @@ STRINGTABLE DISCARDABLE
|
||||||
BEGIN
|
BEGIN
|
||||||
IDS_STARTMENU "Startmenu"
|
IDS_STARTMENU "Startmenu"
|
||||||
IDS_MINIMIZE_ALL "mimimize all windows"
|
IDS_MINIMIZE_ALL "mimimize all windows"
|
||||||
|
IDS_DESKTOP_NUM "Desktop %d"
|
||||||
END
|
END
|
||||||
|
|
||||||
#endif // Romanian resources
|
#endif // Romanian resources
|
||||||
|
@ -503,6 +504,7 @@ STRINGTABLE DISCARDABLE
|
||||||
BEGIN
|
BEGIN
|
||||||
IDS_STARTMENU "Startmenu"
|
IDS_STARTMENU "Startmenu"
|
||||||
IDS_MINIMIZE_ALL "alle Fenster minimieren"
|
IDS_MINIMIZE_ALL "alle Fenster minimieren"
|
||||||
|
IDS_DESKTOP_NUM "Desktop %d"
|
||||||
END
|
END
|
||||||
|
|
||||||
#endif // German (Germany) resources
|
#endif // German (Germany) resources
|
||||||
|
@ -846,6 +848,7 @@ STRINGTABLE DISCARDABLE
|
||||||
BEGIN
|
BEGIN
|
||||||
IDS_STARTMENU "Startmenu"
|
IDS_STARTMENU "Startmenu"
|
||||||
IDS_MINIMIZE_ALL "mimimize all windows"
|
IDS_MINIMIZE_ALL "mimimize all windows"
|
||||||
|
IDS_DESKTOP_NUM "Desktop %d"
|
||||||
END
|
END
|
||||||
|
|
||||||
#endif // English (U.S.) resources
|
#endif // English (U.S.) resources
|
||||||
|
@ -967,6 +970,7 @@ STRINGTABLE DISCARDABLE
|
||||||
BEGIN
|
BEGIN
|
||||||
IDS_STARTMENU "Startmenu"
|
IDS_STARTMENU "Startmenu"
|
||||||
IDS_MINIMIZE_ALL "mimimize all windows"
|
IDS_MINIMIZE_ALL "mimimize all windows"
|
||||||
|
IDS_DESKTOP_NUM "Desktop %d"
|
||||||
END
|
END
|
||||||
|
|
||||||
#endif // French (France) resources
|
#endif // French (France) resources
|
||||||
|
@ -1168,6 +1172,7 @@ STRINGTABLE DISCARDABLE
|
||||||
BEGIN
|
BEGIN
|
||||||
IDS_STARTMENU "Startmenu"
|
IDS_STARTMENU "Startmenu"
|
||||||
IDS_MINIMIZE_ALL "mimimize all windows"
|
IDS_MINIMIZE_ALL "mimimize all windows"
|
||||||
|
IDS_DESKTOP_NUM "Desktop %d"
|
||||||
END
|
END
|
||||||
|
|
||||||
#endif // Portuguese (Portugal) resources
|
#endif // Portuguese (Portugal) resources
|
||||||
|
|
|
@ -35,10 +35,10 @@ extern "C" {
|
||||||
extern int startup(int argc, char *argv[]);
|
extern int startup(int argc, char *argv[]);
|
||||||
|
|
||||||
// explorer main routine
|
// explorer main routine
|
||||||
extern int explorer_main(HINSTANCE hinstance, HWND hwndDesktop, LPTSTR lpCmdLine, int cmdshow);
|
extern int explorer_main(HINSTANCE hinstance, LPTSTR lpCmdLine, int cmdshow);
|
||||||
|
|
||||||
// display explorer/file manager window
|
// display explorer/file manager window
|
||||||
extern void explorer_show_frame(HWND hwndDesktop, int cmdshow, LPTSTR lpCmdLine=NULL);
|
extern void explorer_show_frame(int cmdshow, LPTSTR lpCmdLine=NULL);
|
||||||
|
|
||||||
// display explorer "About" dialog
|
// display explorer "About" dialog
|
||||||
extern void explorer_about(HWND hwndParent);
|
extern void explorer_about(HWND hwndParent);
|
||||||
|
|
|
@ -142,6 +142,65 @@ protected:
|
||||||
extern HBITMAP create_bitmap_from_icon(HICON hIcon, HBRUSH hbrush_bkgnd, HDC hdc_wnd);
|
extern HBITMAP create_bitmap_from_icon(HICON hIcon, HBRUSH hbrush_bkgnd, HDC hdc_wnd);
|
||||||
|
|
||||||
|
|
||||||
|
/// desktop management
|
||||||
|
#ifdef _USE_HDESK
|
||||||
|
|
||||||
|
typedef auto_ptr<struct DesktopThread> DesktopThreadPtr;
|
||||||
|
|
||||||
|
struct Desktop
|
||||||
|
{
|
||||||
|
HDESK _hdesktop;
|
||||||
|
// HWINSTA _hwinsta;
|
||||||
|
DesktopThreadPtr _pThread;
|
||||||
|
WindowHandle _hwndDesktop;
|
||||||
|
|
||||||
|
Desktop(HDESK hdesktop=0/*, HWINSTA hwinsta=0*/);
|
||||||
|
~Desktop();
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef auto_ptr<Desktop> DesktopPtr;
|
||||||
|
typedef DesktopPtr DesktopRef;
|
||||||
|
|
||||||
|
/// Thread class for additional desktops
|
||||||
|
struct DesktopThread : public Thread
|
||||||
|
{
|
||||||
|
DesktopThread(Desktop& desktop)
|
||||||
|
: _desktop(desktop)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int Run();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Desktop& _desktop;
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
struct Desktop
|
||||||
|
{
|
||||||
|
set<HWND> _windows;
|
||||||
|
};
|
||||||
|
typedef Desktop DesktopRef;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define DESKTOP_COUNT 4
|
||||||
|
|
||||||
|
struct Desktops : public vector<DesktopRef>
|
||||||
|
{
|
||||||
|
Desktops();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void SwitchToDesktop(int idx);
|
||||||
|
|
||||||
|
DesktopRef& get_current_Desktop() {return (*this)[_current_desktop];}
|
||||||
|
|
||||||
|
int _current_desktop;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// structure containing global variables of Explorer
|
/// structure containing global variables of Explorer
|
||||||
extern struct ExplorerGlobals
|
extern struct ExplorerGlobals
|
||||||
{
|
{
|
||||||
|
@ -167,6 +226,8 @@ extern struct ExplorerGlobals
|
||||||
|
|
||||||
HWND _hwndDesktopBar;
|
HWND _hwndDesktopBar;
|
||||||
HWND _hwndShellView;
|
HWND _hwndShellView;
|
||||||
|
|
||||||
|
Desktops _desktops;
|
||||||
} g_Globals;
|
} g_Globals;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@ FileChildWindow::FileChildWindow(HWND hwnd, const FileChildWndInfo& info)
|
||||||
lstrcpy(_root._fs, TEXT("Shell"));
|
lstrcpy(_root._fs, TEXT("Shell"));
|
||||||
|
|
||||||
const ShellChildWndInfo& shell_info = static_cast<const ShellChildWndInfo&>(info);
|
const ShellChildWndInfo& shell_info = static_cast<const ShellChildWndInfo&>(info);
|
||||||
_root._entry = new ShellDirectory(Desktop(), DesktopFolderPath(), hwnd);
|
_root._entry = new ShellDirectory(GetDesktopFolder(), DesktopFolderPath(), hwnd);
|
||||||
entry = _root._entry->read_tree((LPCTSTR)&*shell_info._shell_path, SORT_NAME);
|
entry = _root._entry->read_tree((LPCTSTR)&*shell_info._shell_path, SORT_NAME);
|
||||||
break;}
|
break;}
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ FileChildWindow::FileChildWindow(HWND hwnd, const FileChildWndInfo& info)
|
||||||
if (info._etype != ET_SHELL)
|
if (info._etype != ET_SHELL)
|
||||||
wsprintf(_root._entry->_data.cFileName, TEXT("%s - %s"), drv, _root._fs);
|
wsprintf(_root._entry->_data.cFileName, TEXT("%s - %s"), drv, _root._fs);
|
||||||
/*@@else
|
/*@@else
|
||||||
lstrcpy(_root._entry->_data.cFileName, TEXT("Desktop"));*/
|
lstrcpy(_root._entry->_data.cFileName, TEXT("GetDesktopFolder"));*/
|
||||||
|
|
||||||
_root._entry->_data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
_root._entry->_data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
||||||
|
|
||||||
|
@ -452,7 +452,7 @@ LRESULT FileChildWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
|
||||||
LPCITEMIDLIST pidl = shell_path;
|
LPCITEMIDLIST pidl = shell_path;
|
||||||
|
|
||||||
///@todo use parent folder instead of desktop -> correct "Properties" dialog, ...
|
///@todo use parent folder instead of desktop -> correct "Properties" dialog, ...
|
||||||
CHECKERROR(ShellFolderContextMenu(Desktop(), _hwnd, 1, &pidl, pos.x, pos.y));
|
CHECKERROR(ShellFolderContextMenu(GetDesktopFolder(), _hwnd, 1, &pidl, pos.x, pos.y));
|
||||||
}
|
}
|
||||||
break;}
|
break;}
|
||||||
|
|
||||||
|
|
|
@ -645,11 +645,11 @@ int MainFrame::Command(int id, int code)
|
||||||
#ifndef _NO_MDI
|
#ifndef _NO_MDI
|
||||||
return DefFrameProc(_hwnd, _hmdiclient, WM_COMMAND, MAKELONG(id,code), 0);
|
return DefFrameProc(_hwnd, _hmdiclient, WM_COMMAND, MAKELONG(id,code), 0);
|
||||||
#else
|
#else
|
||||||
return 0;
|
return 1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ void ShellBrowserChild::InitializeTree()
|
||||||
TreeView_SetImageList(_left_hwnd, _himlSmall, TVSIL_NORMAL);
|
TreeView_SetImageList(_left_hwnd, _himlSmall, TVSIL_NORMAL);
|
||||||
TreeView_SetScrollTime(_left_hwnd, 100);
|
TreeView_SetScrollTime(_left_hwnd, 100);
|
||||||
|
|
||||||
const String& root_name = Desktop().get_name(_create_info._root_shell_path, SHGDN_FORPARSING);
|
const String& root_name = GetDesktopFolder().get_name(_create_info._root_shell_path, SHGDN_FORPARSING);
|
||||||
|
|
||||||
_root._drive_type = DRIVE_UNKNOWN;
|
_root._drive_type = DRIVE_UNKNOWN;
|
||||||
lstrcpy(_root._volname, root_name); // most of the time "Desktop"
|
lstrcpy(_root._volname, root_name); // most of the time "Desktop"
|
||||||
|
@ -125,7 +125,7 @@ void ShellBrowserChild::InitializeTree()
|
||||||
we should call read_tree() here to iterate through the hierarchy and open all folders from shell_info._root_shell_path to shell_info._shell_path
|
we should call read_tree() here to iterate through the hierarchy and open all folders from shell_info._root_shell_path to shell_info._shell_path
|
||||||
-> see FileChildWindow::FileChildWindow()
|
-> see FileChildWindow::FileChildWindow()
|
||||||
*/
|
*/
|
||||||
_root._entry = new ShellDirectory(Desktop(), _create_info._root_shell_path, _hwnd);
|
_root._entry = new ShellDirectory(GetDesktopFolder(), _create_info._root_shell_path, _hwnd);
|
||||||
_root._entry->read_directory();
|
_root._entry->read_directory();
|
||||||
|
|
||||||
/* already filled by ShellDirectory constructor
|
/* already filled by ShellDirectory constructor
|
||||||
|
@ -213,7 +213,7 @@ void ShellBrowserChild::Tree_DoItemMenu(HWND hwndTreeView, HTREEITEM hItem, LPPO
|
||||||
|
|
||||||
if (entry->_etype == ET_SHELL) {
|
if (entry->_etype == ET_SHELL) {
|
||||||
ShellDirectory* dir = static_cast<ShellDirectory*>(entry->_up);
|
ShellDirectory* dir = static_cast<ShellDirectory*>(entry->_up);
|
||||||
ShellFolder folder = dir? dir->_folder: Desktop();
|
ShellFolder folder = dir? dir->_folder: GetDesktopFolder();
|
||||||
LPCITEMIDLIST pidl = static_cast<ShellEntry*>(entry)->_pidl;
|
LPCITEMIDLIST pidl = static_cast<ShellEntry*>(entry)->_pidl;
|
||||||
|
|
||||||
CHECKERROR(ShellFolderContextMenu(folder, ::GetParent(hwndTreeView), 1, &pidl, pptScreen->x, pptScreen->y));
|
CHECKERROR(ShellFolderContextMenu(folder, ::GetParent(hwndTreeView), 1, &pidl, pptScreen->x, pptScreen->y));
|
||||||
|
@ -222,7 +222,7 @@ void ShellBrowserChild::Tree_DoItemMenu(HWND hwndTreeView, HTREEITEM hItem, LPPO
|
||||||
LPCITEMIDLIST pidl = shell_path;
|
LPCITEMIDLIST pidl = shell_path;
|
||||||
|
|
||||||
///@todo use parent folder instead of desktop
|
///@todo use parent folder instead of desktop
|
||||||
CHECKERROR(ShellFolderContextMenu(Desktop(), _hwnd, 1, &pidl, pptScreen->x, pptScreen->y));
|
CHECKERROR(ShellFolderContextMenu(GetDesktopFolder(), _hwnd, 1, &pidl, pptScreen->x, pptScreen->y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,5 +119,5 @@ inline IShellFolder* ShellEntry::get_parent_folder() const
|
||||||
if (_up)
|
if (_up)
|
||||||
return static_cast<ShellDirectory*>(_up)->_folder;
|
return static_cast<ShellDirectory*>(_up)->_folder;
|
||||||
else
|
else
|
||||||
return Desktop();
|
return GetDesktopFolder();
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,10 +201,10 @@ WebChildWindow::WebChildWindow(HWND hwnd, const WebChildWndInfo& info)
|
||||||
|
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
// handling events using DWebBrowserEvents
|
// handling events using DWebBrowserEvents
|
||||||
_evt_handler1 = new DWebBrowserEventsHandler(_hwnd, _control);
|
_evt_handler1 = auto_ptr<DWebBrowserEventsHandler>(new DWebBrowserEventsHandler(_hwnd, _control));
|
||||||
|
|
||||||
// handling events using DWebBrowserEvents2
|
// handling events using DWebBrowserEvents2
|
||||||
_evt_handler2 = new DWebBrowserEvents2Handler(_hwnd, _control);
|
_evt_handler2 = auto_ptr<DWebBrowserEvents2Handler>(new DWebBrowserEvents2Handler(_hwnd, _control));
|
||||||
|
|
||||||
SIfacePtr<IWebBrowser2> browser(get_browser());
|
SIfacePtr<IWebBrowser2> browser(get_browser());
|
||||||
|
|
||||||
|
@ -217,12 +217,6 @@ WebChildWindow::WebChildWindow(HWND hwnd, const WebChildWndInfo& info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebChildWindow::~WebChildWindow()
|
|
||||||
{
|
|
||||||
delete _evt_handler2;
|
|
||||||
delete _evt_handler1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LRESULT WebChildWindow::WndProc(UINT message, WPARAM wparam, LPARAM lparam)
|
LRESULT WebChildWindow::WndProc(UINT message, WPARAM wparam, LPARAM lparam)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1341,7 +1341,6 @@ struct WebChildWindow : public IPCtrlWindow<ChildWindow, SIfacePtr<IWebBrowser2>
|
||||||
typedef IPCtrlWindow<ChildWindow, SIfacePtr<IWebBrowser2> > super;
|
typedef IPCtrlWindow<ChildWindow, SIfacePtr<IWebBrowser2> > super;
|
||||||
|
|
||||||
WebChildWindow(HWND hwnd, const WebChildWndInfo& info);
|
WebChildWindow(HWND hwnd, const WebChildWndInfo& info);
|
||||||
~WebChildWindow();
|
|
||||||
|
|
||||||
static WebChildWindow* create(HWND hmdiclient, const FileChildWndInfo& info)
|
static WebChildWindow* create(HWND hmdiclient, const FileChildWndInfo& info)
|
||||||
{
|
{
|
||||||
|
@ -1359,8 +1358,8 @@ struct WebChildWindow : public IPCtrlWindow<ChildWindow, SIfacePtr<IWebBrowser2>
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DWebBrowserEventsHandler* _evt_handler1;
|
auto_ptr<DWebBrowserEventsHandler> _evt_handler1;
|
||||||
DWebBrowserEvents2Handler* _evt_handler2;
|
auto_ptr<DWebBrowserEvents2Handler> _evt_handler2;
|
||||||
|
|
||||||
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam);
|
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam);
|
||||||
};
|
};
|
||||||
|
|
|
@ -63,6 +63,8 @@ DesktopBar::~DesktopBar()
|
||||||
|
|
||||||
HWND DesktopBar::Create()
|
HWND DesktopBar::Create()
|
||||||
{
|
{
|
||||||
|
static BtnWindowClass wcDesktopBar(CLASSNAME_EXPLORERBAR);
|
||||||
|
|
||||||
RECT rect;
|
RECT rect;
|
||||||
|
|
||||||
rect.left = -2; // hide left border
|
rect.left = -2; // hide left border
|
||||||
|
@ -75,7 +77,7 @@ HWND DesktopBar::Create()
|
||||||
rect.bottom = rect.top + DESKTOPBARBAR_HEIGHT + 2;
|
rect.bottom = rect.top + DESKTOPBARBAR_HEIGHT + 2;
|
||||||
|
|
||||||
return Window::Create(WINDOW_CREATOR(DesktopBar), WS_EX_PALETTEWINDOW,
|
return Window::Create(WINDOW_CREATOR(DesktopBar), WS_EX_PALETTEWINDOW,
|
||||||
BtnWindowClass(CLASSNAME_EXPLORERBAR), TITLE_EXPLORERBAR,
|
wcDesktopBar, TITLE_EXPLORERBAR,
|
||||||
WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE,
|
WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE,
|
||||||
rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0);
|
rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0);
|
||||||
}
|
}
|
||||||
|
@ -124,7 +126,7 @@ void DesktopBar::RegisterHotkeys()
|
||||||
void DesktopBar::ProcessHotKey(int id_hotkey)
|
void DesktopBar::ProcessHotKey(int id_hotkey)
|
||||||
{
|
{
|
||||||
switch(id_hotkey) {
|
switch(id_hotkey) {
|
||||||
case 0: explorer_show_frame(_hwnd, SW_SHOWNORMAL); break;
|
case 0: explorer_show_frame(SW_SHOWNORMAL); break;
|
||||||
///@todo implement all common hotkeys
|
///@todo implement all common hotkeys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,9 +237,31 @@ int DesktopBar::Command(int id, int code)
|
||||||
ExplorerPropertySheet(_hwnd);
|
ExplorerPropertySheet(_hwnd);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ID_MINIMIZE_ALL:
|
||||||
|
; ///@todo minimize/restore all windows on the desktop
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ID_EXPLORE:
|
||||||
|
explorer_show_frame(SW_SHOWNORMAL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ID_SWITCH_DESKTOP_1:
|
||||||
|
case ID_SWITCH_DESKTOP_1+1:
|
||||||
|
case ID_SWITCH_DESKTOP_1+2:
|
||||||
|
case ID_SWITCH_DESKTOP_1+3: {
|
||||||
|
int desktop_idx = id - ID_SWITCH_DESKTOP_1;
|
||||||
|
|
||||||
|
g_Globals._desktops.SwitchToDesktop(desktop_idx);
|
||||||
|
|
||||||
|
if (_hwndQuickLaunch)
|
||||||
|
PostMessage(_hwndQuickLaunch, PM_UPDATE_DESKTOP, desktop_idx, 0);
|
||||||
|
break;}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if ((id&~0xFF) == IDC_FIRST_QUICK_ID)
|
if (_hwndQuickLaunch)
|
||||||
SendMessage(_hwndQuickLaunch, WM_COMMAND, MAKEWPARAM(id,code), 0);
|
return SendMessage(_hwndQuickLaunch, WM_COMMAND, MAKEWPARAM(id,code), 0);
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -104,7 +104,7 @@ void QuickLaunchBar::AddShortcuts()
|
||||||
_stprintf(path, TEXT("%s\\")QUICKLAUNCH_FOLDER, (LPCTSTR)app_data);
|
_stprintf(path, TEXT("%s\\")QUICKLAUNCH_FOLDER, (LPCTSTR)app_data);
|
||||||
|
|
||||||
RecursiveCreateDirectory(path);
|
RecursiveCreateDirectory(path);
|
||||||
_dir = new ShellDirectory(Desktop(), path, _hwnd);
|
_dir = new ShellDirectory(GetDesktopFolder(), path, _hwnd);
|
||||||
|
|
||||||
_dir->smart_scan(SCAN_EXTRACT_ICONS|SCAN_FILESYSTEM);
|
_dir->smart_scan(SCAN_EXTRACT_ICONS|SCAN_FILESYSTEM);
|
||||||
} catch(COMException&) {
|
} catch(COMException&) {
|
||||||
|
@ -118,8 +118,38 @@ void QuickLaunchBar::AddShortcuts()
|
||||||
COLORREF bk_color = GetSysColor(COLOR_BTNFACE);
|
COLORREF bk_color = GetSysColor(COLOR_BTNFACE);
|
||||||
HBRUSH bk_brush = GetSysColorBrush(COLOR_BTNFACE);
|
HBRUSH bk_brush = GetSysColorBrush(COLOR_BTNFACE);
|
||||||
|
|
||||||
AddButton(g_Globals._icon_cache.get_icon(ICID_LOGOFF/*@@*/).create_bitmap(bk_color, bk_brush, canvas), ResString(IDS_MINIMIZE_ALL), NULL);
|
AddButton(ID_MINIMIZE_ALL, g_Globals._icon_cache.get_icon(ICID_LOGOFF/*@@*/).create_bitmap(bk_color, bk_brush, canvas), ResString(IDS_MINIMIZE_ALL), NULL);
|
||||||
AddButton(g_Globals._icon_cache.get_icon(ICID_EXPLORER).create_bitmap(bk_color, bk_brush, canvas), ResString(IDS_TITLE), NULL);
|
AddButton(ID_EXPLORE, g_Globals._icon_cache.get_icon(ICID_EXPLORER).create_bitmap(bk_color, bk_brush, canvas), ResString(IDS_TITLE), NULL);
|
||||||
|
|
||||||
|
TBBUTTON sep = {0, -1, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0};
|
||||||
|
SendMessage(_hwnd, TB_INSERTBUTTON, INT_MAX, (LPARAM)&sep);
|
||||||
|
|
||||||
|
int cur_desktop = g_Globals._desktops._current_desktop;
|
||||||
|
ResString desktop_fmt(IDS_DESKTOP_NUM);
|
||||||
|
|
||||||
|
HDC hdc = CreateCompatibleDC(canvas);
|
||||||
|
DWORD size = SendMessage(_hwnd, TB_GETBUTTONSIZE, 0, 0);
|
||||||
|
int cx = LOWORD(size);
|
||||||
|
int cy = HIWORD(size);
|
||||||
|
RECT rect = {0, 0, cx, cy};
|
||||||
|
RECT textRect = {0, 0, cx-7, cy-7};
|
||||||
|
for(int i=0; i<DESKTOP_COUNT; ++i) {
|
||||||
|
HBITMAP hbmp = CreateCompatibleBitmap(canvas, cx, cy);
|
||||||
|
HBITMAP hbmp_old = SelectBitmap(hdc, hbmp);
|
||||||
|
|
||||||
|
FmtString num_txt(TEXT("%d"), i+1);
|
||||||
|
TextColor color(hdc, RGB(64,64,64));
|
||||||
|
BkMode mode(hdc, TRANSPARENT);
|
||||||
|
FillRect(hdc, &rect, GetSysColorBrush(COLOR_BTNFACE));
|
||||||
|
DrawText(hdc, num_txt, num_txt.length(), &textRect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
|
||||||
|
|
||||||
|
SelectBitmap(hdc, hbmp_old);
|
||||||
|
|
||||||
|
AddButton(ID_SWITCH_DESKTOP_1+i, hbmp, FmtString(desktop_fmt, i+1), NULL, cur_desktop==i?TBSTATE_ENABLED|TBSTATE_CHECKED:TBSTATE_ENABLED);
|
||||||
|
}
|
||||||
|
DeleteDC(hdc);
|
||||||
|
|
||||||
|
SendMessage(_hwnd, TB_INSERTBUTTON, INT_MAX, (LPARAM)&sep);
|
||||||
|
|
||||||
for(Entry*entry=_dir->_down; entry; entry=entry->_next) {
|
for(Entry*entry=_dir->_down; entry; entry=entry->_next) {
|
||||||
// hide files like "desktop.ini"
|
// hide files like "desktop.ini"
|
||||||
|
@ -130,7 +160,7 @@ void QuickLaunchBar::AddShortcuts()
|
||||||
if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
HBITMAP hbmp = g_Globals._icon_cache.get_icon(entry->_icon_id).create_bitmap(bk_color, bk_brush, canvas);
|
HBITMAP hbmp = g_Globals._icon_cache.get_icon(entry->_icon_id).create_bitmap(bk_color, bk_brush, canvas);
|
||||||
|
|
||||||
AddButton(hbmp, entry->_display_name, entry); //entry->_etype==ET_SHELL? desktop_folder.get_name(static_cast<ShellEntry*>(entry)->_pidl): entry->_display_name);
|
AddButton(_next_id++, hbmp, entry->_display_name, entry); //entry->_etype==ET_SHELL? desktop_folder.get_name(static_cast<ShellEntry*>(entry)->_pidl): entry->_display_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,28 +168,34 @@ void QuickLaunchBar::AddShortcuts()
|
||||||
SendMessage(GetParent(_hwnd), PM_RESIZE_CHILDREN, 0, 0);
|
SendMessage(GetParent(_hwnd), PM_RESIZE_CHILDREN, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickLaunchBar::AddButton(HBITMAP hbmp, LPCTSTR name, Entry* entry)
|
void QuickLaunchBar::AddButton(int id, HBITMAP hbmp, LPCTSTR name, Entry* entry, int flags)
|
||||||
{
|
{
|
||||||
TBADDBITMAP ab = {0, (UINT_PTR)hbmp};
|
TBADDBITMAP ab = {0, (UINT_PTR)hbmp};
|
||||||
int bmp_idx = SendMessage(_hwnd, TB_ADDBITMAP, 1, (LPARAM)&ab);
|
int bmp_idx = SendMessage(_hwnd, TB_ADDBITMAP, 1, (LPARAM)&ab);
|
||||||
|
|
||||||
QuickLaunchEntry qle;
|
QuickLaunchEntry qle;
|
||||||
|
|
||||||
int id = _next_id++;
|
|
||||||
|
|
||||||
qle._hbmp = hbmp;
|
qle._hbmp = hbmp;
|
||||||
qle._title = name;
|
qle._title = name;
|
||||||
qle._entry = entry;
|
qle._entry = entry;
|
||||||
|
|
||||||
_entries[id] = qle;
|
_entries[id] = qle;
|
||||||
|
|
||||||
TBBUTTON btn = {0, 0, TBSTATE_ENABLED, BTNS_BUTTON|BTNS_NOPREFIX, {0, 0}, 0, 0};
|
TBBUTTON btn = {0, 0, flags, BTNS_BUTTON|BTNS_NOPREFIX, {0, 0}, 0, 0};
|
||||||
|
|
||||||
btn.idCommand = id;
|
btn.idCommand = id;
|
||||||
btn.iBitmap = bmp_idx;
|
btn.iBitmap = bmp_idx;
|
||||||
int idx = SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0);
|
|
||||||
|
|
||||||
SendMessage(_hwnd, TB_INSERTBUTTON, idx, (LPARAM)&btn);
|
SendMessage(_hwnd, TB_INSERTBUTTON, INT_MAX, (LPARAM)&btn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuickLaunchBar::UpdateDesktopButtons(int desktop_idx)
|
||||||
|
{
|
||||||
|
for(int i=0; i<DESKTOP_COUNT; ++i) {
|
||||||
|
TBBUTTONINFO tbi = {sizeof(TBBUTTONINFO), TBIF_STATE, 0, 0, desktop_idx==i? TBSTATE_ENABLED|TBSTATE_CHECKED: TBSTATE_ENABLED};
|
||||||
|
|
||||||
|
SendMessage(_hwnd, TB_SETBUTTONINFO, ID_SWITCH_DESKTOP_1+i, (LPARAM)&tbi);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT QuickLaunchBar::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
|
LRESULT QuickLaunchBar::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
|
||||||
|
@ -172,6 +208,10 @@ LRESULT QuickLaunchBar::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
|
||||||
case PM_GET_WIDTH:
|
case PM_GET_WIDTH:
|
||||||
return _entries.size()*_btn_dist;
|
return _entries.size()*_btn_dist;
|
||||||
|
|
||||||
|
case PM_UPDATE_DESKTOP:
|
||||||
|
UpdateDesktopButtons(wparam);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return super::WndProc(nmsg, wparam, lparam);
|
return super::WndProc(nmsg, wparam, lparam);
|
||||||
}
|
}
|
||||||
|
@ -183,17 +223,17 @@ int QuickLaunchBar::Command(int id, int code)
|
||||||
{
|
{
|
||||||
CONTEXT("QuickLaunchBar::Command()");
|
CONTEXT("QuickLaunchBar::Command()");
|
||||||
|
|
||||||
|
if ((id&~0xFF) == IDC_FIRST_QUICK_ID) {
|
||||||
QuickLaunchEntry& qle = _entries[id];
|
QuickLaunchEntry& qle = _entries[id];
|
||||||
|
|
||||||
if (qle._entry)
|
if (qle._entry) {
|
||||||
qle._entry->launch_entry(_hwnd);
|
qle._entry->launch_entry(_hwnd);
|
||||||
else if (id == IDC_FIRST_QUICK_ID)
|
|
||||||
; ///@todo minimize/restore all windows
|
|
||||||
else if (id == IDC_FIRST_QUICK_ID+1)
|
|
||||||
explorer_show_frame(_hwnd, SW_SHOWNORMAL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int QuickLaunchBar::Notify(int id, NMHDR* pnmh)
|
int QuickLaunchBar::Notify(int id, NMHDR* pnmh)
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#define IDW_QUICKLAUNCHBAR 101
|
#define IDW_QUICKLAUNCHBAR 101
|
||||||
|
|
||||||
#define PM_REFRESH (WM_APP+0x1B)
|
#define PM_REFRESH (WM_APP+0x1B)
|
||||||
|
#define PM_UPDATE_DESKTOP (WM_APP+0x1C)
|
||||||
|
|
||||||
#define IDC_FIRST_QUICK_ID 0x4000
|
#define IDC_FIRST_QUICK_ID 0x4000
|
||||||
|
|
||||||
|
@ -78,5 +79,6 @@ protected:
|
||||||
int _btn_dist;
|
int _btn_dist;
|
||||||
|
|
||||||
void AddShortcuts();
|
void AddShortcuts();
|
||||||
void AddButton(HBITMAP hbmp, LPCTSTR name, Entry* entry);
|
void AddButton(int id, HBITMAP hbmp, LPCTSTR name, Entry* entry, int flags=TBSTATE_ENABLED);
|
||||||
|
void UpdateDesktopButtons(int desktop_idx);
|
||||||
};
|
};
|
||||||
|
|
|
@ -64,7 +64,7 @@ StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info)
|
||||||
{
|
{
|
||||||
for(StartMenuFolders::const_iterator it=create_info._folders.begin(); it!=create_info._folders.end(); ++it)
|
for(StartMenuFolders::const_iterator it=create_info._folders.begin(); it!=create_info._folders.end(); ++it)
|
||||||
if (*it)
|
if (*it)
|
||||||
_dirs.push_back(ShellDirectory(Desktop(), *it, _hwnd));
|
_dirs.push_back(ShellDirectory(GetDesktopFolder(), *it, _hwnd));
|
||||||
|
|
||||||
_next_id = IDC_FIRST_MENU;
|
_next_id = IDC_FIRST_MENU;
|
||||||
_submenu_id = 0;
|
_submenu_id = 0;
|
||||||
|
@ -1252,7 +1252,7 @@ StartMenuRoot::StartMenuRoot(HWND hwnd)
|
||||||
#endif
|
#endif
|
||||||
try {
|
try {
|
||||||
// insert directory "All Users\Start Menu"
|
// insert directory "All Users\Start Menu"
|
||||||
ShellDirectory cmn_startmenu(Desktop(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
|
ShellDirectory cmn_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
|
||||||
_dirs.push_back(StartMenuDirectory(cmn_startmenu, false)); // don't add subfolders
|
_dirs.push_back(StartMenuDirectory(cmn_startmenu, false)); // don't add subfolders
|
||||||
} catch(COMException&) {
|
} catch(COMException&) {
|
||||||
// ignore exception and don't show additional shortcuts
|
// ignore exception and don't show additional shortcuts
|
||||||
|
@ -1260,7 +1260,8 @@ StartMenuRoot::StartMenuRoot(HWND hwnd)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// insert directory "<user name>\Start Menu"
|
// insert directory "<user name>\Start Menu"
|
||||||
ShellDirectory usr_startmenu(Desktop(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
|
|
||||||
|
ShellDirectory usr_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
|
||||||
_dirs.push_back(StartMenuDirectory(usr_startmenu, false)); // don't add subfolders
|
_dirs.push_back(StartMenuDirectory(usr_startmenu, false)); // don't add subfolders
|
||||||
} catch(COMException&) {
|
} catch(COMException&) {
|
||||||
// ignore exception and don't show additional shortcuts
|
// ignore exception and don't show additional shortcuts
|
||||||
|
@ -1529,7 +1530,7 @@ int StartMenuHandler::Command(int id, int code)
|
||||||
|
|
||||||
case IDC_EXPLORE:
|
case IDC_EXPLORE:
|
||||||
CloseStartMenu(id);
|
CloseStartMenu(id);
|
||||||
explorer_show_frame(_hwnd, SW_SHOWNORMAL);
|
explorer_show_frame(SW_SHOWNORMAL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IDC_LAUNCH:
|
case IDC_LAUNCH:
|
||||||
|
|
|
@ -121,15 +121,17 @@ NotifyArea::~NotifyArea()
|
||||||
|
|
||||||
HWND NotifyArea::Create(HWND hwndParent)
|
HWND NotifyArea::Create(HWND hwndParent)
|
||||||
{
|
{
|
||||||
|
static BtnWindowClass wcTrayNotify(CLASSNAME_TRAYNOTIFY, CS_DBLCLKS);
|
||||||
|
|
||||||
ClientRect clnt(hwndParent);
|
ClientRect clnt(hwndParent);
|
||||||
|
|
||||||
#ifndef _ROS_
|
#ifndef _ROS_
|
||||||
return Window::Create(WINDOW_CREATOR(NotifyArea), WS_EX_STATICEDGE,
|
return Window::Create(WINDOW_CREATOR(NotifyArea), WS_EX_STATICEDGE,
|
||||||
BtnWindowClass(CLASSNAME_TRAYNOTIFY,CS_DBLCLKS), TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE,
|
wcTrayNotify, TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE,
|
||||||
clnt.right-(NOTIFYAREA_WIDTH_DEF+1), 1, NOTIFYAREA_WIDTH_DEF, clnt.bottom-2, hwndParent);
|
clnt.right-(NOTIFYAREA_WIDTH_DEF+1), 1, NOTIFYAREA_WIDTH_DEF, clnt.bottom-2, hwndParent);
|
||||||
#else
|
#else
|
||||||
return Window::Create(WINDOW_CREATOR(NotifyArea), 0,
|
return Window::Create(WINDOW_CREATOR(NotifyArea), 0,
|
||||||
BtnWindowClass(CLASSNAME_TRAYNOTIFY,CS_DBLCLKS), TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE,
|
wcTrayNotify, TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE,
|
||||||
clnt.right-(NOTIFYAREA_WIDTH_DEF+1), 1, NOTIFYAREA_WIDTH_DEF, clnt.bottom-2, hwndParent);
|
clnt.right-(NOTIFYAREA_WIDTH_DEF+1), 1, NOTIFYAREA_WIDTH_DEF, clnt.bottom-2, hwndParent);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -347,6 +349,8 @@ ClockWindow::ClockWindow(HWND hwnd)
|
||||||
|
|
||||||
HWND ClockWindow::Create(HWND hwndParent)
|
HWND ClockWindow::Create(HWND hwndParent)
|
||||||
{
|
{
|
||||||
|
static BtnWindowClass wcClock(CLASSNAME_CLOCKWINDOW, CS_DBLCLKS);
|
||||||
|
|
||||||
ClientRect clnt(hwndParent);
|
ClientRect clnt(hwndParent);
|
||||||
|
|
||||||
WindowCanvas canvas(hwndParent);
|
WindowCanvas canvas(hwndParent);
|
||||||
|
@ -362,7 +366,7 @@ HWND ClockWindow::Create(HWND hwndParent)
|
||||||
int clockwindowWidth = rect.right-rect.left + 4;
|
int clockwindowWidth = rect.right-rect.left + 4;
|
||||||
|
|
||||||
return Window::Create(WINDOW_CREATOR(ClockWindow), 0,
|
return Window::Create(WINDOW_CREATOR(ClockWindow), 0,
|
||||||
BtnWindowClass(CLASSNAME_CLOCKWINDOW,CS_DBLCLKS), NULL, WS_CHILD|WS_VISIBLE,
|
wcClock, NULL, WS_CHILD|WS_VISIBLE,
|
||||||
clnt.right-(clockwindowWidth), 1, clockwindowWidth, clnt.bottom-2, hwndParent);
|
clnt.right-(clockwindowWidth), 1, clockwindowWidth, clnt.bottom-2, hwndParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ CommonShellMalloc ShellMalloc::s_cmn_shell_malloc;
|
||||||
|
|
||||||
// common desktop object
|
// common desktop object
|
||||||
|
|
||||||
ShellFolder& Desktop()
|
ShellFolder& GetDesktopFolder()
|
||||||
{
|
{
|
||||||
static CommonDesktop s_desktop;
|
static CommonDesktop s_desktop;
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ ShellFolder::ShellFolder(LPCITEMIDLIST pidl)
|
||||||
CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
|
CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
|
||||||
|
|
||||||
IShellFolder* ptr;
|
IShellFolder* ptr;
|
||||||
IShellFolder* parent = Desktop();
|
IShellFolder* parent = GetDesktopFolder();
|
||||||
|
|
||||||
if (pidl && pidl->mkid.cb)
|
if (pidl && pidl->mkid.cb)
|
||||||
CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr));
|
CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr));
|
||||||
|
@ -274,7 +274,7 @@ ShellFolder::ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl)
|
||||||
if (pidl && pidl->mkid.cb)
|
if (pidl && pidl->mkid.cb)
|
||||||
CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p));
|
CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p));
|
||||||
else
|
else
|
||||||
_p = Desktop();
|
_p = GetDesktopFolder();
|
||||||
|
|
||||||
_p->AddRef();
|
_p->AddRef();
|
||||||
}
|
}
|
||||||
|
@ -284,9 +284,9 @@ ShellFolder::ShellFolder(LPCITEMIDLIST pidl)
|
||||||
CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
|
CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
|
||||||
|
|
||||||
if (pidl && pidl->mkid.cb)
|
if (pidl && pidl->mkid.cb)
|
||||||
CHECKERROR(Desktop()->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p));
|
CHECKERROR(GetDesktopFolder()->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p));
|
||||||
else
|
else
|
||||||
_p = Desktop();
|
_p = GetDesktopFolder();
|
||||||
|
|
||||||
_p->AddRef();
|
_p->AddRef();
|
||||||
}
|
}
|
||||||
|
|
|
@ -523,7 +523,7 @@ struct ShellLinkPtr : public SIfacePtr<IShellLink>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
extern ShellFolder& Desktop();
|
extern ShellFolder& GetDesktopFolder();
|
||||||
|
|
||||||
|
|
||||||
#ifdef UNICODE
|
#ifdef UNICODE
|
||||||
|
@ -574,7 +574,7 @@ struct ShellPath : public SShellPtr<ITEMIDLIST>
|
||||||
OBJ_CONTEXT("ShellPath::ShellPath(LPCWSTR)", path);
|
OBJ_CONTEXT("ShellPath::ShellPath(LPCWSTR)", path);
|
||||||
|
|
||||||
if (path)
|
if (path)
|
||||||
CHECKERROR(Desktop()->ParseDisplayName(0, 0, (LPOLESTR)path, NULL, &_p, 0));
|
CHECKERROR(GetDesktopFolder()->ParseDisplayName(0, 0, (LPOLESTR)path, NULL, &_p, 0));
|
||||||
else
|
else
|
||||||
_p = NULL;
|
_p = NULL;
|
||||||
}
|
}
|
||||||
|
@ -600,7 +600,7 @@ struct ShellPath : public SShellPtr<ITEMIDLIST>
|
||||||
|
|
||||||
if (path) {
|
if (path) {
|
||||||
MultiByteToWideChar(CP_ACP, 0, path, -1, b, MAX_PATH);
|
MultiByteToWideChar(CP_ACP, 0, path, -1, b, MAX_PATH);
|
||||||
CHECKERROR(Desktop()->ParseDisplayName(0, 0, b, NULL, &_p, 0));
|
CHECKERROR(GetDesktopFolder()->ParseDisplayName(0, 0, b, NULL, &_p, 0));
|
||||||
} else
|
} else
|
||||||
_p = NULL;
|
_p = NULL;
|
||||||
}
|
}
|
||||||
|
@ -728,7 +728,7 @@ struct ShellPath : public SShellPtr<ITEMIDLIST>
|
||||||
|
|
||||||
void split(ShellPath& parent, ShellPath& obj) const;
|
void split(ShellPath& parent, ShellPath& obj) const;
|
||||||
|
|
||||||
void GetUIObjectOf(REFIID riid, LPVOID* ppvOut, HWND hWnd=0, ShellFolder& sf=Desktop());
|
void GetUIObjectOf(REFIID riid, LPVOID* ppvOut, HWND hWnd=0, ShellFolder& sf=GetDesktopFolder());
|
||||||
|
|
||||||
ShellFolder get_folder()
|
ShellFolder get_folder()
|
||||||
{
|
{
|
||||||
|
@ -891,7 +891,7 @@ struct DesktopFolderPath : public SpecialFolderPath
|
||||||
struct SpecialFolder : public ShellFolder
|
struct SpecialFolder : public ShellFolder
|
||||||
{
|
{
|
||||||
SpecialFolder(int folder, HWND hwnd)
|
SpecialFolder(int folder, HWND hwnd)
|
||||||
: ShellFolder(Desktop(), SpecialFolderPath(folder, hwnd))
|
: ShellFolder(GetDesktopFolder(), SpecialFolderPath(folder, hwnd))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,7 +100,7 @@ int main()
|
||||||
// example for enumerating shell namespace objects
|
// example for enumerating shell namespace objects
|
||||||
|
|
||||||
cout << "Desktop:\n";
|
cout << "Desktop:\n";
|
||||||
dump_shell_namespace(Desktop());
|
dump_shell_namespace(GetDesktopFolder());
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
|
||||||
cout << "C:\\\n";
|
cout << "C:\\\n";
|
||||||
|
|
|
@ -290,7 +290,7 @@ LRESULT Window::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
|
||||||
|
|
||||||
int Window::Command(int id, int code)
|
int Window::Command(int id, int code)
|
||||||
{
|
{
|
||||||
return 1; // WM_COMMAND not yet handled
|
return 1; // no command handler found
|
||||||
}
|
}
|
||||||
|
|
||||||
int Window::Notify(int id, NMHDR* pnmh)
|
int Window::Notify(int id, NMHDR* pnmh)
|
||||||
|
@ -359,7 +359,7 @@ LRESULT SubclassedWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
|
||||||
|
|
||||||
int SubclassedWindow::Command(int id, int code)
|
int SubclassedWindow::Command(int id, int code)
|
||||||
{
|
{
|
||||||
return 1; // WM_COMMAND not yet handled
|
return 1; // no command handler found
|
||||||
}
|
}
|
||||||
|
|
||||||
int SubclassedWindow::Notify(int id, NMHDR* pnmh)
|
int SubclassedWindow::Notify(int id, NMHDR* pnmh)
|
||||||
|
|
Loading…
Reference in a new issue