mirror of
https://github.com/reactos/reactos.git
synced 2024-08-20 02:13:29 +00:00
![David Quintana](/assets/img/avatar_default.png)
* Make use of the wine debug functions and turn DbgPrints into TRACE/WARN/ERR. [SHELL32] * Turn some DbgPrints into TRACEs. svn path=/branches/shell-experiments/; revision=63912
2237 lines
64 KiB
C
2237 lines
64 KiB
C
/*
|
|
* ReactOS Explorer
|
|
*
|
|
* Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
/* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
|
|
5 seconds */
|
|
#define DUMP_TASKS 0
|
|
|
|
static const TCHAR szTaskSwitchWndClass[] = TEXT("MSTaskSwWClass");
|
|
static const TCHAR szRunningApps[] = TEXT("Running Applications");
|
|
|
|
typedef struct _TASK_GROUP
|
|
{
|
|
/* We have to use a linked list instead of an array so we don't have to
|
|
update all pointers to groups in the task item array when removing
|
|
groups. */
|
|
struct _TASK_GROUP *Next;
|
|
|
|
DWORD dwTaskCount;
|
|
DWORD dwProcessId;
|
|
INT Index;
|
|
union
|
|
{
|
|
DWORD dwFlags;
|
|
struct
|
|
{
|
|
|
|
DWORD IsCollapsed : 1;
|
|
};
|
|
};
|
|
} TASK_GROUP, *PTASK_GROUP;
|
|
|
|
typedef struct _TASK_ITEM
|
|
{
|
|
HWND hWnd;
|
|
PTASK_GROUP Group;
|
|
INT Index;
|
|
INT IconIndex;
|
|
|
|
|
|
|
|
union
|
|
{
|
|
DWORD dwFlags;
|
|
struct
|
|
{
|
|
|
|
/* IsFlashing is TRUE when the task bar item should be flashing. */
|
|
DWORD IsFlashing : 1;
|
|
|
|
/* RenderFlashed is only TRUE if the task bar item should be
|
|
drawn with a flash. */
|
|
DWORD RenderFlashed : 1;
|
|
};
|
|
};
|
|
} TASK_ITEM, *PTASK_ITEM;
|
|
|
|
#define TASK_ITEM_ARRAY_ALLOC 64
|
|
|
|
typedef struct _TASK_SWITCH_WND
|
|
{
|
|
HWND hWnd;
|
|
HWND hWndNotify;
|
|
|
|
UINT ShellHookMsg;
|
|
ITrayWindow *Tray;
|
|
|
|
PTASK_GROUP TaskGroups;
|
|
|
|
WORD TaskItemCount;
|
|
WORD AllocatedTaskItems;
|
|
PTASK_ITEM TaskItems;
|
|
PTASK_ITEM ActiveTaskItem;
|
|
|
|
HTHEME TaskBandTheme;
|
|
HWND hWndToolbar;
|
|
UINT TbButtonsPerLine;
|
|
WORD ToolbarBtnCount;
|
|
HIMAGELIST TaskIcons;
|
|
|
|
union
|
|
{
|
|
DWORD dwFlags;
|
|
struct
|
|
{
|
|
DWORD IsGroupingEnabled : 1;
|
|
DWORD IsDestroying : 1;
|
|
DWORD IsToolbarSubclassed : 1;
|
|
};
|
|
};
|
|
|
|
SIZE ButtonSize;
|
|
TCHAR szBuf[255];
|
|
} TASK_SWITCH_WND, *PTASK_SWITCH_WND;
|
|
|
|
#define TSW_TOOLBAR_SUBCLASS_ID 1
|
|
|
|
#define MAX_TASKS_COUNT (0x7FFF)
|
|
|
|
static VOID TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This,
|
|
IN BOOL bRedrawDisabled);
|
|
|
|
static LPTSTR
|
|
TaskSwitchWnd_GetWndTextFromTaskItem(IN OUT PTASK_SWITCH_WND This,
|
|
IN PTASK_ITEM TaskItem)
|
|
{
|
|
/* Get the window text without sending a message so we don't hang if an
|
|
application isn't responding! */
|
|
if (InternalGetWindowText(TaskItem->hWnd,
|
|
This->szBuf,
|
|
sizeof(This->szBuf) / sizeof(This->szBuf[0])) > 0)
|
|
{
|
|
return This->szBuf;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#if DUMP_TASKS != 0
|
|
static VOID
|
|
TaskSwitchWnd_DumpTasks(IN OUT PTASK_SWITCH_WND This)
|
|
{
|
|
PTASK_GROUP CurrentGroup;
|
|
PTASK_ITEM CurrentTaskItem, LastTaskItem;
|
|
|
|
TRACE("Tasks dump:\n");
|
|
if (This->IsGroupingEnabled)
|
|
{
|
|
CurrentGroup = This->TaskGroups;
|
|
while (CurrentGroup != NULL)
|
|
{
|
|
TRACE("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup->dwProcessId, CurrentGroup->dwTaskCount, CurrentGroup->Index);
|
|
|
|
CurrentTaskItem = This->TaskItems;
|
|
LastTaskItem = CurrentTaskItem + This->TaskItemCount;
|
|
while (CurrentTaskItem != LastTaskItem)
|
|
{
|
|
if (CurrentTaskItem->Group == CurrentGroup)
|
|
{
|
|
TRACE(" + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
|
|
}
|
|
CurrentTaskItem++;
|
|
}
|
|
|
|
CurrentGroup = CurrentGroup->Next;
|
|
}
|
|
|
|
CurrentTaskItem = This->TaskItems;
|
|
LastTaskItem = CurrentTaskItem + This->TaskItemCount;
|
|
while (CurrentTaskItem != LastTaskItem)
|
|
{
|
|
if (CurrentTaskItem->Group == NULL)
|
|
{
|
|
TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
|
|
}
|
|
CurrentTaskItem++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CurrentTaskItem = This->TaskItems;
|
|
LastTaskItem = CurrentTaskItem + This->TaskItemCount;
|
|
while (CurrentTaskItem != LastTaskItem)
|
|
{
|
|
TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
|
|
CurrentTaskItem++;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static VOID
|
|
TaskSwitchWnd_BeginUpdate(IN OUT PTASK_SWITCH_WND This)
|
|
{
|
|
SendMessage(This->hWndToolbar,
|
|
WM_SETREDRAW,
|
|
FALSE,
|
|
0);
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_EndUpdate(IN OUT PTASK_SWITCH_WND This)
|
|
{
|
|
SendMessage(This->hWndToolbar,
|
|
WM_SETREDRAW,
|
|
TRUE,
|
|
0);
|
|
InvalidateRect(This->hWndToolbar,
|
|
NULL,
|
|
TRUE);
|
|
}
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_SetToolbarButtonCommandId(IN OUT PTASK_SWITCH_WND This,
|
|
IN INT iButtonIndex,
|
|
IN INT iCommandId)
|
|
{
|
|
TBBUTTONINFO tbbi;
|
|
|
|
tbbi.cbSize = sizeof(tbbi);
|
|
tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
|
|
tbbi.idCommand = iCommandId;
|
|
|
|
return SendMessage(This->hWndToolbar,
|
|
TB_SETBUTTONINFO,
|
|
(WPARAM)iButtonIndex,
|
|
(LPARAM)&tbbi) != 0;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_UpdateIndexesAfterButtonInserted(IN OUT PTASK_SWITCH_WND This,
|
|
IN INT iIndex)
|
|
{
|
|
PTASK_GROUP CurrentGroup;
|
|
PTASK_ITEM CurrentTaskItem, LastTaskItem;
|
|
INT NewIndex;
|
|
|
|
if (This->IsGroupingEnabled)
|
|
{
|
|
/* Update all affected groups */
|
|
CurrentGroup = This->TaskGroups;
|
|
while (CurrentGroup != NULL)
|
|
{
|
|
if (CurrentGroup->IsCollapsed &&
|
|
CurrentGroup->Index >= iIndex)
|
|
{
|
|
/* Update the toolbar buttons */
|
|
NewIndex = CurrentGroup->Index + 1;
|
|
if (TaskSwitchWnd_SetToolbarButtonCommandId(This,
|
|
CurrentGroup->Index + 1,
|
|
NewIndex))
|
|
{
|
|
CurrentGroup->Index = NewIndex;
|
|
}
|
|
else
|
|
CurrentGroup->Index = -1;
|
|
}
|
|
|
|
CurrentGroup = CurrentGroup->Next;
|
|
}
|
|
}
|
|
|
|
/* Update all affected task items */
|
|
CurrentTaskItem = This->TaskItems;
|
|
LastTaskItem = CurrentTaskItem + This->TaskItemCount;
|
|
while (CurrentTaskItem != LastTaskItem)
|
|
{
|
|
CurrentGroup = CurrentTaskItem->Group;
|
|
if (CurrentGroup != NULL)
|
|
{
|
|
if (!CurrentGroup->IsCollapsed &&
|
|
CurrentTaskItem->Index >= iIndex)
|
|
{
|
|
goto UpdateTaskItemBtn;
|
|
}
|
|
}
|
|
else if (CurrentTaskItem->Index >= iIndex)
|
|
{
|
|
UpdateTaskItemBtn:
|
|
/* Update the toolbar buttons */
|
|
NewIndex = CurrentTaskItem->Index + 1;
|
|
if (TaskSwitchWnd_SetToolbarButtonCommandId(This,
|
|
CurrentTaskItem->Index + 1,
|
|
NewIndex))
|
|
{
|
|
CurrentTaskItem->Index = NewIndex;
|
|
}
|
|
else
|
|
CurrentTaskItem->Index = -1;
|
|
}
|
|
|
|
CurrentTaskItem++;
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_UpdateIndexesAfterButtonDeleted(IN OUT PTASK_SWITCH_WND This,
|
|
IN INT iIndex)
|
|
{
|
|
PTASK_GROUP CurrentGroup;
|
|
PTASK_ITEM CurrentTaskItem, LastTaskItem;
|
|
INT NewIndex;
|
|
|
|
if (This->IsGroupingEnabled)
|
|
{
|
|
/* Update all affected groups */
|
|
CurrentGroup = This->TaskGroups;
|
|
while (CurrentGroup != NULL)
|
|
{
|
|
if (CurrentGroup->IsCollapsed &&
|
|
CurrentGroup->Index > iIndex)
|
|
{
|
|
/* Update the toolbar buttons */
|
|
NewIndex = CurrentGroup->Index - 1;
|
|
if (TaskSwitchWnd_SetToolbarButtonCommandId(This,
|
|
CurrentGroup->Index - 1,
|
|
NewIndex))
|
|
{
|
|
CurrentGroup->Index = NewIndex;
|
|
}
|
|
else
|
|
CurrentGroup->Index = -1;
|
|
}
|
|
|
|
CurrentGroup = CurrentGroup->Next;
|
|
}
|
|
}
|
|
|
|
/* Update all affected task items */
|
|
CurrentTaskItem = This->TaskItems;
|
|
LastTaskItem = CurrentTaskItem + This->TaskItemCount;
|
|
while (CurrentTaskItem != LastTaskItem)
|
|
{
|
|
CurrentGroup = CurrentTaskItem->Group;
|
|
if (CurrentGroup != NULL)
|
|
{
|
|
if (!CurrentGroup->IsCollapsed &&
|
|
CurrentTaskItem->Index > iIndex)
|
|
{
|
|
goto UpdateTaskItemBtn;
|
|
}
|
|
}
|
|
else if (CurrentTaskItem->Index > iIndex)
|
|
{
|
|
UpdateTaskItemBtn:
|
|
/* Update the toolbar buttons */
|
|
NewIndex = CurrentTaskItem->Index - 1;
|
|
if (TaskSwitchWnd_SetToolbarButtonCommandId(This,
|
|
CurrentTaskItem->Index - 1,
|
|
NewIndex))
|
|
{
|
|
CurrentTaskItem->Index = NewIndex;
|
|
}
|
|
else
|
|
CurrentTaskItem->Index = -1;
|
|
}
|
|
|
|
CurrentTaskItem++;
|
|
}
|
|
}
|
|
|
|
static INT
|
|
TaskSwitchWnd_UpdateTaskGroupButton(IN OUT PTASK_SWITCH_WND This,
|
|
IN PTASK_GROUP TaskGroup)
|
|
{
|
|
ASSERT(TaskGroup->Index >= 0);
|
|
|
|
/* FIXME: Implement */
|
|
|
|
return TaskGroup->Index;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_ExpandTaskGroup(IN OUT PTASK_SWITCH_WND This,
|
|
IN PTASK_GROUP TaskGroup)
|
|
{
|
|
ASSERT(TaskGroup->dwTaskCount > 0);
|
|
ASSERT(TaskGroup->IsCollapsed);
|
|
ASSERT(TaskGroup->Index >= 0);
|
|
|
|
/* FIXME: Implement */
|
|
}
|
|
|
|
static HICON
|
|
TaskSwitchWnd_GetWndIcon(HWND hwnd)
|
|
{
|
|
HICON hIcon = 0;
|
|
|
|
SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
|
|
|
|
if (!hIcon)
|
|
SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
|
|
|
|
if (!hIcon)
|
|
SendMessageTimeout(hwnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
|
|
|
|
if (!hIcon)
|
|
hIcon = (HICON)GetClassLongPtr(hwnd, GCL_HICONSM);
|
|
|
|
if (!hIcon)
|
|
hIcon = (HICON)GetClassLongPtr(hwnd, GCL_HICON);
|
|
|
|
return hIcon;
|
|
}
|
|
static INT
|
|
TaskSwitchWnd_UpdateTaskItemButton(IN OUT PTASK_SWITCH_WND This,
|
|
IN PTASK_ITEM TaskItem)
|
|
{
|
|
TBBUTTONINFO tbbi;
|
|
HICON icon;
|
|
|
|
ASSERT(TaskItem->Index >= 0);
|
|
|
|
tbbi.cbSize = sizeof(tbbi);
|
|
tbbi.dwMask = TBIF_BYINDEX | TBIF_STATE | TBIF_TEXT | TBIF_IMAGE;
|
|
tbbi.fsState = TBSTATE_ENABLED;
|
|
if (This->ActiveTaskItem == TaskItem)
|
|
tbbi.fsState |= TBSTATE_CHECKED;
|
|
|
|
if (TaskItem->RenderFlashed)
|
|
tbbi.fsState |= TBSTATE_MARKED;
|
|
|
|
/* Check if we're updating a button that is the last one in the
|
|
line. If so, we need to set the TBSTATE_WRAP flag! */
|
|
if (This->TbButtonsPerLine != 0 &&
|
|
(TaskItem->Index + 1) % This->TbButtonsPerLine == 0)
|
|
{
|
|
tbbi.fsState |= TBSTATE_WRAP;
|
|
}
|
|
|
|
tbbi.pszText = TaskSwitchWnd_GetWndTextFromTaskItem(This,
|
|
TaskItem);
|
|
|
|
icon = TaskSwitchWnd_GetWndIcon(TaskItem->hWnd);
|
|
TaskItem->IconIndex = ImageList_ReplaceIcon(This->TaskIcons,
|
|
TaskItem->IconIndex,
|
|
icon);
|
|
tbbi.iImage = TaskItem->IconIndex;
|
|
|
|
if (!SendMessage(This->hWndToolbar,
|
|
TB_SETBUTTONINFO,
|
|
(WPARAM)TaskItem->Index,
|
|
(LPARAM)&tbbi))
|
|
{
|
|
TaskItem->Index = -1;
|
|
return -1;
|
|
}
|
|
|
|
TRACE("Updated button %d for hwnd 0x%p\n", TaskItem->Index, TaskItem->hWnd);
|
|
return TaskItem->Index;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_RemoveIcon(IN OUT PTASK_SWITCH_WND This,
|
|
IN PTASK_ITEM TaskItem)
|
|
{
|
|
TBBUTTONINFO tbbi;
|
|
PTASK_ITEM currentTaskItem, LastItem;
|
|
|
|
if (TaskItem->IconIndex == -1)
|
|
return;
|
|
|
|
tbbi.cbSize = sizeof(tbbi);
|
|
tbbi.dwMask = TBIF_IMAGE;
|
|
|
|
currentTaskItem = This->TaskItems;
|
|
LastItem = currentTaskItem + This->TaskItemCount;
|
|
while (currentTaskItem != LastItem)
|
|
{
|
|
if (currentTaskItem->IconIndex > TaskItem->IconIndex)
|
|
{
|
|
currentTaskItem->IconIndex--;
|
|
tbbi.iImage = currentTaskItem->IconIndex;
|
|
|
|
SendMessage(This->hWndToolbar,
|
|
TB_SETBUTTONINFO,
|
|
currentTaskItem->Index,
|
|
(LPARAM)&tbbi);
|
|
}
|
|
currentTaskItem++;
|
|
}
|
|
|
|
ImageList_Remove(This->TaskIcons, TaskItem->IconIndex);
|
|
}
|
|
|
|
static PTASK_ITEM
|
|
TaskSwitchWnd_FindLastTaskItemOfGroup(IN OUT PTASK_SWITCH_WND This,
|
|
IN PTASK_GROUP TaskGroup OPTIONAL,
|
|
IN PTASK_ITEM NewTaskItem OPTIONAL)
|
|
{
|
|
PTASK_ITEM TaskItem, LastTaskItem, FoundTaskItem = NULL;
|
|
DWORD dwTaskCount;
|
|
|
|
ASSERT(This->IsGroupingEnabled);
|
|
|
|
TaskItem = This->TaskItems;
|
|
LastTaskItem = TaskItem + This->TaskItemCount;
|
|
|
|
dwTaskCount = (TaskGroup != NULL ? TaskGroup->dwTaskCount : MAX_TASKS_COUNT);
|
|
|
|
ASSERT(dwTaskCount > 0);
|
|
|
|
while (TaskItem != LastTaskItem)
|
|
{
|
|
if (TaskItem->Group == TaskGroup)
|
|
{
|
|
if ((NewTaskItem != NULL && TaskItem != NewTaskItem) || NewTaskItem == NULL)
|
|
{
|
|
FoundTaskItem = TaskItem;
|
|
}
|
|
|
|
if (--dwTaskCount == 0)
|
|
{
|
|
/* We found the last task item in the group! */
|
|
break;
|
|
}
|
|
}
|
|
|
|
TaskItem++;
|
|
}
|
|
|
|
return FoundTaskItem;
|
|
}
|
|
|
|
static INT
|
|
TaskSwitchWnd_CalculateTaskItemNewButtonIndex(IN OUT PTASK_SWITCH_WND This,
|
|
IN PTASK_ITEM TaskItem)
|
|
{
|
|
PTASK_GROUP TaskGroup;
|
|
PTASK_ITEM LastTaskItem;
|
|
|
|
/* NOTE: This routine assumes that the group is *not* collapsed! */
|
|
|
|
TaskGroup = TaskItem->Group;
|
|
if (This->IsGroupingEnabled)
|
|
{
|
|
if (TaskGroup != NULL)
|
|
{
|
|
ASSERT(TaskGroup->Index < 0);
|
|
ASSERT(!TaskGroup->IsCollapsed);
|
|
|
|
if (TaskGroup->dwTaskCount > 1)
|
|
{
|
|
LastTaskItem = TaskSwitchWnd_FindLastTaskItemOfGroup(This,
|
|
TaskGroup,
|
|
TaskItem);
|
|
if (LastTaskItem != NULL)
|
|
{
|
|
/* Since the group is expanded the task items must have an index */
|
|
ASSERT(LastTaskItem->Index >= 0);
|
|
|
|
return LastTaskItem->Index + 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Find the last NULL group button. NULL groups are added at the end of the
|
|
task item list when grouping is enabled */
|
|
LastTaskItem = TaskSwitchWnd_FindLastTaskItemOfGroup(This,
|
|
NULL,
|
|
TaskItem);
|
|
if (LastTaskItem != NULL)
|
|
{
|
|
ASSERT(LastTaskItem->Index >= 0);
|
|
|
|
return LastTaskItem->Index + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return This->ToolbarBtnCount;
|
|
}
|
|
|
|
static INT
|
|
TaskSwitchWnd_AddTaskItemButton(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem)
|
|
{
|
|
TBBUTTON tbBtn;
|
|
INT iIndex;
|
|
HICON icon;
|
|
|
|
if (TaskItem->Index >= 0)
|
|
{
|
|
return TaskSwitchWnd_UpdateTaskItemButton(This,
|
|
TaskItem);
|
|
}
|
|
|
|
if (TaskItem->Group != NULL &&
|
|
TaskItem->Group->IsCollapsed)
|
|
{
|
|
/* The task group is collapsed, we only need to update the group button */
|
|
return TaskSwitchWnd_UpdateTaskGroupButton(This,
|
|
TaskItem->Group);
|
|
}
|
|
|
|
icon = TaskSwitchWnd_GetWndIcon(TaskItem->hWnd);
|
|
TaskItem->IconIndex = ImageList_AddIcon(This->TaskIcons, icon);
|
|
|
|
tbBtn.iBitmap = TaskItem->IconIndex;
|
|
tbBtn.fsState = TBSTATE_ENABLED | TBSTATE_ELLIPSES;
|
|
tbBtn.fsStyle = BTNS_CHECK | BTNS_NOPREFIX | BTNS_SHOWTEXT;
|
|
tbBtn.dwData = TaskItem->Index;
|
|
|
|
tbBtn.iString = (DWORD_PTR)TaskSwitchWnd_GetWndTextFromTaskItem(This,
|
|
TaskItem);
|
|
|
|
/* Find out where to insert the new button */
|
|
iIndex = TaskSwitchWnd_CalculateTaskItemNewButtonIndex(This,
|
|
TaskItem);
|
|
ASSERT(iIndex >= 0);
|
|
tbBtn.idCommand = iIndex;
|
|
|
|
TaskSwitchWnd_BeginUpdate(This);
|
|
|
|
if (SendMessage(This->hWndToolbar,
|
|
TB_INSERTBUTTON,
|
|
(WPARAM)iIndex,
|
|
(LPARAM)&tbBtn))
|
|
{
|
|
TaskSwitchWnd_UpdateIndexesAfterButtonInserted(This,
|
|
iIndex);
|
|
|
|
TRACE("Added button %d for hwnd 0x%p\n", iIndex, TaskItem->hWnd);
|
|
|
|
TaskItem->Index = iIndex;
|
|
This->ToolbarBtnCount++;
|
|
|
|
/* Update button sizes and fix the button wrapping */
|
|
TaskSwitchWnd_UpdateButtonsSize(This,
|
|
TRUE);
|
|
return iIndex;
|
|
}
|
|
|
|
TaskSwitchWnd_EndUpdate(This);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_DeleteTaskItemButton(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem)
|
|
{
|
|
PTASK_GROUP TaskGroup;
|
|
INT iIndex;
|
|
|
|
TaskGroup = TaskItem->Group;
|
|
|
|
if (TaskItem->Index >= 0)
|
|
{
|
|
if ((TaskGroup != NULL && !TaskGroup->IsCollapsed) ||
|
|
TaskGroup == NULL)
|
|
{
|
|
TaskSwitchWnd_BeginUpdate(This);
|
|
|
|
TaskSwitchWnd_RemoveIcon(This, TaskItem);
|
|
iIndex = TaskItem->Index;
|
|
if (SendMessage(This->hWndToolbar,
|
|
TB_DELETEBUTTON,
|
|
(WPARAM)iIndex,
|
|
0))
|
|
{
|
|
TaskItem->Index = -1;
|
|
This->ToolbarBtnCount--;
|
|
|
|
TaskSwitchWnd_UpdateIndexesAfterButtonDeleted(This,
|
|
iIndex);
|
|
|
|
/* Update button sizes and fix the button wrapping */
|
|
TaskSwitchWnd_UpdateButtonsSize(This,
|
|
TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
TaskSwitchWnd_EndUpdate(This);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static PTASK_GROUP
|
|
TaskSwitchWnd_AddToTaskGroup(IN OUT PTASK_SWITCH_WND This,
|
|
IN HWND hWnd)
|
|
{
|
|
DWORD dwProcessId;
|
|
PTASK_GROUP TaskGroup, *PrevLink;
|
|
|
|
if (!GetWindowThreadProcessId(hWnd,
|
|
&dwProcessId))
|
|
{
|
|
TRACE("Cannot get process id of hwnd 0x%p\n", hWnd);
|
|
return NULL;
|
|
}
|
|
|
|
/* Try to find an existing task group */
|
|
TaskGroup = This->TaskGroups;
|
|
PrevLink = &This->TaskGroups;
|
|
while (TaskGroup != NULL)
|
|
{
|
|
if (TaskGroup->dwProcessId == dwProcessId)
|
|
{
|
|
TaskGroup->dwTaskCount++;
|
|
return TaskGroup;
|
|
}
|
|
|
|
PrevLink = &TaskGroup->Next;
|
|
TaskGroup = TaskGroup->Next;
|
|
}
|
|
|
|
/* Allocate a new task group */
|
|
TaskGroup = HeapAlloc(hProcessHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(*TaskGroup));
|
|
if (TaskGroup != NULL)
|
|
{
|
|
TaskGroup->dwTaskCount = 1;
|
|
TaskGroup->dwProcessId = dwProcessId;
|
|
TaskGroup->Index = -1;
|
|
|
|
/* Add the task group to the list */
|
|
*PrevLink = TaskGroup;
|
|
}
|
|
|
|
return TaskGroup;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_RemoveTaskFromTaskGroup(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem)
|
|
{
|
|
PTASK_GROUP TaskGroup, CurrentGroup, *PrevLink;
|
|
|
|
TaskGroup = TaskItem->Group;
|
|
if (TaskGroup != NULL)
|
|
{
|
|
DWORD dwNewTaskCount = --TaskGroup->dwTaskCount;
|
|
if (dwNewTaskCount == 0)
|
|
{
|
|
/* Find the previous pointer in the chain */
|
|
CurrentGroup = This->TaskGroups;
|
|
PrevLink = &This->TaskGroups;
|
|
while (CurrentGroup != TaskGroup)
|
|
{
|
|
PrevLink = &CurrentGroup->Next;
|
|
CurrentGroup = CurrentGroup->Next;
|
|
}
|
|
|
|
/* Remove the group from the list */
|
|
ASSERT(TaskGroup == CurrentGroup);
|
|
*PrevLink = TaskGroup->Next;
|
|
|
|
/* Free the task group */
|
|
HeapFree(hProcessHeap,
|
|
0,
|
|
TaskGroup);
|
|
}
|
|
else if (TaskGroup->IsCollapsed &&
|
|
TaskGroup->Index >= 0)
|
|
{
|
|
if (dwNewTaskCount > 1)
|
|
{
|
|
/* FIXME: Check if we should expand the group */
|
|
/* Update the task group button */
|
|
TaskSwitchWnd_UpdateTaskGroupButton(This,
|
|
TaskGroup);
|
|
}
|
|
else
|
|
{
|
|
/* Expand the group of one task button to a task button */
|
|
TaskSwitchWnd_ExpandTaskGroup(This,
|
|
TaskGroup);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static PTASK_ITEM
|
|
TaskSwitchWnd_FindTaskItem(IN OUT PTASK_SWITCH_WND This,
|
|
IN HWND hWnd)
|
|
{
|
|
PTASK_ITEM TaskItem, LastItem;
|
|
|
|
TaskItem = This->TaskItems;
|
|
LastItem = TaskItem + This->TaskItemCount;
|
|
while (TaskItem != LastItem)
|
|
{
|
|
if (TaskItem->hWnd == hWnd)
|
|
return TaskItem;
|
|
|
|
TaskItem++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static PTASK_ITEM
|
|
TaskSwitchWnd_FindOtherTaskItem(IN OUT PTASK_SWITCH_WND This,
|
|
IN HWND hWnd)
|
|
{
|
|
PTASK_ITEM LastItem, TaskItem;
|
|
PTASK_GROUP TaskGroup;
|
|
DWORD dwProcessId;
|
|
|
|
if (!GetWindowThreadProcessId(hWnd,
|
|
&dwProcessId))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Try to find another task that belongs to the same
|
|
process as the given window */
|
|
TaskItem = This->TaskItems;
|
|
LastItem = TaskItem + This->TaskItemCount;
|
|
while (TaskItem != LastItem)
|
|
{
|
|
TaskGroup = TaskItem->Group;
|
|
if (TaskGroup != NULL)
|
|
{
|
|
if (TaskGroup->dwProcessId == dwProcessId)
|
|
return TaskItem;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwProcessIdTask;
|
|
|
|
if (GetWindowThreadProcessId(TaskItem->hWnd,
|
|
&dwProcessIdTask) &&
|
|
dwProcessIdTask == dwProcessId)
|
|
{
|
|
return TaskItem;
|
|
}
|
|
}
|
|
|
|
TaskItem++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static PTASK_ITEM
|
|
TaskSwitchWnd_AllocTaskItem(IN OUT PTASK_SWITCH_WND This)
|
|
{
|
|
if (This->TaskItemCount >= MAX_TASKS_COUNT)
|
|
{
|
|
/* We need the most significant bit in 16 bit command IDs to indicate whether it
|
|
is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT(This->AllocatedTaskItems >= This->TaskItemCount);
|
|
|
|
if (This->TaskItemCount == 0)
|
|
{
|
|
This->TaskItems = HeapAlloc(hProcessHeap,
|
|
0,
|
|
TASK_ITEM_ARRAY_ALLOC * sizeof(*This->TaskItems));
|
|
if (This->TaskItems != NULL)
|
|
{
|
|
This->AllocatedTaskItems = TASK_ITEM_ARRAY_ALLOC;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
else if (This->TaskItemCount >= This->AllocatedTaskItems)
|
|
{
|
|
PTASK_ITEM NewArray;
|
|
SIZE_T NewArrayLength, ActiveTaskItemIndex;
|
|
|
|
NewArrayLength = This->AllocatedTaskItems + TASK_ITEM_ARRAY_ALLOC;
|
|
|
|
NewArray = HeapReAlloc(hProcessHeap,
|
|
0,
|
|
This->TaskItems,
|
|
NewArrayLength * sizeof(*This->TaskItems));
|
|
if (NewArray != NULL)
|
|
{
|
|
if (This->ActiveTaskItem != NULL)
|
|
{
|
|
/* Fixup the ActiveTaskItem pointer */
|
|
ActiveTaskItemIndex = This->ActiveTaskItem - This->TaskItems;
|
|
This->ActiveTaskItem = NewArray + ActiveTaskItemIndex;
|
|
}
|
|
This->AllocatedTaskItems = (WORD)NewArrayLength;
|
|
This->TaskItems = NewArray;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
return This->TaskItems + This->TaskItemCount++;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_FreeTaskItem(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem)
|
|
{
|
|
WORD wIndex;
|
|
|
|
if (TaskItem == This->ActiveTaskItem)
|
|
This->ActiveTaskItem = NULL;
|
|
|
|
wIndex = (WORD)(TaskItem - This->TaskItems);
|
|
if (wIndex + 1 < This->TaskItemCount)
|
|
{
|
|
MoveMemory(TaskItem,
|
|
TaskItem + 1,
|
|
(This->TaskItemCount - wIndex - 1) * sizeof(*TaskItem));
|
|
}
|
|
|
|
This->TaskItemCount--;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_DeleteTaskItem(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem)
|
|
{
|
|
if (!This->IsDestroying)
|
|
{
|
|
/* Delete the task button from the toolbar */
|
|
TaskSwitchWnd_DeleteTaskItemButton(This,
|
|
TaskItem);
|
|
}
|
|
|
|
/* Remove the task from it's group */
|
|
TaskSwitchWnd_RemoveTaskFromTaskGroup(This,
|
|
TaskItem);
|
|
|
|
/* Free the task item */
|
|
TaskSwitchWnd_FreeTaskItem(This,
|
|
TaskItem);
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_CheckActivateTaskItem(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem)
|
|
{
|
|
PTASK_ITEM CurrentTaskItem;
|
|
PTASK_GROUP TaskGroup = NULL;
|
|
|
|
CurrentTaskItem = This->ActiveTaskItem;
|
|
|
|
if (TaskItem != NULL)
|
|
TaskGroup = TaskItem->Group;
|
|
|
|
if (This->IsGroupingEnabled &&
|
|
TaskGroup != NULL &&
|
|
TaskGroup->IsCollapsed)
|
|
{
|
|
/* FIXME */
|
|
return;
|
|
}
|
|
|
|
if (CurrentTaskItem != NULL)
|
|
{
|
|
PTASK_GROUP CurrentTaskGroup;
|
|
|
|
if (CurrentTaskItem == TaskItem)
|
|
return;
|
|
|
|
CurrentTaskGroup = CurrentTaskItem->Group;
|
|
|
|
if (This->IsGroupingEnabled &&
|
|
CurrentTaskGroup != NULL &&
|
|
CurrentTaskGroup->IsCollapsed)
|
|
{
|
|
if (CurrentTaskGroup == TaskGroup)
|
|
return;
|
|
|
|
/* FIXME */
|
|
}
|
|
else
|
|
{
|
|
This->ActiveTaskItem = NULL;
|
|
if (CurrentTaskItem->Index >= 0)
|
|
{
|
|
TaskSwitchWnd_UpdateTaskItemButton(This, CurrentTaskItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
This->ActiveTaskItem = TaskItem;
|
|
|
|
if (TaskItem != NULL && TaskItem->Index >= 0)
|
|
{
|
|
TaskSwitchWnd_UpdateTaskItemButton(This, TaskItem);
|
|
}
|
|
else if (TaskItem == NULL)
|
|
{
|
|
TRACE("Active TaskItem now NULL\n");
|
|
}
|
|
}
|
|
|
|
static PTASK_ITEM
|
|
FindTaskItemByIndex(IN OUT PTASK_SWITCH_WND This,
|
|
IN INT Index)
|
|
{
|
|
PTASK_ITEM TaskItem, LastItem;
|
|
|
|
TaskItem = This->TaskItems;
|
|
LastItem = TaskItem + This->TaskItemCount;
|
|
while (TaskItem != LastItem)
|
|
{
|
|
if (TaskItem->Index == Index)
|
|
return TaskItem;
|
|
|
|
TaskItem++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static PTASK_GROUP
|
|
FindTaskGroupByIndex(IN OUT PTASK_SWITCH_WND This,
|
|
IN INT Index)
|
|
{
|
|
PTASK_GROUP CurrentGroup;
|
|
|
|
CurrentGroup = This->TaskGroups;
|
|
while (CurrentGroup != NULL)
|
|
{
|
|
if (CurrentGroup->Index == Index)
|
|
break;
|
|
|
|
CurrentGroup = CurrentGroup->Next;
|
|
}
|
|
|
|
return CurrentGroup;
|
|
}
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_AddTask(IN OUT PTASK_SWITCH_WND This,
|
|
IN HWND hWnd)
|
|
{
|
|
PTASK_ITEM TaskItem;
|
|
|
|
if (!IsWindow(hWnd) || ITrayWindow_IsSpecialHWND(This->Tray, hWnd))
|
|
return FALSE;
|
|
|
|
TaskItem = TaskSwitchWnd_FindTaskItem(This,
|
|
hWnd);
|
|
if (TaskItem == NULL)
|
|
{
|
|
TRACE("Add window 0x%p\n", hWnd);
|
|
TaskItem = TaskSwitchWnd_AllocTaskItem(This);
|
|
if (TaskItem != NULL)
|
|
{
|
|
ZeroMemory(TaskItem,
|
|
sizeof(*TaskItem));
|
|
TaskItem->hWnd = hWnd;
|
|
TaskItem->Index = -1;
|
|
TaskItem->Group = TaskSwitchWnd_AddToTaskGroup(This,
|
|
hWnd);
|
|
|
|
if (!This->IsDestroying)
|
|
{
|
|
TaskSwitchWnd_AddTaskItemButton(This,
|
|
TaskItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TaskItem != NULL;
|
|
}
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_ActivateTaskItem(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem OPTIONAL)
|
|
{
|
|
if (TaskItem != NULL)
|
|
{
|
|
TRACE("Activate window 0x%p on button %d\n", TaskItem->hWnd, TaskItem->Index);
|
|
}
|
|
|
|
TaskSwitchWnd_CheckActivateTaskItem(This, TaskItem);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_ActivateTask(IN OUT PTASK_SWITCH_WND This,
|
|
IN HWND hWnd)
|
|
{
|
|
PTASK_ITEM TaskItem;
|
|
|
|
if (!hWnd)
|
|
{
|
|
return TaskSwitchWnd_ActivateTaskItem(This, NULL);
|
|
}
|
|
|
|
TaskItem = TaskSwitchWnd_FindTaskItem(This,
|
|
hWnd);
|
|
if (TaskItem == NULL)
|
|
{
|
|
TaskItem = TaskSwitchWnd_FindOtherTaskItem(This,
|
|
hWnd);
|
|
}
|
|
|
|
if (TaskItem == NULL)
|
|
{
|
|
TRACE("Activate window 0x%p, could not find task\n", hWnd);
|
|
}
|
|
|
|
return TaskSwitchWnd_ActivateTaskItem(This, TaskItem);
|
|
}
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_DeleteTask(IN OUT PTASK_SWITCH_WND This,
|
|
IN HWND hWnd)
|
|
{
|
|
PTASK_ITEM TaskItem;
|
|
|
|
TaskItem = TaskSwitchWnd_FindTaskItem(This,
|
|
hWnd);
|
|
if (TaskItem != NULL)
|
|
{
|
|
TRACE("Delete window 0x%p on button %d\n", hWnd, TaskItem->Index);
|
|
TaskSwitchWnd_DeleteTaskItem(This,
|
|
TaskItem);
|
|
return TRUE;
|
|
}
|
|
//else
|
|
//TRACE("Failed to delete window 0x%p\n", hWnd);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_DeleteAllTasks(IN OUT PTASK_SWITCH_WND This)
|
|
{
|
|
PTASK_ITEM CurrentTask;
|
|
|
|
if (This->TaskItemCount > 0)
|
|
{
|
|
CurrentTask = This->TaskItems + This->TaskItemCount;
|
|
do
|
|
{
|
|
TaskSwitchWnd_DeleteTaskItem(This,
|
|
--CurrentTask);
|
|
} while (CurrentTask != This->TaskItems);
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_FlashTaskItem(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem)
|
|
{
|
|
TaskItem->RenderFlashed = 1;
|
|
TaskSwitchWnd_UpdateTaskItemButton(This,
|
|
TaskItem);
|
|
}
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_FlashTask(IN OUT PTASK_SWITCH_WND This,
|
|
IN HWND hWnd)
|
|
{
|
|
PTASK_ITEM TaskItem;
|
|
|
|
TaskItem = TaskSwitchWnd_FindTaskItem(This,
|
|
hWnd);
|
|
if (TaskItem != NULL)
|
|
{
|
|
TRACE("Flashing window 0x%p on button %d\n", hWnd, TaskItem->Index);
|
|
TaskSwitchWnd_FlashTaskItem(This,
|
|
TaskItem);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_RedrawTaskItem(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem)
|
|
{
|
|
PTASK_GROUP TaskGroup;
|
|
|
|
TaskGroup = TaskItem->Group;
|
|
if (This->IsGroupingEnabled && TaskGroup != NULL)
|
|
{
|
|
if (TaskGroup->IsCollapsed && TaskGroup->Index >= 0)
|
|
{
|
|
TaskSwitchWnd_UpdateTaskGroupButton(This,
|
|
TaskGroup);
|
|
}
|
|
else if (TaskItem->Index >= 0)
|
|
{
|
|
goto UpdateTaskItem;
|
|
}
|
|
}
|
|
else if (TaskItem->Index >= 0)
|
|
{
|
|
UpdateTaskItem:
|
|
TaskItem->RenderFlashed = 0;
|
|
TaskSwitchWnd_UpdateTaskItemButton(This,
|
|
TaskItem);
|
|
}
|
|
}
|
|
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_RedrawTask(IN OUT PTASK_SWITCH_WND This,
|
|
IN HWND hWnd)
|
|
{
|
|
PTASK_ITEM TaskItem;
|
|
|
|
TaskItem = TaskSwitchWnd_FindTaskItem(This,
|
|
hWnd);
|
|
if (TaskItem != NULL)
|
|
{
|
|
TaskSwitchWnd_RedrawTaskItem(This,
|
|
TaskItem);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static INT
|
|
TaskSwitchWnd_UpdateTbButtonSpacing(IN OUT PTASK_SWITCH_WND This,
|
|
IN BOOL bHorizontal,
|
|
IN UINT uiRows,
|
|
IN UINT uiBtnsPerLine)
|
|
{
|
|
TBMETRICS tbm;
|
|
|
|
tbm.cbSize = sizeof(tbm);
|
|
tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING;
|
|
|
|
tbm.cxBarPad = tbm.cyBarPad = 0;
|
|
|
|
if (bHorizontal || uiBtnsPerLine > 1)
|
|
tbm.cxButtonSpacing = (3 * GetSystemMetrics(SM_CXEDGE) / 2);
|
|
else
|
|
tbm.cxButtonSpacing = 0;
|
|
|
|
if (!bHorizontal || uiRows > 1)
|
|
tbm.cyButtonSpacing = (3 * GetSystemMetrics(SM_CYEDGE) / 2);
|
|
else
|
|
tbm.cyButtonSpacing = 0;
|
|
|
|
SendMessage(This->hWndToolbar,
|
|
TB_SETMETRICS,
|
|
0,
|
|
(LPARAM)&tbm);
|
|
|
|
return tbm.cxButtonSpacing;
|
|
}
|
|
|
|
|
|
static VOID
|
|
TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This,
|
|
IN BOOL bRedrawDisabled)
|
|
{
|
|
RECT rcClient;
|
|
UINT uiRows, uiMax, uiMin, uiBtnsPerLine, ui;
|
|
LONG NewBtnSize;
|
|
BOOL Horizontal;
|
|
TBBUTTONINFO tbbi;
|
|
TBMETRICS tbm;
|
|
|
|
if (GetClientRect(This->hWnd,
|
|
&rcClient) &&
|
|
!IsRectEmpty(&rcClient))
|
|
{
|
|
if (This->ToolbarBtnCount > 0)
|
|
{
|
|
ZeroMemory (&tbm, sizeof (tbm));
|
|
tbm.cbSize = sizeof(tbm);
|
|
tbm.dwMask = TBMF_BUTTONSPACING;
|
|
SendMessage(This->hWndToolbar,
|
|
TB_GETMETRICS,
|
|
0,
|
|
(LPARAM)&tbm);
|
|
|
|
uiRows = (rcClient.bottom + tbm.cyButtonSpacing) / (This->ButtonSize.cy + tbm.cyButtonSpacing);
|
|
if (uiRows == 0)
|
|
uiRows = 1;
|
|
|
|
uiBtnsPerLine = (This->ToolbarBtnCount + uiRows - 1) / uiRows;
|
|
|
|
Horizontal = ITrayWindow_IsHorizontal(This->Tray);
|
|
|
|
if (!bRedrawDisabled)
|
|
TaskSwitchWnd_BeginUpdate(This);
|
|
|
|
/* We might need to update the button spacing */
|
|
tbm.cxButtonSpacing = TaskSwitchWnd_UpdateTbButtonSpacing(This,
|
|
Horizontal,
|
|
uiRows,
|
|
uiBtnsPerLine);
|
|
|
|
/* Calculate the ideal width and make sure it's within the allowed range */
|
|
NewBtnSize = (rcClient.right - (uiBtnsPerLine * tbm.cxButtonSpacing)) / uiBtnsPerLine;
|
|
|
|
/* Determine the minimum and maximum width of a button */
|
|
if (Horizontal)
|
|
uiMax = GetSystemMetrics(SM_CXMINIMIZED);
|
|
else
|
|
uiMax = rcClient.right;
|
|
|
|
uiMin = GetSystemMetrics(SM_CXSIZE) + (2 * GetSystemMetrics(SM_CXEDGE));
|
|
|
|
if (NewBtnSize < (LONG)uiMin)
|
|
NewBtnSize = uiMin;
|
|
if (NewBtnSize > (LONG)uiMax)
|
|
NewBtnSize = uiMax;
|
|
|
|
This->ButtonSize.cx = NewBtnSize;
|
|
|
|
/* Recalculate how many buttons actually fit into one line */
|
|
uiBtnsPerLine = rcClient.right / (NewBtnSize + tbm.cxButtonSpacing);
|
|
if (uiBtnsPerLine == 0)
|
|
uiBtnsPerLine++;
|
|
This->TbButtonsPerLine = uiBtnsPerLine;
|
|
|
|
tbbi.cbSize = sizeof(tbbi);
|
|
tbbi.dwMask = TBIF_BYINDEX | TBIF_SIZE | TBIF_STATE;
|
|
tbbi.cx = (INT)NewBtnSize;
|
|
|
|
for (ui = 0; ui != This->ToolbarBtnCount; ui++)
|
|
{
|
|
tbbi.fsState = TBSTATE_ENABLED;
|
|
|
|
/* Check if we're updating a button that is the last one in the
|
|
line. If so, we need to set the TBSTATE_WRAP flag! */
|
|
if ((ui + 1) % uiBtnsPerLine == 0)
|
|
tbbi.fsState |= TBSTATE_WRAP;
|
|
|
|
if (This->ActiveTaskItem != NULL &&
|
|
This->ActiveTaskItem->Index == ui)
|
|
{
|
|
tbbi.fsState |= TBSTATE_CHECKED;
|
|
}
|
|
|
|
SendMessage(This->hWndToolbar,
|
|
TB_SETBUTTONINFO,
|
|
(WPARAM)ui,
|
|
(LPARAM)&tbbi);
|
|
}
|
|
|
|
#if 0
|
|
/* FIXME: Force the window to the correct position in case some idiot
|
|
did something to us */
|
|
SetWindowPos(This->hWndToolbar,
|
|
NULL,
|
|
0,
|
|
0,
|
|
rcClient.right, /* FIXME */
|
|
rcClient.bottom, /* FIXME */
|
|
SWP_NOACTIVATE | SWP_NOZORDER);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
This->TbButtonsPerLine = 0;
|
|
This->ButtonSize.cx = 0;
|
|
}
|
|
}
|
|
|
|
TaskSwitchWnd_EndUpdate(This);
|
|
}
|
|
|
|
static BOOL CALLBACK
|
|
TaskSwitchWnd_EnumWindowsProc(IN HWND hWnd,
|
|
IN LPARAM lParam)
|
|
{
|
|
PTASK_SWITCH_WND This = (PTASK_SWITCH_WND)lParam;
|
|
|
|
/* Only show windows that still exist and are visible and none of explorer's
|
|
special windows (such as the desktop or the tray window) */
|
|
if (IsWindow(hWnd) && IsWindowVisible(hWnd) &&
|
|
!ITrayWindow_IsSpecialHWND(This->Tray, hWnd))
|
|
{
|
|
DWORD exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
|
|
/* Don't list popup windows and also no tool windows */
|
|
if ((GetWindow(hWnd, GW_OWNER) == NULL || exStyle & WS_EX_APPWINDOW) &&
|
|
!(exStyle & WS_EX_TOOLWINDOW))
|
|
{
|
|
TaskSwitchWnd_AddTask(This,
|
|
hWnd);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static LRESULT CALLBACK
|
|
TaskSwitchWnd_ToolbarSubclassedProc(IN HWND hWnd,
|
|
IN UINT msg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam,
|
|
IN UINT_PTR uIdSubclass,
|
|
IN DWORD_PTR dwRefData)
|
|
{
|
|
LRESULT Ret;
|
|
|
|
Ret = DefSubclassProc(hWnd,
|
|
msg,
|
|
wParam,
|
|
lParam);
|
|
|
|
if (msg == WM_NCHITTEST && Ret == HTCLIENT)
|
|
{
|
|
POINT pt;
|
|
|
|
/* See if the mouse is on a button */
|
|
pt.x = (SHORT)LOWORD(lParam);
|
|
pt.y = (SHORT)HIWORD(lParam);
|
|
|
|
if (MapWindowPoints(HWND_DESKTOP,
|
|
hWnd,
|
|
&pt,
|
|
1) != 0 &&
|
|
(INT)SendMessage(hWnd,
|
|
TB_HITTEST,
|
|
0,
|
|
(LPARAM)&pt) < 0)
|
|
{
|
|
/* Make the control appear to be transparent outside of any buttons */
|
|
Ret = HTTRANSPARENT;
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_UpdateTheme(IN OUT PTASK_SWITCH_WND This)
|
|
{
|
|
if (This->TaskBandTheme)
|
|
CloseThemeData(This->TaskBandTheme);
|
|
|
|
if (IsThemeActive())
|
|
This->TaskBandTheme = OpenThemeData(This->hWnd, L"TaskBand");
|
|
else
|
|
This->TaskBandTheme = NULL;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_Create(IN OUT PTASK_SWITCH_WND This)
|
|
{
|
|
This->hWndToolbar = CreateWindowEx(0,
|
|
TOOLBARCLASSNAME,
|
|
szRunningApps,
|
|
WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
|
|
TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_LIST |
|
|
TBSTYLE_TRANSPARENT |
|
|
CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
This->hWnd,
|
|
NULL,
|
|
hExplorerInstance,
|
|
NULL);
|
|
|
|
if (This->hWndToolbar != NULL)
|
|
{
|
|
HMODULE hShell32;
|
|
SIZE BtnSize;
|
|
|
|
SetWindowTheme(This->hWndToolbar, L"TaskBand", NULL);
|
|
TaskSwitchWnd_UpdateTheme(This);
|
|
/* Identify the version we're using */
|
|
SendMessage(This->hWndToolbar,
|
|
TB_BUTTONSTRUCTSIZE,
|
|
sizeof(TBBUTTON),
|
|
0);
|
|
|
|
This->TaskIcons = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1000);
|
|
SendMessage(This->hWndToolbar, TB_SETIMAGELIST, 0, (LPARAM)This->TaskIcons);
|
|
|
|
/* Calculate the default button size. Don't save this in This->ButtonSize.cx so that
|
|
the actual button width gets updated correctly on the first recalculation */
|
|
BtnSize.cx = GetSystemMetrics(SM_CXMINIMIZED);
|
|
This->ButtonSize.cy = BtnSize.cy = GetSystemMetrics(SM_CYSIZE) + (2 * GetSystemMetrics(SM_CYEDGE));
|
|
SendMessage(This->hWndToolbar,
|
|
TB_SETBUTTONSIZE,
|
|
0,
|
|
(LPARAM)MAKELONG(BtnSize.cx,
|
|
BtnSize.cy));
|
|
|
|
/* We don't want to see partially clipped buttons...not that we could see them... */
|
|
#if 0
|
|
SendMessage(This->hWndToolbar,
|
|
TB_SETEXTENDEDSTYLE,
|
|
0,
|
|
TBSTYLE_EX_HIDECLIPPEDBUTTONS);
|
|
#endif
|
|
|
|
/* Set proper spacing between buttons */
|
|
TaskSwitchWnd_UpdateTbButtonSpacing(This,
|
|
ITrayWindow_IsHorizontal(This->Tray),
|
|
0,
|
|
0);
|
|
|
|
/* Register the shell hook */
|
|
This->ShellHookMsg = RegisterWindowMessage(TEXT("SHELLHOOK"));
|
|
hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
|
|
if (hShell32 != NULL)
|
|
{
|
|
REGSHELLHOOK RegShellHook;
|
|
|
|
/* RegisterShellHook */
|
|
RegShellHook = (REGSHELLHOOK)GetProcAddress(hShell32,
|
|
(LPCSTR)((LONG)181));
|
|
if (RegShellHook != NULL)
|
|
{
|
|
RegShellHook(This->hWnd,
|
|
3); /* 1 if no NT! We're targeting NT so we don't care! */
|
|
}
|
|
}
|
|
|
|
/* Add all windows to the toolbar */
|
|
EnumWindows(TaskSwitchWnd_EnumWindowsProc,
|
|
(LPARAM)This);
|
|
|
|
/* Recalculate the button size */
|
|
TaskSwitchWnd_UpdateButtonsSize(This,
|
|
FALSE);
|
|
|
|
/* Subclass the toolbar control because it doesn't provide a
|
|
NM_NCHITTEST notification */
|
|
This->IsToolbarSubclassed = SetWindowSubclass(This->hWndToolbar,
|
|
TaskSwitchWnd_ToolbarSubclassedProc,
|
|
TSW_TOOLBAR_SUBCLASS_ID,
|
|
(DWORD_PTR)This);
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_NCDestroy(IN OUT PTASK_SWITCH_WND This)
|
|
{
|
|
HMODULE hShell32;
|
|
|
|
This->IsDestroying = TRUE;
|
|
|
|
/* Unregister the shell hook */
|
|
hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
|
|
if (hShell32 != NULL)
|
|
{
|
|
REGSHELLHOOK RegShellHook;
|
|
|
|
/* RegisterShellHook */
|
|
RegShellHook = (REGSHELLHOOK)GetProcAddress(hShell32,
|
|
(LPCSTR)((LONG)181));
|
|
if (RegShellHook != NULL)
|
|
{
|
|
RegShellHook(This->hWnd,
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
CloseThemeData(This->TaskBandTheme);
|
|
TaskSwitchWnd_DeleteAllTasks(This);
|
|
}
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_HandleAppCommand(IN OUT PTASK_SWITCH_WND This,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam)
|
|
{
|
|
BOOL Ret = FALSE;
|
|
|
|
switch (GET_APPCOMMAND_LPARAM(lParam))
|
|
{
|
|
case APPCOMMAND_BROWSER_SEARCH:
|
|
Ret = SHFindFiles(NULL,
|
|
NULL);
|
|
break;
|
|
|
|
case APPCOMMAND_BROWSER_HOME:
|
|
case APPCOMMAND_LAUNCH_MAIL:
|
|
default:
|
|
TRACE("Shell app command %d unhandled!\n", (INT)GET_APPCOMMAND_LPARAM(lParam));
|
|
break;
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
TaskSwitchWnd_HandleShellHookMsg(IN OUT PTASK_SWITCH_WND This,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam)
|
|
{
|
|
BOOL Ret = FALSE;
|
|
|
|
switch ((INT)wParam)
|
|
{
|
|
case HSHELL_APPCOMMAND:
|
|
TaskSwitchWnd_HandleAppCommand(This,
|
|
wParam,
|
|
lParam);
|
|
Ret = TRUE;
|
|
break;
|
|
|
|
case HSHELL_WINDOWCREATED:
|
|
Ret = TaskSwitchWnd_AddTask(This,
|
|
(HWND)lParam);
|
|
break;
|
|
|
|
case HSHELL_WINDOWDESTROYED:
|
|
/* The window still exists! Delay destroying it a bit */
|
|
TaskSwitchWnd_DeleteTask(This, (HWND)lParam);
|
|
Ret = TRUE;
|
|
break;
|
|
|
|
case HSHELL_RUDEAPPACTIVATED:
|
|
case HSHELL_WINDOWACTIVATED:
|
|
if (lParam)
|
|
{
|
|
TaskSwitchWnd_ActivateTask(This, (HWND) lParam);
|
|
Ret = TRUE;
|
|
}
|
|
break;
|
|
|
|
case HSHELL_GETMINRECT:
|
|
goto UnhandledShellMessage;
|
|
|
|
case HSHELL_FLASH:
|
|
TaskSwitchWnd_FlashTask(This,
|
|
(HWND)lParam);
|
|
Ret = TRUE;
|
|
break;
|
|
|
|
case HSHELL_REDRAW:
|
|
TaskSwitchWnd_RedrawTask(This,
|
|
(HWND)lParam);
|
|
Ret = TRUE;
|
|
break;
|
|
|
|
case HSHELL_TASKMAN:
|
|
PostMessage(ITrayWindow_GetHWND(This->Tray), TWM_OPENSTARTMENU,0, 0);
|
|
break;
|
|
|
|
case HSHELL_ACTIVATESHELLWINDOW:
|
|
case HSHELL_LANGUAGE:
|
|
case HSHELL_SYSMENU:
|
|
case HSHELL_ENDTASK:
|
|
case HSHELL_ACCESSIBILITYSTATE:
|
|
case HSHELL_WINDOWREPLACED:
|
|
case HSHELL_WINDOWREPLACING:
|
|
default:
|
|
{
|
|
static const struct {
|
|
INT msg;
|
|
LPCWSTR msg_name;
|
|
} hshell_msg[] = {
|
|
{HSHELL_WINDOWCREATED, L"HSHELL_WINDOWCREATED"},
|
|
{HSHELL_WINDOWDESTROYED, L"HSHELL_WINDOWDESTROYED"},
|
|
{HSHELL_ACTIVATESHELLWINDOW, L"HSHELL_ACTIVATESHELLWINDOW"},
|
|
{HSHELL_WINDOWACTIVATED, L"HSHELL_WINDOWACTIVATED"},
|
|
{HSHELL_GETMINRECT, L"HSHELL_GETMINRECT"},
|
|
{HSHELL_REDRAW, L"HSHELL_REDRAW"},
|
|
{HSHELL_TASKMAN, L"HSHELL_TASKMAN"},
|
|
{HSHELL_LANGUAGE, L"HSHELL_LANGUAGE"},
|
|
{HSHELL_SYSMENU, L"HSHELL_SYSMENU"},
|
|
{HSHELL_ENDTASK, L"HSHELL_ENDTASK"},
|
|
{HSHELL_ACCESSIBILITYSTATE, L"HSHELL_ACCESSIBILITYSTATE"},
|
|
{HSHELL_APPCOMMAND, L"HSHELL_APPCOMMAND"},
|
|
{HSHELL_WINDOWREPLACED, L"HSHELL_WINDOWREPLACED"},
|
|
{HSHELL_WINDOWREPLACING, L"HSHELL_WINDOWREPLACING"},
|
|
{HSHELL_RUDEAPPACTIVATED, L"HSHELL_RUDEAPPACTIVATED"},
|
|
};
|
|
int i, found;
|
|
UnhandledShellMessage:
|
|
for (i = 0, found = 0; i != sizeof(hshell_msg) / sizeof(hshell_msg[0]); i++)
|
|
{
|
|
if (hshell_msg[i].msg == (INT)wParam)
|
|
{
|
|
TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam);
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT)wParam, lParam);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_EnableGrouping(IN OUT PTASK_SWITCH_WND This,
|
|
IN BOOL bEnable)
|
|
{
|
|
This->IsGroupingEnabled = bEnable;
|
|
|
|
/* Collapse or expand groups if neccessary */
|
|
TaskSwitchWnd_UpdateButtonsSize(This,
|
|
FALSE);
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_HandleTaskItemClick(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem)
|
|
{
|
|
BOOL bIsMinimized;
|
|
BOOL bIsActive;
|
|
|
|
if (IsWindow(TaskItem->hWnd))
|
|
{
|
|
bIsMinimized = IsIconic(TaskItem->hWnd);
|
|
bIsActive = (TaskItem == This->ActiveTaskItem);
|
|
|
|
TRACE("Active TaskItem %p, selected TaskItem %p\n", This->ActiveTaskItem, TaskItem);
|
|
if (This->ActiveTaskItem)
|
|
TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", This->ActiveTaskItem->hWnd, TaskItem->hWnd);
|
|
|
|
TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n",
|
|
TaskItem->hWnd, bIsMinimized ? "Yes" : "No", bIsActive ? "Yes" : "No");
|
|
|
|
if (!bIsMinimized && bIsActive)
|
|
{
|
|
PostMessage(TaskItem->hWnd,
|
|
WM_SYSCOMMAND,
|
|
SC_MINIMIZE,
|
|
0);
|
|
TRACE("Valid button clicked. App window Minimized.\n");
|
|
}
|
|
else
|
|
{
|
|
if (bIsMinimized)
|
|
{
|
|
PostMessage(TaskItem->hWnd,
|
|
WM_SYSCOMMAND,
|
|
SC_RESTORE,
|
|
0);
|
|
TRACE("Valid button clicked. App window Restored.\n");
|
|
}
|
|
|
|
SetForegroundWindow(TaskItem->hWnd);
|
|
TRACE("Valid button clicked. App window Activated.\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_HandleTaskGroupClick(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_GROUP TaskGroup)
|
|
{
|
|
/* TODO: Show task group menu */
|
|
}
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_HandleButtonClick(IN OUT PTASK_SWITCH_WND This,
|
|
IN WORD wIndex)
|
|
{
|
|
PTASK_ITEM TaskItem;
|
|
PTASK_GROUP TaskGroup;
|
|
|
|
if (This->IsGroupingEnabled)
|
|
{
|
|
TaskGroup = FindTaskGroupByIndex(This,
|
|
(INT)wIndex);
|
|
if (TaskGroup != NULL && TaskGroup->IsCollapsed)
|
|
{
|
|
TaskSwitchWnd_HandleTaskGroupClick(This,
|
|
TaskGroup);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
TaskItem = FindTaskItemByIndex(This,
|
|
(INT)wIndex);
|
|
if (TaskItem != NULL)
|
|
{
|
|
TaskSwitchWnd_HandleTaskItemClick(This,
|
|
TaskItem);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static VOID
|
|
TaskSwitchWnd_HandleTaskItemRightClick(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_ITEM TaskItem)
|
|
{
|
|
|
|
HMENU hmenu = GetSystemMenu(TaskItem->hWnd, FALSE);
|
|
|
|
if (hmenu) {
|
|
POINT pt;
|
|
int cmd;
|
|
GetCursorPos(&pt);
|
|
cmd = TrackPopupMenu(hmenu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_RETURNCMD, pt.x, pt.y, 0, This->hWndToolbar, NULL);
|
|
if (cmd) {
|
|
SetForegroundWindow(TaskItem->hWnd); // reactivate window after the context menu has closed
|
|
PostMessage(TaskItem->hWnd, WM_SYSCOMMAND, cmd, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_HandleTaskGroupRightClick(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT PTASK_GROUP TaskGroup)
|
|
{
|
|
/* TODO: Show task group right click menu */
|
|
}
|
|
|
|
static BOOL
|
|
TaskSwitchWnd_HandleButtonRightClick(IN OUT PTASK_SWITCH_WND This,
|
|
IN WORD wIndex)
|
|
{
|
|
PTASK_ITEM TaskItem;
|
|
PTASK_GROUP TaskGroup;
|
|
if (This->IsGroupingEnabled)
|
|
{
|
|
TaskGroup = FindTaskGroupByIndex(This,
|
|
(INT)wIndex);
|
|
if (TaskGroup != NULL && TaskGroup->IsCollapsed)
|
|
{
|
|
TaskSwitchWnd_HandleTaskGroupRightClick(This,
|
|
TaskGroup);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
TaskItem = FindTaskItemByIndex(This,
|
|
(INT)wIndex);
|
|
|
|
if (TaskItem != NULL)
|
|
{
|
|
TaskSwitchWnd_HandleTaskItemRightClick(This,
|
|
TaskItem);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
TaskSwitchWnd_HandleItemPaint(IN OUT PTASK_SWITCH_WND This,
|
|
IN OUT NMTBCUSTOMDRAW *nmtbcd)
|
|
{
|
|
LRESULT Ret = CDRF_DODEFAULT;
|
|
PTASK_GROUP TaskGroup;
|
|
PTASK_ITEM TaskItem;
|
|
|
|
TaskItem = FindTaskItemByIndex(This,
|
|
(INT)nmtbcd->nmcd.dwItemSpec);
|
|
TaskGroup = FindTaskGroupByIndex(This,
|
|
(INT)nmtbcd->nmcd.dwItemSpec);
|
|
if (TaskGroup == NULL && TaskItem != NULL)
|
|
{
|
|
ASSERT(TaskItem != NULL);
|
|
|
|
if (TaskItem != NULL && IsWindow(TaskItem->hWnd))
|
|
{
|
|
/* Make the entire button flashing if neccessary */
|
|
if (nmtbcd->nmcd.uItemState & CDIS_MARKED)
|
|
{
|
|
Ret = TBCDRF_NOBACKGROUND;
|
|
if (!This->TaskBandTheme)
|
|
{
|
|
SelectObject(nmtbcd->nmcd.hdc, GetSysColorBrush(COLOR_HIGHLIGHT));
|
|
Rectangle(nmtbcd->nmcd.hdc,
|
|
nmtbcd->nmcd.rc.left,
|
|
nmtbcd->nmcd.rc.top,
|
|
nmtbcd->nmcd.rc.right,
|
|
nmtbcd->nmcd.rc.bottom);
|
|
}
|
|
else
|
|
{
|
|
DrawThemeBackground(This->TaskBandTheme, nmtbcd->nmcd.hdc, TDP_FLASHBUTTON, 0, &nmtbcd->nmcd.rc, 0);
|
|
}
|
|
nmtbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
|
|
return Ret;
|
|
}
|
|
}
|
|
}
|
|
else if (TaskGroup != NULL)
|
|
{
|
|
/* FIXME: Implement painting for task groups */
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
static LRESULT
|
|
TaskSwitchWnd_HandleToolbarNotification(IN OUT PTASK_SWITCH_WND This,
|
|
IN const NMHDR *nmh)
|
|
{
|
|
LRESULT Ret = 0;
|
|
|
|
switch (nmh->code)
|
|
{
|
|
case NM_CUSTOMDRAW:
|
|
{
|
|
LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW)nmh;
|
|
|
|
switch (nmtbcd->nmcd.dwDrawStage)
|
|
{
|
|
|
|
case CDDS_ITEMPREPAINT:
|
|
Ret = TaskSwitchWnd_HandleItemPaint(This,
|
|
nmtbcd);
|
|
break;
|
|
|
|
case CDDS_PREPAINT:
|
|
Ret = CDRF_NOTIFYITEMDRAW;
|
|
break;
|
|
|
|
default:
|
|
Ret = CDRF_DODEFAULT;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static VOID
|
|
TaskSwitchWnd_DrawBackground(HWND hwnd,
|
|
HDC hdc)
|
|
{
|
|
RECT rect;
|
|
|
|
GetClientRect(hwnd, &rect);
|
|
DrawThemeParentBackground(hwnd, hdc, &rect);
|
|
}
|
|
|
|
static LRESULT CALLBACK
|
|
TaskSwitchWndProc(IN HWND hwnd,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam)
|
|
{
|
|
PTASK_SWITCH_WND This = NULL;
|
|
LRESULT Ret = FALSE;
|
|
|
|
if (uMsg != WM_NCCREATE)
|
|
{
|
|
This = (PTASK_SWITCH_WND)GetWindowLongPtr(hwnd,
|
|
0);
|
|
}
|
|
|
|
if (This != NULL || uMsg == WM_NCCREATE)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_THEMECHANGED:
|
|
TaskSwitchWnd_UpdateTheme(This);
|
|
break;
|
|
case WM_ERASEBKGND:
|
|
TaskSwitchWnd_DrawBackground(hwnd, (HDC)wParam);
|
|
break;
|
|
case WM_SIZE:
|
|
{
|
|
SIZE szClient;
|
|
|
|
szClient.cx = LOWORD(lParam);
|
|
szClient.cy = HIWORD(lParam);
|
|
if (This->hWndToolbar != NULL)
|
|
{
|
|
SetWindowPos(This->hWndToolbar,
|
|
NULL,
|
|
0,
|
|
0,
|
|
szClient.cx,
|
|
szClient.cy,
|
|
SWP_NOZORDER);
|
|
|
|
TaskSwitchWnd_UpdateButtonsSize(This,
|
|
FALSE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_NCHITTEST:
|
|
{
|
|
/* We want the tray window to be draggable everywhere, so make the control
|
|
appear transparent */
|
|
Ret = DefWindowProc(hwnd,
|
|
uMsg,
|
|
wParam,
|
|
lParam);
|
|
if (Ret != HTVSCROLL && Ret != HTHSCROLL)
|
|
Ret = HTTRANSPARENT;
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
if (lParam != 0 && (HWND)lParam == This->hWndToolbar)
|
|
{
|
|
TaskSwitchWnd_HandleButtonClick(This,
|
|
LOWORD(wParam));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
const NMHDR *nmh = (const NMHDR *)lParam;
|
|
|
|
if (nmh->hwndFrom == This->hWndToolbar)
|
|
{
|
|
Ret = TaskSwitchWnd_HandleToolbarNotification(This,
|
|
nmh);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TSWM_ENABLEGROUPING:
|
|
{
|
|
Ret = This->IsGroupingEnabled;
|
|
if (wParam != This->IsGroupingEnabled)
|
|
{
|
|
TaskSwitchWnd_EnableGrouping(This,
|
|
(BOOL)wParam);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TSWM_UPDATETASKBARPOS:
|
|
{
|
|
/* Update the button spacing */
|
|
TaskSwitchWnd_UpdateTbButtonSpacing(This,
|
|
ITrayWindow_IsHorizontal(This->Tray),
|
|
0,
|
|
0);
|
|
break;
|
|
}
|
|
|
|
case WM_CONTEXTMENU:
|
|
{
|
|
if (This->hWndToolbar != NULL)
|
|
{
|
|
POINT pt;
|
|
INT_PTR iBtn;
|
|
|
|
pt.x = (LONG)LOWORD(lParam);
|
|
pt.y = (LONG)HIWORD(lParam);
|
|
|
|
MapWindowPoints(NULL,
|
|
This->hWndToolbar,
|
|
&pt,
|
|
1);
|
|
|
|
iBtn = (INT_PTR)SendMessage(This->hWndToolbar,
|
|
TB_HITTEST,
|
|
0,
|
|
(LPARAM)&pt);
|
|
if (iBtn >= 0)
|
|
{
|
|
TaskSwitchWnd_HandleButtonRightClick(This,
|
|
iBtn);
|
|
}
|
|
else
|
|
goto ForwardContextMenuMsg;
|
|
}
|
|
else
|
|
{
|
|
ForwardContextMenuMsg:
|
|
/* Forward message */
|
|
Ret = SendMessage(ITrayWindow_GetHWND(This->Tray),
|
|
uMsg,
|
|
wParam,
|
|
lParam);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_NCCREATE:
|
|
{
|
|
LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
|
|
This = HeapAlloc(hProcessHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(*This));
|
|
if (This == NULL)
|
|
return FALSE;
|
|
|
|
This->hWnd = hwnd;
|
|
This->hWndNotify = CreateStruct->hwndParent;
|
|
This->Tray = (ITrayWindow*)CreateStruct->lpCreateParams;
|
|
This->IsGroupingEnabled = TRUE; /* FIXME */
|
|
SetWindowLongPtr(hwnd,
|
|
0,
|
|
(LONG_PTR)This);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_CREATE:
|
|
TaskSwitchWnd_Create(This);
|
|
|
|
#if DUMP_TASKS != 0
|
|
SetTimer(hwnd,
|
|
1,
|
|
5000,
|
|
NULL);
|
|
#endif
|
|
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (This->IsToolbarSubclassed)
|
|
{
|
|
if (RemoveWindowSubclass(This->hWndToolbar,
|
|
TaskSwitchWnd_ToolbarSubclassedProc,
|
|
TSW_TOOLBAR_SUBCLASS_ID))
|
|
{
|
|
This->IsToolbarSubclassed = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_NCDESTROY:
|
|
TaskSwitchWnd_NCDestroy(This);
|
|
HeapFree(hProcessHeap,
|
|
0,
|
|
This);
|
|
SetWindowLongPtr(hwnd,
|
|
0,
|
|
0);
|
|
break;
|
|
|
|
#if DUMP_TASKS != 0
|
|
case WM_TIMER:
|
|
switch (wParam)
|
|
{
|
|
case 1:
|
|
TaskSwitchWnd_DumpTasks(This);
|
|
break;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case WM_KLUDGEMINRECT:
|
|
{
|
|
PTASK_ITEM TaskItem = TaskSwitchWnd_FindTaskItem(This, (HWND)wParam);
|
|
if (TaskItem)
|
|
{
|
|
RECT* prcMinRect = (RECT*)lParam;
|
|
RECT rcItem, rcToolbar;
|
|
SendMessageW(This->hWndToolbar,TB_GETITEMRECT, TaskItem->Index, (LPARAM)&rcItem);
|
|
GetWindowRect(This->hWndToolbar, &rcToolbar);
|
|
|
|
OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top);
|
|
|
|
*prcMinRect = rcItem;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
default:
|
|
/* HandleDefaultMessage: */
|
|
if (uMsg == This->ShellHookMsg && This->ShellHookMsg != 0)
|
|
{
|
|
/* Process shell messages */
|
|
Ret = (LRESULT)TaskSwitchWnd_HandleShellHookMsg(This,
|
|
wParam,
|
|
lParam);
|
|
break;
|
|
}
|
|
|
|
Ret = DefWindowProc(hwnd,
|
|
uMsg,
|
|
wParam,
|
|
lParam);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Ret = DefWindowProc(hwnd,
|
|
uMsg,
|
|
wParam,
|
|
lParam);
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
HWND
|
|
CreateTaskSwitchWnd(IN HWND hWndParent,
|
|
IN OUT ITrayWindow *Tray)
|
|
{
|
|
HWND hwndTaskBar;
|
|
|
|
hwndTaskBar = CreateWindowEx(0,
|
|
szTaskSwitchWndClass,
|
|
szRunningApps,
|
|
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
hWndParent,
|
|
NULL,
|
|
hExplorerInstance,
|
|
(LPVOID)Tray);
|
|
|
|
return hwndTaskBar;
|
|
}
|
|
|
|
BOOL
|
|
RegisterTaskSwitchWndClass(VOID)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
wc.style = CS_DBLCLKS;
|
|
wc.lpfnWndProc = TaskSwitchWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = sizeof(PTASK_SWITCH_WND);
|
|
wc.hInstance = hExplorerInstance;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = LoadCursor(NULL,
|
|
IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = szTaskSwitchWndClass;
|
|
|
|
return RegisterClass(&wc) != 0;
|
|
}
|
|
|
|
VOID
|
|
UnregisterTaskSwitchWndClass(VOID)
|
|
{
|
|
UnregisterClass(szTaskSwitchWndClass,
|
|
hExplorerInstance);
|
|
}
|