|
|
#include "precomp.h"
#include <version.h>
#include <nmhelp.h>
//
// VIEW.CPP
// The frame, widgets, and client area that presents the shared apps/desktop
// for a remote host.
//
// Copyright(c) Microsoft 1997-
//
//
// NOTE:
// The client of the shared view frame represents the virtual desktop (VD)
// of the host. For 3.0 hosts, the VD is the same as the screen. But for
// 2.x hosts, the VD is the union of the screen size of all hosts. Hence
// the recalculation every time someone starts sharing or changes screen
// size, and the extra fun this entails for existing shared 2.x views.
//
#define MLZ_FILE_ZONE ZONE_CORE
// Help file
static const TCHAR s_cszHtmlHelpFile[] = TEXT("conf.chm");
//
// VIEW_Init()
//
BOOL VIEW_Init(void) { BOOL rc = FALSE; WNDCLASSEX wc;
DebugEntry(VIEW_Init);
//
// Register the frame window class.
// NOTE: Change CS_NOCLOSE if/when we ever let you close the view
// of a person's shared apps.
//
wc.cbSize = sizeof(wc); wc.style = CS_DBLCLKS | CS_NOCLOSE; wc.lpfnWndProc = VIEWFrameWindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_asInstance; wc.hIcon = NULL; wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1); wc.lpszMenuName = NULL; wc.lpszClassName = VIEW_FRAME_CLASS_NAME; wc.hIconSm = NULL;
if (!RegisterClassEx(&wc)) { ERROR_OUT(("Failed to register AS Frame class")); DC_QUIT; }
//
// Register the view window class. This sits in the client area of
// the frame along with the statusbar, tray, etc. It displays
// the remote host's shared contents.
//
wc.cbSize = sizeof(wc); wc.style = CS_DBLCLKS | CS_NOCLOSE; wc.lpfnWndProc = VIEWClientWindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_asInstance; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = VIEW_CLIENT_CLASS_NAME; wc.hIconSm = NULL;
if (!RegisterClassEx(&wc)) { ERROR_OUT(("Failed to register AS Client class")); DC_QUIT; }
//
// Register the window bar class. This hugs the bottom of
// frames for shared apps (not desktop) and acts like a tray
// surrogate. It allows controllers to minimize, restore, and
// activate shared windows that may not be on screen currently
// and therefore not in the view area.
//
// It also is handy reference for what top level windows are shared
// currently.
//
wc.cbSize = sizeof(wc); wc.style = 0; wc.lpfnWndProc = VIEWWindowBarProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_asInstance; wc.hIcon = NULL; wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1); wc.lpszMenuName = NULL; wc.lpszClassName = VIEW_WINDOWBAR_CLASS_NAME; wc.hIconSm = NULL;
if (!RegisterClassEx(&wc)) { ERROR_OUT(("Failed to register AS WindowBar class")); DC_QUIT; }
//
// Register the window bar items class. This is a child of the window
// bar and contains the actual items.
//
wc.cbSize = sizeof(wc); wc.style = 0; wc.lpfnWndProc = VIEWWindowBarItemsProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_asInstance; wc.hIcon = NULL; wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1); wc.lpszMenuName = NULL; wc.lpszClassName = VIEW_WINDOWBARITEMS_CLASS_NAME; wc.hIconSm = NULL;
if (!RegisterClassEx(&wc)) { ERROR_OUT(("Failed to register AS WindowBarItems class")); DC_QUIT; }
//
// Register the full screen exit button class. This is a child of the
// the view client when present.
//
wc.cbSize = sizeof(wc); wc.style = 0; wc.lpfnWndProc = VIEWFullScreenExitProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_asInstance; wc.hIcon = NULL; wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = VIEW_FULLEXIT_CLASS_NAME; wc.hIconSm = NULL;
if (!RegisterClassEx(&wc)) { ERROR_OUT(("Failed to register AS full screen exit class")); DC_QUIT; }
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(VIEW_Init, rc); return(rc); }
//
// VIEW_Term()
//
void VIEW_Term(void) { DebugEntry(VIEW_Term);
//
// Free all resources we created (or may have created in window class
// case).
//
UnregisterClass(VIEW_FULLEXIT_CLASS_NAME, g_asInstance); UnregisterClass(VIEW_WINDOWBARITEMS_CLASS_NAME, g_asInstance); UnregisterClass(VIEW_WINDOWBAR_CLASS_NAME, g_asInstance); UnregisterClass(VIEW_CLIENT_CLASS_NAME, g_asInstance); UnregisterClass(VIEW_FRAME_CLASS_NAME, g_asInstance);
DebugExitVOID(VIEW_Term); }
//
// VIEW_ShareStarting()
// Creates share resources
//
BOOL ASShare::VIEW_ShareStarting(void) { BOOL rc = FALSE; HBITMAP hbmpT; TEXTMETRIC tm; HDC hdc; HFONT hfnOld; char szRestore[256]; SIZE extent;
DebugEntry(ASShare::VIEW_ShareStarting);
ASSERT(m_viewVDSize.x == 0); ASSERT(m_viewVDSize.y == 0);
//
// Get NODROP cursor
//
m_viewNotInControl = ::LoadCursor(NULL, IDC_NO);
//
// Get MOUSEWHEEL lines metric
//
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &m_viewMouseWheelScrollLines, 0);
//
// Create a pattern brush from the obscured bitmap
//
hbmpT = LoadBitmap(g_asInstance, MAKEINTRESOURCE(IDB_OBSCURED)); m_viewObscuredBrush = CreatePatternBrush(hbmpT); DeleteBitmap(hbmpT);
if (!m_viewObscuredBrush) { ERROR_OUT(( "Failed to create obscured bitmap brush")); DC_QUIT; }
//
// NOTE THAT since the icons are VGA colors, we don't need to recreate
// our brush on a SYSCOLOR change.
//
// Get the full screen cancel icon
m_viewFullScreenExitIcon = (HICON)LoadImage(g_asInstance, MAKEINTRESOURCE(IDI_CANCELFULLSCREEN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
m_viewEdgeCX = ::GetSystemMetrics(SM_CXEDGE); m_viewEdgeCY = ::GetSystemMetrics(SM_CYEDGE);
//
// Get metrics of GUI_FONT, the one we use in the window bar and
// status bar.
//
LoadString(g_asInstance, IDS_RESTORE, szRestore, sizeof(szRestore));
hdc = ::GetDC(NULL); hfnOld = (HFONT)::SelectObject(hdc, ::GetStockObject(DEFAULT_GUI_FONT));
::GetTextMetrics(hdc, &tm);
::GetTextExtentPoint(hdc, szRestore, lstrlen(szRestore), &extent);
::SelectObject(hdc, hfnOld); ::ReleaseDC(NULL, hdc);
//
// Calculate size of full screen button
// Edge on left + margin on left + sm icon + margin + text + margin on
// right + edge on right == 5 edges + sm icon + text
//
m_viewFullScreenCX = extent.cx + 5*m_viewEdgeCX + GetSystemMetrics(SM_CXSMICON); m_viewFullScreenCY = max(GetSystemMetrics(SM_CYSMICON), extent.cy) + 4*m_viewEdgeCY;
//
// Calculate size of items on window bar
//
m_viewItemCX = 4*m_viewEdgeCX + ::GetSystemMetrics(SM_CXSMICON) + m_viewEdgeCX + VIEW_MAX_ITEM_CHARS * tm.tmAveCharWidth; m_viewItemCY = max(::GetSystemMetrics(SM_CYSMICON), tm.tmHeight) + 2*m_viewEdgeCY + 2*m_viewEdgeCY;
//
// Calculate the width & height of the items scroll buttons. We want
// to make sure it fits, but isn't ungainly.
//
m_viewItemScrollCX = ::GetSystemMetrics(SM_CXHSCROLL); m_viewItemScrollCX = 2 * min(m_viewItemScrollCX, m_viewItemCY);
m_viewItemScrollCY = ::GetSystemMetrics(SM_CYHSCROLL); m_viewItemScrollCY = min(m_viewItemScrollCY, m_viewItemCY);
//
// Calculate height of active window bar. We leave a CYEDGE gap on the
// top. between it and the sunken border around the view client.
//
m_viewWindowBarCY = m_viewItemCY + m_viewEdgeCY;
//
// Calculate height of status bar. It's height of GUIFONT plus edge
// space.
//
m_viewStatusBarCY = tm.tmHeight + 4*m_viewEdgeCY;
rc = TRUE;
DC_EXIT_POINT: DebugExitBOOL(ASShare::VIEW_ShareStarting, rc); return(rc); }
//
// VIEW_ShareEnded()
// Cleans up resources for share
//
void ASShare::VIEW_ShareEnded(void) { DebugEntry(ASShare::VIEW_ShareEnded);
//
// Destroy the full screen cancel icon
//
if (m_viewFullScreenExitIcon != NULL) { DestroyIcon(m_viewFullScreenExitIcon); m_viewFullScreenExitIcon = NULL; }
if (m_viewObscuredBrush != NULL) { DeleteBrush(m_viewObscuredBrush); m_viewObscuredBrush = NULL; }
DebugExitVOID(ASShre::VIEW_ShareEnded); }
//
// VIEW_PartyLeftShare()
//
// This is called when somebody leaves a share. We need this to
// simulate what back-level systems did to calculate the virtual desktop
// size. They didn't recalc when someone stopped shared, that person's
// screne size counted until they left the share.
//
void ASShare::VIEW_PartyLeftShare(ASPerson * pasPerson) { DebugEntry(ASShare::VIEW_PartyLeftShare);
ValidatePerson(pasPerson);
// If this dude ever shared, now remove him from the VD total
if (pasPerson->viewExtent.x != 0) { pasPerson->viewExtent.x = 0; pasPerson->viewExtent.y = 0; VIEW_RecalcVD(); }
DebugExitVOID(ASShare::VIEW_PartyLeftShare); }
//
// VIEW_HostStarting()
//
// Called when we start to host.
//
BOOL ASHost::VIEW_HostStarting(void) { DebugEntry(ASHost:VIEW_HostStarting);
m_pShare->VIEW_RecalcExtent(m_pShare->m_pasLocal); m_pShare->VIEW_RecalcVD();
DebugExitBOOL(ASHost::VIEW_HostStarting, TRUE); return(TRUE); }
//
// VIEW_ViewStarting()
// Called when someone in the meeting starts to share. For all in the
// conference, we keep a running tally of the VD, but use it only for
// 2.x views. For remotes only, we create a view of their desktop.
//
BOOL ASShare::VIEW_ViewStarting(ASPerson * pasHost) { BOOL rc = FALSE; HWND hwnd; RECT rcSize;
DebugEntry(ASShare::VIEW_ViewStarting);
ValidateView(pasHost);
//
// First, calculate the extents, and the VD size.
//
VIEW_RecalcExtent(pasHost); VIEW_RecalcVD();
//
// Next, create scratch regions
//
pasHost->m_pView->m_viewExtentRgn = CreateRectRgn(0, 0, 0, 0); pasHost->m_pView->m_viewScreenRgn = CreateRectRgn(0, 0, 0, 0); pasHost->m_pView->m_viewPaintRgn = CreateRectRgn(0, 0, 0, 0); pasHost->m_pView->m_viewScratchRgn = CreateRectRgn(0, 0, 0, 0);
if (!pasHost->m_pView->m_viewExtentRgn || !pasHost->m_pView->m_viewScreenRgn || !pasHost->m_pView->m_viewPaintRgn || !pasHost->m_pView->m_viewScratchRgn) { ERROR_OUT(("ViewStarting: Couldn't create scratch regions")); DC_QUIT; }
ASSERT(pasHost->m_pView->m_viewFrame == NULL); ASSERT(pasHost->m_pView->m_viewClient == NULL); ASSERT(pasHost->m_pView->m_viewSharedRgn == NULL); ASSERT(pasHost->m_pView->m_viewObscuredRgn == NULL); ASSERT(pasHost->m_pView->m_viewPos.x == 0); ASSERT(pasHost->m_pView->m_viewPos.y == 0); ASSERT(pasHost->m_pView->m_viewPage.x == 0); ASSERT(pasHost->m_pView->m_viewPage.y == 0);
ASSERT(!pasHost->m_pView->m_viewStatusBarOn); ASSERT(!pasHost->m_pView->m_viewWindowBarOn); ASSERT(!pasHost->m_pView->m_viewFullScreen);
pasHost->m_pView->m_viewStatusBarOn = TRUE; if (pasHost->hetCount != HET_DESKTOPSHARED) { pasHost->m_pView->m_viewWindowBarOn = TRUE; }
//
// Calculate the ideal size for this window.
//
VIEWFrameGetSize(pasHost, &rcSize);
//
// Create the frame. This will in turn create its children.
//
pasHost->m_pView->m_viewMenuBar = ::LoadMenu(g_asInstance, MAKEINTRESOURCE(IDM_FRAME)); if (!pasHost->m_pView->m_viewMenuBar) { ERROR_OUT(("ViewStarting: couldn't load frame menu")); DC_QUIT; }
//
// Do once-only capabilities/menu stuff.
//
//
// SEND CTRL+ALT+DEL:
// Append Ctrl+Alt+Del after separator to control menu, if this
// is a view of a service host on NT.
//
if (pasHost->hetCount == HET_DESKTOPSHARED) { //
// Remove applications submenu
//
DeleteMenu(pasHost->m_pView->m_viewMenuBar, IDSM_WINDOW, MF_BYPOSITION);
if ((pasHost->cpcCaps.general.typeFlags & AS_SERVICE) && (pasHost->cpcCaps.general.OS == CAPS_WINDOWS) && (pasHost->cpcCaps.general.OSVersion == CAPS_WINDOWS_NT)) { HMENU hSubMenu; MENUITEMINFO mii; CHAR szMenu[32];
hSubMenu = GetSubMenu(pasHost->m_pView->m_viewMenuBar, IDSM_CONTROL);
ZeroMemory(&mii, sizeof(mii));
// Separator
mii.cbSize = sizeof(mii); mii.fMask = MIIM_TYPE; mii.fType = MFT_SEPARATOR; InsertMenuItem(hSubMenu, -1, TRUE, &mii);
// Send Ctrl-Alt-Del command
mii.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE; mii.fType = MFT_STRING; mii.fState = MFS_ENABLED; mii.wID = CMD_CTRLALTDEL;
LoadString(g_asInstance, IDS_CMD_CTRLALTDEL, szMenu, sizeof(szMenu)); mii.dwTypeData = szMenu; mii.cch = lstrlen(szMenu);
InsertMenuItem(hSubMenu, -1, TRUE, &mii); } }
//
// FULL SCREEN:
// We only enable Full Screen for 3.0 hosts (since with 2.x desktop
// scrolling the view area can change) who have screen sizes identical
// to ours.
//
if ((pasHost->cpcCaps.general.version >= CAPS_VERSION_30) && (pasHost->cpcCaps.screen.capsScreenWidth == m_pasLocal->cpcCaps.screen.capsScreenWidth) && (pasHost->cpcCaps.screen.capsScreenHeight == m_pasLocal->cpcCaps.screen.capsScreenHeight)) { ::EnableMenuItem(pasHost->m_pView->m_viewMenuBar, CMD_VIEWFULLSCREEN, MF_ENABLED | MF_BYCOMMAND); }
if (m_pasLocal->m_caControlledBy) { WARNING_OUT(("VIEWStarting: currently controlled, create view hidden")); }
//
// If we are currently controlled, create the frame invisible since
// we hid all the visible ones when we started being this way.
//
hwnd = CreateWindowEx( WS_EX_WINDOWEDGE, VIEW_FRAME_CLASS_NAME, // See RegisterClass() call.
NULL, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN | (!m_pasLocal->m_caControlledBy ? WS_VISIBLE : 0), CW_USEDEFAULT, CW_USEDEFAULT, pasHost->viewExtent.x >= m_pasLocal->cpcCaps.screen.capsScreenWidth ? CW_USEDEFAULT : rcSize.right - rcSize.left,
pasHost->viewExtent.y >= m_pasLocal->cpcCaps.screen.capsScreenHeight ? CW_USEDEFAULT : rcSize.bottom - rcSize.top, NULL, pasHost->m_pView->m_viewMenuBar, g_asInstance, pasHost // Pass in person ptr
);
if (hwnd == NULL) { ERROR_OUT(("ViewStarting: couldn't create frame window")); DC_QUIT; }
//
// OK, now we've created this frame window. Go through the sizing
// process again to make sure the scrollbars are OK.
//
VIEWClientExtentChange(pasHost, FALSE);
if (!m_pasLocal->m_caControlledBy) { SetForegroundWindow(hwnd); UpdateWindow(hwnd); }
#ifdef _DEBUG
TRACE_OUT(("TIME TO SEE SOMETHING: %08d MS", ::GetTickCount() - g_asSession.scShareTime)); g_asSession.scShareTime = 0; #endif // DEBUG
rc = TRUE;
DC_EXIT_POINT: DebugExitBOOL(ASShare::VIEW_ViewStarting, rc); return(rc); }
//
// VIEW_ViewEnded()
//
// Called when someone we are viewing stops hosting, so we can clean up.
//
void ASShare::VIEW_ViewEnded(ASPerson * pasHost) { DebugEntry(ASShare::VIEW_ViewEnded);
ValidateView(pasHost);
if (pasHost->m_pView->m_viewInformDlg != NULL) { SendMessage(pasHost->m_pView->m_viewInformDlg, WM_COMMAND, IDCANCEL, 0); ASSERT(!pasHost->m_pView->m_viewInformDlg); ASSERT(!pasHost->m_pView->m_viewInformMsg); ASSERT(IsWindowEnabled(pasHost->m_pView->m_viewFrame)); }
if (pasHost->m_pView->m_viewFrame != NULL) { //
// The frame is the parent of the view, toolbar, etc. Those
// should all be NULL when we return.
//
DestroyWindow(pasHost->m_pView->m_viewFrame); ASSERT(pasHost->m_pView->m_viewFrame == NULL); }
ASSERT(pasHost->m_pView->m_viewClient == NULL);
if (pasHost->m_pView->m_viewMenuBar != NULL) { ::DestroyMenu(pasHost->m_pView->m_viewMenuBar); pasHost->m_pView->m_viewMenuBar = NULL; }
if (pasHost->m_pView->m_viewSharedRgn != NULL) { DeleteRgn(pasHost->m_pView->m_viewSharedRgn); pasHost->m_pView->m_viewSharedRgn = NULL; }
if (pasHost->m_pView->m_viewObscuredRgn != NULL) { DeleteRgn(pasHost->m_pView->m_viewObscuredRgn); pasHost->m_pView->m_viewObscuredRgn = NULL; }
//
// Destroy scratch regions
//
if (pasHost->m_pView->m_viewScratchRgn != NULL) { DeleteRgn(pasHost->m_pView->m_viewScratchRgn); pasHost->m_pView->m_viewScratchRgn = NULL; }
if (pasHost->m_pView->m_viewPaintRgn != NULL) { DeleteRgn(pasHost->m_pView->m_viewPaintRgn); pasHost->m_pView->m_viewPaintRgn = NULL; }
if (pasHost->m_pView->m_viewScreenRgn != NULL) { DeleteRgn(pasHost->m_pView->m_viewScreenRgn); pasHost->m_pView->m_viewScreenRgn = NULL; }
if (pasHost->m_pView->m_viewExtentRgn != NULL) { DeleteRgn(pasHost->m_pView->m_viewExtentRgn); pasHost->m_pView->m_viewExtentRgn = NULL; }
pasHost->m_pView->m_viewPos.x = 0; pasHost->m_pView->m_viewPos.y = 0; pasHost->m_pView->m_viewPage.x = 0; pasHost->m_pView->m_viewPage.y = 0; pasHost->m_pView->m_viewPgSize.x = 0; pasHost->m_pView->m_viewPgSize.y = 0; pasHost->m_pView->m_viewLnSize.x = 0; pasHost->m_pView->m_viewLnSize.y = 0;
DebugExitVOID(ASShare::VIEW_ViewEnded); }
//
// VIEW_InControl()
//
// Called when we start/stop controlling this host. We enable the
// toolbar, statusbar, tray, etc., and change the cursor from being the
// nodrop.
//
void ASShare::VIEW_InControl ( ASPerson * pasHost, BOOL fStart ) { POINT ptCursor;
DebugEntry(ASShare::VIEW_InControl);
//
// We're changing our state, and that affects the contents of our
// menu bar. So cancel out of menu mode, and spare problems/faults/
// inapplicable commands.
//
if (pasHost->m_pView->m_viewInMenuMode) { SendMessage(pasHost->m_pView->m_viewFrame, WM_CANCELMODE, 0, 0); ASSERT(!pasHost->m_pView->m_viewInMenuMode); }
//
// If starting in control and a message is up, kill it. Then bring our
// window to the front.
//
if (fStart) { if (pasHost->m_pView->m_viewInformDlg != NULL) { SendMessage(pasHost->m_pView->m_viewInformDlg, WM_COMMAND, IDCANCEL, 0); ASSERT(!pasHost->m_pView->m_viewInformDlg); ASSERT(!pasHost->m_pView->m_viewInformMsg); ASSERT(IsWindowEnabled(pasHost->m_pView->m_viewFrame)); }
SetForegroundWindow(pasHost->m_pView->m_viewFrame); }
//
// App Sharing (not desktop sharing) stuff
//
if (pasHost->hetCount && (pasHost->hetCount != HET_DESKTOPSHARED)) { //
// Enable/disable window bar
//
ASSERT(IsWindow(pasHost->m_pView->m_viewWindowBar)); ::EnableWindow(::GetDlgItem(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS), fStart);
//
// Enable/Disable Applications submenu
//
EnableMenuItem(pasHost->m_pView->m_viewMenuBar, IDSM_WINDOW, (fStart ? MF_ENABLED : MF_GRAYED) | MF_BYPOSITION);
if (!pasHost->m_pView->m_viewFullScreen) { DrawMenuBar(pasHost->m_pView->m_viewFrame); } }
//
// Change title bar
//
VIEW_HostStateChange(pasHost);
//
// Turn off/on shadow cursors
//
CM_UpdateShadowCursor(pasHost, fStart, pasHost->cmPos.x, pasHost->cmPos.y, pasHost->cmHotSpot.x, pasHost->cmHotSpot.y);
//
// This will reset cursor image:
// * from nodrop to shared if in control
// * from shared to nodrop if not in control
//
// This will also, if in control, cause a mousemove to get sent to the
// host we're controlling so his cursor pos is synced with ours, if the
// mouse is over the frame client area.
//
GetCursorPos(&ptCursor); SetCursorPos(ptCursor.x, ptCursor.y);
DebugExitVOID(ASShare::VIEW_InControl); }
//
// VIEW_PausedInControl()
//
// Updates status bar etc. when control is paused.
//
void ASShare::VIEW_PausedInControl ( ASPerson * pasHost, BOOL fPaused ) { DebugEntry(ASShare::VIEW_PausedInControl);
ValidatePerson(pasHost);
ASSERT(pasHost->m_caControlledBy == m_pasLocal);
//
// Update status bar
//
//
// Disable/Enable window menu
//
//
// Put shadow cursors on/off
//
//
// Jiggle cursor
//
DebugExitVOID(ASShare::VIEW_PausedInControl); }
//
// VIEW_HostStateChange()
//
// Called when a host's state has changed, via broadcast notification or
// local operations.
//
// We update the titlebar and commands.
//
void ASShare::VIEW_HostStateChange ( ASPerson * pasHost ) { char szFormat[256]; char szTitleText[256]; char szOtherPart[128];
DebugEntry(ASShare::VIEW_HostStateChange);
ValidatePerson(pasHost);
//
// If this person isn't hosting anymore, don't do anything. We're
// cleaning up after him.
//
if (!pasHost->hetCount) { DC_QUIT; }
//
// Make up trailing string
//
if (pasHost->m_caControlledBy) { LoadString(g_asInstance, IDS_TITLE_INCONTROL, szFormat, sizeof(szFormat)); wsprintf(szOtherPart, szFormat, pasHost->m_caControlledBy->scName); } else if (pasHost->m_caAllowControl) { LoadString(g_asInstance, IDS_TITLE_CONTROLLABLE, szOtherPart, sizeof(szOtherPart)); } else { szOtherPart[0] = 0; }
if (pasHost->hetCount == HET_DESKTOPSHARED) { LoadString(g_asInstance, IDS_TITLE_SHAREDDESKTOP, szFormat, sizeof(szFormat)); } else { ASSERT(pasHost->hetCount); LoadString(g_asInstance, IDS_TITLE_SHAREDPROGRAMS, szFormat, sizeof(szFormat)); }
wsprintf(szTitleText, szFormat, pasHost->scName, szOtherPart);
::SetWindowText(pasHost->m_pView->m_viewFrame, szTitleText);
DC_EXIT_POINT: DebugExitVOID(ASShare::VIEW_HostStateChange); }
//
// VIEW_UpdateStatus()
//
// Updates the PERMANENT status of this frame. When we go into menu mode,
// the strings shown are temporary only, not saved. When we come out of
// menu mode, we put back the temporary status.
//
void ASShare::VIEW_UpdateStatus ( ASPerson * pasHost, UINT idsStatus ) { DebugEntry(ASShare::VIEW_UpdateStatus);
ValidatePerson(pasHost);
pasHost->m_pView->m_viewStatus = idsStatus; VIEWFrameSetStatus(pasHost, idsStatus);
DebugExitVOID(ASShare::VIEW_UpdateStatus); }
void ASShare::VIEWFrameSetStatus ( ASPerson * pasHost, UINT idsStatus ) { char szStatus[256];
DebugEntry(ASShare::VIEWFrameSetStatus);
if (idsStatus != IDS_STATUS_NONE) { LoadString(g_asInstance, idsStatus, szStatus, sizeof(szStatus)); } else { szStatus[0] = 0; } ::SetWindowText(pasHost->m_pView->m_viewStatusBar, szStatus);
DebugExitVOID(ASShare::VIEWFrameSetStatus); }
//
// VIEW_Message()
//
// Puts up a message to inform the end user of something.
//
void ASShare::VIEW_Message ( ASPerson * pasHost, UINT ids ) { DebugEntry(ASShare::VIEW_Message);
ValidateView(pasHost);
if (!pasHost->m_pView) { WARNING_OUT(("Can't show VIEW message; [%d] not hosting", pasHost->mcsID)); DC_QUIT; }
if (pasHost->m_pView->m_viewInformDlg) { // Kill the previous one
TRACE_OUT(("Killing previous informational mesage for [%d]", pasHost->mcsID)); SendMessage(pasHost->m_pView->m_viewInformDlg, WM_COMMAND, IDCANCEL, 0); ASSERT(!pasHost->m_pView->m_viewInformDlg); ASSERT(!pasHost->m_pView->m_viewInformMsg); }
if (m_pasLocal->m_caControlledBy) { WARNING_OUT(("VIEW_Message: ignoring, view is hidden since we're controlled")); } else { pasHost->m_pView->m_viewInformMsg = ids; pasHost->m_pView->m_viewInformDlg = CreateDialogParam(g_asInstance, ((ids != IDS_ABOUT) ? MAKEINTRESOURCE(IDD_INFORM) : MAKEINTRESOURCE(IDD_ABOUT)), pasHost->m_pView->m_viewFrame, VIEWDlgProc, (LPARAM)pasHost); if (!pasHost->m_pView->m_viewInformDlg) { ERROR_OUT(("Failed to create inform message box for [%d]", pasHost->mcsID)); pasHost->m_pView->m_viewInformMsg = 0; } }
DC_EXIT_POINT: DebugExitVOID(ASShare::VIEW_Message); }
//
// VIEWStartControlled()
//
// If we are about to start being controlled, we hide all the frames
// to get them out of the way AND prevent hangs caused by modal loop code
// in Win9x title bar dragging.
//
void ASShare::VIEWStartControlled(BOOL fStart) { ASPerson * pasT;
DebugEntry(ASShare::VIEWStartControlled);
for (pasT = m_pasLocal; pasT != NULL; pasT = pasT->pasNext) { if (pasT->m_pView) { if (fStart) { ASSERT(IsWindowVisible(pasT->m_pView->m_viewFrame)); ShowOwnedPopups(pasT->m_pView->m_viewFrame, FALSE); SetWindowPos(pasT->m_pView->m_viewFrame, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_HIDEWINDOW); } else { ASSERT(!IsWindowVisible(pasT->m_pView->m_viewFrame)); SetWindowPos(pasT->m_pView->m_viewFrame, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW); ShowOwnedPopups(pasT->m_pView->m_viewFrame, TRUE); } } }
DebugEntry(ASShare::VIEWStartControlled); }
//
// VIEW_DlgProc()
//
// Handles informing user dialog
//
INT_PTR CALLBACK VIEWDlgProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { return(g_asSession.pShare->VIEW_DlgProc(hwnd, message, wParam, lParam)); }
BOOL ASShare::VIEW_DlgProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { BOOL rc = TRUE; ASPerson * pasHost;
DebugEntry(VIEW_DlgProc);
pasHost = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pasHost) { ValidateView(pasHost); }
switch (message) { case WM_INITDIALOG: { char szT[256]; char szRes[512]; RECT rcText; RECT rcOwner;
pasHost = (ASPerson *)lParam; ValidateView(pasHost); pasHost->m_pView->m_viewInformDlg = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
ASSERT(pasHost->m_pView->m_viewInformMsg);
if (pasHost->m_pView->m_viewInformMsg == IDS_ABOUT) { // About box
GetDlgItemText(hwnd, CTRL_ABOUTVERSION, szT, sizeof(szT)); wsprintf(szRes, szT, VER_PRODUCTRELEASE_STR, VER_PRODUCTVERSION_STR); SetDlgItemText(hwnd, CTRL_ABOUTVERSION, szRes); } else { HDC hdc; HFONT hfn;
// Set title.
if ((pasHost->m_pView->m_viewInformMsg >= IDS_ERR_TAKECONTROL_FIRST) && (pasHost->m_pView->m_viewInformMsg <= IDS_ERR_TAKECONTROL_LAST)) { LoadString(g_asInstance, IDS_TITLE_TAKECONTROL_FAILED, szT, sizeof(szT)); SetWindowText(hwnd, szT); }
// Set message
LoadString(g_asInstance, pasHost->m_pView->m_viewInformMsg, szT, sizeof(szT)); wsprintf(szRes, szT, pasHost->scName); SetDlgItemText(hwnd, CTRL_INFORM, szRes);
// Center the message vertically
GetWindowRect(GetDlgItem(hwnd, CTRL_INFORM), &rcOwner); MapWindowPoints(NULL, hwnd, (LPPOINT)&rcOwner, 2);
rcText = rcOwner;
hdc = GetDC(hwnd); hfn = (HFONT)SendDlgItemMessage(hwnd, CTRL_INFORM, WM_GETFONT, 0, 0); hfn = SelectFont(hdc, hfn);
DrawText(hdc, szRes, -1, &rcText, DT_NOCLIP | DT_EXPANDTABS | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
SelectFont(hdc, hfn); ReleaseDC(hwnd, hdc);
ASSERT((rcText.bottom - rcText.top) <= (rcOwner.bottom - rcOwner.top));
SetWindowPos(GetDlgItem(hwnd, CTRL_INFORM), NULL, rcOwner.left, ((rcOwner.top + rcOwner.bottom) - (rcText.bottom - rcText.top)) / 2, (rcOwner.right - rcOwner.left), rcText.bottom - rcText.top, SWP_NOACTIVATE | SWP_NOZORDER); }
// Disable owner
EnableWindow(pasHost->m_pView->m_viewFrame, FALSE);
// Show window, centered around owner midpoint
GetWindowRect(pasHost->m_pView->m_viewFrame, &rcOwner); GetWindowRect(hwnd, &rcText);
SetWindowPos(hwnd, NULL, ((rcOwner.left + rcOwner.right) - (rcText.right - rcText.left)) / 2, ((rcOwner.top + rcOwner.bottom) - (rcText.bottom - rcText.top)) / 2, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
ShowWindow(hwnd, SW_SHOWNORMAL); UpdateWindow(hwnd); break; }
case WM_COMMAND: { switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDOK: case IDCANCEL: { if(pasHost) { ASSERT(!IsWindowEnabled(pasHost->m_pView->m_viewFrame)); EnableWindow(pasHost->m_pView->m_viewFrame, TRUE); }
DestroyWindow(hwnd); break; } } break; }
case WM_DESTROY: { if (pasHost) { pasHost->m_pView->m_viewInformDlg = NULL; pasHost->m_pView->m_viewInformMsg = 0; }
SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL); break; }
default: { rc = FALSE; break; } }
DebugExitBOOL(VIEW_DlgProc, rc); return(rc); }
//
// VIEW_RecalcExtent()
//
// This recalculates the extent of the view of the host.
//
void ASShare::VIEW_RecalcExtent(ASPerson * pasHost) { DebugEntry(ASShare::VIEW_RecalcExtent);
TRACE_OUT(("VIEW_RecalcExtent: New view extent (%04d, %04d) for [%d] version %x", pasHost->viewExtent.x, pasHost->viewExtent.y, pasHost->mcsID, pasHost->cpcCaps.general.version));
//
// Compute the extent of the view:
// For 2.x dudes, it's the VD size (union of all hosts)
// For 3.0 dudes, it's the host screen size
//
// REMOVE THIS WHEN 2.X COMPAT IS GONE
//
if (pasHost->cpcCaps.general.version >= CAPS_VERSION_30) { pasHost->viewExtent.x = pasHost->cpcCaps.screen.capsScreenWidth; pasHost->viewExtent.y = pasHost->cpcCaps.screen.capsScreenHeight; } else { //
// We do this so that the window is created the right size in the
// first place. Then in VIEW_RecalcVD nothing will happen to it,
// because the extent won't alter.
//
pasHost->viewExtent.x = max(m_viewVDSize.x, pasHost->cpcCaps.screen.capsScreenWidth); pasHost->viewExtent.y = max(m_viewVDSize.y, pasHost->cpcCaps.screen.capsScreenHeight); }
DebugExitVOID(ASShare::VIEW_RecalcExtent); }
//
// VIEW_RecalcVD()
// This recalculates the virtual desktop size when a remote starts/stops
// sharing, or if a host's screen changes size. The VD is the union of
// all the screen sizes of those hosting. 2.x nodes work in a virtual
// desktop, and may scroll over. For each 2.x view, we want the client to
// represent the VD, but with only the stuff on screen on the host to be
// interactable.
//
void ASShare::VIEW_RecalcVD(void) { POINT ptVDNew; ASPerson * pas;
DebugEntry(ASShare::VIEW_RecalcVD);
//
// First, loop through all the hosts and recompute the VD.
//
ptVDNew.x = 0; ptVDNew.y = 0;
for (pas = m_pasLocal; pas != NULL; pas = pas->pasNext) { //
// NOTE:
// For local dudes, we won't have an HWND. Use viewExtent, if
// we don't think the person is sharing, it will be zero.
//
if (pas->viewExtent.x != 0) { TRACE_OUT(("VIEW_RecalcVD: Found host [%d], screen size (%04d, %04d)", pas->mcsID, pas->cpcCaps.screen.capsScreenWidth, pas->cpcCaps.screen.capsScreenHeight));
ptVDNew.x = max(ptVDNew.x, pas->cpcCaps.screen.capsScreenWidth); ptVDNew.y = max(ptVDNew.y, pas->cpcCaps.screen.capsScreenHeight);
TRACE_OUT(("VIEW_RecalcVD: Computed VD size now (%04d, %04d)", ptVDNew.x, ptVDNew.y)); } }
//
// If the VD size didn't change, we're done.
//
if ((ptVDNew.x != m_viewVDSize.x) || (ptVDNew.y != m_viewVDSize.y)) { TRACE_OUT(("VIEW_RecalcVD: VD size changed from (%04d, %04d) to (%04d, %04d)", m_viewVDSize.x, m_viewVDSize.y, ptVDNew.x, ptVDNew.y));
m_viewVDSize = ptVDNew;
//
// Now loop through all the 2.x hosts, and update their extent, then
// have them do the resize voodoo so the scrollbar pos isn't out of
// range, etc.
//
// NOTE: Since us, the local guy, is not 2.x we can skip ourselves.
//
ValidatePerson(m_pasLocal);
for (pas = m_pasLocal->pasNext; pas != NULL; pas = pas->pasNext) { if ((pas->cpcCaps.general.version < CAPS_VERSION_30) && (pas->m_pView != NULL)) { ASSERT(m_viewVDSize.x != 0); ASSERT(m_viewVDSize.y != 0);
// Potential resize/range change
if ((pas->viewExtent.x != m_viewVDSize.x) || (pas->viewExtent.y != m_viewVDSize.y)) { TRACE_OUT(("VIEW_RecalcVD: Found 2.x host [%d], must update old extent (%04d, %04d)", pas->mcsID, pas->viewExtent.x, pas->viewExtent.y));
VIEW_RecalcExtent(pas); VIEWClientExtentChange(pas, TRUE); } } } }
DebugExitVOID(ASShare::VIEW_RecalcVD); }
//
// VIEW_IsPointShared()
// This determines, given a point relative to the client of the view for
// the remote on this system, if it is in a shared area.
//
BOOL ASShare::VIEW_IsPointShared ( ASPerson * pasHost, POINT ptLocal ) { BOOL rc = FALSE; RECT rcClient;
DebugEntry(ASShare::VIEW_IsPointShared);
ValidateView(pasHost);
//
// Convert to client coords, and adjust for scrolling offset. That
// result is the equivalent point on the host desktop.
//
GetClientRect(pasHost->m_pView->m_viewClient, &rcClient); if (!PtInRect(&rcClient, ptLocal)) { TRACE_OUT(("VIEW_IsPointShared: point not in client area")); return(FALSE); }
//
// The obscured and shared areas are saved in frame client coords,
// so we don't need to account for the scroll position all the time.
// When the scroll position changes the regions are updated.
//
//
// NOTE that this order works for both desktop and app sharing
//
if ((pasHost->m_pView->m_viewObscuredRgn != NULL) && PtInRegion(pasHost->m_pView->m_viewObscuredRgn, ptLocal.x, ptLocal.y)) { rc = FALSE; } else if ((pasHost->m_pView->m_viewSharedRgn != NULL) && !PtInRegion(pasHost->m_pView->m_viewSharedRgn, ptLocal.x, ptLocal.y)) { rc = FALSE; } else { //
// 2.x hosts may be scrolled over. If so, shared stuff offscreen
// is also considered to be obscured.
//
RECT rcScreen;
//
// Compute what part of the VD, in local client coords, is visible
// on the remote's screen.
//
SetRect(&rcScreen, 0, 0, pasHost->cpcCaps.screen.capsScreenWidth, pasHost->cpcCaps.screen.capsScreenHeight);
OffsetRect(&rcScreen, pasHost->m_pView->m_dsScreenOrigin.x - pasHost->m_pView->m_viewPos.x, pasHost->m_pView->m_dsScreenOrigin.y - pasHost->m_pView->m_viewPos.y); if (!PtInRect(&rcScreen, ptLocal)) { TRACE_OUT(("VIEW_IsPointShared: point is in shared stuff but not visible on remote screen")); rc = FALSE; } else { rc = TRUE; } }
DebugExitBOOL(AShare::VIEW_IsPointShared, rc); return(rc); }
//
// VIEW_ScreenChanged()
//
void ASShare::VIEW_ScreenChanged(ASPerson * pasPerson) { DebugEntry(ASShare::VIEW_ScreenChanged);
ValidatePerson(pasPerson);
//
// Recompute the extent
//
VIEW_RecalcExtent(pasPerson); VIEWClientExtentChange(pasPerson, TRUE);
VIEW_RecalcVD();
DebugExitVOID(ASShare::VIEW_ScreenChanged); }
//
// VIEW_SetHostRegions()
// This sets the new shared & obscured areas.
//
// Note that this routine takes responsibility for the regions pass in; it
// will delete them and/or the old ones if necessary.
//
void ASShare::VIEW_SetHostRegions ( ASPerson * pasHost, HRGN rgnShared, HRGN rgnObscured ) { DebugEntry(ASShare::VIEW_SetHostRegions);
ValidateView(pasHost);
//
// Return immediately if either region handle is bogus. This can happen
// when we are running low on memory.
//
if (!rgnShared || !rgnObscured) { ERROR_OUT(("Bad host regions for person [%u]", pasHost->mcsID));
if (rgnShared != NULL) { DeleteRgn(rgnShared); }
if (rgnObscured != NULL) { DeleteRgn(rgnObscured); } } else { HRGN hrgnInvalid; #ifdef _DEBUG
RECT rcT;
::GetRgnBox(rgnShared, &rcT); TRACE_OUT(("Shared region {%04d, %04d, %04d, %04d} for host [%d]", rcT.left, rcT.top, rcT.right, rcT.bottom, pasHost->mcsID));
::GetRgnBox(rgnObscured, &rcT); TRACE_OUT(("Obscured region {%04d, %04d, %04d, %04d} for host [%d]", rcT.left, rcT.top, rcT.right, rcT.bottom, pasHost->mcsID)); #endif // _DEBUG
//
// Update the current shared, obscured areas. Adjust for the
// scroll position so these are saved in client-relative coords.
//
OffsetRgn(rgnShared, -pasHost->m_pView->m_viewPos.x, -pasHost->m_pView->m_viewPos.y); OffsetRgn(rgnObscured, -pasHost->m_pView->m_viewPos.x, -pasHost->m_pView->m_viewPos.y);
//
// The invalid area is whatever changed in the obscured area and
// the shared area. In other words, the union - the intersection.
//
hrgnInvalid = NULL;
if (pasHost->m_pView->m_viewSharedRgn != NULL) { HRGN hrgnU; HRGN hrgnI;
ASSERT(pasHost->m_pView->m_viewObscuredRgn != NULL);
//
// If we're in a low memory situation, just invalidate everything
// and hope it can be repainted.
//
hrgnU = CreateRectRgn(0, 0, 0, 0); hrgnI = CreateRectRgn(0, 0, 0, 0); if (!hrgnU || !hrgnI) goto SkipMinimalInvalidate;
hrgnInvalid = CreateRectRgn(0, 0, 0, 0); if (!hrgnInvalid) goto SkipMinimalInvalidate;
//
// WE'RE GOING TO DO THE SAME THING FOR BOTH SHARED AND
// OBSCURED REGIONS.
//
// Get the union of the old and new shared regions
UnionRgn(hrgnU, pasHost->m_pView->m_viewSharedRgn, rgnShared);
// Get the intersection of the old and new shared regions
IntersectRgn(hrgnI, pasHost->m_pView->m_viewSharedRgn, rgnShared);
//
// The intersection is what used to be shared and is still shared.
// The rest is changing, it needs to be repainted. That's the
// union minus the intersection.
//
SubtractRgn(hrgnU, hrgnU, hrgnI); #ifdef _DEBUG
GetRgnBox(hrgnU, &rcT); TRACE_OUT(("VIEW_SetHostRegions: Shared area change {%04d, %04d, %04d, %04d}", rcT.left, rcT.top, rcT.right, rcT.bottom)); #endif // _DEBUG
// Add this to the invalidate total
UnionRgn(hrgnInvalid, hrgnInvalid, hrgnU);
//
// REPEAT FOR THE OBSCURED REGION
//
UnionRgn(hrgnU, pasHost->m_pView->m_viewObscuredRgn, rgnObscured); IntersectRgn(hrgnI, pasHost->m_pView->m_viewObscuredRgn, rgnObscured); SubtractRgn(hrgnU, hrgnU, hrgnI);
#ifdef _DEBUG
GetRgnBox(hrgnU, &rcT); TRACE_OUT(("VIEW_SetHostRegions: Obscured area change {%04d, %04d, %04d, %04d}", rcT.left, rcT.top, rcT.right, rcT.bottom)); #endif // _DEBUG
UnionRgn(hrgnInvalid, hrgnInvalid, hrgnU);
SkipMinimalInvalidate: //
// Clean up scratch regions
//
if (hrgnI != NULL) DeleteRgn(hrgnI); if (hrgnU != NULL) DeleteRgn(hrgnU);
DeleteRgn(pasHost->m_pView->m_viewSharedRgn); pasHost->m_pView->m_viewSharedRgn = rgnShared;
DeleteRgn(pasHost->m_pView->m_viewObscuredRgn); pasHost->m_pView->m_viewObscuredRgn = rgnObscured;
//
// DO NOT CALL VIEW_InvalidateRgn here, that expects a region in
// screen coords of pasHost. We have a region that is
// client coords relative. So just call InvalidateRgn() directly.
//
InvalidateRgn(pasHost->m_pView->m_viewClient, hrgnInvalid, FALSE);
if (hrgnInvalid != NULL) DeleteRgn(hrgnInvalid); } else { RECT rcBound;
// The shared & obscured regions are both NULL or both non-NULL
ASSERT(pasHost->m_pView->m_viewObscuredRgn == NULL);
pasHost->m_pView->m_viewSharedRgn = rgnShared; pasHost->m_pView->m_viewObscuredRgn = rgnObscured;
//
// This is the first SWL packet we've received. Snap the
// scrollbars to the start of the total shared area. This avoids
// having the view come up, but look empty because the shared
// apps are out of the range. We do this even if the user
// scrolled around in the window already.
//
// The total shared area is the union of the real shared +
// obscured shared areas. Convert back to remote VD coords!
//
UnionRgn(pasHost->m_pView->m_viewScratchRgn, rgnShared, rgnObscured); GetRgnBox(pasHost->m_pView->m_viewScratchRgn, &rcBound); OffsetRect(&rcBound, pasHost->m_pView->m_viewPos.x, pasHost->m_pView->m_viewPos.y);
//
// Is any part of what was shared within the extent of the view?
// If not, we can't do anything--there's nothing to show.
//
if ((rcBound.right <= 0) || (rcBound.left >= pasHost->viewExtent.x) || (rcBound.bottom <= 0) || (rcBound.top >= pasHost->viewExtent.y)) { TRACE_OUT(("VIEW_SetHostRegions: Can't snap to shared area; none is visible")); } else { //
// Use top left corner of bounds
// VIEWClientScroll() will pin position w/in range
//
VIEWClientScroll(pasHost, rcBound.left, rcBound.top); }
InvalidateRgn(pasHost->m_pView->m_viewClient, NULL, FALSE); } }
DebugExitVOID(ASShare::VIEW_SetHostRegions); }
//
// VIEW_InvalidateRect()
// Repaints the given rect. This is for EXTERNAL code which passes in VD
// coords. We convert to client coordinates by accounting for the scroll
// position.
//
void ASShare::VIEW_InvalidateRect ( ASPerson * pasPerson, LPRECT lprc ) { DebugEntry(ASShare::VIEW_InvalidateRect);
ValidateView(pasPerson);
//
// Convert to client coords
//
if (lprc != NULL) { OffsetRect(lprc, -pasPerson->m_pView->m_viewPos.x, -pasPerson->m_pView->m_viewPos.y); }
InvalidateRect(pasPerson->m_pView->m_viewClient, lprc, FALSE);
//
// Convert back so caller doesn't get a modified lprc
//
if (lprc != NULL) { OffsetRect(lprc, pasPerson->m_pView->m_viewPos.x, pasPerson->m_pView->m_viewPos.y); }
DebugExitVOID(ASShare::VIEW_InvalidateRect); }
//
// VIEW_InvalidateRgn()
// Repaints the given region. This is for EXTERNAL code which passes in VD
// coords. We convert to client coordinates by accounting fo the scroll
// position.
//
void ASShare::VIEW_InvalidateRgn ( ASPerson * pasHost, HRGN rgnInvalid ) { #ifdef _DEBUG
//
// Make sure we the invalid region goes back to the caller unaltered,
// even though we modify it temporarily here to avoid a copy.
//
RECT rcBoundBefore; RECT rcBoundAfter; #endif // _DEBUG
DebugEntry(ASShare::VIEW_InvalidateRgn);
ValidatePerson(pasHost);
//
// Adjust the region if the frame view is scrolled over.
//
if (rgnInvalid != NULL) { #ifdef _DEBUG
GetRgnBox(rgnInvalid, &rcBoundBefore); #endif // _DEBUG
OffsetRgn(rgnInvalid, -pasHost->m_pView->m_viewPos.x, -pasHost->m_pView->m_viewPos.y);
#ifdef _DEBUG
TRACE_OUT(("VIEW_InvalidateRgn: Invalidating area {%04d, %04d, %04d, %04d}", rcBoundBefore.left, rcBoundBefore.top, rcBoundBefore.right, rcBoundBefore.bottom)); #endif // _DEBUG
} else { TRACE_OUT(("VIEW_InvalidateRgn: Invalidating entire client area")); }
InvalidateRgn(pasHost->m_pView->m_viewClient, rgnInvalid, FALSE);
if (rgnInvalid != NULL) { OffsetRgn(rgnInvalid, pasHost->m_pView->m_viewPos.x, pasHost->m_pView->m_viewPos.y); #ifdef _DEBUG
GetRgnBox(rgnInvalid, &rcBoundAfter); ASSERT(EqualRect(&rcBoundBefore, &rcBoundAfter)); #endif // _DEBUG
}
DebugExitVOID(ASShare::VIEW_InvalidateRgn); }
//
// VIEWClientExtentChange()
//
void ASShare::VIEWClientExtentChange(ASPerson * pasHost, BOOL fRedraw) { RECT rcl; SCROLLINFO si;
DebugEntry(ASShare::VIEWClientExtentChange);
ValidatePerson(pasHost); if (!pasHost->m_pView) DC_QUIT;
#ifdef _DEBUG
//
// The client area (page size) shouldn't have changed. Only the
// extent has.
//
GetClientRect(pasHost->m_pView->m_viewClient, &rcl); ASSERT(pasHost->m_pView->m_viewPage.x == rcl.right - rcl.left); ASSERT(pasHost->m_pView->m_viewPage.y == rcl.bottom - rcl.top); #endif // _DEBUG
pasHost->m_pView->m_viewPgSize.x = pasHost->viewExtent.x / 8; pasHost->m_pView->m_viewPgSize.y = pasHost->viewExtent.y / 8; pasHost->m_pView->m_viewLnSize.x = pasHost->viewExtent.x / 64; pasHost->m_pView->m_viewLnSize.y = pasHost->viewExtent.y / 64;
//
// Move the scroll position to the origin.
//
//
// Clip the current scroll pos if we need to, now that the extent
// has changed size.
//
VIEWClientScroll(pasHost, pasHost->m_pView->m_viewPos.x, pasHost->m_pView->m_viewPos.y);
si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_DISABLENOSCROLL;
// Set vertical info. Is vert pos out of range now?
si.nMin = 0; si.nMax = pasHost->viewExtent.y - 1; si.nPage = pasHost->m_pView->m_viewPage.y; si.nPos = pasHost->m_pView->m_viewPos.y; ASSERT(si.nPos <= si.nMax);
TRACE_OUT(("VIEWClientExtentChange: Setting VERT scroll info:")); TRACE_OUT(("VIEWClientExtentChange: nMin %04d", si.nMin)); TRACE_OUT(("VIEWClientExtentChange: nMax %04d", si.nMax)); TRACE_OUT(("VIEWClientExtentChange: nPage %04d", si.nPage)); TRACE_OUT(("VIEWClientExtentChange: nPos %04d", si.nPos)); SetScrollInfo(pasHost->m_pView->m_viewClient, SB_VERT, &si, TRUE );
// Set horizontal (x) information
si.nMin = 0; si.nMax = pasHost->viewExtent.x - 1; si.nPage = pasHost->m_pView->m_viewPage.x; si.nPos = pasHost->m_pView->m_viewPos.x; ASSERT(si.nPos <= si.nMax);
TRACE_OUT(("VIEWClientExtentChange: Setting HORZ scroll info:")); TRACE_OUT(("VIEWClientExtentChange: nMin %04d", si.nMin)); TRACE_OUT(("VIEWClientExtentChange: nMax %04d", si.nMax)); TRACE_OUT(("VIEWClientExtentChange: nPage %04d", si.nPage)); TRACE_OUT(("VIEWClientExtentChange: nPos %04d", si.nPos)); SetScrollInfo(pasHost->m_pView->m_viewClient, SB_HORZ, &si, TRUE );
if (fRedraw) { // Is the frame window too big now?
if ( (pasHost->m_pView->m_viewPage.x > pasHost->viewExtent.x) || (pasHost->m_pView->m_viewPage.y > pasHost->viewExtent.y) ) { TRACE_OUT(("VIEWClientExtentChange: client size (%04d, %04d) now bigger than view extent (%04d, %04d)", pasHost->m_pView->m_viewPage.x, pasHost->m_pView->m_viewPage.y, pasHost->viewExtent.x, pasHost->viewExtent.y));
//
// Calculate the ideal size for this window.
//
VIEWFrameGetSize(pasHost, &rcl);
SetWindowPos( pasHost->m_pView->m_viewFrame, NULL, 0, 0, rcl.right - rcl.left, rcl.bottom - rcl.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); }
TRACE_OUT(("VIEWClientExtentChange: Invalidating client area")); VIEW_InvalidateRgn(pasHost, NULL); }
DC_EXIT_POINT: DebugExitVOID(ASShare::VIEWClientExtentChange); }
//
// VIEWFrameWindowProc()
//
LRESULT CALLBACK VIEWFrameWindowProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { return(g_asSession.pShare->VIEW_FrameWindowProc(hwnd, message, wParam, lParam)); }
LRESULT ASShare::VIEW_FrameWindowProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { LRESULT rc = 0; ASPerson * pasHost;
DebugEntry(VIEW_FrameWindowProc);
pasHost = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pasHost) { ValidateView(pasHost); }
switch (message) { case WM_NCCREATE: { // Get the passed in host pointer, and set in our window long
pasHost = (ASPerson *)((LPCREATESTRUCT)lParam)->lpCreateParams; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pasHost);
pasHost->m_pView->m_viewFrame = hwnd;
//
// Set the window icon
//
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) ((pasHost->hetCount == HET_DESKTOPSHARED) ? g_hetDeskIcon : g_hetASIcon)); goto DefWndProc; break; }
case WM_NCDESTROY: { if (pasHost != NULL) { pasHost->m_pView->m_viewFrame = NULL; }
goto DefWndProc; break; }
case WM_CREATE: { // Set title
VIEW_HostStateChange(pasHost);
if (!VIEWFrameCreate(pasHost)) { ERROR_OUT(("VIEWFrameWindowProc: errors in creation handling for [%d]", pasHost->mcsID)); rc = -1; }
break; }
case WM_DESTROY: { //
// Clear menu bar; we always destroy it ourself.
//
::SetMenu(hwnd, NULL); break; }
case WM_ACTIVATE: { //
// If we're switching back to the view of the host we're in
// control of, update the key states.
//
if (wParam) { SetFocus(pasHost->m_pView->m_viewClient); } else { //
// If we're full screen but are deactivating, kick out of
// full screenmode.
//
if (pasHost->m_pView->m_viewFullScreen) { // Do this later, so title bar state isn't messed up
::PostMessage(hwnd, WM_COMMAND, MAKELONG(CMD_VIEWFULLSCREEN, 0), 0); } } break; }
case WM_ENTERMENULOOP: { pasHost->m_pView->m_viewInMenuMode = TRUE; break; }
case WM_EXITMENULOOP: { pasHost->m_pView->m_viewInMenuMode = FALSE; break; }
case WM_COMMAND: { VIEWFrameCommand(pasHost, wParam, lParam); break; }
case WM_INITMENU: { if ((HMENU)wParam == pasHost->m_pView->m_viewMenuBar) { VIEWFrameInitMenuBar(pasHost); } break; }
case WM_MENUSELECT: { VIEWFrameOnMenuSelect(pasHost, wParam, lParam); break; }
case WM_PALETTECHANGED: //
// The system palette has changed - repaint the window.
//
VIEW_InvalidateRgn(pasHost, NULL);
//
// The system palette has changed. If we are not the
// window that triggered this message then realize our
// palette now to set up our new palette mapping.
//
if ((HWND)wParam == hwnd) { //
// If this window caused the change return without
// realizing our logical palette or we could end up in
// an infinite loop.
//
break; } TRACE_OUT(("Palette changed - fall through to realize palette (%x)", hwnd));
//
// Do not break here but FALL THROUGH to the code which
// realizes the remote palette into this window. This allows
// the window to grab some color entries for itself in the new
// system palette.
//
case WM_QUERYNEWPALETTE: rc = FALSE;
if (message == WM_QUERYNEWPALETTE) { TRACE_OUT(( "WM_QUERYNEWPALETTE hwnd(%x)", hwnd)); }
if (g_usrPalettized) { HDC hdc; HPALETTE hPalOld; UINT cChangedEntries;
//
// Realize this window's palette, and force a repaint
// if necessary.
//
hdc = GetDC(hwnd); hPalOld = SelectPalette(hdc, pasHost->pmPalette, FALSE); cChangedEntries = RealizePalette(hdc); SelectPalette(hdc, hPalOld, FALSE); ReleaseDC(hwnd, hdc);
rc = (cChangedEntries > 0); if (rc) { // Have to repaint this window
VIEW_InvalidateRgn(pasHost, NULL); } } break;
case WM_GETMINMAXINFO: { RECT rcFrame; LPMINMAXINFO lpmmi = (LPMINMAXINFO) lParam; int cx,cy;
if (!pasHost) { // We're not created yet; bail.
break; }
//
// Calculate the ideal maximized size for this window
//
VIEWFrameGetSize(pasHost, &rcFrame);
//
// If it's bigger than the local screen, clip it.
//
cx = min(rcFrame.right - rcFrame.left, m_pasLocal->cpcCaps.screen.capsScreenWidth); cy = min(rcFrame.bottom - rcFrame.top, m_pasLocal->cpcCaps.screen.capsScreenHeight);
lpmmi->ptMaxSize.x = cx; lpmmi->ptMaxSize.y = cy;
lpmmi->ptMaxTrackSize.x = cx; lpmmi->ptMaxTrackSize.y = cy;
//
// Make sure that we don't size this window too narrow. Keep
// space for borders and one window bar button + scroll ctl.
//
lpmmi->ptMinTrackSize.x = 2*::GetSystemMetrics(SM_CXSIZEFRAME) + (m_viewItemCX + m_viewEdgeCX) + m_viewItemScrollCX;
//
// And prevent sizing too short. Keep space for borders, menu
// bar, status bar, and window bar
//
lpmmi->ptMinTrackSize.y = 2*::GetSystemMetrics(SM_CYSIZEFRAME) + ::GetSystemMetrics(SM_CYCAPTION) + ::GetSystemMetrics(SM_CYMENU);
if (pasHost->m_pView->m_viewWindowBarOn) { lpmmi->ptMinTrackSize.y += m_viewWindowBarCY + m_viewEdgeCY; }
if (pasHost->m_pView->m_viewStatusBarOn) { lpmmi->ptMinTrackSize.y += m_viewStatusBarCY + m_viewEdgeCY; } break; }
case WM_SIZE: { if (wParam != SIZE_MINIMIZED) { VIEWFrameResize(pasHost); } break; }
default: DefWndProc: rc = DefWindowProc(hwnd, message, wParam, lParam); break;
}
DebugExitDWORD(ASShare::VIEW_FrameWindowProc, rc); return(rc); }
//
// VIEWFrameCreate()
//
BOOL ASShare::VIEWFrameCreate(ASPerson * pasPerson) { RECT rect; BOOL rc = FALSE;
DebugEntry(VIEWFrameCreate);
ValidateView(pasPerson);
//
// Creates the children which lie in the frame's client:
// * the toolbar hugs the top
// * the statusbar hugs the bottom
// * the tray hugs the left underneath the toolbar and above the
// statusbar
// * the view fills in what's left
//
GetClientRect(pasPerson->m_pView->m_viewFrame, &rect);
//
// Create the statusbar (hugs bottom)
//
pasPerson->m_pView->m_viewStatusBar = ::CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | CCS_NOPARENTALIGN | CCS_NOMOVEY | CCS_NORESIZE | SBARS_SIZEGRIP, rect.left, rect.bottom - m_viewStatusBarCY, rect.right - rect.left, m_viewStatusBarCY, pasPerson->m_pView->m_viewFrame, NULL, g_asInstance, NULL); if (!pasPerson->m_pView->m_viewStatusBar) { ERROR_OUT(("Couldn't create statusbar for frame of person [%d]", pasPerson->mcsID)); DC_QUIT; }
rect.bottom -= m_viewStatusBarCY + m_viewEdgeCY;
//
// Create the tray (hugs top of status bar, bottom of view)
// BUT NOT FOR DESKTOP SHARING
//
if (pasPerson->hetCount != HET_DESKTOPSHARED) { pasPerson->m_pView->m_viewWindowBar = ::CreateWindowEx(0, VIEW_WINDOWBAR_CLASS_NAME, NULL, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_CHILD, rect.left, rect.bottom - m_viewWindowBarCY, rect.right - rect.left, m_viewWindowBarCY, pasPerson->m_pView->m_viewFrame, NULL, g_asInstance, pasPerson); if (!pasPerson->m_pView->m_viewWindowBar) { ERROR_OUT(("VIEWFrameCreate: Failed to create window bar")); DC_QUIT; }
// Subtract tray space + an edge above it of margin
rect.bottom -= m_viewWindowBarCY + m_viewEdgeCY; }
//
// Create the view (takes up rest of client)
//
if (!CreateWindowEx(WS_EX_CLIENTEDGE, VIEW_CLIENT_CLASS_NAME, NULL, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_HSCROLL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pasPerson->m_pView->m_viewFrame, NULL, g_asInstance, pasPerson)) { ERROR_OUT(("VIEWFrameCreate: Failed to create view")); DC_QUIT; }
rc = TRUE;
DC_EXIT_POINT: DebugExitBOOL(ASShare::VIEWFrameCreate, rc); return(rc); }
//
// VIEWFrameResize()
// Repositions the child windows when the frame is resized.
//
void ASShare::VIEWFrameResize(ASPerson * pasPerson) { RECT rect;
DebugEntry(ASShare::VIEWFrameResize);
ValidateView(pasPerson);
GetClientRect(pasPerson->m_pView->m_viewFrame, &rect);
//
// Move the statusbar
//
if ((pasPerson->m_pView->m_viewStatusBar != NULL) && (pasPerson->m_pView->m_viewStatusBarOn)) { MoveWindow(pasPerson->m_pView->m_viewStatusBar, rect.left, rect.bottom - m_viewStatusBarCY, rect.right - rect.left, m_viewStatusBarCY, TRUE); rect.bottom -= m_viewStatusBarCY + m_viewEdgeCY; }
//
// Move the tray
//
if ((pasPerson->m_pView->m_viewWindowBar != NULL) && (pasPerson->m_pView->m_viewWindowBarOn)) { MoveWindow(pasPerson->m_pView->m_viewWindowBar, rect.left, rect.bottom - m_viewWindowBarCY, rect.right - rect.left, m_viewWindowBarCY, TRUE); rect.bottom -= m_viewWindowBarCY + m_viewEdgeCY; }
//
// Move the view
//
MoveWindow(pasPerson->m_pView->m_viewClient, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
DebugExitVOID(ASShare::VIEWFrameResize); }
//
// VIEWFrameResizeChanged()
//
// Called when the widgets of the frame (the status bar, the window bar, etc.)
// come or go. We may need to shrink the window, if the view is going
// to end up being bigger than the host's desktop.
//
void ASShare::VIEWFrameResizeChanged(ASPerson * pasHost) { RECT rcView;
DebugEntry(ASShare::VIEWFrameResizeChanged);
// Get current view size
GetClientRect(pasHost->m_pView->m_viewClient, &rcView);
//
// The view area can't be bigger than the remote's desktop area
//
if ((rcView.bottom - rcView.top) >= pasHost->viewExtent.y) { RECT rcWindowCur; RECT rcWindowMax;
// Get current frame size
GetWindowRect(pasHost->m_pView->m_viewFrame, &rcWindowCur);
// Get maximum frame size
VIEWFrameGetSize(pasHost, &rcWindowMax);
// Resize vertically to just hold everything
SetWindowPos(pasHost->m_pView->m_viewFrame, NULL, 0, 0, rcWindowCur.right - rcWindowCur.left, rcWindowMax.bottom - rcWindowMax.top, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER); } else { // We can stay the same size, and just shuffle the pieces around
VIEWFrameResize(pasHost); }
DebugExitVOID(ASShare::VIEWFrameResizeChanged); }
//
// VIEWFrameCommand()
//
// Handles commands from menus/accelerators for frame views
//
void ASShare::VIEWFrameCommand ( ASPerson* pasHost, WPARAM wParam, LPARAM lParam ) { UINT cmd; MENUITEMINFO mi;
DebugEntry(ASShare::VIEWFrameCommand);
ValidateView(pasHost);
cmd = GET_WM_COMMAND_ID(wParam, lParam); switch (cmd) { case CMD_TAKECONTROL: { CA_TakeControl(pasHost); break; }
case CMD_CANCELCONTROL: { CA_CancelTakeControl(pasHost, TRUE); break; }
case CMD_RELEASECONTROL: { CA_ReleaseControl(pasHost, TRUE); break; }
case CMD_CTRLALTDEL: { AWC_SendMsg(pasHost->mcsID, AWC_MSG_SAS, 0, 0); break; }
case CMD_VIEWSTATUSBAR: { ASSERT(::IsWindow(pasHost->m_pView->m_viewStatusBar));
// Toggle show/hide of status bar, then resize
if (pasHost->m_pView->m_viewStatusBarOn) { pasHost->m_pView->m_viewStatusBarOn = FALSE; ::ShowWindow(pasHost->m_pView->m_viewStatusBar, SW_HIDE); } else { pasHost->m_pView->m_viewStatusBarOn = TRUE; ::ShowWindow(pasHost->m_pView->m_viewStatusBar, SW_SHOW); }
VIEWFrameResizeChanged(pasHost); break; }
case CMD_VIEWWINDOWBAR: { ASSERT(::IsWindow(pasHost->m_pView->m_viewWindowBar));
// Toggle show/hide of window bar, then resize
if (pasHost->m_pView->m_viewWindowBarOn) { pasHost->m_pView->m_viewWindowBarOn = FALSE; ::ShowWindow(pasHost->m_pView->m_viewWindowBar, SW_HIDE); } else { pasHost->m_pView->m_viewWindowBarOn = TRUE; ::ShowWindow(pasHost->m_pView->m_viewWindowBar, SW_SHOW); }
VIEWFrameResizeChanged(pasHost); break; }
case CMD_VIEWFULLSCREEN: { VIEWFrameFullScreen(pasHost, (pasHost->m_pView->m_viewFullScreen == 0)); break; }
case CMD_HELPTOPICS: { VIEWFrameHelp(pasHost); break; }
case CMD_HELPABOUT: { VIEWFrameAbout(pasHost); break; }
default: { if ((cmd >= CMD_APPSTART) && (cmd < CMD_APPMAX)) { if ((pasHost->m_caControlledBy == m_pasLocal) && !pasHost->m_caControlPaused) { //
// This is a request to activate a host window.
// Get the item data, the remote HWND, then look to see
// if it's still on the tray.
//
ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); mi.fMask = MIIM_DATA; GetMenuItemInfo(GetSubMenu(pasHost->m_pView->m_viewMenuBar, IDSM_WINDOW), cmd, FALSE, &mi); if (!mi.dwItemData) { ERROR_OUT(("No item data for command %d", cmd)); } else { PWNDBAR_ITEM pItem;
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pasHost->m_pView->m_viewWindowBarItems), (void**)&pItem, FIELD_OFFSET(WNDBAR_ITEM, chain), FIELD_OFFSET(WNDBAR_ITEM, winIDRemote), mi.dwItemData, FIELD_SIZE(WNDBAR_ITEM, winIDRemote)); if (pItem) { VIEWWindowBarDoActivate(pasHost, pItem); } } } } else if ((cmd >= CMD_FORWARDCONTROLSTART) && (cmd < CMD_FORWARDCONTROLMAX)) { if ((pasHost->m_caControlledBy == m_pasLocal) && !pasHost->m_caControlPaused) { //
// This is a request to pass control. Get the item data,
// the remote's MCS ID, then look to see if this person is
// still in the share. If so, pass control to them.
//
ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); mi.fMask = MIIM_DATA; GetMenuItemInfo(GetSubMenu(GetSubMenu(pasHost->m_pView->m_viewMenuBar, IDSM_CONTROL), POS_FORWARDCONTROLCMD), cmd, FALSE, &mi); if (!mi.dwItemData) { ERROR_OUT(("No item data for command %d", cmd)); } else { ASPerson * pasT;
if (SC_ValidateNetID((MCSID)mi.dwItemData, &pasT)) { CA_PassControl(pasHost, pasT); } } } } else { ERROR_OUT(("Unrecognized WM_COMMAND id")); } break; } }
DebugExitVOID(ASShare::VIEWFrameCommand); }
//
// ASShare::VIEWFrameInitMenuBar()
//
void ASShare::VIEWFrameInitMenuBar(ASPerson* pasHost) { HMENU hMenu; HMENU hSubMenu; int iItem; MENUITEMINFO mi; UINT cmd; UINT ids; UINT flags; char szItem[256];
DebugEntry(ASShare::VIEWFrameInitMenu);
ValidateView(pasHost); hMenu = pasHost->m_pView->m_viewMenuBar; ASSERT(hMenu);
//
// CONTROL MENU
//
cmd = CMD_TAKECONTROL; ids = IDS_CMD_TAKECONTROL; flags = MF_ENABLED;
if (pasHost->m_caControlledBy == m_pasLocal) { ASSERT(pasHost->m_caAllowControl);
cmd = CMD_RELEASECONTROL; ids = IDS_CMD_RELEASECONTROL;
//
// If the remote is unattended and we're in control, no releasing.
//
if (pasHost->cpcCaps.general.typeFlags & AS_UNATTENDED) flags = MF_GRAYED; } else if ((m_caWaitingForReplyFrom == pasHost) && (m_caWaitingForReplyMsg == CA_REPLY_REQUEST_TAKECONTROL)) { ASSERT(pasHost->m_caAllowControl);
cmd = CMD_CANCELCONTROL; ids = IDS_CMD_CANCELCONTROL; } else if (!pasHost->m_caAllowControl || pasHost->m_caControlledBy) { //
// Host isn't allowing control, or somebody else is in control right
// now.
//
flags = MF_GRAYED; } flags |= MF_STRING | MF_BYPOSITION;
::LoadString(g_asInstance, ids, szItem, sizeof(szItem));
hSubMenu = GetSubMenu(hMenu, IDSM_CONTROL); ModifyMenu(hSubMenu, POS_CONTROLCMD, flags, cmd, szItem);
//
// If we're in control, and there's another 3.0 dude in the conference,
// enable PassControl and build the popup.
//
EnableMenuItem(hSubMenu, POS_FORWARDCONTROLCMD, MF_GRAYED | MF_BYPOSITION); if ((pasHost->m_caControlledBy == m_pasLocal) && !pasHost->m_caControlPaused && (pasHost->cpcCaps.general.version >= CAPS_VERSION_30)) { ASPerson * pasT; HMENU hPassMenu;
hPassMenu = GetSubMenu(hSubMenu, POS_FORWARDCONTROLCMD); ASSERT(IsMenu(hPassMenu));
//
// Delete existing items.
//
iItem = GetMenuItemCount(hPassMenu); while (iItem > 0) { iItem--; DeleteMenu(hPassMenu, iItem, MF_BYPOSITION); }
//
// Add items for the other 3.0 nodes besides us & the host.
//
iItem = CMD_FORWARDCONTROLSTART; pasT = m_pasLocal->pasNext; while (pasT != NULL) { if ((pasT != pasHost) && (pasT->cpcCaps.general.version >= CAPS_VERSION_30)) { //
// This dude is a candidate. We must store the MCS IDs since the
// any person could go away while we're in menu mode.
//
ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); mi.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE | MIIM_DATA; mi.fType = MFT_STRING; mi.fState = MFS_ENABLED; mi.wID = iItem; mi.dwItemData = pasT->mcsID; mi.dwTypeData = pasT->scName; mi.cch = lstrlen(pasT->scName);
//
// Append this to the menu
//
InsertMenuItem(hPassMenu, -1, TRUE, &mi);
iItem++; }
pasT = pasT->pasNext; }
//
// Enable the Pass Control submenu if there's somebody on the
// menu.
//
if (iItem != CMD_FORWARDCONTROLSTART) { EnableMenuItem(hSubMenu, POS_FORWARDCONTROLCMD, MF_ENABLED | MF_BYPOSITION); } }
//
// APPLICATIONS MENU
//
if ((pasHost->hetCount != HET_DESKTOPSHARED) && (pasHost->m_caControlledBy == m_pasLocal) && !pasHost->m_caControlPaused) { PWNDBAR_ITEM pItem;
hSubMenu = GetSubMenu(hMenu, IDSM_WINDOW);
//
// Delete existing items.
//
iItem = GetMenuItemCount(hSubMenu); while (iItem > 0) { iItem--; DeleteMenu(hSubMenu, iItem, MF_BYPOSITION); }
//
// Add window bar items.
//
iItem = CMD_APPSTART; pItem = (PWNDBAR_ITEM)COM_BasedListFirst(&(pasHost->m_pView->m_viewWindowBarItems), FIELD_OFFSET(WNDBAR_ITEM, chain)); while (pItem && (iItem < CMD_APPMAX)) { ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); mi.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE | MIIM_DATA; mi.fType = MFT_STRING;
mi.fState = MFS_ENABLED; if (pItem == pasHost->m_pView->m_viewWindowBarActiveItem) { mi.fState |= MFS_CHECKED; }
mi.wID = iItem; mi.dwItemData = pItem->winIDRemote; mi.dwTypeData = pItem->szText; mi.cch = lstrlen(pItem->szText);
//
// Append this to the menu
//
InsertMenuItem(hSubMenu, -1, TRUE, &mi);
iItem++; pItem = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems), pItem, FIELD_OFFSET(WNDBAR_ITEM, chain)); }
if (iItem == CMD_APPSTART) { char szBlank[128];
//
// Append a disabled, blank item
//
ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); mi.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE; mi.fType = MFT_STRING; mi.fState = MFS_DISABLED; mi.wID = iItem;
LoadString(g_asInstance, IDS_CMD_BLANKPROGRAM, szBlank, sizeof(szBlank)); mi.dwTypeData = szBlank; mi.cch = lstrlen(szBlank);
InsertMenuItem(hSubMenu, -1, TRUE, &mi); } }
//
// VIEW MENU
//
// Status bar
ASSERT(::IsWindow(pasHost->m_pView->m_viewStatusBar)); if (pasHost->m_pView->m_viewStatusBarOn) { ::CheckMenuItem(hMenu, CMD_VIEWSTATUSBAR, MF_CHECKED | MF_BYCOMMAND); } else { ::CheckMenuItem(hMenu, CMD_VIEWSTATUSBAR, MF_UNCHECKED | MF_BYCOMMAND); }
// Window bar
if (!pasHost->m_pView->m_viewWindowBar) { ::EnableMenuItem(hMenu, CMD_VIEWWINDOWBAR, MF_GRAYED | MF_BYCOMMAND); } else if (pasHost->m_pView->m_viewWindowBarOn) { ::CheckMenuItem(hMenu, CMD_VIEWWINDOWBAR, MF_CHECKED | MF_BYCOMMAND); } else { ::CheckMenuItem(hMenu, CMD_VIEWWINDOWBAR, MF_UNCHECKED | MF_BYCOMMAND); }
DebugExitVOID(ASShare::VIEWFrameInitMenu); }
//
// VIEWFrameOnMenuSelect()
//
void ASShare::VIEWFrameOnMenuSelect ( ASPerson * pasHost, WPARAM wParam, LPARAM lParam ) { HMENU hMenu; int uItem; UINT flags; UINT idsStatus = IDS_STATUS_NONE;
DebugEntry(ASShare::VIEWFrameOnMenuSelect);
//
// Extract the params out (menuselect is messy)
//
hMenu = (HMENU)lParam; uItem = (int)LOWORD(wParam); if ((short)HIWORD(wParam) == -1) { flags = 0xFFFFFFFF; } else { flags = HIWORD(wParam); }
if ((LOWORD(flags) == 0xFFFF) && !hMenu) { // Menu mode is ending. Put back original status.
idsStatus = pasHost->m_pView->m_viewStatus; DC_QUIT; }
if (!(flags & MF_POPUP)) { if (flags & MF_SEPARATOR) { // No status
} else if (flags & MF_SYSMENU) { // No status
} else if ((uItem >= CMD_APPSTART) && (uItem < CMD_APPMAX)) { // One of an unbounded set of items in the Window popup
idsStatus = IDS_STATUS_CMDS_APP; } else if ((uItem >= CMD_FORWARDCONTROLSTART) && (uItem < CMD_FORWARDCONTROLMAX)) { // One of an unbounded set of items in the Forward Control popup
idsStatus = IDS_STATUS_CMDS_FORWARD; } else { // A normal command, just add offset to CMD id
idsStatus = uItem + IDS_STATUS_CMD_START; } } else { // This is a popup menu
if (hMenu == pasHost->m_pView->m_viewMenuBar) { // It's a popup from the top level menu bar. uItem is the index
switch (uItem) { case IDSM_CONTROL: idsStatus = IDS_STATUS_MENU_CONTROL; break;
case IDSM_VIEW: idsStatus = IDS_STATUS_MENU_VIEW; break;
case IDSM_WINDOW: idsStatus = IDS_STATUS_MENU_WINDOW; break;
case IDSM_HELP: idsStatus = IDS_STATUS_MENU_HELP; break;
default: ERROR_OUT(("AS: Unknown submenu index %d of frame", uItem)); break; } } else if (hMenu == GetSubMenu(pasHost->m_pView->m_viewMenuBar, IDSM_CONTROL)) { // This is a popup off the Control menu. The only one we have is Forward
idsStatus = IDS_STATUS_MENU_FORWARDCONTROL; } else if (flags & MF_SYSMENU) { // System menu
} }
DC_EXIT_POINT: VIEWFrameSetStatus(pasHost, idsStatus);
DebugEntry(ASShare::VIEWFrameOnMenuSelect); }
//
// VIEWFrameHelp()
//
void ASShare::VIEWFrameHelp(ASPerson * pasHost) { DebugEntry(ASShare::VIEWFrameHelp);
ShowNmHelp(s_cszHtmlHelpFile);
DebugExitVOID(ASShare::VIEWFrameHelp); }
//
// VIEWFrameAbout()
//
void ASShare::VIEWFrameAbout(ASPerson * pasHost) { DebugEntry(ASShare::VIEWFrameAbout);
//
// We make use of the standard centered-disabled-goes-away properly
// VIEW_Message() stuff.
//
VIEW_Message(pasHost, IDS_ABOUT);
DebugExitVOID(ASShare::VIEWFrameAbout); }
//
// VIEWFrameGetSize()
// This returns back a rectangle for the ideal size of the frame. It will
// fit the view, menu, tools, tray, status, etc.
//
void ASShare::VIEWFrameGetSize(ASPerson * pasPerson, LPRECT lprc) { DebugEntry(ASShare::VIEWFrameGetSize);
ValidateView(pasPerson);
VIEWClientGetSize(pasPerson, lprc);
//
// Add in space for tray.
// NOTE that for DESKTOP SHARING we don't have a tray
//
if (pasPerson->m_pView->m_viewWindowBarOn) { lprc->bottom += m_viewWindowBarCY + m_viewEdgeCY; }
//
// Add in space for statusbar if it's on, etc.
//
if (pasPerson->m_pView->m_viewStatusBarOn) { lprc->bottom += m_viewStatusBarCY + m_viewEdgeCY; }
if (!pasPerson->m_pView->m_viewFullScreen) { //
// Adjust for frame styles including menu bar.
//
AdjustWindowRectEx(lprc, WS_OVERLAPPEDWINDOW, TRUE, WS_EX_WINDOWEDGE); }
DebugExitVOID(ASShare::VIEWFrameGetSize); }
//
// VIEWFrameFullScreen()
//
// This puts into or out of screen mode. We remove all the frame goop
// including scrollbars, so that the view area is identical to the screen.
//
void ASShare::VIEWFrameFullScreen(ASPerson * pasPerson, BOOL fFull) { LONG lStyle; RECT rcNew;
DebugEntry(ASShare::VIEWFrameFullScreen);
//
// Turn redraw OFF
//
::SendMessage(pasPerson->m_pView->m_viewFrame, WM_SETREDRAW, FALSE, 0);
if (fFull) { //
// We're going into full screen mode.
//
ASSERT(!pasPerson->m_pView->m_viewFullScreen); pasPerson->m_pView->m_viewFullScreen = TRUE;
//
// Save old window rect
//
::GetWindowRect(pasPerson->m_pView->m_viewFrame, &pasPerson->m_pView->m_viewSavedWindowRect);
//
// Save old scroll pos and set to the origin. Do this BEFORE
// clearing style bits.
//
pasPerson->m_pView->m_viewSavedPos = pasPerson->m_pView->m_viewPos; VIEWClientScroll(pasPerson, 0, 0);
//
// Save current status bar state before turning it off temporarily.
//
if (pasPerson->m_pView->m_viewStatusBarOn) { pasPerson->m_pView->m_viewSavedStatusBarOn = TRUE; pasPerson->m_pView->m_viewStatusBarOn = FALSE; ::ShowWindow(pasPerson->m_pView->m_viewStatusBar, SW_HIDE); } else { pasPerson->m_pView->m_viewSavedStatusBarOn = FALSE; }
//
// Save current window bar state before turning it off temporarily.
//
if (pasPerson->m_pView->m_viewWindowBarOn) { pasPerson->m_pView->m_viewSavedWindowBarOn = TRUE; pasPerson->m_pView->m_viewWindowBarOn = FALSE; ::ShowWindow(pasPerson->m_pView->m_viewWindowBar, SW_HIDE); } else { pasPerson->m_pView->m_viewSavedWindowBarOn = FALSE; }
//
// Remove all frame and client bits.
//
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_EXSTYLE); lStyle &= ~WS_EX_WINDOWEDGE; ::SetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_EXSTYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_STYLE); lStyle &= ~(WS_CAPTION | WS_THICKFRAME); lStyle |= WS_POPUP; ::SetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_STYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewClient, GWL_EXSTYLE); lStyle &= ~WS_EX_CLIENTEDGE; ::SetWindowLong(pasPerson->m_pView->m_viewClient, GWL_EXSTYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewClient, GWL_STYLE); lStyle &= ~(WS_HSCROLL | WS_VSCROLL); ::SetWindowLong(pasPerson->m_pView->m_viewClient, GWL_STYLE, lStyle);
//
// Remove the menu bar
//
::SetMenu(pasPerson->m_pView->m_viewFrame, NULL);
//
// Set up to size window the size of the screen.
//
rcNew.left = 0; rcNew.top = 0; rcNew.right = m_pasLocal->cpcCaps.screen.capsScreenWidth; rcNew.bottom = m_pasLocal->cpcCaps.screen.capsScreenHeight;
//
// Create the moveable escape-out button in the lower right corner.
//
::CreateWindowEx(0, VIEW_FULLEXIT_CLASS_NAME, NULL, WS_CHILD | WS_VISIBLE, rcNew.right - m_viewFullScreenCX - 2*m_viewEdgeCX, rcNew.top + 2*m_viewEdgeCY, m_viewFullScreenCX, m_viewFullScreenCY, pasPerson->m_pView->m_viewClient, (HMENU)0, g_asInstance, pasPerson); } else { //
// We're coming out of full screen mode.
//
//
// Destroy the escape-out button
//
::DestroyWindow(::GetDlgItem(pasPerson->m_pView->m_viewClient, 0));
//
// Put back the menu bar. Do this BEFORE clearing the full screen bit
//
::SetMenu(pasPerson->m_pView->m_viewFrame, pasPerson->m_pView->m_viewMenuBar);
ASSERT(pasPerson->m_pView->m_viewFullScreen); pasPerson->m_pView->m_viewFullScreen = FALSE;
//
// Put back old status bar state.
//
if (pasPerson->m_pView->m_viewSavedStatusBarOn) { pasPerson->m_pView->m_viewStatusBarOn = TRUE; ::ShowWindow(pasPerson->m_pView->m_viewStatusBar, SW_SHOW); }
//
// Put back old window bar state.
//
if (pasPerson->m_pView->m_viewSavedWindowBarOn) { pasPerson->m_pView->m_viewWindowBarOn = TRUE; ::ShowWindow(pasPerson->m_pView->m_viewWindowBar, SW_SHOW); }
//
// Add back all frame and client bits.
//
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_EXSTYLE); lStyle |= WS_EX_WINDOWEDGE; ::SetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_EXSTYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_STYLE); lStyle &= ~(WS_POPUP); lStyle |= (WS_CAPTION | WS_THICKFRAME); ::SetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_STYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewClient, GWL_EXSTYLE); lStyle |= WS_EX_CLIENTEDGE; ::SetWindowLong(pasPerson->m_pView->m_viewClient, GWL_EXSTYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewClient, GWL_STYLE); lStyle |= (WS_HSCROLL | WS_VSCROLL); ::SetWindowLong(pasPerson->m_pView->m_viewClient, GWL_STYLE, lStyle);
//
// Put back old scroll pos AFTER style bits restore.
//
VIEWClientScroll(pasPerson, pasPerson->m_pView->m_viewSavedPos.x, pasPerson->m_pView->m_viewSavedPos.y);
//
// Restore the window back to where it started.
//
rcNew = pasPerson->m_pView->m_viewSavedWindowRect; }
//
// Resize, reframe, and repaint from scratch.
//
::SendMessage(pasPerson->m_pView->m_viewFrame, WM_SETREDRAW, TRUE, 0);
::SetWindowPos(pasPerson->m_pView->m_viewFrame, NULL, rcNew.left, rcNew.top, rcNew.right - rcNew.left, rcNew.bottom - rcNew.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOCOPYBITS);
DebugExitVOID(ASShare::VIEWFrameFullScreen); }
//
// VIEWClientGetSize()
// This returns back a rectangle for the ideal size of the view part of the
// frame client. It will fit the extent of what we're viewing on the remote
// plus scrollbars.
//
void ASShare::VIEWClientGetSize(ASPerson * pasPerson, LPRECT lprc) { DebugEntry(ASShare::VIEWClientGetSize);
ValidateView(pasPerson);
lprc->left = 0; lprc->top = 0; lprc->right = pasPerson->viewExtent.x; lprc->bottom = pasPerson->viewExtent.y;
if (!pasPerson->m_pView->m_viewFullScreen) { AdjustWindowRectEx(lprc, WS_CHILD, FALSE, WS_EX_CLIENTEDGE);
lprc->right += GetSystemMetrics(SM_CXVSCROLL); lprc->bottom += GetSystemMetrics(SM_CYHSCROLL); }
DebugExitVOID(ASShare::VIEWClientGetSize); }
//
// VIEWClientWindowProc()
// Handles messages for the view window, a child in the client of the frame
// which displays the contents of the remote host's shared apps.
//
LRESULT CALLBACK VIEWClientWindowProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { return(g_asSession.pShare->VIEW_ViewWindowProc(hwnd, message, wParam, lParam)); }
LRESULT ASShare::VIEW_ViewWindowProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { LRESULT rc = 0; RECT rcl; POINT mousePos; SCROLLINFO si; ASPerson * pasPerson;
DebugEntry(ASShare::VIEW_ViewWindowProc);
pasPerson = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pasPerson) { ValidateView(pasPerson); }
switch (message) { case WM_NCCREATE: { // Get the passed in host pointer, and set in our window long
pasPerson = (ASPerson *)((LPCREATESTRUCT)lParam)->lpCreateParams; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pasPerson);
pasPerson->m_pView->m_viewClient = hwnd; goto DefWndProc; break; }
case WM_NCDESTROY: { if (pasPerson != NULL) { pasPerson->m_pView->m_viewClient = NULL; }
goto DefWndProc; break; }
case WM_ERASEBKGND: { //
// BOGUS LAURABU: Paint on erase then validate for faster
// response.
//
rc = TRUE; break; }
case WM_PAINT: { VIEWClientPaint(pasPerson); break; }
case WM_SETFOCUS: { pasPerson->m_pView->m_viewFocus = TRUE; pasPerson->m_pView->m_viewMouseWheelDelta = 0; break; }
case WM_KILLFOCUS: { pasPerson->m_pView->m_viewFocus = FALSE; pasPerson->m_pView->m_viewMouseWheelDelta = 0; break; }
case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: { VIEWClientMouseDown(pasPerson, message, wParam, lParam); break; }
case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: { VIEWClientMouseUp(pasPerson, message, wParam, lParam, TRUE); break; }
case WM_MOUSEMOVE: { VIEWClientMouseMove(pasPerson, message, wParam, lParam); break; }
case WM_MOUSEWHEEL: { //
// We've handled it no matter what, don't pass it up the chain.
//
rc = TRUE;
//
// If we're not controlling this dude, try to use the mousewheel
// to scroll.
//
if ((pasPerson->m_caControlledBy != m_pasLocal) || pasPerson->m_caControlPaused) { VIEWClientMouseWheel(pasPerson, wParam, lParam); break; }
//
// FALL THROUGH
// Otherwise, we send the MOUSEWHEEL message to the host.
//
}
case WM_LBUTTONDBLCLK: case WM_RBUTTONDBLCLK: case WM_MBUTTONDBLCLK: { VIEWClientMouseMsg(pasPerson, message, wParam, lParam); break; }
case WM_TIMER: { if (wParam == IDT_AUTOSCROLL) { VIEWClientAutoScroll(pasPerson); } break; }
case WM_CAPTURECHANGED: { //
// Check if capture got stolen away from us, if we think the
// buttons are down fake a button up.
//
if (pasPerson->m_pView->m_viewMouseFlags != 0) { VIEWClientCaptureStolen(pasPerson); } break; }
case WM_KEYDOWN: { WPARAM wScrollNotify; UINT uMsg;
if ((pasPerson->m_caControlledBy == m_pasLocal) && !pasPerson->m_caControlPaused) { goto KeyInput; }
if (pasPerson->m_pView->m_viewFullScreen) { if (wParam == VK_ESCAPE) { //
// Kick out of full screen mode.
//
VIEWFrameFullScreen(pasPerson, FALSE); }
goto DefWndProc; }
//
// UP, DOWN, LEFT, and RIGHT are unambiguous about which
// scrollbar is intended.
//
// For the others, unmodified is vertical and SHIFT is
// horizontal.
//
if (::GetKeyState(VK_SHIFT) < 0) { uMsg = WM_HSCROLL; } else { uMsg = WM_VSCROLL; }
switch (wParam) { //
// These aren't ambiguous, we know which scrollbar is meant
// by the direction.
//
case VK_UP: wScrollNotify = SB_LINEUP; uMsg = WM_VSCROLL; break;
case VK_DOWN: wScrollNotify = SB_LINEDOWN; uMsg = WM_VSCROLL; break;
case VK_LEFT: wScrollNotify = SB_LINEUP; uMsg = WM_HSCROLL; break;
case VK_RIGHT: wScrollNotify = SB_LINEDOWN; uMsg = WM_HSCROLL; break;
//
// These are ambiguous, hence the SHIFT key as a
// modifier.
//
case VK_PRIOR: wScrollNotify = SB_PAGEUP; break;
case VK_NEXT: wScrollNotify = SB_PAGEDOWN; break;
case VK_HOME: wScrollNotify = SB_TOP; break;
case VK_END: wScrollNotify = SB_BOTTOM; break;
default: goto DefWndProc; break; }
SendMessage(hwnd, uMsg, MAKELONG(wScrollNotify, 0), 0L); break; }
case WM_SYSKEYDOWN: { if ((pasPerson->m_caControlledBy == m_pasLocal) && !pasPerson->m_caControlPaused) { goto KeyInput; }
//
// ALT-ENTER toggles full screen state, if it's available
//
if ((wParam == VK_RETURN) && !(::GetMenuState(pasPerson->m_pView->m_viewMenuBar, CMD_VIEWFULLSCREEN, MF_BYCOMMAND) & MF_DISABLED)) { VIEWFrameFullScreen(pasPerson, (pasPerson->m_pView->m_viewFullScreen == 0)); } goto DefWndProc; break; }
case WM_KEYUP: case WM_SYSKEYUP: { //
// If we're controlling this node, pass it along. Otherwise,
// call DefWindowProc() so key accels like Alt+Space for system
// menu will kick in.
//
if ((pasPerson->m_caControlledBy == m_pasLocal) && !pasPerson->m_caControlPaused) { KeyInput: IM_OutgoingKeyboardInput(pasPerson, (UINT)wParam, (UINT)lParam); } else { goto DefWndProc; } break; }
case WM_SETCURSOR: { if ((LOWORD(lParam) == HTCLIENT) && ((HWND)wParam == hwnd)) { HCURSOR hCursor; POINT cursorPoint;
if ((pasPerson->m_caControlledBy == m_pasLocal) && !pasPerson->m_caControlPaused) { hCursor = m_cmArrowCursor;
//
// Only set the remote cursor if we're over shared space.
//
if (pasPerson->m_pView->m_viewFocus) { GetCursorPos(&cursorPoint); ScreenToClient(hwnd, &cursorPoint);
if (VIEW_IsPointShared(pasPerson, cursorPoint)) { hCursor = pasPerson->cmhRemoteCursor; } } } else { // NoDrop
hCursor = m_viewNotInControl; }
SetCursor(hCursor);
rc = TRUE; } else { // Let defwindowproc handle it
goto DefWndProc; } break; }
case WM_SIZE: { //
// If we're in full screen mode, there are no scrollbars.
//
if (!pasPerson->m_pView->m_viewFullScreen) { int xNewPos; int yNewPos;
xNewPos = pasPerson->m_pView->m_viewPos.x; yNewPos = pasPerson->m_pView->m_viewPos.y;
GetClientRect(hwnd, &rcl); pasPerson->m_pView->m_viewPage.x = rcl.right - rcl.left; pasPerson->m_pView->m_viewPage.y = rcl.bottom - rcl.top; TRACE_OUT(("WM_SIZE: Set page size (%04d, %04d)", pasPerson->m_pView->m_viewPage.x, pasPerson->m_pView->m_viewPage.y));
//
// Scroll window if necessary.
//
si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_PAGE|SIF_DISABLENOSCROLL;
// Set new HORIZONTAL proportional scroll button size
si.nPage = pasPerson->m_pView->m_viewPage.x; SetScrollInfo(hwnd, SB_HORZ, &si, TRUE );
// Set new VERTICAL proportional scroll button size
si.nPage = pasPerson->m_pView->m_viewPage.y; SetScrollInfo(hwnd, SB_VERT, &si, TRUE );
//
// This will make sure the scroll pos is pinned properly
//
VIEWClientScroll(pasPerson, pasPerson->m_pView->m_viewPos.x, pasPerson->m_pView->m_viewPos.y); } break; }
case WM_HSCROLL: { int xNewPos; // new position
switch (GET_WM_HSCROLL_CODE(wParam, lParam)) { case SB_PAGEUP: xNewPos = pasPerson->m_pView->m_viewPos.x - pasPerson->m_pView->m_viewPgSize.x; break; case SB_PAGEDOWN: xNewPos = pasPerson->m_pView->m_viewPos.x + pasPerson->m_pView->m_viewPgSize.x; break; case SB_LINEUP: xNewPos = pasPerson->m_pView->m_viewPos.x - pasPerson->m_pView->m_viewLnSize.x; break; case SB_LINEDOWN: xNewPos = pasPerson->m_pView->m_viewPos.x + pasPerson->m_pView->m_viewLnSize.x; break; case SB_TOP: xNewPos = 0; break; case SB_BOTTOM: xNewPos = pasPerson->viewExtent.x; break;
case SB_THUMBTRACK: case SB_THUMBPOSITION: xNewPos = GET_WM_HSCROLL_POS(wParam, lParam); break;
default: xNewPos = pasPerson->m_pView->m_viewPos.x; break; }
//
// This will pin the desired scroll pos in the range, and if
// nothing has changed, won't scroll.
//
VIEWClientScroll(pasPerson, xNewPos, pasPerson->m_pView->m_viewPos.y); break; }
case WM_VSCROLL: { int yNewPos; // new position
switch (GET_WM_VSCROLL_CODE(wParam, lParam)) { case SB_PAGEUP: yNewPos = pasPerson->m_pView->m_viewPos.y - pasPerson->m_pView->m_viewPgSize.y; break; case SB_PAGEDOWN: yNewPos = pasPerson->m_pView->m_viewPos.y + pasPerson->m_pView->m_viewPgSize.y; break; case SB_LINEUP: yNewPos = pasPerson->m_pView->m_viewPos.y - pasPerson->m_pView->m_viewLnSize.y; break; case SB_LINEDOWN: yNewPos = pasPerson->m_pView->m_viewPos.y + pasPerson->m_pView->m_viewLnSize.y; break; case SB_TOP: yNewPos = 0; break; case SB_BOTTOM: yNewPos = pasPerson->viewExtent.y; break;
case SB_THUMBTRACK: case SB_THUMBPOSITION: yNewPos = GET_WM_VSCROLL_POS(wParam, lParam); break;
default: yNewPos = pasPerson->m_pView->m_viewPos.y; break; }
//
// This will pin the desired scroll pos in the range, and if
// nothing has changed, won't scroll.
//
VIEWClientScroll(pasPerson, pasPerson->m_pView->m_viewPos.x, yNewPos); break; }
default: DefWndProc: rc = DefWindowProc(hwnd, message, wParam, lParam); break; }
DebugExitDWORD(ASShare::VIEW_ViewWindowProc, rc); return(rc); }
//
// VIEWClientPaint()
//
// This paints the client area of the view frame. We paint
// (1) The obscured area, in the obscured pattern
// * parts of shared regions that are covered up
// * parts of shared regions that are offscreen/off the VD
// (2) The shared area, from the bitmap
// (3) The deadspace, in COLOR_APPWORKSPACE
//
void ASShare::VIEWClientPaint(ASPerson * pasPerson) { PAINTSTRUCT ps; HDC hdcView; HPALETTE hOldPal; HPALETTE hOldPal2; RECT rcT;
DebugEntry(ASShare::VIEWClientPaint);
ValidateView(pasPerson);
hdcView = BeginPaint(pasPerson->m_pView->m_viewClient, &ps); if (hdcView == NULL) { WARNING_OUT(( "Failed to get hdc for frame window %08X", pasPerson->m_pView->m_viewClient)); DC_QUIT; }
if (IsRectEmpty(&ps.rcPaint)) { TRACE_OUT(("Nothing to paint but got WM_PAINT message")); DC_QUIT; }
TRACE_OUT(("VIEWClientPaint: Painting total client area {%04d, %04d, %04d, %04d}", ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom));
//
// In desktop sharing, viewSharedRgn is NULL
//
if (pasPerson->m_pView->m_viewSharedRgn != NULL) { POINT ptOrigin; HBRUSH hbrT;
//
// First, create paint area region
//
SetRectRgn(pasPerson->m_pView->m_viewPaintRgn, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
//
// Second, compute the VD area not currently on screen. Do this
// in CLIENT coords.
//
SetRectRgn(pasPerson->m_pView->m_viewExtentRgn, -pasPerson->m_pView->m_viewPos.x, -pasPerson->m_pView->m_viewPos.y, -pasPerson->m_pView->m_viewPos.x + pasPerson->viewExtent.x, -pasPerson->m_pView->m_viewPos.y + pasPerson->viewExtent.y);
SetRectRgn(pasPerson->m_pView->m_viewScreenRgn, -pasPerson->m_pView->m_viewPos.x + pasPerson->m_pView->m_dsScreenOrigin.x, -pasPerson->m_pView->m_viewPos.y + pasPerson->m_pView->m_dsScreenOrigin.y, -pasPerson->m_pView->m_viewPos.x + pasPerson->m_pView->m_dsScreenOrigin.x + pasPerson->cpcCaps.screen.capsScreenWidth, -pasPerson->m_pView->m_viewPos.y + pasPerson->m_pView->m_dsScreenOrigin.y + pasPerson->cpcCaps.screen.capsScreenHeight);
SubtractRgn(pasPerson->m_pView->m_viewExtentRgn, pasPerson->m_pView->m_viewExtentRgn, pasPerson->m_pView->m_viewScreenRgn);
//
// pasPerson->m_pView->m_viewExtentRgn is now the offscreen parts of the VD, and therefore
// any shared areas lying in them should be treated as obscured.
//
//
// Now, compute the real obscured area. It's the covered up bits
// plus open parts of shared stuff not currently on screen.
//
IntersectRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewExtentRgn, pasPerson->m_pView->m_viewSharedRgn); UnionRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewObscuredRgn);
// Calc what part of the obscured region to actually paint
IntersectRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewPaintRgn); if (GetRgnBox(pasPerson->m_pView->m_viewScratchRgn, &rcT) > NULLREGION) { TRACE_OUT(("VIEWClientPaint: Painting obscured client area {%04d, %04d, %04d, %04d}", rcT.left, rcT.top, rcT.right, rcT.bottom));
//
// Remove this area so we have what's left to paint.
//
SubtractRgn(pasPerson->m_pView->m_viewPaintRgn, pasPerson->m_pView->m_viewPaintRgn, pasPerson->m_pView->m_viewScratchRgn);
//
// We do NOT want to use FillRgn; it ignores the brush origin.
// So we select this in as the clip region and PatBlt instead.
//
SelectClipRgn(hdcView, pasPerson->m_pView->m_viewScratchRgn);
#ifdef _DEBUG
//
// NOTE: Do NOT move this--we're using ptOrigin for scratch.
//
GetDCOrgEx(hdcView, &ptOrigin); TRACE_OUT(("VIEWClientPaint: Setting brush origin to {%04d, %04d}, screen {%04d, %04d}", -pasPerson->m_pView->m_viewPos.x, -pasPerson->m_pView->m_viewPos.y, ptOrigin.x - pasPerson->m_pView->m_viewPos.x, ptOrigin.y - pasPerson->m_pView->m_viewPos.y)); #endif
//
// Align the brush with where the view's real origin would be, in
// client coords. We do that by accounting for being scrolled over.
//
SetBrushOrgEx(hdcView, -pasPerson->m_pView->m_viewPos.x, -pasPerson->m_pView->m_viewPos.y, &ptOrigin); UnrealizeObject(m_viewObscuredBrush); hbrT = SelectBrush(hdcView, m_viewObscuredBrush);
PatBlt(hdcView, rcT.left, rcT.top, rcT.right - rcT.left, rcT.bottom - rcT.top, PATCOPY);
SelectBrush(hdcView, hbrT); SetBrushOrgEx(hdcView, ptOrigin.x, ptOrigin.y, NULL);
SelectClipRgn(hdcView, NULL); }
//
// Paint the deadspace area, set up clipping for app sharing.
// This also works for desktop sharing, where there are no obscured or
// shared regions, the whole area paints.
//
//
// The deadspace is whatever's left over in the paint region
// (already subtracted the obscured region) after subtracting the
// shared area
//
SubtractRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewPaintRgn, pasPerson->m_pView->m_viewSharedRgn);
if (GetRgnBox(pasPerson->m_pView->m_viewScratchRgn, &rcT) > NULLREGION) { TRACE_OUT(("VIEWClientPaint: Painting dead client area {%04d, %04d, %04d, %04d}", rcT.left, rcT.top, rcT.right, rcT.bottom)); FillRgn(hdcView, pasPerson->m_pView->m_viewScratchRgn, GetSysColorBrush(COLOR_APPWORKSPACE)); }
//
// Compute what part of the shared area needs painting (the part
// that lies on the remote screen actually).
//
IntersectRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewSharedRgn, pasPerson->m_pView->m_viewScreenRgn); IntersectRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewPaintRgn);
// Now select in the piece of what we're painting as the clip region
SelectClipRgn(hdcView, pasPerson->m_pView->m_viewScratchRgn); }
//
// Blt the shared region
//
if (GetClipBox(hdcView, &rcT) > NULLREGION) { TRACE_OUT(("VIEWClientPaint: Painting shared client area {%04x, %04x, %04x, %04x}", rcT.left, rcT.top, rcT.right, rcT.bottom));
if (g_usrPalettized) { ASSERT(pasPerson->pmPalette != NULL);
//
// Select and realize the current remote palette into the
// screen and shadow bitmap DCs.
//
hOldPal = SelectPalette(pasPerson->m_pView->m_usrDC, pasPerson->pmPalette, FALSE); RealizePalette(pasPerson->m_pView->m_usrDC);
hOldPal2 = SelectPalette( hdcView, pasPerson->pmPalette, FALSE); RealizePalette(hdcView); }
//
// The host bitmap is in screen coords, not VD coords, so
// adjust for being scrolled over...
//
BitBlt(hdcView, rcT.left, rcT.top, rcT.right - rcT.left, rcT.bottom - rcT.top, pasPerson->m_pView->m_usrDC, rcT.left + pasPerson->m_pView->m_viewPos.x - pasPerson->m_pView->m_dsScreenOrigin.x, rcT.top + pasPerson->m_pView->m_viewPos.y - pasPerson->m_pView->m_dsScreenOrigin.y, SRCCOPY);
if (g_usrPalettized) { ASSERT(pasPerson->pmPalette != NULL);
SelectPalette(pasPerson->m_pView->m_usrDC, hOldPal, FALSE); SelectPalette(hdcView, hOldPal2, FALSE); } }
//
// Deselect the clip region, or we won't be able to draw shadow cursors
// that lie outside the shared area.
//
if (pasPerson->m_pView->m_viewSharedRgn != NULL) { SelectClipRgn(hdcView, NULL); }
//
// Draw the shadow cursor.
//
CM_DrawShadowCursor(pasPerson, hdcView);
DC_EXIT_POINT:
if (hdcView != NULL) EndPaint(pasPerson->m_pView->m_viewClient, &ps);
DebugExitVOID(ASShare::VIEWClientPaint); }
//
// VIEWClientScroll()
//
// This is the common place where the scroll position is altered. If
// necessary the contents are scrolled over, the regions (always in client
// coords) are adjusted, and new info about our origin is sent to remotes.
//
// We first make sure the scroll position is pinned properly within the
// range.
//
// The return value is whether scrolling happened or not.
//
BOOL ASShare::VIEWClientScroll ( ASPerson * pasPerson, int xNew, int yNew ) { int dx; int dy;
DebugEntry(ASShare::VIEWClientScroll);
//
// First, pin the requested new position within the range
//
//
// Pin x pos
//
if (xNew < 0) xNew = 0;
if (xNew + pasPerson->m_pView->m_viewPage.x > pasPerson->viewExtent.x) xNew = pasPerson->viewExtent.x - pasPerson->m_pView->m_viewPage.x;
//
// Pin y pos
//
if (yNew < 0) yNew = 0;
if (yNew + pasPerson->m_pView->m_viewPage.y > pasPerson->viewExtent.y) yNew = pasPerson->viewExtent.y - pasPerson->m_pView->m_viewPage.y;
//
// How much are we going to scroll by?
//
dx = pasPerson->m_pView->m_viewPos.x - xNew; dy = pasPerson->m_pView->m_viewPos.y - yNew;
// Updates
if (dx || dy) { //
// Adjust regions
//
if (pasPerson->m_pView->m_viewObscuredRgn != NULL) OffsetRgn(pasPerson->m_pView->m_viewObscuredRgn, dx, dy);
if (pasPerson->m_pView->m_viewSharedRgn != NULL) OffsetRgn(pasPerson->m_pView->m_viewSharedRgn, dx, dy);
pasPerson->m_pView->m_viewPos.x = xNew; pasPerson->m_pView->m_viewPos.y = yNew;
ScrollWindowEx(pasPerson->m_pView->m_viewClient, dx, dy, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE);
if (dx) { SetScrollPos(pasPerson->m_pView->m_viewClient, SB_HORZ, xNew, TRUE); }
if (dy) { SetScrollPos(pasPerson->m_pView->m_viewClient, SB_VERT, yNew, TRUE); } }
DebugExitBOOL(ASShare::VIEWClientScroll, (dx || dy)); return(dx || dy); }
//
// VIEWClientMouseDown()
//
void ASShare::VIEWClientMouseDown ( ASPerson * pasPerson, UINT message, WPARAM wParam, LPARAM lParam ) { DebugEntry(ASShare::VIEWClientMouseDown);
ValidateView(pasPerson);
//
// On the first button down, set capture so all mouse messages come
// to us until capture is released or stolen.
//
if (!pasPerson->m_pView->m_viewMouseFlags) { //
// If this is RBUTTONDOWN, track the Collaborate pop up...
//
ASSERT(!pasPerson->m_pView->m_viewMouseOutside); SetCapture(pasPerson->m_pView->m_viewClient); }
//
// Remember what button is down.
//
switch (message) { case WM_LBUTTONDOWN: pasPerson->m_pView->m_viewMouseFlags |= MK_LBUTTON; break;
case WM_RBUTTONDOWN: pasPerson->m_pView->m_viewMouseFlags |= MK_RBUTTON; break;
case WM_MBUTTONDOWN: pasPerson->m_pView->m_viewMouseFlags |= MK_MBUTTON; break; }
//
// Save the current mouse position
//
pasPerson->m_pView->m_viewMouse.x = GET_X_LPARAM(lParam); pasPerson->m_pView->m_viewMouse.y = GET_Y_LPARAM(lParam);
VIEWClientMouseMsg(pasPerson, message, wParam, lParam);
DebugExitVOID(ASShare::VIEWClientMouseDown); }
//
// VIEWClientMouseUp()
//
void ASShare::VIEWClientMouseUp ( ASPerson * pasPerson, UINT message, WPARAM wParam, LPARAM lParam, BOOL fReleaseCapture ) { DebugEntry(ASShare::VIEWClientMouseUp);
switch (message) { case WM_LBUTTONUP: if (pasPerson->m_pView->m_viewMouseFlags & MK_LBUTTON) pasPerson->m_pView->m_viewMouseFlags &= ~MK_LBUTTON; else fReleaseCapture = FALSE; // From dbl-click
break;
case WM_RBUTTONUP: if (pasPerson->m_pView->m_viewMouseFlags & MK_RBUTTON) pasPerson->m_pView->m_viewMouseFlags &= ~MK_RBUTTON; else fReleaseCapture = FALSE; // From dbl-click
break;
case WM_MBUTTONUP: if (pasPerson->m_pView->m_viewMouseFlags & MK_MBUTTON) pasPerson->m_pView->m_viewMouseFlags &= ~MK_MBUTTON; else fReleaseCapture = FALSE; // From dbl-click
break; }
//
// Should we release capture?
// We don't just want to release capture on a button up. The user may
// press one button down then another; we don't want to release capture
// until all buttons are up.
//
if (!pasPerson->m_pView->m_viewMouseFlags) { if (pasPerson->m_pView->m_viewMouseOutside) { pasPerson->m_pView->m_viewMouseOutside = FALSE; KillTimer(pasPerson->m_pView->m_viewClient, IDT_AUTOSCROLL); }
if (fReleaseCapture) ReleaseCapture(); }
//
// Save the current mouse position
//
pasPerson->m_pView->m_viewMouse.x = GET_X_LPARAM(lParam); pasPerson->m_pView->m_viewMouse.y = GET_Y_LPARAM(lParam);
VIEWClientMouseMsg(pasPerson, message, wParam, lParam);
DebugExitVOID(ASShare::VIEWClientMouseUp); }
//
// VIEWClientCaptureStolen()
// Called when capture gets stolen away from us, like by Alt-Tab.
//
void ASShare::VIEWClientCaptureStolen(ASPerson * pasPerson) { DebugEntry(ASShare::VIEWClientCaptureStolen);
//
// We need to fake a button up for each button we think is down.
// Use the current cursor pos.
//
if (pasPerson->m_pView->m_viewMouseFlags & MK_MBUTTON) { VIEWClientMouseUp(pasPerson, WM_MBUTTONUP, pasPerson->m_pView->m_viewMouseFlags, MAKELPARAM(pasPerson->m_pView->m_viewMouse.x, pasPerson->m_pView->m_viewMouse.y), FALSE); }
if (pasPerson->m_pView->m_viewMouseFlags & MK_RBUTTON) { VIEWClientMouseUp(pasPerson, WM_RBUTTONUP, pasPerson->m_pView->m_viewMouseFlags, MAKELPARAM(pasPerson->m_pView->m_viewMouse.x, pasPerson->m_pView->m_viewMouse.y), FALSE); }
if (pasPerson->m_pView->m_viewMouseFlags & MK_LBUTTON) { VIEWClientMouseUp(pasPerson, WM_LBUTTONUP, pasPerson->m_pView->m_viewMouseFlags, MAKELPARAM(pasPerson->m_pView->m_viewMouse.x, pasPerson->m_pView->m_viewMouse.y), FALSE); }
DebugExitVOID(ASShare::VIEWClientCaptureStolen); }
//
// VIEWClientMouseMove()
//
void ASShare::VIEWClientMouseMove ( ASPerson * pasPerson, UINT message, WPARAM wParam, LPARAM lParam ) { RECT rcClient;
DebugEntry(ASShare::VIEWClientMouseMove);
if (!pasPerson->m_pView->m_viewFocus) { // Ignore mouse moves over windows that don't have the focus
DC_QUIT; }
//
// Save the current mouse position
//
pasPerson->m_pView->m_viewMouse.x = GET_X_LPARAM(lParam); pasPerson->m_pView->m_viewMouse.y = GET_Y_LPARAM(lParam);
GetClientRect(pasPerson->m_pView->m_viewClient, &rcClient);
//
// If any button is down, check whether we should kick in
// autoscroll detection.
//
if (pasPerson->m_pView->m_viewMouseFlags) { // Is the mouse inside or outside the client for the first time?
if (PtInRect(&rcClient, pasPerson->m_pView->m_viewMouse)) { //
// Was the mouse outside the client before? If so, kill our
// autoscroll timer, we're not dragging outside.
//
if (pasPerson->m_pView->m_viewMouseOutside) { pasPerson->m_pView->m_viewMouseOutside = FALSE; KillTimer(pasPerson->m_pView->m_viewClient, IDT_AUTOSCROLL); } } else { //
// Is the first time the mouse is outside the client? If so,
// set our autoscroll timer to the default value. When it goes
// off, the autoscroll code will scroll by some multiple of
// how far away the mouse is from the client.
//
if (!pasPerson->m_pView->m_viewMouseOutside) { //
// The Windows scrollbar code uses 1/8 of the double-click
// time, so we do also.
//
pasPerson->m_pView->m_viewMouseOutside = TRUE; SetTimer(pasPerson->m_pView->m_viewClient, IDT_AUTOSCROLL, GetDoubleClickTime() / 8, NULL); }
//
// LAURABU BOGUS!
// When IM_Periodic goop is gone for controlling, do NOT
// pass along mouse outside messages. Only the autoscroll
// timer will fake a mouse move in this case. Either that,
// or clip the position to the nearest client area equivalent.
//
} }
VIEWClientMouseMsg(pasPerson, message, wParam, lParam);
DC_EXIT_POINT: DebugExitVOID(ASShare::VIEWClientMouseMove); }
//
// VIEWClientMouseMsg()
//
void ASShare::VIEWClientMouseMsg ( ASPerson * pasPerson, UINT message, WPARAM wParam, LPARAM lParam ) { POINT mousePos;
DebugEntry(ASShare::VIEWClientMouseMsg);
//
// Extract the mouse position from <lParam> and package it
// in a POINT structure. These coordinates are relative to our
// client area. So convert to remote's desktop by adjusting for
// scroll position.
//
// Be careful when converting the LOWORD and HIWORD values
// because the positions are signed values.
//
mousePos.x = GET_X_LPARAM(lParam) + pasPerson->m_pView->m_viewPos.x; mousePos.y = GET_Y_LPARAM(lParam) + pasPerson->m_pView->m_viewPos.y;
//
// These coords represent the SCREEN coords on the host.
//
if (pasPerson->m_caControlledBy == m_pasLocal) { if (!pasPerson->m_caControlPaused) { IM_OutgoingMouseInput(pasPerson, &mousePos, message, (UINT)wParam); } } else if (pasPerson->m_caAllowControl && !pasPerson->m_caControlledBy && (message == WM_LBUTTONDBLCLK)) { //
// If we're already waiting for control of this person, don't bother
// trying to take control again.
//
if ((m_caWaitingForReplyFrom != pasPerson) && (m_caWaitingForReplyMsg != CA_REPLY_REQUEST_TAKECONTROL)) { CA_TakeControl(pasPerson); } }
DebugExitVOID(ASShare::VIEWClientMouse); }
//
// VIEWClientMouseWheel()
//
// Unbelievably complicated, messy, nonsensical Intellimouse wheel handling
// to scroll the client. Since the Intellimouse makes no distinction for
// which direction to scroll in, we basically have to guess. We don't want
// to be unpredictable and decide which direction to scroll based on how
// much is visible in each dimenion.
//
// So instead, we assume horizontal. If the horizontal scrollbar is disabled,
// then we try vertical. If that's disabled, we do nothing.
//
// We do NOT handle zoom and datazoom flavors.
//
// Note that this code comes from the listbox/sample source.
//
void ASShare::VIEWClientMouseWheel ( ASPerson * pasHost, WPARAM wParam, LPARAM lParam ) { int cDetants;
DebugEntry(ASShare::VIEWClientMouseWheel);
//
// The LOWORD of wParam has key state information.
// The HIWORD of wParam is the number of mouse wheel clicks.
//
//
// We don't do zoom/datazoom
//
if (wParam & (MK_SHIFT | MK_CONTROL)) { DC_QUIT; }
pasHost->m_pView->m_viewMouseWheelDelta -= (int)(short)HIWORD(wParam); cDetants = pasHost->m_pView->m_viewMouseWheelDelta / WHEEL_DELTA;
if (cDetants && (m_viewMouseWheelScrollLines > 0)) { POINT ptPos;
pasHost->m_pView->m_viewMouseWheelDelta %= WHEEL_DELTA;
//
// The basic idea is that we scroll some number of lines, the
// number being cDetants.
//
ptPos = pasHost->m_pView->m_viewPos;
//
// To be consistent with other apps, and with our keyboard
// accelerators, try the vertical direction first.
//
if (pasHost->m_pView->m_viewPage.y < pasHost->viewExtent.y) { ptPos.y += cDetants * pasHost->m_pView->m_viewLnSize.y; } else if (pasHost->m_pView->m_viewPage.x < pasHost->viewExtent.x) { ptPos.x += cDetants * pasHost->m_pView->m_viewLnSize.x; } else { // Nothing to scroll, the whole view fits in the client area.
}
VIEWClientScroll(pasHost, ptPos.x, ptPos.y); }
DC_EXIT_POINT: DebugExitVOID(ASShare::VIEWClientMouseWheel); }
//
// VIEWClientAutoScroll()
//
void ASShare::VIEWClientAutoScroll(ASPerson * pasPerson) { int dx; int dy; RECT rcClient;
DebugEntry(ASShare::VIEWClientAutoScroll);
ValidateView(pasPerson); ASSERT(pasPerson->m_pView->m_viewMouseOutside);
//
// Do scrolling. The amount is dependent on how far outside the
// client area we are.
//
GetClientRect(pasPerson->m_pView->m_viewClient, &rcClient);
// Horizontal scrolling?
if (pasPerson->m_pView->m_viewMouse.x < rcClient.left) { dx = pasPerson->m_pView->m_viewMouse.x - rcClient.left; } else if (pasPerson->m_pView->m_viewMouse.x >= rcClient.right) { dx = pasPerson->m_pView->m_viewMouse.x - rcClient.right + 1; } else { dx = 0; }
// Vertical scrolling?
if (pasPerson->m_pView->m_viewMouse.y < rcClient.top) { dy = pasPerson->m_pView->m_viewMouse.y - rcClient.top; } else if (pasPerson->m_pView->m_viewMouse.y >= rcClient.bottom) { dy = pasPerson->m_pView->m_viewMouse.y - rcClient.bottom + 1; } else { dy = 0; }
// For every 32 pixel blocks outside the client, scroll one line amount
if (dx) dx = MulDiv(pasPerson->m_pView->m_viewLnSize.x, dx, 32); if (dy) dy = MulDiv(pasPerson->m_pView->m_viewLnSize.y, dy, 32);
// Do scrolling.
if (VIEWClientScroll(pasPerson, pasPerson->m_pView->m_viewPos.x + dx, pasPerson->m_pView->m_viewPos.y + dy)) { //
// The scroll position actually changed. So fake a mouse move
// to the current location so that the remote's
// cursor will be in the same spot as ours. If our scroll pos has
// changed, we're mapping to a different place on the remote.
//
VIEWClientMouseMsg(pasPerson, WM_MOUSEMOVE, pasPerson->m_pView->m_viewMouseFlags, MAKELPARAM(pasPerson->m_pView->m_viewMouse.x, pasPerson->m_pView->m_viewMouse.y)); }
DebugExitVOID(ASShare::VIEWClientAutoScroll); }
//
// VIEW_SyncCursorPos()
//
// This is called when we see a CM_SYNC pos packet broadcasted from a
// host. It means that we should sync our cursor to the corresponding
// position in our view. This happens when the cursor is moved by
// an app, constrained by clipping, or we're too out of whack because it's
// taking too long.
//
// This will only do something if the frame is active and our cursor is
// currently over the client area. If we need to, we will scroll the
// client over to make the corresponding point visible.
//
void ASShare::VIEW_SyncCursorPos ( ASPerson * pasHost, int xRemote, int yRemote ) { POINT ptCursor; RECT rcClient; int xNewPos; int yNewPos; int xMargin; int yMargin;
DebugEntry(ASShare::VIEW_SyncCursorPos);
ValidateView(pasHost); if (!pasHost->m_pView->m_viewFocus) { // The frame isn't active, do nothing
DC_QUIT; }
//
// Is our mouse currently over the client area?
//
GetCursorPos(&ptCursor); ScreenToClient(pasHost->m_pView->m_viewClient, &ptCursor); GetClientRect(pasHost->m_pView->m_viewClient, &rcClient);
if (!PtInRect(&rcClient, ptCursor)) { // No sense in snapping cursor
DC_QUIT; }
//
// Is the remote point in range of our view? If not, we must scroll it.
//
// The margin is the page size if there's room, nothing if not
xMargin = pasHost->m_pView->m_viewPgSize.x; if (xMargin >= rcClient.right - rcClient.left) xMargin = 0;
xNewPos = pasHost->m_pView->m_viewPos.x; if ((xRemote < pasHost->m_pView->m_viewPos.x) || (xRemote >= pasHost->m_pView->m_viewPos.x + (rcClient.right - rcClient.left))) { //
// Scroll over more than just enough to pin the point on the left
// side.
//
xNewPos = xRemote - xMargin; }
yMargin = pasHost->m_pView->m_viewPgSize.y; if (yMargin >= rcClient.bottom - rcClient.top) yMargin = 0;
yNewPos = pasHost->m_pView->m_viewPos.y; if ((yRemote < pasHost->m_pView->m_viewPos.y) || (yRemote >= yNewPos + (rcClient.bottom - rcClient.top))) { //
// Scroll over more than just enough to pin the point on the top
// side.
//
yNewPos = yRemote - yMargin; }
VIEWClientScroll(pasHost, xNewPos, yNewPos);
ptCursor.x = xRemote - pasHost->m_pView->m_viewPos.x; ptCursor.y = yRemote - pasHost->m_pView->m_viewPos.y; ClientToScreen(pasHost->m_pView->m_viewClient, &ptCursor);
SetCursorPos(ptCursor.x, ptCursor.y);
DC_EXIT_POINT: DebugExitVOID(ASShare::VIEW_SyncCursorPos); }
//
// VIEWWindowBarProc()
//
LRESULT CALLBACK VIEWWindowBarProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { return(g_asSession.pShare->VIEW_WindowBarProc(hwnd, message, wParam, lParam)); }
LRESULT ASShare::VIEW_WindowBarProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { LRESULT rc = 0; ASPerson * pasHost;
DebugEntry(ASShare::VIEW_WindowBarProc);
pasHost = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pasHost) { ValidateView(pasHost); }
switch (message) { case WM_NCCREATE: { // Get & save the person this view is for.
pasHost = (ASPerson *)((LPCREATESTRUCT)lParam)->lpCreateParams; ValidateView(pasHost); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pasHost);
pasHost->m_pView->m_viewWindowBar = hwnd; goto DefWndProc; break; }
case WM_NCDESTROY: { if (pasHost != NULL) { pasHost->m_pView->m_viewWindowBar = NULL; }
goto DefWndProc; break; }
case WM_CREATE: { if (!VIEWWindowBarCreate(pasHost, hwnd)) { ERROR_OUT(("VIEWWndBarProc: couldn't create more item")); rc = -1; } break; }
case WM_SIZE: { VIEWWindowBarResize(pasHost, hwnd); break; }
case WM_HSCROLL: { VIEWWindowBarItemsScroll(pasHost, wParam, lParam); break; }
default: DefWndProc: { rc = DefWindowProc(hwnd, message, wParam, lParam); break; } }
DebugExitDWORD(ASShare::VIEW_WindowBarProc, rc); return(rc); }
//
// VIEWWindowBarCreate()
// Handles creation for the window bar. We make the next/prev buttons on
// the right side, which stay there always. They are disabled if all the
// window bar items fit, and one or both are enabled if not.
//
BOOL ASShare::VIEWWindowBarCreate ( ASPerson * pasHost, HWND hwndBar ) { BOOL rc = FALSE; RECT rect;
DebugEntry(ASShare::VIEWWindowBarCreate);
::GetClientRect(hwndBar, &rect); rect.top += m_viewEdgeCY; rect.right -= m_viewItemScrollCX;
//
// Create the scrollbar, vertically centered, right-justified.
//
if (!::CreateWindowEx(0, "ScrollBar", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ | WS_CLIPSIBLINGS | WS_DISABLED, rect.right, (rect.top + rect.bottom - m_viewItemScrollCY) / 2, m_viewItemScrollCX, m_viewItemScrollCY, hwndBar, (HMENU)IDVIEW_SCROLL, g_asInstance, NULL)) { ERROR_OUT(("VIEWWindowBarCreate: Unable to create scroll ctrl")); DC_QUIT; }
//
// Create the windowbar, an integral number of items wide (including
// trailing margin).
//
pasHost->m_pView->m_viewWindowBarItemFitCount = (rect.right - rect.left) / (m_viewItemCX + m_viewEdgeCX);
if (!::CreateWindowEx(0, VIEW_WINDOWBARITEMS_CLASS_NAME, NULL, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_CLIPSIBLINGS, rect.left, rect.top, pasHost->m_pView->m_viewWindowBarItemFitCount * (m_viewItemCX + m_viewEdgeCX), m_viewItemCY, hwndBar, (HMENU)IDVIEW_ITEMS, g_asInstance, pasHost)) { ERROR_OUT(("VIEWWindowBarCreate: Unable to create window bar item list")); DC_QUIT; }
rc = TRUE;
DC_EXIT_POINT: DebugExitBOOL(ASShare::VIEWWindowBarCreate, rc); return(rc); }
//
// VIEWWindowBarResize()
//
// This is called when the window bar is resized, due to the frame being
// sized horizontally.
//
// It right-justifies the scroll control, then resizes the window list to
// hold however many integral items fit across.
//
void ASShare::VIEWWindowBarResize ( ASPerson * pasHost, HWND hwndBar ) { RECT rc;
DebugEntry(ASShare::VIEWWindowBarResize);
ValidateView(pasHost);
//
// Recalculate the page size, the # of items that fit across.
// If it's different, invalidate the right side of the window bar client.
// Move the scrollbar control, and update the scroll info.
//
// What might change is the number that fit across.
::GetClientRect(hwndBar, &rc); rc.top += m_viewEdgeCY; rc.right -= m_viewItemScrollCX;
// Move the scroll control, right justified.
::MoveWindow(::GetDlgItem(hwndBar, IDVIEW_SCROLL), rc.right, (rc.top + rc.bottom - m_viewItemScrollCY) / 2, m_viewItemScrollCX, m_viewItemScrollCY, TRUE);
//
// Resize the window items list to fit an integral # of items again.
//
pasHost->m_pView->m_viewWindowBarItemFitCount = (rc.right - rc.left) / (m_viewItemCX + m_viewEdgeCX);
::MoveWindow(::GetDlgItem(hwndBar, IDVIEW_ITEMS), rc.left, rc.top, pasHost->m_pView->m_viewWindowBarItemFitCount * (m_viewItemCX + m_viewEdgeCX), m_viewItemCY, TRUE);
//
// Update the scroll page and pos if necessary.
//
VIEWWindowBarItemsScroll(pasHost, GET_WM_HSCROLL_MPS(SB_ENDSCROLL, 0, NULL));
DebugExitVOID(ASShare::VIEWWindowBarResize); }
//
// VIEW_WindowBarUpdateItem()
//
// This is ONLY called for items, in the new SWL packet, that are window
// bar items. We don't call it with non-windowbar items. When done
// looping through the SWL entries, we can then remove the items on the
// window bar that were NOT seen in the new SWL packet.
//
// We will either create a new item on the window bar, or update an existing
// one. In the first case, that is always a change. In the latter, there's
// a change only if the item text changed.
//
BOOL ASShare::VIEW_WindowBarUpdateItem ( ASPerson * pasHost, PSWLWINATTRIBUTES pWinNew, LPSTR pText ) { PWNDBAR_ITEM pItem; BOOL viewAnyChanges = FALSE;
DebugEntry(ASView::VIEW_WindowBarUpdateItem);
ValidateView(pasHost);
ASSERT(pWinNew->flags & SWL_FLAG_WINDOW_HOSTED); ASSERT(pWinNew->flags & SWL_FLAG_WINDOW_TASKBAR);
//
// NOTE:
// aswlLast holds the _previous_ attributes for the windows, from
// the previous SWL packet. pWinNew holds the _new_ attributes for
// the window, from the SWL packet being processed, and these
// haven't taken effect yet.
//
// Does this new item already exist on the tray?
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pasHost->m_pView->m_viewWindowBarItems), (void**)&pItem, FIELD_OFFSET(WNDBAR_ITEM, chain), FIELD_OFFSET(WNDBAR_ITEM, winIDRemote), pWinNew->winID, FIELD_SIZE(WNDBAR_ITEM, winIDRemote));
if (pItem) { //
// Update this item, and mark it as seen.
//
ASSERT(pItem->winIDRemote == pWinNew->winID);
pItem->flags = pWinNew->flags | SWL_FLAG_INTERNAL_SEEN;
//
// Is anything going to result in a visual change? That's only
// the text currently. And we only display VIEW_MAX_ITEM_CHARS at
// most, an end ellipsis if there's too much.
//
//
// NOTE that the items are always created with maximum space for
// text, since we cannot realloc.
//
if (lstrcmp(pItem->szText, pText)) { lstrcpyn(pItem->szText, pText, sizeof(pItem->szText)); viewAnyChanges = TRUE; } } else { //
// Create a new item.
//
//
// A WNDBAR_ITEM also includes maximum space for text that we will
// store.
//
pItem = (PWNDBAR_ITEM) new WNDBAR_ITEM; if (!pItem) { ERROR_OUT(("VIEW_WindowBarUpdateItem: no memory to create new item for remote hwnd 0x%08x", pWinNew->winID)); } else { ::ZeroMemory(pItem, sizeof(*pItem));
SET_STAMP(pItem, WNDITEM);
pItem->winIDRemote = pWinNew->winID;
//
// Add SEEN to the flags; when we're done we'll remove items we haven't
// seen.
//
pItem->flags = pWinNew->flags | SWL_FLAG_INTERNAL_SEEN;
lstrcpyn(pItem->szText, pText, sizeof(pItem->szText));
// Append to end of list
COM_BasedListInsertBefore(&(pasHost->m_pView->m_viewWindowBarItems), &(pItem->chain));
// Success!
pasHost->m_pView->m_viewWindowBarItemCount++;
viewAnyChanges = TRUE; } }
DebugExitBOOL(ASShare::VIEW_UpdateWindowItem, viewAnyChanges); return(viewAnyChanges); }
//
// VIEW_WindowBarEndUpdateItems()
//
// This turns redraw on and invalidates the window bar so it will repaint.
//
void ASShare::VIEW_WindowBarEndUpdateItems ( ASPerson * pasHost, BOOL viewAnyChanges ) { PWNDBAR_ITEM pItem; PWNDBAR_ITEM pNext;
DebugEntry(ASShare::VIEW_WindowBarEndUpdateItems);
ValidateView(pasHost);
//
// Walk the window bar item list. Keep the ones marked as seen, but
// remove the ones we haven't seen.
//
pItem = (PWNDBAR_ITEM)COM_BasedListFirst(&(pasHost->m_pView->m_viewWindowBarItems), FIELD_OFFSET(WNDBAR_ITEM, chain)); while (pItem) { pNext = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems), pItem, FIELD_OFFSET(WNDBAR_ITEM, chain));
//
// If this item wasn't seen (existing & still existing, or new)
// during processing, it's gone. Delete it.
//
if (pItem->flags & SWL_FLAG_INTERNAL_SEEN) { //
// This was just added or is still around, keep it.
// But of course clear the flag, so we are clear for
// processing the next SWL packet.
//
pItem->flags &= ~SWL_FLAG_INTERNAL_SEEN; } else { //
// Remove it.
//
// We're killing the active item, clear it out.
if (pItem == pasHost->m_pView->m_viewWindowBarActiveItem) { pasHost->m_pView->m_viewWindowBarActiveItem = NULL; }
COM_BasedListRemove(&(pItem->chain));
delete pItem; --pasHost->m_pView->m_viewWindowBarItemCount; ASSERT(pasHost->m_pView->m_viewWindowBarItemCount >= 0);
//
// Something changed in our list
//
viewAnyChanges = TRUE; }
pItem = pNext; }
//
// No need to check for changes here--they would only occur if
// an item was removed in the middle, caused by Destroy which we already
// account for, or if items were appended to the end, which we account
// for in Update.
//
if (viewAnyChanges) { // Turn off redraw on window list
::SendDlgItemMessage(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS, WM_SETREDRAW, FALSE, 0);
// Adjust pos
VIEWWindowBarItemsScroll(pasHost, GET_WM_HSCROLL_MPS(SB_ENDSCROLL, 0, NULL));
// Figure out active window again.
VIEW_WindowBarChangedActiveWindow(pasHost);
// Turn back on redraw
::SendDlgItemMessage(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS, WM_SETREDRAW, TRUE, 0);
// Repaint the items.
::InvalidateRect(::GetDlgItem(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS), NULL, TRUE); } else { //
// ALWAYS do this -- our real SWL list has changed, regardless of whether
// the window bar has. And therefore we may have a different ancestor
// relationship.
//
VIEW_WindowBarChangedActiveWindow(pasHost); }
DebugExitVOID(ASShare::VIEW_EndUpdateWindowList); }
//
// VIEW_WindowBarChangedActiveWindow()
//
// This is called when the active window has changed, as discovered via an
// AWC packet from the host, or when we get a new SWL packet and the shared
// list is different so the window bar items may have changed.
//
// It's quite common for the active window to be (a) nothing, meaning no
// shared app window is active or (b) not something relating to what's on
// the window bar currently. The latter is a transitory condition, caused
// because SWL packets come before AWC packets.
//
void ASShare::VIEW_WindowBarChangedActiveWindow(ASPerson * pasHost) { PWNDBAR_ITEM pItem; PSWLWINATTRIBUTES pWin; int iWin; UINT_PTR activeWinID; TSHR_UINT32 ownerWinID;
DebugEntry(ASShare::VIEW_WindowBarChangedActiveWindow);
ValidateView(pasHost);
//
// Map this remote window to the closest window bar item in the
// ancestor hierarchy.
//
pItem = NULL; activeWinID = pasHost->awcActiveWinID;
while (activeWinID != 0) { //
// Is this on the window bar?
//
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pasHost->m_pView->m_viewWindowBarItems), (void**)&pItem, FIELD_OFFSET(WNDBAR_ITEM, chain), FIELD_OFFSET(WNDBAR_ITEM, winIDRemote), activeWinID, FIELD_SIZE(WNDBAR_ITEM, winIDRemote));
if (pItem) { // Yes.
TRACE_OUT(("VIEW_UpdateActiveWindow: Window 0x%08x found", activeWinID)); break; }
//
// Try to go up the chain to this window's owner. Find this item,
// then grab the owner of it, and try again.
//
ownerWinID = 0;
for (iWin = 0, pWin = pasHost->m_pView->m_aswlLast; iWin < pasHost->m_pView->m_swlCount; iWin++, pWin++) { if (pWin->winID == activeWinID) { // Found it.
ownerWinID = pWin->ownerWinID; break; } }
activeWinID = ownerWinID; }
//
// Now see if the active item is different.
//
VIEWWindowBarChangeActiveItem(pasHost, pItem);
DebugExitVOID(ASShare::VIEW_WindowBarChangedActiveWindow); }
//
// VIEWWindowBarFirstVisibleItem()
//
// This returns a pointer to the first visible item. We must loop through
// the invisible items first. Since this doesn't happen with a lot of
// frequence, and the size of the list is rarely that big, this is fine.
//
// We return NULL if the list is empty.
//
PWNDBAR_ITEM ASShare::VIEWWindowBarFirstVisibleItem(ASPerson * pasHost) { PWNDBAR_ITEM pItem; int iItem;
ValidateView(pasHost);
if (!pasHost->m_pView->m_viewWindowBarItemCount) { pItem = NULL; DC_QUIT; }
ASSERT(pasHost->m_pView->m_viewWindowBarItemFirst < pasHost->m_pView->m_viewWindowBarItemCount);
pItem = (PWNDBAR_ITEM)COM_BasedListFirst(&(pasHost->m_pView->m_viewWindowBarItems), FIELD_OFFSET(WNDBAR_ITEM, chain)); for (iItem = 0; iItem < pasHost->m_pView->m_viewWindowBarItemFirst; iItem++) { ASSERT(pItem);
pItem = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems), pItem, FIELD_OFFSET(WNDBAR_ITEM, chain)); }
ASSERT(pItem);
DC_EXIT_POINT: DebugExitPVOID(ASShare::VIEWWindowBarFirstVisibleItem, pItem); return(pItem); }
//
// VIEWWindowBarChangeActiveItem()
//
// Updates the active item on the window bar. This happens when either
// we get a new AWC packet telling us there's a new active window on the host,
// or when we get a SWL packet, which may have added/removed items. This
// also happens when one is clicked on and the user is in control of the host.
//
void ASShare::VIEWWindowBarChangeActiveItem ( ASPerson * pasHost, PWNDBAR_ITEM pItem ) { DebugEntry(ASShare::VIEWWindowBarChangeActiveItem);
//
// If it's the active one already, nothing to do.
//
if (pItem == pasHost->m_pView->m_viewWindowBarActiveItem) { TRACE_OUT(("VIEWWindowBarChangeActiveItem: activating current item, nothing to do")); DC_QUIT; }
//
// Now make the visual change
//
if (pasHost->m_pView->m_viewWindowBarActiveItem) { VIEWWindowBarItemsInvalidate(pasHost, pasHost->m_pView->m_viewWindowBarActiveItem); }
pasHost->m_pView->m_viewWindowBarActiveItem = pItem;
if (pItem) { VIEWWindowBarItemsInvalidate(pasHost, pItem); }
DC_EXIT_POINT: DebugExitVOID(ASShare::VIEWWindowBarChangeActiveItem); }
//
// VIEWWindowBarItemsScroll()
//
// This is called when the end user presses a scroll button to shuffle over
// the visible window bar items. And also when items are added/removed
// so that scroll stuff is adjusted.
//
void ASShare::VIEWWindowBarItemsScroll ( ASPerson * pasHost, WPARAM wParam, LPARAM lParam ) { int oldPos; int newPos; SCROLLINFO si;
DebugEntry(ASShare::VIEWWindowBarItemsScroll);
ValidateView(pasHost);
oldPos = pasHost->m_pView->m_viewWindowBarItemFirst;
switch (GET_WM_HSCROLL_CODE(wParam, lParam)) { case SB_LINEUP: case SB_PAGEUP: newPos = oldPos - 1; break;
case SB_LINEDOWN: case SB_PAGEDOWN: newPos = oldPos + 1; break;
case SB_TOP: newPos = 0; break;
case SB_BOTTOM: newPos = pasHost->m_pView->m_viewWindowBarItemCount; break;
case SB_THUMBTRACK: case SB_THUMBPOSITION: newPos = GET_WM_HSCROLL_POS(wParam, lParam); break;
default: newPos = oldPos; break;
}
//
// Pin position into range, taking care to show the maximum number
// of items that will fit in the space.
//
if (newPos + pasHost->m_pView->m_viewWindowBarItemFitCount > pasHost->m_pView->m_viewWindowBarItemCount) { newPos = pasHost->m_pView->m_viewWindowBarItemCount - pasHost->m_pView->m_viewWindowBarItemFitCount; }
if (newPos < 0) newPos = 0;
//
// Has the position changed?
//
if (newPos != oldPos) { pasHost->m_pView->m_viewWindowBarItemFirst = newPos;
//
// Scroll the item area over. This will do nothing if redraw is off.
// Conveniently!
//
::ScrollWindowEx(::GetDlgItem(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS), (oldPos - newPos) * (m_viewItemCX + m_viewEdgeCX), 0, NULL, NULL, NULL, NULL, SW_INVALIDATE | SW_ERASE); }
//
// If nothing's changed, no big deal.
//
::ZeroMemory(&si, sizeof(si));
si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_DISABLENOSCROLL | SIF_POS | SIF_PAGE | SIF_RANGE;
si.nMin = 0; si.nMax = pasHost->m_pView->m_viewWindowBarItemCount - 1; si.nPage = pasHost->m_pView->m_viewWindowBarItemFitCount; si.nPos = pasHost->m_pView->m_viewWindowBarItemFirst;
::SetScrollInfo(::GetDlgItem(pasHost->m_pView->m_viewWindowBar, IDVIEW_SCROLL), SB_CTL, &si, TRUE);
DebugExitVOID(ASShare::VIEWWindowBarItemsScroll); }
//
// VIEWWindowBarItemsProc()
//
LRESULT CALLBACK VIEWWindowBarItemsProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { return(g_asSession.pShare->VIEW_WindowBarItemsProc(hwnd, message, wParam, lParam)); }
LRESULT ASShare::VIEW_WindowBarItemsProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { LRESULT rc = 0; ASPerson * pasHost;
DebugEntry(ASShare::VIEW_WindowBarItemsProc);
pasHost = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pasHost) { ValidateView(pasHost); }
switch (message) { case WM_NCCREATE: { // Get & save the person this view is for.
pasHost = (ASPerson *)((LPCREATESTRUCT)lParam)->lpCreateParams; ValidateView(pasHost); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pasHost);
COM_BasedListInit(&(pasHost->m_pView->m_viewWindowBarItems)); goto DefWndProc; break; }
case WM_NCDESTROY: { if (pasHost != NULL) { // Loop through the items, killing the head, until done.
PWNDBAR_ITEM pItem;
while (pItem = (PWNDBAR_ITEM)COM_BasedListFirst( &(pasHost->m_pView->m_viewWindowBarItems), FIELD_OFFSET(WNDBAR_ITEM, chain))) { COM_BasedListRemove(&(pItem->chain));
delete pItem; }
//
// Zero these out for safety. Yes, we're about to free
// m_pView altogether, so find out if we're referencing
// stuff that's gone.
//
pasHost->m_pView->m_viewWindowBarItemCount = 0; pasHost->m_pView->m_viewWindowBarActiveItem = NULL; }
goto DefWndProc; break; }
case WM_ENABLE: { // Repaint the items, disabled or pressable.
::InvalidateRect(hwnd, NULL, FALSE); break; }
case WM_PAINT: { VIEWWindowBarItemsPaint(pasHost, hwnd); break; }
case WM_LBUTTONDOWN: { VIEWWindowBarItemsClick(pasHost, hwnd, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; }
default: DefWndProc: { rc = DefWindowProc(hwnd, message, wParam, lParam); break; } }
DebugExitDWORD(ASShare::VIEW_WindowBarItemsProc, rc); return(rc); }
//
// VIEWWindowBarItemsPaint()
//
void ASShare::VIEWWindowBarItemsPaint ( ASPerson * pasHost, HWND hwndItems ) { HFONT hfnT; COLORREF clrText; int bkMode; PWNDBAR_ITEM pItem; PAINTSTRUCT ps; int xT; RECT rcItem;
DebugEntry(ASShare::VIEWWindowBarItemsPaint);
ValidateView(pasHost);
::BeginPaint(hwndItems, &ps);
//
// Skip over the visible items to the left of the paint area.
//
xT = 0; pItem = VIEWWindowBarFirstVisibleItem(pasHost); while (pItem && (xT + m_viewItemCX < ps.rcPaint.left)) { pItem = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems), pItem, FIELD_OFFSET(WNDBAR_ITEM, chain)); xT += m_viewItemCX + m_viewEdgeCX; }
//
// Setup painting objects, etc.
//
hfnT = SelectFont(ps.hdc, ::GetStockObject(DEFAULT_GUI_FONT)); if ((pasHost->m_caControlledBy != m_pasLocal) || pasHost->m_caControlPaused) { clrText = ::GetSysColor(COLOR_GRAYTEXT); } else { clrText = ::GetSysColor(COLOR_BTNTEXT); } clrText = ::SetTextColor(ps.hdc, clrText); bkMode = ::SetBkMode(ps.hdc, TRANSPARENT);
//
// Now paint the visible items within the paint area.
//
while (pItem && (xT < ps.rcPaint.right)) { rcItem.left = xT; rcItem.top = 0; rcItem.right = rcItem.left + m_viewItemCX; rcItem.bottom = rcItem.top + m_viewItemCY;
//
// Draw button area, pressed in & checked for current tray item.
//
DrawFrameControl(ps.hdc, &rcItem, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_ADJUSTRECT | ((pItem == pasHost->m_pView->m_viewWindowBarActiveItem) ? (DFCS_PUSHED | DFCS_CHECKED) : 0));
// Subtract some margin.
::InflateRect(&rcItem, -m_viewEdgeCX, -m_viewEdgeCY);
if (pItem == pasHost->m_pView->m_viewWindowBarActiveItem) { // Offset one for pushed effect
::OffsetRect(&rcItem, 1, 1); }
//
// Draw icon
//
::DrawIconEx(ps.hdc, rcItem.left, (rcItem.top + rcItem.bottom - ::GetSystemMetrics(SM_CYSMICON)) / 2, g_hetASIconSmall, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0, NULL, DI_NORMAL);
rcItem.left += ::GetSystemMetrics(SM_CXSMICON) + m_viewEdgeCX;
//
// Draw item text
//
::DrawText(ps.hdc, pItem->szText, -1, &rcItem, DT_NOCLIP | DT_EXPANDTABS | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
pItem = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems), pItem, FIELD_OFFSET(WNDBAR_ITEM, chain)); xT += m_viewItemCX + m_viewEdgeCX; }
::SetBkMode(ps.hdc, bkMode); ::SetTextColor(ps.hdc, clrText); SelectFont(ps.hdc, hfnT);
::EndPaint(hwndItems, &ps);
DebugExitVOID(ASShare::VIEWWindowBarItemsPaint); }
//
// VIEWWindowBarItemsClick()
//
// Handles a left click on the window bar area. When we are in control, this
// will try to activate/restore the remote window the clicked item represents.
//
void ASShare::VIEWWindowBarItemsClick ( ASPerson * pasHost, HWND hwndItems, int x, int y ) { RECT rc; PWNDBAR_ITEM pItemT;
DebugEntry(ASShare::VIEWWindowBarClick);
ValidateView(pasHost);
//
// If we're not in control of this host, or there aren't any items, we're
// done.
//
if ((pasHost->m_caControlledBy != m_pasLocal) || pasHost->m_caControlPaused || (!pasHost->m_pView->m_viewWindowBarItemCount)) { DC_QUIT; }
::GetClientRect(hwndItems, &rc);
//
// Start at first visible item.
//
pItemT = VIEWWindowBarFirstVisibleItem(pasHost); while (pItemT && (rc.left < rc.right)) { // Is x in range?
if ((x >= rc.left) && (x < rc.left + m_viewItemCX)) { // YES! We've found the item. If it's different than the
// current one, send a packet to the host.
//
// LAURABU BUGBUG:
// Should we do this always? Is it possible to have an active
// item whose z-order would change if the active button was
// pressed again?
//
// We're trying to avoid sending a ton of requests from somebody
// who clicks repeatedly on the same button, when we haven't
// received an AWC notification back.
//
VIEWWindowBarDoActivate(pasHost, pItemT); break; }
pItemT = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems), pItemT, FIELD_OFFSET(WNDBAR_ITEM, chain)); rc.left += m_viewItemCX + m_viewEdgeCX; }
DC_EXIT_POINT: DebugExitVOID(ASShare::VIEWWindowBarItemsClick); }
//
// VIEWWindowBarDoActivate()
//
// Sends command to remote host requesting the window be activated and
// maybe unminimized.
//
// This is used when clicking on a button or choosing the window's item in
// the Applications menu.
//
void ASShare::VIEWWindowBarDoActivate ( ASPerson * pasHost, PWNDBAR_ITEM pItem ) { DebugEntry(ASShare::VIEWWindowBarDoActivate);
ValidateView(pasHost); if (pItem != pasHost->m_pView->m_viewWindowBarActiveItem) { // Activate it. If we can't send an activate request,
// do not update the active item.
//
if (!AWC_SendMsg(pasHost->mcsID, AWC_MSG_ACTIVATE_WINDOW, pItem->winIDRemote, 0)) { ERROR_OUT(("VIEWWindowBarDoActivate: can't send AWC packet so failing")); } else { VIEWWindowBarChangeActiveItem(pasHost, pItem); } }
// Try to restore if minimized no matter what.
if (pItem->flags & SWL_FLAG_WINDOW_MINIMIZED) { AWC_SendMsg(pasHost->mcsID, AWC_MSG_RESTORE_WINDOW, pItem->winIDRemote, 0); }
DebugExitVOID(ASShare::VIEWWindowBarDoActivate); }
//
// VIEWWindowBarItemsInvalidate()
//
// This invalidates the window bar item, if it's visible in the window bar
// list currently.
//
void ASShare::VIEWWindowBarItemsInvalidate ( ASPerson * pasHost, PWNDBAR_ITEM pItem ) { PWNDBAR_ITEM pItemT; RECT rc;
DebugEntry(ASShare::VIEWWindowBarItemsInvalidate);
ValidateView(pasHost);
ASSERT(pItem);
::GetClientRect(::GetDlgItem(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS), &rc);
//
// Start at the first visible item, and see if any in the visible range
// are this one. There will never be that many items visible across,
// it's not heinous to do this.
//
pItemT = VIEWWindowBarFirstVisibleItem(pasHost); while (pItemT && (rc.left < rc.right)) { if (pItemT == pItem) { // Found it, it's in the visible range. Invalidate it.
rc.right = rc.left + m_viewItemCX; ::InvalidateRect(::GetDlgItem(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS), &rc, TRUE); break; }
pItemT = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems), pItemT, FIELD_OFFSET(WNDBAR_ITEM, chain)); rc.left += m_viewItemCX + m_viewEdgeCX; }
DebugExitVOID(ASShare::VIEWWindowBarItemsInvalidate); }
//
// VIEWFullScreenExitProc()
//
// Window handler for full screen exit button.
//
LRESULT CALLBACK VIEWFullScreenExitProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { return(g_asSession.pShare->VIEW_FullScreenExitProc(hwnd, message, wParam, lParam)); }
//
// VIEW_FullScreenExitProc()
//
LRESULT ASShare::VIEW_FullScreenExitProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { LRESULT rc = 0; ASPerson * pasHost;
DebugEntry(VIEW_FullScreenExitProc);
pasHost = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pasHost) { ValidateView(pasHost); }
switch (message) { case WM_NCCREATE: { // Get the passed in host pointer, and set in our window long
pasHost = (ASPerson *)((LPCREATESTRUCT)lParam)->lpCreateParams; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pasHost);
goto DefWndProc; break; }
case WM_NCDESTROY: { //
// Make sure tracking is stopped.
//
pasHost->m_pView->m_viewFullScreenExitTrack = FALSE; break; }
case WM_ERASEBKGND: { rc = TRUE; break; }
case WM_PAINT: { VIEWFullScreenExitPaint(pasHost, hwnd); break; }
case WM_LBUTTONDOWN: { //
// Start tracking to move or click button.
//
pasHost->m_pView->m_viewFullScreenExitTrack = TRUE; pasHost->m_pView->m_viewFullScreenExitMove = FALSE;
// Original click, relative to our client
pasHost->m_pView->m_viewFullScreenExitStart.x = GET_X_LPARAM(lParam); pasHost->m_pView->m_viewFullScreenExitStart.y = GET_Y_LPARAM(lParam);
// Set capture, and wait for moves/button up
SetCapture(hwnd); break; }
case WM_MOUSEMOVE: { if (pasHost->m_pView->m_viewFullScreenExitTrack) { POINT ptMove;
ptMove.x = GET_X_LPARAM(lParam); ptMove.y = GET_Y_LPARAM(lParam);
//
// If we're not in move mode, see if this has pushed us over
// the tolerance.
//
if (!pasHost->m_pView->m_viewFullScreenExitMove) { if ((abs(ptMove.x - pasHost->m_pView->m_viewFullScreenExitStart.x) > GetSystemMetrics(SM_CXDRAG)) || (abs(ptMove.y - pasHost->m_pView->m_viewFullScreenExitStart.y) > GetSystemMetrics(SM_CYDRAG))) { //
// User has moved out of tolerance zone, must be
// dragging to move the button out of the way.
//
pasHost->m_pView->m_viewFullScreenExitMove = TRUE; } }
if (pasHost->m_pView->m_viewFullScreenExitMove) { RECT rcWindow;
//
// Move the button so that the cursor is over the
// same point as originally clicked on.
//
// Get our current position, in parent coordsinates.
GetWindowRect(hwnd, &rcWindow); MapWindowPoints(NULL, GetParent(hwnd), (LPPOINT)&rcWindow, 2);
// Offset it by the amount of the move.
OffsetRect(&rcWindow, ptMove.x - pasHost->m_pView->m_viewFullScreenExitStart.x, ptMove.y - pasHost->m_pView->m_viewFullScreenExitStart.y); SetWindowPos(hwnd, NULL, rcWindow.left, rcWindow.top, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); } } break; }
case WM_LBUTTONUP: { if (pasHost->m_pView->m_viewFullScreenExitTrack) { //
// This will send us CAPTURECHANGED, causing us to clear
// the ExitTrack flag.
//
ReleaseCapture();
//
// If we never transitioned into move mode, then this was
// a click on the button.
//
if (!pasHost->m_pView->m_viewFullScreenExitMove) { //
// This was a click, send a command.
//
PostMessage(pasHost->m_pView->m_viewFrame, WM_COMMAND, CMD_VIEWFULLSCREEN, 0); } } break; }
case WM_CAPTURECHANGED: { //
// If we're tracking, something happened, so cancel out.
//
if (pasHost->m_pView->m_viewFullScreenExitTrack) { pasHost->m_pView->m_viewFullScreenExitTrack = FALSE; } break; }
default: DefWndProc: rc = DefWindowProc(hwnd, message, wParam, lParam); break; }
DebugExitDWORD(VIEW_FullScreenExitProc, rc); return(rc);
}
//
// VIEWFullScreenExitPaint()
//
// Paints the full screen button.
//
void ASShare::VIEWFullScreenExitPaint ( ASPerson * pasHost, HWND hwnd ) { RECT rc; PAINTSTRUCT ps; char szRestore[256]; HFONT hfnOld; COLORREF txtColor; COLORREF bkColor;
DebugEntry(ASShare::VIEWFullScreenExitPaint);
BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc); DrawFrameControl(ps.hdc, &rc, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_ADJUSTRECT);
// Margin adjustments...
InflateRect(&rc, -m_viewEdgeCX, -m_viewEdgeCY);
DrawIconEx(ps.hdc, rc.left, (rc.top + rc.bottom - GetSystemMetrics(SM_CYSMICON)) / 2, m_viewFullScreenExitIcon, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0, NULL, DI_NORMAL); rc.left += GetSystemMetrics(SM_CXSMICON) + m_viewEdgeCX;
hfnOld = SelectFont(ps.hdc, GetStockObject(DEFAULT_GUI_FONT)); txtColor = SetTextColor(ps.hdc, GetSysColor(COLOR_BTNTEXT)); bkColor = SetBkColor(ps.hdc, GetSysColor(COLOR_BTNFACE));
LoadString(g_asInstance, IDS_RESTORE, szRestore, sizeof(szRestore)); DrawText(ps.hdc, szRestore, -1, &rc, DT_NOCLIP | DT_EXPANDTABS | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE);
SetBkColor(ps.hdc, bkColor); SetTextColor(ps.hdc, txtColor); SelectFont(ps.hdc, hfnOld); EndPaint(hwnd, &ps);
DebugExitVOID(ASShare::VIEWFullScreenExitPaint); }
|