You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
9693 lines
260 KiB
9693 lines
260 KiB
//
|
|
// MWND.CPP
|
|
// Main WB Window
|
|
//
|
|
// Copyright Microsoft 1998-
|
|
//
|
|
|
|
// PRECOMP
|
|
#include "precomp.h"
|
|
#include <dde.h>
|
|
#include "version.h"
|
|
|
|
|
|
static const TCHAR s_cszHtmlHelpFile[] = TEXT("nmwhiteb.chm");
|
|
|
|
// Class name
|
|
TCHAR szMainClassName[] = "Wb32MainWindowClass";
|
|
|
|
|
|
//
|
|
// Scroll accelerators
|
|
//
|
|
typedef struct tagSCROLL
|
|
{
|
|
UINT uiMenuId;
|
|
UINT uiMessage;
|
|
UINT uiScrollCode;
|
|
}
|
|
SCROLL;
|
|
|
|
static const SCROLL s_MenuToScroll[] =
|
|
{
|
|
{ IDM_PAGEUP, WM_VSCROLL, SB_PAGEUP },
|
|
{ IDM_PAGEDOWN, WM_VSCROLL, SB_PAGEDOWN },
|
|
{ IDM_SHIFTPAGEUP, WM_HSCROLL, SB_PAGEUP },
|
|
{ IDM_SHIFTPAGEDOWN, WM_HSCROLL, SB_PAGEDOWN },
|
|
{ IDM_HOME, WM_HSCROLL, SB_TOP },
|
|
{ IDM_HOME, WM_VSCROLL, SB_TOP },
|
|
{ IDM_END, WM_HSCROLL, SB_BOTTOM },
|
|
{ IDM_END, WM_VSCROLL, SB_BOTTOM },
|
|
{ IDM_LINEUP, WM_VSCROLL, SB_LINEUP },
|
|
{ IDM_LINEDOWN, WM_VSCROLL, SB_LINEDOWN },
|
|
{ IDM_SHIFTLINEUP, WM_HSCROLL, SB_LINEUP },
|
|
{ IDM_SHIFTLINEDOWN, WM_HSCROLL, SB_LINEDOWN }
|
|
};
|
|
|
|
|
|
// tooltip data
|
|
// check codes
|
|
#define NA 0 // dont't check checked state
|
|
#define TB 1 // check toolbar for checked state
|
|
#define BT 2 // check tipped wnd (a button) for checked state
|
|
|
|
typedef struct
|
|
{
|
|
UINT nID;
|
|
UINT nCheck;
|
|
UINT nUpTipID;
|
|
UINT nDownTipID;
|
|
}
|
|
TIPIDS;
|
|
|
|
TIPIDS g_tipIDsArray[] =
|
|
{
|
|
{IDM_SELECT, TB, IDS_HINT_SELECT, IDS_HINT_SELECT},
|
|
{IDM_ERASER, TB, IDS_HINT_ERASER, IDS_HINT_ERASER},
|
|
{IDM_TEXT, TB, IDS_HINT_TEXT, IDS_HINT_TEXT},
|
|
{IDM_HIGHLIGHT, TB, IDS_HINT_HIGHLIGHT, IDS_HINT_HIGHLIGHT},
|
|
{IDM_PEN, TB, IDS_HINT_PEN, IDS_HINT_PEN},
|
|
{IDM_LINE, TB, IDS_HINT_LINE, IDS_HINT_LINE},
|
|
{IDM_BOX, TB, IDS_HINT_BOX, IDS_HINT_BOX},
|
|
{IDM_FILLED_BOX, TB, IDS_HINT_FBOX, IDS_HINT_FBOX},
|
|
{IDM_ELLIPSE, TB, IDS_HINT_ELLIPSE, IDS_HINT_ELLIPSE},
|
|
{IDM_FILLED_ELLIPSE, TB, IDS_HINT_FELLIPSE, IDS_HINT_FELLIPSE},
|
|
{IDM_ZOOM, TB, IDS_HINT_ZOOM_UP, IDS_HINT_ZOOM_DOWN},
|
|
{IDM_REMOTE, TB, IDS_HINT_REMOTE_UP, IDS_HINT_REMOTE_DOWN},
|
|
{IDM_LOCK, TB, IDS_HINT_LOCK_UP, IDS_HINT_LOCK_DOWN},
|
|
{IDM_SYNC, TB, IDS_HINT_SYNC_UP, IDS_HINT_SYNC_DOWN},
|
|
{IDM_GRAB_AREA, TB, IDS_HINT_GRAB_AREA, IDS_HINT_GRAB_AREA},
|
|
{IDM_GRAB_WINDOW, TB, IDS_HINT_GRAB_WINDOW, IDS_HINT_GRAB_WINDOW},
|
|
|
|
{IDM_WIDTH_1, NA, IDS_HINT_WIDTH_1, IDS_HINT_WIDTH_1},
|
|
{IDM_WIDTH_2, NA, IDS_HINT_WIDTH_2, IDS_HINT_WIDTH_2},
|
|
{IDM_WIDTH_3, NA, IDS_HINT_WIDTH_3, IDS_HINT_WIDTH_3},
|
|
{IDM_WIDTH_4, NA, IDS_HINT_WIDTH_4, IDS_HINT_WIDTH_4},
|
|
|
|
{IDM_PAGE_FIRST, BT, IDS_HINT_PAGE_FIRST, IDS_HINT_PAGE_FIRST},
|
|
{IDM_PAGE_PREV, BT, IDS_HINT_PAGE_PREVIOUS, IDS_HINT_PAGE_PREVIOUS},
|
|
{IDM_PAGE_ANY, NA, IDS_HINT_PAGE_ANY, IDS_HINT_PAGE_ANY},
|
|
{IDM_PAGE_NEXT, BT, IDS_HINT_PAGE_NEXT, IDS_HINT_PAGE_NEXT},
|
|
{IDM_PAGE_LAST, BT, IDS_HINT_PAGE_LAST, IDS_HINT_PAGE_LAST},
|
|
{IDM_PAGE_INSERT_AFTER, BT, IDS_HINT_PAGE_INSERT, IDS_HINT_PAGE_INSERT}
|
|
};
|
|
////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: WbMainWindow constructor
|
|
//
|
|
// Purpose: Create the main Whiteboard window. An exception is thrown
|
|
// if an error occurs during construction.
|
|
//
|
|
//
|
|
WbMainWindow::WbMainWindow(void)
|
|
{
|
|
OSVERSIONINFO OsData;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::WbMainWindow");
|
|
|
|
//
|
|
// Initialize member vars first!
|
|
//
|
|
m_hwnd = NULL;
|
|
ZeroMemory(m_ToolArray, sizeof(m_ToolArray));
|
|
|
|
m_hwndToolTip = NULL;
|
|
ZeroMemory(&m_tiLastHit, sizeof(m_tiLastHit));
|
|
m_nLastHit = -1;
|
|
|
|
m_bInitOk = FALSE;
|
|
m_bDisplayingError = FALSE;
|
|
|
|
g_pwbCore = NULL;
|
|
|
|
m_dwDomain = 0;
|
|
m_bTimerActive = FALSE;
|
|
m_bSyncUpdateNeeded = FALSE;
|
|
|
|
m_hPageClip = WB_PAGE_HANDLE_NULL;
|
|
m_hGraphicClip = NULL;
|
|
m_pDelayedGraphicClip = NULL;
|
|
m_pDelayedDataClip = NULL;
|
|
|
|
m_bToolBarOn = FALSE;
|
|
|
|
// Load the main accelerator table
|
|
m_hAccelTable =
|
|
::LoadAccelerators(g_hInstance, MAKEINTRESOURCE(MAINACCELTABLE));
|
|
|
|
m_hwndPageSortDlg = NULL;
|
|
m_hwndQuerySaveDlg = NULL;
|
|
m_hwndWaitForEventDlg = NULL;
|
|
m_hwndWaitForLockDlg = NULL;
|
|
m_hwndInitDlg = NULL;
|
|
|
|
m_hwndSB = NULL;
|
|
m_bStatusBarOn = TRUE;
|
|
|
|
m_pCurrentTool = NULL;
|
|
m_uiSavedLockType = WB_LOCK_TYPE_NONE;
|
|
ZeroMemory(m_strFileName, sizeof(m_strFileName));
|
|
|
|
m_hCurrentPage = WB_PAGE_HANDLE_NULL;
|
|
|
|
// Load the alternative accelerator table for the pages edit
|
|
// field and text editor
|
|
m_hAccelPagesGroup =
|
|
::LoadAccelerators(g_hInstance, MAKEINTRESOURCE(PAGESGROUPACCELTABLE));
|
|
m_hAccelTextEdit =
|
|
::LoadAccelerators(g_hInstance, MAKEINTRESOURCE(TEXTEDITACCELTABLE));
|
|
|
|
m_pLocalUser = NULL;
|
|
m_pLockOwner = NULL;
|
|
|
|
// Show that we are not yet in a call
|
|
m_uiState = STARTING;
|
|
m_uiSubState = SUBSTATE_IDLE;
|
|
|
|
// We are not currently displaying a menu
|
|
m_hContextMenuBar = NULL;
|
|
m_hContextMenu = NULL;
|
|
m_hGrobjContextMenuBar = NULL;
|
|
m_hGrobjContextMenu = NULL;
|
|
|
|
m_bPromptingJoinCall = FALSE;
|
|
m_bInSaveDialog = FALSE;
|
|
m_bJoinCallPending = FALSE;
|
|
m_dwPendingDomain = 0;
|
|
m_bPendingCallKeepContents = FALSE;
|
|
m_dwJoinDomain = 0;
|
|
m_bCallActive = FALSE;
|
|
|
|
m_hInitMenu = NULL;
|
|
m_numRemoteUsers = 0;
|
|
m_bSelectAllInProgress = FALSE;
|
|
m_bUnlockStateSettled = TRUE;
|
|
m_bQuerySysShutdown = FALSE;
|
|
|
|
// figure out if we're on Win95
|
|
m_bIsWin95 = FALSE;
|
|
OsData.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if( GetVersionEx( &OsData ) )
|
|
{
|
|
if( OsData.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
|
|
m_bIsWin95 = TRUE;
|
|
}
|
|
|
|
m_cancelModeSent = FALSE;
|
|
|
|
//
|
|
// We only do this once for the lifetime of the DLL. There is no
|
|
// way really to clean up registered window messages, and each register
|
|
// bumps up a ref count. If we registered each time WB was started up
|
|
// during one session of CONF, we'd overflow the refcount.
|
|
//
|
|
if (!g_uConfShutdown)
|
|
{
|
|
g_uConfShutdown = ::RegisterWindowMessage( NM_ENDSESSION_MSG_NAME );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Open()
|
|
// Do Main window initialization (stuff that can fail). After this,
|
|
// the run code will try to join the current domain and do message loop
|
|
// stuff.
|
|
//
|
|
BOOL WbMainWindow::Open(int iCommand)
|
|
{
|
|
WNDCLASSEX wc;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::Open");
|
|
|
|
//
|
|
// CREATE OTHER GLOBALS
|
|
//
|
|
|
|
// Start the Whiteboard Core
|
|
if (!CreateWBObject(WbMainWindowEventHandler, &g_pwbCore))
|
|
{
|
|
ERROR_OUT(("WBP_Start failed"));
|
|
DefaultExceptionHandler(WBFE_RC_WB, UT_RC_NO_MEM);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!InitToolArray())
|
|
{
|
|
ERROR_OUT(("Can't create tools; failing to start up"));
|
|
return(FALSE);
|
|
}
|
|
|
|
g_pUsers = new WbUserList;
|
|
if (!g_pUsers)
|
|
{
|
|
ERROR_OUT(("Can't create g_pUsers"));
|
|
return(FALSE);
|
|
}
|
|
|
|
g_pIcons = new DCWbColorToIconMap();
|
|
if (!g_pIcons)
|
|
{
|
|
ERROR_OUT(("Can't create g_pIcons"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Init comon controls
|
|
//
|
|
InitCommonControls();
|
|
|
|
//
|
|
// CREATE THE MAIN FRAME WINDOW
|
|
//
|
|
ASSERT(!m_hwnd);
|
|
|
|
// Get the class info for it, and change the name.
|
|
ZeroMemory(&wc, sizeof(wc));
|
|
wc.cbSize = sizeof(wc);
|
|
wc.style = CS_DBLCLKS; // CS_HREDRAW | CS_VREDRAW?
|
|
wc.lpfnWndProc = WbMainWindowProc;
|
|
wc.hInstance = g_hInstance;
|
|
wc.hIcon = ::LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_APP));
|
|
wc.hCursor = ::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
|
|
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
|
|
wc.lpszMenuName = MAKEINTRESOURCE(MAINMENU);
|
|
wc.lpszClassName = szMainClassName;
|
|
|
|
if (!::RegisterClassEx(&wc))
|
|
{
|
|
ERROR_OUT(("Can't register private frame window class"));
|
|
return(FALSE);
|
|
}
|
|
|
|
// Create the main drawing window.
|
|
if (!::CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, szMainClassName,
|
|
NULL, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
|
|
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, g_hInstance, this))
|
|
{
|
|
// Could not create the main window
|
|
ERROR_OUT(("Failed to create main window"));
|
|
return(FALSE);
|
|
}
|
|
|
|
ASSERT(m_hwnd);
|
|
|
|
// Create the pop-up context menu
|
|
if (!CreateContextMenus())
|
|
{
|
|
ERROR_OUT(("Failed to create context menus"));
|
|
return(FALSE);
|
|
}
|
|
|
|
// Register the the main window for Drag/Drop messages.
|
|
DragAcceptFiles(m_hwnd, TRUE);
|
|
|
|
|
|
//
|
|
// CREATE THE CHILD WINDOWS
|
|
//
|
|
|
|
// Create the drawing pane
|
|
// (the Create call throws an exception on error)
|
|
RECT clientRect;
|
|
RECT drawingAreaRect;
|
|
|
|
::GetClientRect(m_hwnd, &clientRect);
|
|
drawingAreaRect = clientRect;
|
|
|
|
// Every control in the main window has a border on it, so increase the
|
|
// client size by 1 to force these borders to be drawn under the inside
|
|
// black line in the window frame. This prevents a 2 pel wide border
|
|
// being drawn
|
|
::InflateRect(&clientRect, 1, 1);
|
|
|
|
SIZE sizeAG;
|
|
m_AG.GetNaturalSize(&sizeAG);
|
|
|
|
//
|
|
// The drawing area is the top part of the client. The attributes group
|
|
// and status bar are below it.
|
|
//
|
|
drawingAreaRect.bottom -= (STATUSBAR_HEIGHT
|
|
+ GetSystemMetrics(SM_CYBORDER)
|
|
+ sizeAG.cy);
|
|
if (!m_drawingArea.Create(m_hwnd, &drawingAreaRect))
|
|
{
|
|
ERROR_OUT(("Failed to create drawing area"));
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
if (!m_TB.Create(m_hwnd))
|
|
{
|
|
ERROR_OUT(("Failed to create tool window"));
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
// Lock the drawing area initially. This prevents the user attempting
|
|
// to make changes before we are in a call.
|
|
LockDrawingArea();
|
|
|
|
// disable remote pointer while we are initing (bug 4767)
|
|
m_TB.Disable(IDM_REMOTE);
|
|
|
|
|
|
m_hwndSB = ::CreateWindowEx(0, STATUSCLASSNAME, NULL,
|
|
WS_CHILD | WS_VISIBLE | CCS_NOPARENTALIGN | CCS_NOMOVEY |
|
|
CCS_NORESIZE | SBARS_SIZEGRIP,
|
|
clientRect.left, clientRect.bottom - STATUSBAR_HEIGHT,
|
|
(clientRect.right - clientRect.left), STATUSBAR_HEIGHT,
|
|
m_hwnd, 0, g_hInstance, NULL);
|
|
if (!m_hwndSB)
|
|
{
|
|
ERROR_OUT(("Failed to create status bar window"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Create the attributes group
|
|
// The attributes group is on the bottom, underneath the
|
|
// drawing area, above the status bar.
|
|
//
|
|
RECT rectAG;
|
|
|
|
rectAG.left = clientRect.left;
|
|
rectAG.right = clientRect.right;
|
|
rectAG.top = drawingAreaRect.bottom;
|
|
rectAG.bottom = rectAG.top + sizeAG.cy;
|
|
|
|
if (!m_AG.Create(m_hwnd, &rectAG))
|
|
{
|
|
ERROR_OUT(("Failed to create attributes group window"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Create the widths group.
|
|
// The widths group is on the left side, underneath the tools group
|
|
//
|
|
SIZE sizeWG;
|
|
RECT rectWG;
|
|
|
|
|
|
// The widths group is on the left side, underneath the toolbar
|
|
m_WG.GetNaturalSize(&sizeWG);
|
|
rectWG.left = 0;
|
|
rectWG.right = rectWG.left + sizeWG.cx;
|
|
rectWG.bottom = rectAG.top;
|
|
rectWG.top = rectWG.bottom - sizeWG.cy;
|
|
|
|
if (!m_WG.Create(m_hwnd, &rectWG))
|
|
{
|
|
ERROR_OUT(("Failed to create widths group window"));
|
|
return(FALSE);
|
|
}
|
|
|
|
// The main window is created with the status bar visible. So make sure
|
|
// that the relevant menu item is checked. This is subject to change
|
|
// depending on options in the Open member function.
|
|
CheckMenuItem(IDM_STATUS_BAR_TOGGLE);
|
|
|
|
// Initialize the color, width and tool menus
|
|
InitializeMenus();
|
|
|
|
m_currentMenuTool = IDM_SELECT;
|
|
m_pCurrentTool = m_ToolArray[TOOL_INDEX(IDM_SELECT)];
|
|
|
|
|
|
m_hwndToolTip = ::CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
|
|
WS_POPUP | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, NULL, g_hInstance, NULL);
|
|
if (!m_hwndToolTip)
|
|
{
|
|
ERROR_OUT(("Unable to create tooltip window"));
|
|
return(FALSE);
|
|
}
|
|
|
|
// Add a dead-area tooltip
|
|
TOOLINFO ti;
|
|
|
|
ZeroMemory(&ti, sizeof(ti));
|
|
ti.cbSize = sizeof(TOOLINFO);
|
|
ti.uFlags = TTF_IDISHWND;
|
|
ti.hwnd = m_hwnd;
|
|
ti.uId = (UINT_PTR)m_hwnd;
|
|
::SendMessage(m_hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
|
|
|
|
// Ensure the page buttons are disabled while starting
|
|
UpdatePageButtons();
|
|
|
|
// If this is the first time we have created a clipboard object,
|
|
// register the private Whiteboard formats.
|
|
if (g_ClipboardFormats[CLIPBOARD_PRIVATE_SINGLE_OBJ] == 0)
|
|
{
|
|
g_ClipboardFormats[CLIPBOARD_PRIVATE_SINGLE_OBJ] =
|
|
(int) ::RegisterClipboardFormat("DCGWbClipFormat");
|
|
}
|
|
|
|
if (g_ClipboardFormats[CLIPBOARD_PRIVATE_MULTI_OBJ] == 0)
|
|
{
|
|
g_ClipboardFormats[CLIPBOARD_PRIVATE_MULTI_OBJ] =
|
|
(int) ::RegisterClipboardFormat("DCGWbMultiObjClipFormat");
|
|
}
|
|
|
|
// There is no deleted graphic yet
|
|
m_LastDeletedGraphic.BurnTrash();
|
|
|
|
m_bInitOk = TRUE;
|
|
|
|
BOOL bSuccess = TRUE; // indicates whether window opened successfully
|
|
|
|
// Get the position of the window from options
|
|
RECT rectWindow;
|
|
RECT rectDefault;
|
|
|
|
::SetRectEmpty(&rectDefault);
|
|
|
|
OPT_GetWindowRectOption(OPT_MAIN_MAINWINDOWRECT, &rectWindow, &rectDefault);
|
|
|
|
if (!::IsRectEmpty(&rectWindow))
|
|
{
|
|
::MoveWindow(m_hwnd, rectWindow.left, rectWindow.top,
|
|
rectWindow.right - rectWindow.left,
|
|
rectWindow.bottom - rectWindow.top, FALSE );
|
|
}
|
|
|
|
|
|
// Check whether the help bar is to be visible
|
|
if (!OPT_GetBooleanOption(OPT_MAIN_STATUSBARVISIBLE, DFLT_MAIN_STATUSBARVISIBLE))
|
|
{
|
|
// Update the window to turn the help bar off
|
|
OnStatusBarToggle();
|
|
}
|
|
|
|
//
|
|
// Position the toolbar
|
|
//
|
|
|
|
// Hide the tool bar before moving it (otherwise we get some
|
|
// problems redrawing it).
|
|
::ShowWindow(m_TB.m_hwnd, SW_HIDE);
|
|
|
|
// Resize the window panes to allow room for the tools
|
|
if (m_bToolBarOn)
|
|
{
|
|
ResizePanes();
|
|
::ShowWindow(m_TB.m_hwnd, SW_SHOW);
|
|
}
|
|
|
|
// Move the focus back from the tool window to the main window
|
|
::SetFocus(m_hwnd);
|
|
|
|
// Check whether the tool window is to be visible
|
|
if (OPT_GetBooleanOption(OPT_MAIN_TOOLBARVISIBLE, DFLT_MAIN_TOOLBARVISIBLE))
|
|
{
|
|
// Display the tool window, and check the associated menu item
|
|
OnToolBarToggle();
|
|
}
|
|
|
|
// Set up the variable saving the maximized/minimized state of
|
|
// the window and the extra style necessary for displaying the
|
|
// window correctly initially.
|
|
if (OPT_GetBooleanOption(OPT_MAIN_MAXIMIZED, DFLT_MAIN_MAXIMIZED))
|
|
{
|
|
m_uiWindowSize = SIZEFULLSCREEN;
|
|
iCommand = SW_SHOWMAXIMIZED;
|
|
}
|
|
else if (OPT_GetBooleanOption(OPT_MAIN_MINIMIZED, DFLT_MAIN_MINIMIZED))
|
|
{
|
|
m_uiWindowSize = SIZEICONIC;
|
|
iCommand = SW_SHOWMINIMIZED;
|
|
}
|
|
else
|
|
{
|
|
// Default
|
|
m_uiWindowSize = SIZENORMAL;
|
|
iCommand = SW_SHOWNORMAL;
|
|
}
|
|
|
|
UpdateWindowTitle();
|
|
::ShowWindow(m_hwnd, iCommand);
|
|
::UpdateWindow(m_hwnd);
|
|
|
|
// Update the tool window
|
|
::UpdateWindow(m_TB.m_hwnd);
|
|
|
|
// Select the tool
|
|
m_currentMenuTool = IDM_SELECT;
|
|
m_pCurrentTool = m_ToolArray[TOOL_INDEX(IDM_SELECT)];
|
|
::PostMessage(m_hwnd, WM_COMMAND, m_currentMenuTool, 0L);
|
|
|
|
// Return value indicating success or failure
|
|
return(bSuccess);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: WbMainWindow destructor
|
|
//
|
|
// Purpose: Tidy up main window on destruction.
|
|
//
|
|
//
|
|
WbMainWindow::~WbMainWindow()
|
|
{
|
|
//
|
|
// Destroy the tooltip window
|
|
//
|
|
if (m_hwndToolTip)
|
|
{
|
|
::DestroyWindow(m_hwndToolTip);
|
|
m_hwndToolTip = NULL;
|
|
}
|
|
|
|
// Make sure the clipboard discards its saved graphic
|
|
// before the drawingArea gets deleted.
|
|
CLP_FreeDelayedGraphic();
|
|
|
|
if (m_hGrobjContextMenuBar != NULL)
|
|
{
|
|
::DestroyMenu(m_hGrobjContextMenuBar);
|
|
m_hGrobjContextMenuBar = NULL;
|
|
}
|
|
m_hGrobjContextMenu = NULL;
|
|
|
|
if (m_hContextMenuBar != NULL)
|
|
{
|
|
::DestroyMenu(m_hContextMenuBar);
|
|
m_hContextMenuBar = NULL;
|
|
}
|
|
m_hContextMenu = NULL;
|
|
|
|
POSITION position = m_pageToPosition.GetHeadPosition();
|
|
|
|
PAGE_POSITION * pPoint;
|
|
|
|
while (position)
|
|
{
|
|
pPoint = (PAGE_POSITION*)m_pageToPosition.GetNext(position);
|
|
delete pPoint;
|
|
}
|
|
|
|
m_pageToPosition.EmptyList();
|
|
|
|
if (g_pwbCore)
|
|
{
|
|
//
|
|
//We must call an explicit stop function, rather than 'delete'
|
|
// because we need to pass in the event proc
|
|
//
|
|
g_pwbCore->WBP_Stop(WbMainWindowEventHandler);
|
|
g_pwbCore = NULL;
|
|
}
|
|
|
|
DestroyToolArray();
|
|
|
|
// Destroy our window
|
|
if (m_hwnd != NULL)
|
|
{
|
|
::DestroyWindow(m_hwnd);
|
|
m_hwnd = NULL;
|
|
}
|
|
|
|
// Deregister our class
|
|
::UnregisterClass(szMainClassName, g_hInstance);
|
|
|
|
//
|
|
// Free the palette
|
|
//
|
|
if (g_hRainbowPaletteDisplay)
|
|
{
|
|
DeletePalette(g_hRainbowPaletteDisplay);
|
|
g_hRainbowPaletteDisplay = NULL;
|
|
}
|
|
|
|
g_bPalettesInitialized = FALSE;
|
|
g_bUsePalettes = FALSE;
|
|
|
|
|
|
if (g_pIcons)
|
|
{
|
|
delete g_pIcons;
|
|
g_pIcons = NULL;
|
|
}
|
|
|
|
if (g_pUsers)
|
|
{
|
|
delete g_pUsers;
|
|
g_pUsers = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// JoinDomain()
|
|
// Attach to the empty domain or current call
|
|
//
|
|
BOOL WbMainWindow::JoinDomain(void)
|
|
{
|
|
BOOL bSuccess;
|
|
|
|
CM_STATUS cmStatus;
|
|
|
|
// If there is a call available - join it.
|
|
if (CMS_GetStatus(&cmStatus))
|
|
{
|
|
m_bCallActive = TRUE;
|
|
|
|
// Get the domain ID of the call
|
|
m_dwJoinDomain = (DWORD) cmStatus.callID;
|
|
|
|
// Join the call
|
|
bSuccess = JoinCall(FALSE);
|
|
}
|
|
else
|
|
{
|
|
// No call available so join the local domain
|
|
|
|
// Set the domain ID to "no call"
|
|
m_dwJoinDomain = (DWORD) OM_NO_CALL;
|
|
|
|
// Join the call
|
|
bSuccess = JoinCall(FALSE);
|
|
}
|
|
|
|
// Wait for the call to be joined, if not abandoned
|
|
if (bSuccess)
|
|
{
|
|
bSuccess = WaitForJoinCallComplete();
|
|
}
|
|
|
|
// take down init dlg
|
|
KillInitDlg();
|
|
|
|
return(bSuccess);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// KillInitDlg()
|
|
// Take down the init dialog
|
|
//
|
|
void WbMainWindow::KillInitDlg(void)
|
|
{
|
|
if (m_hwndInitDlg != NULL )
|
|
{
|
|
::DestroyWindow(m_hwndInitDlg);
|
|
m_hwndInitDlg = NULL;
|
|
|
|
::EnableWindow(m_hwnd, TRUE);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OnToolHitTest()
|
|
// This handles tooltips for child windows.
|
|
//
|
|
int WbMainWindow::OnToolHitTest(POINT pt, TOOLINFO* pTI) const
|
|
{
|
|
HWND hwnd;
|
|
int status;
|
|
int nHit = -1;
|
|
|
|
ASSERT(!IsBadWritePtr(pTI, sizeof(TOOLINFO)));
|
|
|
|
hwnd = ::ChildWindowFromPointEx(m_hwnd, pt, CWP_SKIPINVISIBLE);
|
|
if (hwnd == m_AG.m_hwnd)
|
|
{
|
|
::MapWindowPoints(m_hwnd, m_AG.m_hwnd, &pt, 1);
|
|
hwnd = ::ChildWindowFromPointEx(m_AG.m_hwnd, pt, CWP_SKIPINVISIBLE);
|
|
|
|
if (hwnd != NULL)
|
|
{
|
|
nHit = ::GetDlgCtrlID(hwnd);
|
|
|
|
pTI->hwnd = m_hwnd;
|
|
pTI->uId = (UINT_PTR)hwnd;
|
|
pTI->uFlags |= TTF_IDISHWND;
|
|
pTI->lpszText = LPSTR_TEXTCALLBACK;
|
|
|
|
return(nHit);
|
|
}
|
|
}
|
|
else if (hwnd == m_WG.m_hwnd)
|
|
{
|
|
int iItem;
|
|
|
|
::MapWindowPoints(m_hwnd, m_WG.m_hwnd, &pt, 1);
|
|
|
|
iItem = m_WG.ItemFromPoint(pt.x, pt.y);
|
|
|
|
pTI->hwnd = m_WG.m_hwnd;
|
|
pTI->uId = iItem;
|
|
|
|
// Since the area isn't a window, we must fill in the rect ourself
|
|
m_WG.GetItemRect(iItem, &pTI->rect);
|
|
pTI->lpszText = LPSTR_TEXTCALLBACK;
|
|
|
|
return(iItem);
|
|
}
|
|
else if (hwnd == m_TB.m_hwnd)
|
|
{
|
|
RECT rect;
|
|
TBBUTTON button;
|
|
int i;
|
|
|
|
for (i = 0; i < TOOLBAR_MAXBUTTON; i++)
|
|
{
|
|
if (::SendMessage(m_TB.m_hwnd, TB_GETITEMRECT, i, (LPARAM)&rect) &&
|
|
::PtInRect(&rect, pt) &&
|
|
::SendMessage(m_TB.m_hwnd, TB_GETBUTTON, i, (LPARAM)&button) &&
|
|
!(button.fsStyle & TBSTYLE_SEP))
|
|
{
|
|
nHit = button.idCommand;
|
|
|
|
pTI->hwnd = m_TB.m_hwnd;
|
|
pTI->uId = nHit;
|
|
pTI->rect = rect;
|
|
pTI->lpszText = LPSTR_TEXTCALLBACK;
|
|
|
|
// found matching rect, return the ID of the button
|
|
return(nHit);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
|
|
//
|
|
// WbMainWindowProc()
|
|
// Frame window message handler
|
|
//
|
|
LRESULT WbMainWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT lResult = 0;
|
|
WbMainWindow * pMain;
|
|
|
|
pMain = (WbMainWindow *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
switch (message)
|
|
{
|
|
case WM_NCCREATE:
|
|
pMain = (WbMainWindow *)((LPCREATESTRUCT)lParam)->lpCreateParams;
|
|
ASSERT(pMain);
|
|
|
|
pMain->m_hwnd = hwnd;
|
|
::SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pMain);
|
|
goto DefWndProc;
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
ShutDownHelp();
|
|
break;
|
|
|
|
case WM_NCDESTROY:
|
|
pMain->m_hwnd = NULL;
|
|
break;
|
|
|
|
case WM_MOVE:
|
|
pMain->OnMove();
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
pMain->OnSize((UINT)wParam, LOWORD(lParam), HIWORD(lParam));
|
|
break;
|
|
|
|
case WM_ACTIVATE:
|
|
if (GET_WM_ACTIVATE_STATE(wParam, lParam) == WA_INACTIVE)
|
|
{
|
|
// Cancel the tooltip if it's around
|
|
if (pMain->m_hwndToolTip)
|
|
::SendMessage(pMain->m_hwndToolTip, TTM_ACTIVATE, FALSE, 0);
|
|
}
|
|
goto DefWndProc;
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
pMain->OnSetFocus();
|
|
break;
|
|
|
|
case WM_CANCELMODE:
|
|
pMain->OnCancelMode();
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
pMain->OnTimer(wParam);
|
|
break;
|
|
|
|
case WM_INITMENUPOPUP:
|
|
pMain->OnInitMenuPopup((HMENU)wParam, LOWORD(lParam), HIWORD(lParam));
|
|
break;
|
|
|
|
case WM_MENUSELECT:
|
|
pMain->OnMenuSelect(GET_WM_MENUSELECT_CMD(wParam, lParam),
|
|
GET_WM_MENUSELECT_FLAGS(wParam, lParam),
|
|
GET_WM_MENUSELECT_HMENU(wParam, lParam));
|
|
break;
|
|
|
|
case WM_MEASUREITEM:
|
|
pMain->OnMeasureItem((int)wParam, (LPMEASUREITEMSTRUCT)lParam);
|
|
break;
|
|
|
|
case WM_DRAWITEM:
|
|
pMain->OnDrawItem((int)wParam, (LPDRAWITEMSTRUCT)lParam);
|
|
break;
|
|
|
|
case WM_QUERYNEWPALETTE:
|
|
lResult = pMain->OnQueryNewPalette();
|
|
break;
|
|
|
|
case WM_PALETTECHANGED:
|
|
pMain->OnPaletteChanged((HWND)wParam);
|
|
break;
|
|
|
|
case WM_HELP:
|
|
pMain->OnCommand(IDM_HELP, 0, NULL);
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
pMain->OnClose();
|
|
break;
|
|
|
|
case WM_QUERYENDSESSION:
|
|
lResult = pMain->OnQueryEndSession();
|
|
break;
|
|
|
|
case WM_ENDSESSION:
|
|
pMain->OnEndSession((BOOL)wParam);
|
|
break;
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
pMain->OnSysColorChange();
|
|
break;
|
|
|
|
case WM_USER_PRIVATE_PARENTNOTIFY:
|
|
pMain->OnParentNotify(GET_WM_PARENTNOTIFY_MSG(wParam, lParam));
|
|
break;
|
|
|
|
case WM_GETMINMAXINFO:
|
|
if (pMain)
|
|
pMain->OnGetMinMaxInfo((LPMINMAXINFO)lParam);
|
|
break;
|
|
|
|
case WM_RENDERALLFORMATS:
|
|
pMain->OnRenderAllFormats();
|
|
break;
|
|
|
|
case WM_RENDERFORMAT:
|
|
pMain->CLP_RenderFormat((int)wParam);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
pMain->OnCommand(LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
pMain->OnNotify((UINT)wParam, (NMHDR *)lParam);
|
|
break;
|
|
|
|
case WM_DROPFILES:
|
|
pMain->OnDropFiles((HDROP)wParam);
|
|
break;
|
|
|
|
case WM_USER_GOTO_USER_POSITION:
|
|
pMain->OnGotoUserPosition(lParam);
|
|
break;
|
|
|
|
case WM_USER_GOTO_USER_POINTER:
|
|
pMain->OnGotoUserPointer(lParam);
|
|
break;
|
|
|
|
case WM_USER_JOIN_CALL:
|
|
pMain->OnJoinCall((BOOL)wParam, lParam);
|
|
break;
|
|
|
|
case WM_USER_DISPLAY_ERROR:
|
|
pMain->OnDisplayError(wParam, lParam);
|
|
break;
|
|
|
|
case WM_USER_UPDATE_ATTRIBUTES:
|
|
pMain->m_AG.DisplayTool(pMain->m_pCurrentTool);
|
|
break;
|
|
|
|
case WM_USER_JOIN_PENDING_CALL:
|
|
pMain->OnJoinPendingCall();
|
|
break;
|
|
|
|
default:
|
|
if (message == g_uConfShutdown)
|
|
{
|
|
lResult = pMain->OnConfShutdown(wParam, lParam);
|
|
}
|
|
else
|
|
{
|
|
DefWndProc:
|
|
lResult = DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return(lResult);
|
|
}
|
|
|
|
|
|
//
|
|
// OnCommand()
|
|
// Command dispatcher for the main window
|
|
//
|
|
void WbMainWindow::OnCommand(UINT cmd, UINT code, HWND hwndCtl)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
//
|
|
// FILE MENU
|
|
//
|
|
case IDM_NEW:
|
|
OnNew();
|
|
break;
|
|
|
|
case IDM_OPEN:
|
|
OnOpen();
|
|
break;
|
|
|
|
case IDM_SAVE:
|
|
OnSave(FALSE);
|
|
break;
|
|
|
|
case IDM_SAVE_AS:
|
|
OnSave(TRUE);
|
|
break;
|
|
|
|
case IDM_PRINT:
|
|
OnPrint();
|
|
break;
|
|
|
|
case IDM_EXIT:
|
|
::PostMessage(m_hwnd, WM_CLOSE, 0, 0);
|
|
break;
|
|
|
|
//
|
|
// EDIT MENU
|
|
//
|
|
case IDM_DELETE:
|
|
OnDelete();
|
|
break;
|
|
|
|
case IDM_UNDELETE:
|
|
OnUndelete();
|
|
break;
|
|
|
|
case IDM_CUT:
|
|
OnCut();
|
|
break;
|
|
|
|
case IDM_COPY:
|
|
OnCopy();
|
|
break;
|
|
|
|
case IDM_PASTE:
|
|
OnPaste();
|
|
break;
|
|
|
|
case IDM_SELECTALL:
|
|
OnSelectAll();
|
|
break;
|
|
|
|
case IDM_BRING_TO_TOP:
|
|
m_drawingArea.BringToTopSelection();
|
|
break;
|
|
|
|
case IDM_SEND_TO_BACK:
|
|
m_drawingArea.SendToBackSelection();
|
|
break;
|
|
|
|
case IDM_CLEAR_PAGE:
|
|
OnClearPage();
|
|
break;
|
|
|
|
case IDM_DELETE_PAGE:
|
|
OnDeletePage();
|
|
break;
|
|
|
|
case IDM_PAGE_INSERT_BEFORE:
|
|
OnInsertPageBefore();
|
|
break;
|
|
|
|
case IDM_PAGE_INSERT_AFTER:
|
|
OnInsertPageAfter();
|
|
break;
|
|
|
|
case IDM_PAGE_SORTER:
|
|
OnPageSorter();
|
|
break;
|
|
|
|
//
|
|
// VIEW MENU
|
|
//
|
|
case IDM_TOOL_BAR_TOGGLE:
|
|
OnToolBarToggle();
|
|
break;
|
|
|
|
case IDM_STATUS_BAR_TOGGLE:
|
|
OnStatusBarToggle();
|
|
break;
|
|
|
|
case IDM_ZOOM:
|
|
OnZoom();
|
|
break;
|
|
|
|
//
|
|
// TOOLS MENU
|
|
//
|
|
case IDM_SELECT:
|
|
case IDM_PEN:
|
|
case IDM_HIGHLIGHT:
|
|
case IDM_TEXT:
|
|
case IDM_ERASER:
|
|
case IDM_LINE:
|
|
case IDM_BOX:
|
|
case IDM_FILLED_BOX:
|
|
case IDM_ELLIPSE:
|
|
case IDM_FILLED_ELLIPSE:
|
|
OnSelectTool(cmd);
|
|
break;
|
|
|
|
case IDM_REMOTE:
|
|
OnRemotePointer();
|
|
break;
|
|
|
|
case IDM_GRAB_AREA:
|
|
OnGrabArea();
|
|
break;
|
|
|
|
case IDM_GRAB_WINDOW:
|
|
OnGrabWindow();
|
|
break;
|
|
|
|
case IDM_SYNC:
|
|
OnSync();
|
|
break;
|
|
|
|
case IDM_LOCK:
|
|
OnLock();
|
|
break;
|
|
|
|
//
|
|
// OPTIONS MENU
|
|
//
|
|
case IDM_COLOR:
|
|
OnSelectColor();
|
|
break;
|
|
|
|
case IDM_EDITCOLOR:
|
|
m_AG.OnEditColors();
|
|
break;
|
|
|
|
case IDM_FONT:
|
|
OnChooseFont();
|
|
break;
|
|
|
|
case IDM_WIDTH_1:
|
|
case IDM_WIDTH_2:
|
|
case IDM_WIDTH_3:
|
|
case IDM_WIDTH_4:
|
|
OnSelectWidth(cmd);
|
|
break;
|
|
|
|
//
|
|
// HELP MENU
|
|
//
|
|
case IDM_ABOUT:
|
|
OnAbout();
|
|
break;
|
|
|
|
case IDM_HELP:
|
|
ShowHelp();
|
|
break;
|
|
|
|
//
|
|
// PAGE BAR
|
|
//
|
|
case IDM_PAGE_FIRST:
|
|
OnFirstPage();
|
|
break;
|
|
|
|
case IDM_PAGE_PREV:
|
|
OnPrevPage();
|
|
break;
|
|
|
|
case IDM_PAGE_GOTO:
|
|
OnGotoPage();
|
|
break;
|
|
|
|
case IDM_PAGE_NEXT:
|
|
OnNextPage();
|
|
break;
|
|
|
|
case IDM_PAGE_LAST:
|
|
OnLastPage();
|
|
break;
|
|
|
|
//
|
|
// SCROLLING
|
|
//
|
|
case IDM_PAGEUP:
|
|
case IDM_PAGEDOWN:
|
|
case IDM_SHIFTPAGEUP:
|
|
case IDM_SHIFTPAGEDOWN:
|
|
case IDM_HOME:
|
|
case IDM_END:
|
|
case IDM_LINEUP:
|
|
case IDM_LINEDOWN:
|
|
case IDM_SHIFTLINEUP:
|
|
case IDM_SHIFTLINEDOWN:
|
|
OnScrollAccelerator(cmd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// WinHelp() wrapper
|
|
//
|
|
void WbMainWindow::ShowHelp(void)
|
|
{
|
|
HWND hwndCapture;
|
|
|
|
// Get the main window out of any mode
|
|
::SendMessage(m_hwnd, WM_CANCELMODE, 0, 0);
|
|
|
|
// Cancel any other tracking
|
|
if (hwndCapture = ::GetCapture())
|
|
::SendMessage(hwndCapture, WM_CANCELMODE, 0, 0);
|
|
|
|
// finally, run the Windows Help engine
|
|
ShowNmHelp(s_cszHtmlHelpFile);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnJoinCall
|
|
//
|
|
// Purpose: Join a call - displaying a dialog informing the user of
|
|
// progress.
|
|
//
|
|
//
|
|
void WbMainWindow::OnJoinCall(BOOL bKeep, LPARAM lParam)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnJoinCall");
|
|
|
|
// cancel the load if there's one in progress
|
|
if ( (m_uiState == IN_CALL)
|
|
&& (m_uiSubState == SUBSTATE_LOADING))
|
|
{
|
|
CancelLoad();
|
|
}
|
|
|
|
// Get the parameters for JoinCall
|
|
m_dwJoinDomain = (DWORD) lParam;
|
|
|
|
// Start the process of joining the call
|
|
BOOL bSuccess = JoinCall(bKeep);
|
|
|
|
// Wait for the join call to complete, if not abandoned
|
|
if (bSuccess)
|
|
{
|
|
bSuccess = WaitForJoinCallComplete();
|
|
|
|
if (bSuccess)
|
|
{
|
|
TRACE_MSG(("Joined call OK"));
|
|
}
|
|
else
|
|
{
|
|
// WaitForJoinCallComplete displays appropriate error message
|
|
TRACE_MSG(("Failed to join call"));
|
|
|
|
// get into a good state
|
|
Recover();
|
|
}
|
|
}
|
|
|
|
// take down init dlg
|
|
KillInitDlg();
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: JoinCall
|
|
//
|
|
// Purpose: Join a call - displaying a dialog informing the user of
|
|
// progress.
|
|
//
|
|
//
|
|
BOOL WbMainWindow::JoinCall(BOOL bKeep)
|
|
{
|
|
BOOL bSuccess = TRUE;
|
|
UINT uiReturn;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::JoinCall");
|
|
|
|
// We must not already be in a real call when we get here
|
|
if ((m_uiState == IN_CALL) && (m_dwDomain != OM_NO_CALL))
|
|
{
|
|
ERROR_OUT(("In a call already"));
|
|
}
|
|
|
|
//
|
|
// Prompt the user to save the current contents unless we are in
|
|
// application start-up (when there can be no contents to save) or we
|
|
// are keeping the contents (when there is no need to save).
|
|
//
|
|
if ((m_uiState != STARTING) && !bKeep)
|
|
{
|
|
//
|
|
// Close the page sorter dialog if it's up.
|
|
//
|
|
if (m_hwndPageSortDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndPageSortDlg, WM_COMMAND,
|
|
MAKELONG(IDOK, BN_CLICKED), 0);
|
|
ASSERT(m_hwndPageSortDlg == NULL);
|
|
}
|
|
|
|
TRACE_MSG(("Not in STARTING state - check whether save wanted"));
|
|
|
|
if (m_hwndQuerySaveDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndQuerySaveDlg, WM_COMMAND,
|
|
MAKELONG(IDCANCEL, BN_CLICKED), 0);
|
|
ASSERT(m_hwndQuerySaveDlg == NULL);
|
|
}
|
|
|
|
// flag that we are joining a call
|
|
m_bPromptingJoinCall = TRUE;
|
|
|
|
// ask the user whether to save changes (if required)
|
|
int iDoNew = QuerySaveRequired(FALSE);
|
|
|
|
// remove any save as dialog that is already up.
|
|
if (m_bInSaveDialog)
|
|
{
|
|
m_bPromptingJoinCall = FALSE;
|
|
CancelSaveDialog();
|
|
m_bPromptingJoinCall = TRUE;
|
|
}
|
|
|
|
if (iDoNew == IDYES)
|
|
{
|
|
TRACE_MSG(("User has elected to save the changes"));
|
|
|
|
// Save the changes
|
|
iDoNew = OnSave(FALSE);
|
|
}
|
|
|
|
if (!m_bPromptingJoinCall) // received end call notification
|
|
// (during save-as or query-save)
|
|
{
|
|
TRACE_MSG(("Call ended - abandon JoinCall"));
|
|
return(FALSE);
|
|
}
|
|
|
|
// flag we're no longer in a state where the join call can be
|
|
// cancelled
|
|
m_bPromptingJoinCall = FALSE;
|
|
|
|
//
|
|
// Reset the file name to Untitled, since we are receiving new
|
|
// contents
|
|
//
|
|
ZeroMemory(m_strFileName, sizeof(m_strFileName));
|
|
UpdateWindowTitle();
|
|
|
|
// if we have the lock then release it
|
|
if (WB_GotLock())
|
|
{
|
|
// Release the lock
|
|
g_pwbCore->WBP_Unlock();
|
|
|
|
// Set the locked check mark
|
|
UncheckMenuItem(IDM_LOCK);
|
|
|
|
// Pop up the lock button
|
|
m_TB.PopUp(IDM_LOCK);
|
|
}
|
|
|
|
if(m_pLocalUser != NULL)
|
|
{
|
|
// if the remote pointer is active then turn it off
|
|
DCWbGraphicPointer* pPointer = m_pLocalUser->GetPointer();
|
|
if (pPointer->IsActive())
|
|
{
|
|
OnRemotePointer();
|
|
}
|
|
}
|
|
|
|
// if sync is turned on then turn it off
|
|
Unsync();
|
|
|
|
// If we are not keeping the contents then the only valid current
|
|
// page is the first page.
|
|
g_pwbCore->WBP_PageHandle(WB_PAGE_HANDLE_NULL, PAGE_FIRST, &m_hCurrentPage);
|
|
}
|
|
|
|
//PUTBACK BY RAND - the progress timer meter is kinda the heart beat
|
|
// of this thing which I ripped out when I removed the
|
|
// progress meter. I put it back to fix 1476.
|
|
if (m_bTimerActive)
|
|
{
|
|
::KillTimer(m_hwnd, TIMERID_PROGRESS_METER);
|
|
m_bTimerActive = FALSE;
|
|
}
|
|
|
|
//
|
|
// lock the drawing area until the joining of the call has succeeded
|
|
//
|
|
TRACE_MSG(("Locking drawing area"));
|
|
LockDrawingArea();
|
|
|
|
//
|
|
// Give the drawing area a null page during the joining process. This
|
|
// prevents the drawing area attempting to draw the objects in the page
|
|
// during the process of joining the call.
|
|
//
|
|
TRACE_MSG(("Detaching drawing area"));
|
|
m_drawingArea.Detach();
|
|
|
|
// Show that we are no longer in a call, but joining a new one
|
|
TRACE_MSG(("m_uiState %d", m_uiState));
|
|
if (m_uiState != STARTING)
|
|
{
|
|
m_uiState = JOINING;
|
|
UpdatePageButtons();
|
|
}
|
|
|
|
// put up init dlg
|
|
if (m_bCallActive)
|
|
{
|
|
::UpdateWindow(m_hwnd);
|
|
|
|
//
|
|
// Our init dialog doesn't have a proc, since it has no UI to
|
|
// interact with. We destroy it when we are done. So, do the
|
|
// init stuff here.
|
|
//
|
|
m_hwndInitDlg = ::CreateDialogParam(g_hInstance,
|
|
MAKEINTRESOURCE(IM_INITIALIZING), m_hwnd, NULL, 0);
|
|
|
|
if (!m_hwndInitDlg)
|
|
{
|
|
ERROR_OUT(("Couldn't create startup screen for WB"));
|
|
}
|
|
else
|
|
{
|
|
RECT rcMovie;
|
|
HWND hwndMovieParent;
|
|
HWND hwndMovie;
|
|
|
|
// Get the rectangle to create the animation control in
|
|
hwndMovieParent = ::GetDlgItem(m_hwndInitDlg, IDC_INITIALIZING_ANIMATION);
|
|
::GetClientRect(hwndMovieParent, &rcMovie);
|
|
|
|
hwndMovie = ::CreateWindowEx(0, ANIMATE_CLASS, NULL,
|
|
WS_CHILD | WS_VISIBLE | ACS_TRANSPARENT | ACS_CENTER,
|
|
rcMovie.left, rcMovie.top,
|
|
rcMovie.right - rcMovie.left, rcMovie.bottom - rcMovie.top,
|
|
hwndMovieParent, (HMENU)IDC_INITIALIZING_ANIMATION,
|
|
g_hInstance, NULL);
|
|
|
|
if (hwndMovie != NULL)
|
|
{
|
|
::SendMessage(hwndMovie, ACM_OPEN, 0, (LPARAM)
|
|
MAKEINTRESOURCE(WBMOVIE));
|
|
}
|
|
|
|
// Disable the main window while the dialog is up.
|
|
::EnableWindow(m_hwnd, FALSE);
|
|
|
|
::ShowWindow(m_hwndInitDlg, SW_SHOW);
|
|
::UpdateWindow(m_hwndInitDlg);
|
|
|
|
if (hwndMovie != NULL)
|
|
{
|
|
::SendMessage(hwndMovie, ACM_PLAY, 0xFFFF,
|
|
MAKELPARAM(0, 0xFFFF));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Start joining the call. Throws an exception on error.
|
|
//
|
|
ASSERT(g_pUsers);
|
|
g_pUsers->Clear();
|
|
|
|
uiReturn = g_pwbCore->WBP_JoinCall(bKeep, m_dwJoinDomain);
|
|
if (uiReturn != 0)
|
|
{
|
|
bSuccess = FALSE;
|
|
}
|
|
|
|
return(bSuccess);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: WaitForJoinCallComplete
|
|
//
|
|
// Purpose: Join a call - displaying a dialog informing the user of
|
|
// progress.
|
|
//
|
|
//
|
|
BOOL WbMainWindow::WaitForJoinCallComplete(void)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
TMDLG tmdlg;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::WaitForJoinCallComplete");
|
|
|
|
//
|
|
// Bring up a dialog to wait for call joining to complete. This turns
|
|
// the asynchronous registration process into a synchronous process as
|
|
// far as this routine is concerned.
|
|
//
|
|
|
|
//
|
|
// Set the window title to show we're no longer registering/joining a
|
|
// call.
|
|
//
|
|
UpdateWindowTitle();
|
|
|
|
ASSERT(m_hwndWaitForEventDlg == NULL);
|
|
|
|
//
|
|
// This is the data we use in the timed dialog
|
|
//
|
|
ZeroMemory(&tmdlg, sizeof(tmdlg));
|
|
tmdlg.bVisible = FALSE;
|
|
tmdlg.bLockNotEvent = FALSE;
|
|
tmdlg.uiMaxDisplay = MAIN_REGISTRATION_TIMEOUT;
|
|
|
|
::DialogBoxParam(g_hInstance, MAKEINTRESOURCE(INVISIBLEDIALOG),
|
|
m_hwnd, TimedDlgProc, (LPARAM)&tmdlg);
|
|
|
|
ASSERT(m_hwndWaitForEventDlg == NULL);
|
|
|
|
//
|
|
// Set the window title to show we're no longer registering/joining a
|
|
// call.
|
|
//
|
|
UpdateWindowTitle();
|
|
|
|
if (m_uiState != IN_CALL)
|
|
{
|
|
//
|
|
// We failed to join the call
|
|
//
|
|
WARNING_OUT(("User cancelled or joincall failed, m_uiState %d", m_uiState));
|
|
|
|
//
|
|
// We must display an error inline here because we will close
|
|
// shortly
|
|
//
|
|
OnDisplayError(WBFE_RC_JOIN_CALL_FAILED, 0);
|
|
}
|
|
else
|
|
{
|
|
bResult = TRUE;
|
|
}
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
//
|
|
// TimedDlgProc()
|
|
// This puts up a visible or invisible dialog which only goes away when
|
|
// an event occurs or a certain amount of time has passed. We store the
|
|
// DialogBoxParam parameter, a TMDLG pointer, in our user data. That is
|
|
// from the stack of the DialogBoxParam() caller, so it is valid until that
|
|
// function returns, which won't be until a bit after the dialog has been
|
|
// destroyed.
|
|
//
|
|
INT_PTR CALLBACK TimedDlgProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fHandled = FALSE;
|
|
TMDLG * ptm;
|
|
|
|
switch (uMessage)
|
|
{
|
|
case WM_INITDIALOG:
|
|
ptm = (TMDLG *)lParam;
|
|
ASSERT(!IsBadWritePtr(ptm, sizeof(TMDLG)));
|
|
::SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)ptm);
|
|
|
|
//
|
|
// Set the WbMainWindow hwnd
|
|
//
|
|
if (ptm->bLockNotEvent)
|
|
{
|
|
g_pMain->m_hwndWaitForLockDlg = hwnd;
|
|
}
|
|
else
|
|
{
|
|
g_pMain->m_hwndWaitForEventDlg = hwnd;
|
|
}
|
|
|
|
//
|
|
// Set max timer
|
|
//
|
|
::SetTimer(hwnd, TIMERID_MAXDISPLAY, ptm->uiMaxDisplay, NULL);
|
|
|
|
//
|
|
// Change the cursor if invisible
|
|
//
|
|
if (!ptm->bVisible)
|
|
::SetCursor(::LoadCursor(NULL, IDC_WAIT));
|
|
|
|
fHandled = TRUE;
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
ASSERT(wParam == TIMERID_MAXDISPLAY);
|
|
|
|
// End the dialog--since we timed out, it acts like cancel
|
|
::SendMessage(hwnd, WM_COMMAND, MAKELONG(IDCANCEL, BN_CLICKED), 0);
|
|
|
|
fHandled = TRUE;
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
|
|
{
|
|
ptm = (TMDLG *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
ASSERT(!IsBadWritePtr(ptm, sizeof(TMDLG)));
|
|
|
|
// Clear out the HWND variable
|
|
if (ptm->bLockNotEvent)
|
|
{
|
|
g_pMain->m_hwndWaitForLockDlg = NULL;
|
|
}
|
|
else
|
|
{
|
|
g_pMain->m_hwndWaitForEventDlg = NULL;
|
|
}
|
|
|
|
// Restore the cursor
|
|
if (!ptm->bVisible)
|
|
::SetCursor(::LoadCursor(NULL, IDC_ARROW));
|
|
|
|
::KillTimer(hwnd, TIMERID_MAXDISPLAY);
|
|
|
|
::EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
|
|
}
|
|
break;
|
|
}
|
|
|
|
fHandled = TRUE;
|
|
break;
|
|
|
|
//
|
|
// Don't let these dialogs be killed by any other means than our
|
|
// getting an event or timing out.
|
|
//
|
|
case WM_CLOSE:
|
|
fHandled = TRUE;
|
|
break;
|
|
}
|
|
|
|
return(fHandled);
|
|
}
|
|
|
|
//
|
|
// FilterMessage()
|
|
//
|
|
// This does tooltip message filtering, then translates accelerators.
|
|
//
|
|
BOOL WbMainWindow::FilterMessage(MSG* pMsg)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
|
|
if ((pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST) ||
|
|
(pMsg->message == WM_LBUTTONDOWN || pMsg->message == WM_LBUTTONDBLCLK) ||
|
|
(pMsg->message == WM_RBUTTONDOWN || pMsg->message == WM_RBUTTONDBLCLK) ||
|
|
(pMsg->message == WM_MBUTTONDOWN || pMsg->message == WM_MBUTTONDBLCLK) ||
|
|
(pMsg->message == WM_NCLBUTTONDOWN || pMsg->message == WM_NCLBUTTONDBLCLK) ||
|
|
(pMsg->message == WM_NCRBUTTONDOWN || pMsg->message == WM_NCRBUTTONDBLCLK) ||
|
|
(pMsg->message == WM_NCMBUTTONDOWN || pMsg->message == WM_NCMBUTTONDBLCLK))
|
|
{
|
|
// Cancel any tooltip up
|
|
::SendMessage(m_hwndToolTip, TTM_ACTIVATE, FALSE, 0);
|
|
}
|
|
|
|
// handle tooltip messages (some messages cancel, some may cause it to popup)
|
|
if ((pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE ||
|
|
pMsg->message == WM_LBUTTONUP || pMsg->message == WM_RBUTTONUP ||
|
|
pMsg->message == WM_MBUTTONUP) &&
|
|
(GetKeyState(VK_LBUTTON) >= 0 && GetKeyState(VK_RBUTTON) >= 0 &&
|
|
GetKeyState(VK_MBUTTON) >= 0))
|
|
{
|
|
#if 0
|
|
//
|
|
// If this mouse move isn't for a descendant of the main window, skip
|
|
// it. For example, when the tooltip is shown, it gets a mousemove
|
|
// to itself, which if we didn't check for it, would cause us to
|
|
// immediately dismiss this!
|
|
//
|
|
HWND hwndTmp = pMsg->hwnd;
|
|
|
|
while (hwndTmp && (::GetWindowLong(hwndTmp, GWL_STYLE) & WS_CHILD))
|
|
{
|
|
hwndTmp = ::GetParent(hwndTmp);
|
|
}
|
|
|
|
if (hwndTmp != m_hwnd)
|
|
{
|
|
// This is not for us, it's for another top level window in
|
|
// our app.
|
|
goto DoneToolTipFiltering;
|
|
}
|
|
#endif
|
|
|
|
// determine which tool was hit
|
|
POINT pt;
|
|
|
|
pt = pMsg->pt;
|
|
::ScreenToClient(m_hwnd, &pt);
|
|
|
|
TOOLINFO tiHit;
|
|
|
|
ZeroMemory(&tiHit, sizeof(tiHit));
|
|
tiHit.cbSize = sizeof(TOOLINFO);
|
|
|
|
int nHit = OnToolHitTest(pt, &tiHit);
|
|
|
|
if (m_nLastHit != nHit)
|
|
{
|
|
if (nHit != -1)
|
|
{
|
|
// add new tool and activate the tip
|
|
if (!::SendMessage(m_hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&tiHit))
|
|
{
|
|
ERROR_OUT(("TTM_ADDTOOL failed"));
|
|
}
|
|
|
|
if (::GetActiveWindow() == m_hwnd)
|
|
{
|
|
// allow the tooltip to popup when it should
|
|
::SendMessage(m_hwndToolTip, TTM_ACTIVATE, TRUE, 0);
|
|
|
|
// bring the tooltip window above other popup windows
|
|
::SetWindowPos(m_hwndToolTip, HWND_TOP, 0, 0, 0, 0,
|
|
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE);
|
|
}
|
|
}
|
|
|
|
// relay mouse event before deleting old tool
|
|
::SendMessage(m_hwndToolTip, TTM_RELAYEVENT, 0, (LPARAM)pMsg);
|
|
|
|
// now safe to delete the old tool
|
|
if (m_tiLastHit.cbSize != 0)
|
|
::SendMessage(m_hwndToolTip, TTM_DELTOOL, 0, (LPARAM)&m_tiLastHit);
|
|
|
|
m_nLastHit = nHit;
|
|
m_tiLastHit = tiHit;
|
|
}
|
|
else
|
|
{
|
|
// relay mouse events through the tooltip
|
|
if (nHit != -1)
|
|
::SendMessage(m_hwndToolTip, TTM_RELAYEVENT, 0, (LPARAM)pMsg);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
DoneToolTipFiltering:
|
|
#endif
|
|
// Assume we will use the main accelerator table
|
|
HACCEL hAccelTable = m_hAccelTable;
|
|
|
|
// If this window has focus, just continue
|
|
HWND hwndFocus = ::GetFocus();
|
|
if (hwndFocus && (hwndFocus != m_hwnd))
|
|
{
|
|
// Check whether an edit field in the pages group has the focus
|
|
if (m_AG.IsChildEditField(hwndFocus))
|
|
{
|
|
hAccelTable = m_hAccelPagesGroup;
|
|
}
|
|
// Check whether text editor has the focus and is active
|
|
else if ( (hwndFocus == m_drawingArea.m_hwnd)
|
|
&& (m_drawingArea.TextEditActive()))
|
|
{
|
|
// Let editbox do its own acceleration.
|
|
hAccelTable = NULL;
|
|
}
|
|
}
|
|
|
|
return ( (hAccelTable != NULL)
|
|
&& ::TranslateAccelerator(m_hwnd, hAccelTable, pMsg));
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnDisplayError
|
|
//
|
|
// Purpose: Display an error message
|
|
//
|
|
//
|
|
void WbMainWindow::OnDisplayError(WPARAM uiFEReturnCode, LPARAM uiDCGReturnCode)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnDisplayError");
|
|
|
|
// Only continue if we are not currently displaying an error
|
|
if (!m_bDisplayingError)
|
|
{
|
|
// Show that we are currently displaying an error message
|
|
m_bDisplayingError = TRUE;
|
|
|
|
// Display the error
|
|
::ErrorMessage((UINT)uiFEReturnCode, (UINT)uiDCGReturnCode);
|
|
|
|
// Show that we are no longer displaying an error
|
|
m_bDisplayingError = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnTimer
|
|
//
|
|
// Purpose: Process a timer event. These are used to update the progress
|
|
// meter and the sync position.
|
|
//
|
|
//
|
|
void WbMainWindow::OnTimer(UINT_PTR idTimer)
|
|
{
|
|
TRACE_TIMER(("WbMainWindow::OnTimer"));
|
|
|
|
//
|
|
// Only do anything if the timer has not been switched off (this may be an
|
|
// old timer message left in the queue when we stopped the timer).
|
|
//
|
|
if (m_bTimerActive)
|
|
{
|
|
//
|
|
// Check for sync position update needed
|
|
//
|
|
|
|
// Check whether an update is flagged
|
|
if (m_bSyncUpdateNeeded)
|
|
{
|
|
TRACE_TIMER(("Updating sync position"));
|
|
|
|
// Check whether the local user is still synced
|
|
if ((m_uiState == IN_CALL) &&
|
|
(m_pLocalUser != NULL) &&
|
|
(m_pLocalUser->IsSynced()) &&
|
|
(!WB_ContentsLocked()))
|
|
{
|
|
RECT rcVis;
|
|
|
|
// Update the local user's position information
|
|
m_drawingArea.GetVisibleRect(&rcVis);
|
|
|
|
m_pLocalUser->SetVisibleRect(&rcVis);
|
|
|
|
// Write the sync position from the local user's current position
|
|
m_pLocalUser->PutSyncPosition();
|
|
}
|
|
|
|
// Show that the update has been done
|
|
m_bSyncUpdateNeeded = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnPaletteChanged
|
|
//
|
|
// Purpose: The palette has changed.
|
|
//
|
|
//
|
|
void WbMainWindow::OnPaletteChanged(HWND hwndPalette)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnPaletteChanged");
|
|
|
|
if ((hwndPalette != m_hwnd) &&
|
|
(hwndPalette != m_drawingArea.m_hwnd))
|
|
{
|
|
// Tell the drawing area to realize its palette
|
|
m_drawingArea.RealizePalette( TRUE );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnQueryNewPalette
|
|
//
|
|
// Purpose: We are getting focus and must realize our palette
|
|
//
|
|
//
|
|
LRESULT WbMainWindow::OnQueryNewPalette(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnQueryNewPalette");
|
|
|
|
// Tell the drawing area to realize its palette
|
|
m_drawingArea.RealizePalette( FALSE );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: WbMainWindowEventHandler
|
|
//
|
|
// Purpose: Event handler for WbMainWindow objects. This is a class
|
|
// wide function. The client data passed to it is a pointer
|
|
// to the instance of WbMainWindow for which the event is
|
|
// intended.
|
|
//
|
|
//
|
|
BOOL CALLBACK WbMainWindowEventHandler
|
|
(
|
|
LPVOID utHandle,
|
|
UINT event,
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
if (g_pMain->m_hwnd != NULL)
|
|
{
|
|
return(g_pMain->EventHandler(event, param1, param2));
|
|
}
|
|
else
|
|
{
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: EventHandler
|
|
//
|
|
// Purpose: Handler for DC-Groupware events for this object
|
|
//
|
|
//
|
|
BOOL WbMainWindow::EventHandler(UINT Event, UINT_PTR param1, UINT_PTR param2)
|
|
{
|
|
BOOL processed;
|
|
|
|
switch (Event)
|
|
{
|
|
case CMS_NEW_CALL:
|
|
case CMS_END_CALL:
|
|
|
|
case ALS_LOCAL_LOAD:
|
|
case ALS_REMOTE_LOAD_RESULT:
|
|
|
|
case WBP_EVENT_JOIN_CALL_OK:
|
|
case WBP_EVENT_JOIN_CALL_FAILED:
|
|
case WBP_EVENT_NETWORK_LOST:
|
|
case WBP_EVENT_ERROR:
|
|
case WBP_EVENT_PAGE_CLEAR_IND:
|
|
case WBP_EVENT_PAGE_ORDER_UPDATED:
|
|
case WBP_EVENT_PAGE_DELETE_IND:
|
|
case WBP_EVENT_CONTENTS_LOCKED:
|
|
case WBP_EVENT_PAGE_ORDER_LOCKED:
|
|
case WBP_EVENT_UNLOCKED:
|
|
case WBP_EVENT_LOCK_FAILED:
|
|
case WBP_EVENT_GRAPHIC_ADDED:
|
|
case WBP_EVENT_GRAPHIC_MOVED:
|
|
case WBP_EVENT_GRAPHIC_UPDATE_IND:
|
|
case WBP_EVENT_GRAPHIC_REPLACE_IND:
|
|
case WBP_EVENT_GRAPHIC_DELETE_IND:
|
|
case WBP_EVENT_PERSON_JOINED:
|
|
case WBP_EVENT_PERSON_LEFT:
|
|
case WBP_EVENT_PERSON_UPDATE:
|
|
case WBP_EVENT_PERSON_REPLACE:
|
|
case WBP_EVENT_SYNC_POSITION_UPDATED:
|
|
case WBP_EVENT_LOAD_COMPLETE:
|
|
case WBP_EVENT_LOAD_FAILED:
|
|
// Process the Event
|
|
ProcessEvents(Event, param1, param2);
|
|
processed = TRUE;
|
|
break;
|
|
|
|
default:
|
|
processed = FALSE;
|
|
break;
|
|
}
|
|
|
|
return(processed);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: PopupContextMenu
|
|
//
|
|
// Purpose: Popup the context menu for the drawing area. This is called
|
|
// by the drawing area window on a right mouse click.
|
|
//
|
|
//
|
|
void WbMainWindow::PopupContextMenu(int x, int y)
|
|
{
|
|
POINT surfacePos;
|
|
RECT clientRect;
|
|
DCWbGraphic * pGraphic;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::PopupContextMenu");
|
|
|
|
surfacePos.x = x;
|
|
surfacePos.y = y;
|
|
|
|
// figure out which popup menu to use (bug 426)
|
|
if (m_pCurrentTool->ToolType() == TOOLTYPE_SELECT)
|
|
{
|
|
m_drawingArea.ClientToSurface(&surfacePos);
|
|
if( (pGraphic = m_drawingArea.GetHitObject( surfacePos )) != NULL )
|
|
{
|
|
// we clicked over an object, see if its already selected
|
|
if( !m_drawingArea.IsSelected( pGraphic ) )
|
|
{
|
|
// object is not already selected, zap current selection and then select it
|
|
m_drawingArea.ClearSelection();
|
|
m_drawingArea.SelectGraphic( pGraphic );
|
|
}
|
|
else
|
|
{
|
|
// plug leak by deleteing pGraphic
|
|
delete pGraphic;
|
|
}
|
|
}
|
|
|
|
if( m_drawingArea.GraphicSelected() )
|
|
{
|
|
// selector tool is active, and one or more objects are selected
|
|
m_hInitMenu = m_hGrobjContextMenu;
|
|
}
|
|
else
|
|
{
|
|
// no current selection, show drawing menu
|
|
m_hInitMenu = m_hContextMenu;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no objects selected, use drawing menu
|
|
m_hInitMenu = m_hContextMenu;
|
|
}
|
|
|
|
// set up current menu state
|
|
SetMenuStates(m_hInitMenu);
|
|
|
|
// pop it up
|
|
::GetClientRect(m_drawingArea.m_hwnd, &clientRect);
|
|
::MapWindowPoints(m_drawingArea.m_hwnd, NULL, (LPPOINT)&clientRect.left, 2);
|
|
|
|
::TrackPopupMenu(m_hInitMenu, TPM_RIGHTALIGN | TPM_RIGHTBUTTON,
|
|
x + clientRect.left,
|
|
y + clientRect.top,
|
|
0,
|
|
m_hwnd,
|
|
NULL);
|
|
|
|
// reset m_hInitMenu to indicate the popup menu isn't being shown anymore
|
|
m_hInitMenu = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: ProcessEvents
|
|
//
|
|
// Purpose: Process events that have been queued internally
|
|
//
|
|
//
|
|
void WbMainWindow::ProcessEvents(UINT Event, UINT_PTR param1, UINT_PTR param2)
|
|
{
|
|
HWND hwndLastPopup;
|
|
|
|
TRACE_EVENT(("WbMainWindow::ProcessEvents"));
|
|
|
|
//
|
|
// If we are closing, ignore it.
|
|
//
|
|
if (m_uiState == CLOSING)
|
|
{
|
|
TRACE_EVENT(("ProcessEvents: ignored because WB is closing"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If we are busy drawing, we postpone it until later when we can
|
|
// handle it.
|
|
//
|
|
// If the page sorter dialog is up, it gets notified by the appropriate
|
|
// event handler after the fact.
|
|
//
|
|
if (m_drawingArea.IsBusy())
|
|
{
|
|
TRACE_EVENT(("Reposting event %x, param1 %d param2 %d", Event, param1, param2));
|
|
g_pwbCore->WBP_PostEvent(200, Event, param1, param2);
|
|
return;
|
|
}
|
|
|
|
TRACE_EVENT(("Event %x, m_uiState %d", Event, m_uiState));
|
|
|
|
//
|
|
// Process according to the event type.
|
|
//
|
|
switch (Event)
|
|
{
|
|
case CMS_NEW_CALL:
|
|
OnCMSNewCall((BOOL)param1, (DWORD)param2);
|
|
break;
|
|
|
|
case CMS_END_CALL:
|
|
OnCMSEndCall();
|
|
break;
|
|
|
|
case ALS_LOCAL_LOAD:
|
|
switch (m_uiState)
|
|
{
|
|
case IN_CALL:
|
|
case ERROR_STATE:
|
|
// show the main window normal/minimized as necessary
|
|
hwndLastPopup = ::GetLastActivePopup(m_hwnd);
|
|
|
|
if (::IsIconic(m_hwnd))
|
|
::ShowWindow(m_hwnd, SW_RESTORE);
|
|
else
|
|
::ShowWindow(m_hwnd, SW_SHOW);
|
|
|
|
::SetForegroundWindow(hwndLastPopup);
|
|
|
|
if (param2)
|
|
{
|
|
if (m_uiState == IN_CALL)
|
|
LoadCmdLine((LPCSTR)param2);
|
|
::GlobalFree((HGLOBAL)param2);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
TRACE_MSG(("Joining a call so try load later",
|
|
(LPCTSTR)param2));
|
|
g_pwbCore->WBP_PostEvent(400, Event, param1, param2);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ALS_REMOTE_LOAD_RESULT:
|
|
OnALSLoadResult((UINT)param1);
|
|
break;
|
|
|
|
case WBP_EVENT_JOIN_CALL_OK:
|
|
OnWBPJoinCallOK();
|
|
break;
|
|
|
|
case WBP_EVENT_JOIN_CALL_FAILED:
|
|
OnWBPJoinCallFailed();
|
|
break;
|
|
|
|
case WBP_EVENT_NETWORK_LOST:
|
|
OnWBPNetworkLost();
|
|
break;
|
|
|
|
case WBP_EVENT_ERROR:
|
|
OnWBPError();
|
|
break;
|
|
|
|
case WBP_EVENT_PAGE_CLEAR_IND:
|
|
OnWBPPageClearInd((WB_PAGE_HANDLE) param1);
|
|
break;
|
|
|
|
case WBP_EVENT_PAGE_ORDER_UPDATED:
|
|
OnWBPPageOrderUpdated();
|
|
break;
|
|
|
|
case WBP_EVENT_PAGE_DELETE_IND:
|
|
OnWBPPageDeleteInd((WB_PAGE_HANDLE) param1);
|
|
break;
|
|
|
|
case WBP_EVENT_CONTENTS_LOCKED:
|
|
OnWBPContentsLocked((POM_OBJECT) param2);
|
|
break;
|
|
|
|
case WBP_EVENT_PAGE_ORDER_LOCKED:
|
|
OnWBPPageOrderLocked((POM_OBJECT) param2);
|
|
break;
|
|
|
|
case WBP_EVENT_UNLOCKED:
|
|
OnWBPUnlocked((POM_OBJECT) param2);
|
|
break;
|
|
|
|
case WBP_EVENT_LOCK_FAILED:
|
|
OnWBPLockFailed();
|
|
break;
|
|
|
|
case WBP_EVENT_GRAPHIC_ADDED:
|
|
OnWBPGraphicAdded((WB_PAGE_HANDLE) param1, (WB_GRAPHIC_HANDLE) param2);
|
|
break;
|
|
|
|
case WBP_EVENT_GRAPHIC_MOVED:
|
|
OnWBPGraphicMoved((WB_PAGE_HANDLE) param1, (WB_GRAPHIC_HANDLE) param2);
|
|
break;
|
|
|
|
case WBP_EVENT_GRAPHIC_UPDATE_IND:
|
|
OnWBPGraphicUpdateInd((WB_PAGE_HANDLE) param1, (WB_GRAPHIC_HANDLE) param2);
|
|
break;
|
|
|
|
case WBP_EVENT_GRAPHIC_REPLACE_IND:
|
|
OnWBPGraphicReplaceInd((WB_PAGE_HANDLE) param1, (WB_GRAPHIC_HANDLE) param2);
|
|
break;
|
|
|
|
case WBP_EVENT_GRAPHIC_DELETE_IND:
|
|
OnWBPGraphicDeleteInd((WB_PAGE_HANDLE) param1, (WB_GRAPHIC_HANDLE) param2);
|
|
break;
|
|
|
|
case WBP_EVENT_PERSON_JOINED:
|
|
OnWBPUserJoined((POM_OBJECT) param2);
|
|
break;
|
|
|
|
case WBP_EVENT_PERSON_LEFT:
|
|
OnWBPUserLeftInd((POM_OBJECT) param2);
|
|
break;
|
|
|
|
case WBP_EVENT_PERSON_UPDATE:
|
|
OnWBPUserUpdateInd((POM_OBJECT) param2, FALSE);
|
|
break;
|
|
|
|
case WBP_EVENT_PERSON_REPLACE:
|
|
OnWBPUserUpdateInd((POM_OBJECT) param2, TRUE);
|
|
break;
|
|
|
|
case WBP_EVENT_SYNC_POSITION_UPDATED:
|
|
OnWBPSyncPositionUpdated();
|
|
break;
|
|
|
|
case WBP_EVENT_LOAD_COMPLETE:
|
|
OnWBPLoadComplete();
|
|
break;
|
|
|
|
case WBP_EVENT_LOAD_FAILED:
|
|
OnWBPLoadFailed();
|
|
break;
|
|
|
|
default:
|
|
WARNING_OUT(("Unrecognized event %x", Event));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnCMSNewCall
|
|
//
|
|
// Purpose: Handler for CMS_NEW_CALL
|
|
//
|
|
//
|
|
void WbMainWindow::OnCMSNewCall(BOOL fTopProvider, DWORD _m_dwDomain)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnCMSNewCall");
|
|
|
|
//
|
|
// If we created the call
|
|
//
|
|
if (fTopProvider)
|
|
{
|
|
// Join the call, keep existing contents
|
|
if (m_uiState == IN_CALL)
|
|
{
|
|
//
|
|
// Join the call but keep any existing messages.
|
|
//
|
|
::PostMessage(m_hwnd, WM_USER_JOIN_CALL, 1, (LONG) _m_dwDomain);
|
|
}
|
|
else
|
|
{
|
|
m_bJoinCallPending = TRUE;
|
|
m_dwPendingDomain = _m_dwDomain;
|
|
m_bPendingCallKeepContents = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CM_STATUS status;
|
|
|
|
CMS_GetStatus(&status);
|
|
|
|
if (!(status.attendeePermissions & NM_PERMIT_USEOLDWBATALL))
|
|
{
|
|
WARNING_OUT(("OLD WB: not joining call, not permitted"));
|
|
return;
|
|
}
|
|
|
|
if (m_uiState == IN_CALL)
|
|
{
|
|
//
|
|
// Join the call, throwing away our current contents (after
|
|
// prompting the user to save them first).
|
|
//
|
|
::PostMessage(m_hwnd, WM_USER_JOIN_CALL, 0, (LONG) _m_dwDomain);
|
|
}
|
|
else
|
|
{
|
|
m_bJoinCallPending = TRUE;
|
|
m_dwPendingDomain = _m_dwDomain;
|
|
m_bPendingCallKeepContents = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the call status correct.
|
|
//
|
|
m_bCallActive = TRUE;
|
|
UpdateWindowTitle();
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnJoinPendingCall
|
|
//
|
|
// Purpose: Handler for WM_USER_JOIN_PENDING_CALL
|
|
//
|
|
//
|
|
void WbMainWindow::OnJoinPendingCall(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnJoinPendingCall");
|
|
|
|
//
|
|
// If there's still a pending call (haven't received an end-call message
|
|
// between posting the WM_USER_JOIN_PENDING_CALL and getting here).
|
|
//
|
|
if (m_bJoinCallPending)
|
|
{
|
|
//
|
|
// Post a message to join the call
|
|
//
|
|
::PostMessage(m_hwnd, WM_USER_JOIN_CALL,
|
|
m_bPendingCallKeepContents,
|
|
(LONG) m_dwPendingDomain);
|
|
|
|
// cancel call-pending status
|
|
m_bJoinCallPending = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnCMSEndCall
|
|
//
|
|
// Purpose: Handler for CMS_END_CALL
|
|
//
|
|
//
|
|
void WbMainWindow::OnCMSEndCall(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnCMSEndCall");
|
|
|
|
//
|
|
// Flag to cancel any current join-call processing, and destroy any
|
|
// associated dialogs.
|
|
//
|
|
if (m_bPromptingJoinCall)
|
|
{
|
|
m_bPromptingJoinCall = FALSE;
|
|
if (m_hwndQuerySaveDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndQuerySaveDlg, WM_COMMAND,
|
|
MAKELONG(IDCANCEL, BN_CLICKED), 0);
|
|
ASSERT(m_hwndQuerySaveDlg == NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Show that we are no longer in a call
|
|
//
|
|
m_dwDomain = OM_NO_CALL;
|
|
|
|
//
|
|
// If currently in the process of joining a call, then set the domain
|
|
// we're joining to NO_CALL and join the local (singleton) domain.
|
|
// Get the call status correct.
|
|
//
|
|
m_bCallActive = FALSE;
|
|
TRACE_MSG(("m_uiState %d", m_uiState));
|
|
m_dwDomain = OM_NO_CALL;
|
|
|
|
//
|
|
// Show there is no call pending
|
|
//
|
|
m_bJoinCallPending = FALSE;
|
|
|
|
//
|
|
// Update the window title with "not in call"
|
|
//
|
|
UpdateWindowTitle();
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPJoinCallOK
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_JOIN_CALL_OK
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPJoinCallOK(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPJoinCallOK");
|
|
|
|
//
|
|
// Record that we have joined the call, but the drawing area is not yet
|
|
// ready for input (because we have not yet attached to a page).
|
|
//
|
|
m_uiState = JOINED;
|
|
|
|
//
|
|
// Get the local user
|
|
//
|
|
m_pLocalUser = WB_LocalUser();
|
|
if (!m_pLocalUser)
|
|
{
|
|
ERROR_OUT(("Can't join call; can't create local user object, m_pLocalUser!"));
|
|
m_uiState = ERROR_STATE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get the first user in the call
|
|
//
|
|
WbUser* pUser = WB_GetFirstUser();
|
|
|
|
//
|
|
// Loop through all available users
|
|
//
|
|
while (pUser != NULL)
|
|
{
|
|
//
|
|
// Make updates necessary for a user joining
|
|
//
|
|
UserJoined(pUser);
|
|
|
|
//
|
|
// Get the next user
|
|
//
|
|
pUser = WB_GetNextUser(pUser);
|
|
}
|
|
|
|
//
|
|
// If the registration dialog is up - cancel it
|
|
//
|
|
m_uiState = IN_CALL; // have to set m_uiState before zapping
|
|
// m_hwndWaitForEventDlg or it will
|
|
// think the call failed now (the
|
|
// delay has been removed from
|
|
// EndDialogDelayed() (bug 3881)
|
|
}
|
|
|
|
if (m_hwndWaitForEventDlg != NULL)
|
|
{
|
|
TRACE_MSG(("Joined call OK - end dialog"));
|
|
::SendMessage(m_hwndWaitForEventDlg, WM_COMMAND, MAKELONG(IDOK, BN_CLICKED), 0);
|
|
ASSERT(m_hwndWaitForEventDlg == NULL);
|
|
}
|
|
|
|
if (!m_pLocalUser)
|
|
{
|
|
//
|
|
// BAIL out, we can't join the call
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Now complete the join call processing
|
|
//
|
|
TRACE_MSG(("Successfully joined the call"));
|
|
m_dwDomain = m_dwJoinDomain;
|
|
|
|
//
|
|
// If we have never attached to a page before (ie at start up), attach
|
|
// to the first available page in the drawing area. If we are joining
|
|
// a call then we reattach to the current page
|
|
//
|
|
if (m_hCurrentPage == WB_PAGE_HANDLE_NULL)
|
|
{
|
|
TRACE_MSG(("Attach to first page"));
|
|
g_pwbCore->WBP_PageHandle(WB_PAGE_HANDLE_NULL, PAGE_FIRST, &m_hCurrentPage);
|
|
}
|
|
else
|
|
{
|
|
TRACE_DEBUG(("Just joined new call, reattach to the current page."));
|
|
}
|
|
m_drawingArea.Attach(m_hCurrentPage);
|
|
|
|
// Display the initial status
|
|
UpdateStatus();
|
|
|
|
|
|
::SetTimer(m_hwnd, TIMERID_PROGRESS_METER, MAIN_PROGRESS_TIMER, NULL);
|
|
m_bTimerActive = TRUE;
|
|
|
|
//
|
|
// Unlock the drawing area, allowing user updates (unless its already
|
|
// locked by someone else)
|
|
//
|
|
if (!WB_ContentsLocked())
|
|
{
|
|
UnlockDrawingArea();
|
|
}
|
|
|
|
// Set the substate (also updates page buttons)
|
|
SetSubstate(SUBSTATE_IDLE);
|
|
|
|
//
|
|
// If we aren't synced, then sync now.
|
|
// Set the window title to show we're no longer registering/joining a
|
|
// call
|
|
//
|
|
Sync();
|
|
UpdateWindowTitle();
|
|
|
|
//
|
|
// If we were joining the local domain, and a join call message arrived
|
|
// in the meantime, then join that call now.
|
|
//
|
|
if ((m_bJoinCallPending) && (m_dwJoinDomain == OM_NO_CALL))
|
|
{
|
|
::PostMessage(m_hwnd, WM_USER_JOIN_PENDING_CALL, 0, 0L);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPJoinCallFailed
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_JOIN_CALL_FAILED
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPJoinCallFailed(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPJoinCallFailed");
|
|
|
|
//
|
|
// If we have just failed to join a new call (not a single domain) it
|
|
// may be because the call ended before we had time to join it
|
|
// completely - try joining the single domain.
|
|
//
|
|
if ((m_uiState == STARTING) && (m_dwJoinDomain != OM_NO_CALL))
|
|
{
|
|
WARNING_OUT(("Failed to join call on startup, try local domain"));
|
|
m_dwJoinDomain = OM_NO_CALL;
|
|
m_bCallActive = FALSE;
|
|
JoinCall(FALSE);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Tell the registration dialog to finish
|
|
//
|
|
if (m_hwndWaitForEventDlg != NULL)
|
|
{
|
|
WARNING_OUT(("Failed to join call - end dialog"));
|
|
::SendMessage(m_hwndWaitForEventDlg, WM_COMMAND, MAKELONG(IDOK, BN_CLICKED), 0);
|
|
ASSERT(m_hwndWaitForEventDlg == NULL);
|
|
}
|
|
|
|
m_uiState = ERROR_STATE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPNetworkLost
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_NETWORK_LOST
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPNetworkLost(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPNetworkLost");
|
|
|
|
//
|
|
// We have lost contact with the other people in the call.
|
|
// Treat as if we got an end call (we should get an end call too, but
|
|
// other intervening events might occur (such as trying to join a
|
|
// call).
|
|
//
|
|
OnCMSEndCall();
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPError
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_ERROR
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPError(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPError");
|
|
|
|
// Inform the user of the error.
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, 0);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPPageClearInd
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_PAGE_CLEAR_IND
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPPageClearInd(WB_PAGE_HANDLE hPage)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPPageClearInd");
|
|
|
|
// Confirm the clearing of the page. This is OK even if the page being
|
|
// cleared is the current page because we already know that the drawing
|
|
// area is not busy (otherwise we would not be here).
|
|
|
|
// If there's an object on the page which has been copied to the
|
|
// clipboard with delayed rendering, then save it
|
|
if (CLP_LastCopiedPage() == hPage)
|
|
{
|
|
CLP_SaveDelayedGraphic();
|
|
}
|
|
|
|
// If it is the current page being cleared
|
|
if (m_hCurrentPage == hPage)
|
|
{
|
|
m_drawingArea.PageCleared();
|
|
}
|
|
|
|
// If there is a last deleted graphic
|
|
// and it belongs to the page being cleared.
|
|
if ((m_LastDeletedGraphic.GotTrash()) &&
|
|
(m_LastDeletedGraphic.Page() == hPage))
|
|
{
|
|
// Free the last deleted graphic
|
|
m_LastDeletedGraphic.BurnTrash();
|
|
}
|
|
|
|
g_pwbCore->WBP_PageClearConfirm(hPage);
|
|
|
|
//
|
|
// Notify the page sorter AFTER the page has been cleared
|
|
//
|
|
if (m_hwndPageSortDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndPageSortDlg, WM_PS_PAGECLEARIND, (WPARAM)hPage, 0);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPPageOrderUpdated
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_PAGE_ORDER_UPDATED
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPPageOrderUpdated(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPPageOrderUpdated");
|
|
|
|
m_drawingArea.CancelDrawingMode();
|
|
|
|
// The page order has changed, we just need to update the number of the
|
|
// current page in the pages group.
|
|
UpdateStatus();
|
|
|
|
//
|
|
// Notify the page sorter AFTER the page order has been updated
|
|
//
|
|
if (m_hwndPageSortDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndPageSortDlg, WM_PS_PAGEORDERUPD, 0, 0);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPPageDeleteInd
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_PAGE_DELETE_IND
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPPageDeleteInd(WB_PAGE_HANDLE hPage)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPPageDeleteInd");
|
|
|
|
//
|
|
// Notify the page sorter BEFORE the page is deleted
|
|
//
|
|
if (m_hwndPageSortDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndPageSortDlg, WM_PS_PAGEDELIND, (WPARAM)hPage, 0);
|
|
}
|
|
|
|
m_drawingArea.CancelDrawingMode();
|
|
|
|
// Remove it from the page-position map
|
|
PAGE_POSITION *mapob;
|
|
POSITION savedPos;
|
|
POSITION position = m_pageToPosition.GetHeadPosition();
|
|
BOOL bFound = FALSE;
|
|
while (position && !bFound)
|
|
{
|
|
savedPos = position;
|
|
mapob = (PAGE_POSITION *)m_pageToPosition.GetNext(position);
|
|
if ( mapob->hPage == hPage)
|
|
{
|
|
bFound = TRUE;
|
|
}
|
|
}
|
|
|
|
if(bFound)
|
|
{
|
|
m_pageToPosition.RemoveAt(savedPos);
|
|
delete mapob;
|
|
}
|
|
|
|
// A page has been deleted. If it is the current page we must attach
|
|
// a different page to the drawing area. In any case we should confirm
|
|
// the delete.
|
|
|
|
// If there's an object on the page which has been copied to the
|
|
// clipboard with delayed rendering, then save it
|
|
if (CLP_LastCopiedPage() == hPage)
|
|
{
|
|
CLP_SaveDelayedGraphic();
|
|
}
|
|
|
|
if (hPage == m_hCurrentPage)
|
|
{
|
|
// Check whether we are deleting the last page
|
|
WB_PAGE_HANDLE hNewPage;
|
|
|
|
g_pwbCore->WBP_PageHandle(WB_PAGE_HANDLE_NULL, PAGE_LAST, &hNewPage);
|
|
if (hNewPage == hPage)
|
|
{
|
|
// We are deleting the last page, so go back one
|
|
hNewPage = PG_GetPreviousPage(hPage);
|
|
}
|
|
else
|
|
{
|
|
// We are not deleting the last page, so go forward one
|
|
hNewPage = PG_GetNextPage(hPage);
|
|
}
|
|
|
|
// Check that we got a different page to the one being deleted
|
|
ASSERT(hNewPage != hPage);
|
|
|
|
// Lock the drawing area - this ensures we are no longer editing
|
|
// any text etc.
|
|
LockDrawingArea();
|
|
|
|
// Move to the new page
|
|
GotoPage(hNewPage);
|
|
|
|
// Unlock the drawing area (unless we're doing a new, in which case we
|
|
// leave it locked - it will become unlocked when the new completes)
|
|
if ( (!WB_ContentsLocked())
|
|
&& (m_uiState == IN_CALL)
|
|
&& (m_uiSubState != SUBSTATE_NEW_IN_PROGRESS))
|
|
{
|
|
UnlockDrawingArea();
|
|
}
|
|
}
|
|
|
|
// If there is a last deleted graphic
|
|
if ((m_LastDeletedGraphic.GotTrash()) &&
|
|
(m_LastDeletedGraphic.Page() == hPage))
|
|
{
|
|
// Free the last deleted graphic
|
|
m_LastDeletedGraphic.BurnTrash();
|
|
}
|
|
|
|
// if the remote pointer is on the deleted page then turn it off
|
|
ASSERT(m_pLocalUser);
|
|
DCWbGraphicPointer* pPointer = m_pLocalUser->GetPointer();
|
|
if ( (pPointer->IsActive())
|
|
&& (pPointer->Page() == hPage))
|
|
{
|
|
OnRemotePointer();
|
|
}
|
|
|
|
// Let the core delete the page
|
|
g_pwbCore->WBP_PageDeleteConfirm(hPage);
|
|
|
|
// if this is last page to be deleted, then the file/new is complete
|
|
if ((m_uiSubState == SUBSTATE_NEW_IN_PROGRESS)
|
|
&& (g_pwbCore->WBP_ContentsCountPages() == 1))
|
|
{
|
|
SetSubstate(SUBSTATE_IDLE);
|
|
|
|
ReleasePageOrderLock();
|
|
|
|
if (!WB_ContentsLocked())
|
|
{
|
|
UnlockDrawingArea();
|
|
}
|
|
}
|
|
|
|
// Update the status (there is a new number of pages)
|
|
UpdateStatus();
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPContentsLocked
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_CONTENTS_LOCKED
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPContentsLocked(POM_OBJECT hUser)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPContentsLocked");
|
|
|
|
//
|
|
// Notify page sorter dialog that the lock status has changed
|
|
//
|
|
if (m_hwndPageSortDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndPageSortDlg, WM_PS_LOCKCHANGE, 0, 0);
|
|
}
|
|
|
|
if (m_uiState != IN_CALL)
|
|
{
|
|
TRACE_MSG(("Lock indication received out of call - ignored"));
|
|
}
|
|
else
|
|
{
|
|
ASSERT(m_pLocalUser);
|
|
|
|
if (m_pLocalUser->Handle() == hUser)
|
|
{
|
|
// We have acquired the lock
|
|
|
|
// Set the locked check mark
|
|
CheckMenuItem(IDM_LOCK);
|
|
|
|
// Tell the tool bar of the new selection
|
|
m_TB.PushDown(IDM_LOCK);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// A remote user has acquired the lock:
|
|
// If we're not synced, then sync now.
|
|
//
|
|
Sync();
|
|
|
|
// Tell the drawing area it is now locked
|
|
LockDrawingArea();
|
|
|
|
// ensure the page button enable/disable state is correct
|
|
UpdatePageButtons();
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the lock dialog is up - cancel it.
|
|
//
|
|
if (m_hwndWaitForLockDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndWaitForLockDlg, WM_COMMAND, MAKELONG(IDOK, BN_CLICKED), 0);
|
|
ASSERT(m_hwndWaitForLockDlg == NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPPageOrderLocked
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_PAGE_ORDER_LOCKED
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPPageOrderLocked(POM_OBJECT)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPPageOrderLocked");
|
|
|
|
// If the lock dialog is up - cancel it
|
|
if (m_hwndWaitForLockDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndWaitForLockDlg, WM_COMMAND, MAKELONG(IDOK, BN_CLICKED), 0);
|
|
ASSERT(m_hwndWaitForLockDlg == NULL);
|
|
}
|
|
|
|
// Update the page sorter
|
|
if (m_hwndPageSortDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndPageSortDlg, WM_PS_LOCKCHANGE, 0, 0);
|
|
}
|
|
|
|
if (!WB_GotLock())
|
|
{
|
|
EnableToolbar( FALSE );
|
|
UpdatePageButtons();
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPUnlocked
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_UNLOCKED
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPUnlocked(POM_OBJECT hUser)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPUnlocked");
|
|
|
|
// Update the page sorter if it's around
|
|
if (m_hwndPageSortDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndPageSortDlg, WM_PS_LOCKCHANGE, 0, 0);
|
|
}
|
|
|
|
// Uncheck the lock menu item
|
|
UncheckMenuItem(IDM_LOCK);
|
|
|
|
// Tell the tool bar of the change
|
|
m_TB.PopUp(IDM_LOCK);
|
|
|
|
// If a remote user is releasing the lock, and we're in a state where
|
|
// it's safe to unlock the drawing area...
|
|
if ((m_pLocalUser != NULL) &&
|
|
(m_pLocalUser->Handle() != hUser) &&
|
|
(m_uiState == IN_CALL))
|
|
{
|
|
// Tell the drawing area it is no longer locked
|
|
UnlockDrawingArea();
|
|
}
|
|
|
|
// ensure the page button enable/disable state is correct
|
|
UpdatePageButtons();
|
|
m_bUnlockStateSettled = TRUE; //Allow page operations now
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPLockFailed
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_LOCK_FAILED
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPLockFailed(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPLockFailed");
|
|
|
|
// If the lock dialog is up - kill it
|
|
if (m_hwndWaitForLockDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndWaitForLockDlg, WM_COMMAND, MAKELONG(IDOK, BN_CLICKED), 0);
|
|
ASSERT(m_hwndWaitForLockDlg == NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPGraphicAdded
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_GRAPHIC_ADDED
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPGraphicAdded
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic
|
|
)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPGraphicAdded");
|
|
|
|
// We only need to take action if the page to which the graphic has
|
|
// been added is the current page.
|
|
if (hPage == m_hCurrentPage && (!(hGraphic->flags & DELETED)))
|
|
{
|
|
// Retrieve the graphic that has been added
|
|
DCWbGraphic* pGraphic = DCWbGraphic::ConstructGraphic(hPage, hGraphic);
|
|
|
|
// Tell the drawing area of the new graphic
|
|
m_drawingArea.GraphicAdded(pGraphic);
|
|
|
|
// Free the graphic
|
|
delete pGraphic;
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPGraphicMoved
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_GRAPHIC_MOVED
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPGraphicMoved
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic
|
|
)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPGraphicMoved");
|
|
|
|
// We only need to take action if the graphic belongs to the current page
|
|
if (hPage == m_hCurrentPage && (!(hGraphic->flags & DELETED)))
|
|
{
|
|
// Retrieve the graphic that has been moved
|
|
DCWbGraphic* pGraphic = DCWbGraphic::ConstructGraphic(hPage, hGraphic);
|
|
|
|
if( NULL != pGraphic )
|
|
{
|
|
// Tell the drawing area of the new graphic
|
|
m_drawingArea.GraphicUpdated(pGraphic, TRUE, FALSE);
|
|
|
|
// set paint to draw only objects above this object inclusive
|
|
if (pGraphic->IsGraphicTool() == enumGraphicText)
|
|
{
|
|
m_drawingArea.SetStartPaintGraphic( NULL );
|
|
// this optimization screws up text
|
|
// so short it out if this is text
|
|
// (text draws transparently and background
|
|
// isn't repainted properly if this is active)
|
|
}
|
|
else
|
|
{
|
|
m_drawingArea.SetStartPaintGraphic( hGraphic );
|
|
// not text so optimize by drawing only this
|
|
// object and everthing above it
|
|
}
|
|
|
|
// Free the graphic
|
|
delete pGraphic;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPGraphicUpdateInd
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_GRAPHIC_UPDATE_IND
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPGraphicUpdateInd
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic
|
|
)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPGraphicUpdateInd");
|
|
|
|
if(hGraphic->flags & DELETED)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PWB_GRAPHIC pOldHeader;
|
|
PWB_GRAPHIC pOldHeaderCopy;
|
|
DCWbGraphic* pOldGraphic;
|
|
|
|
PWB_GRAPHIC pNewHeader;
|
|
DCWbGraphic* pNewGraphic;
|
|
|
|
if (hPage != m_hCurrentPage)
|
|
{
|
|
// nothing visual has changed, confirm and we're done
|
|
g_pwbCore->WBP_GraphicUpdateConfirm(hPage, hGraphic);
|
|
return;
|
|
}
|
|
|
|
|
|
// Retrieve the original graphic and make a copy
|
|
// Get the page of the update
|
|
pOldHeader = PG_GetData(hPage, hGraphic);
|
|
pOldHeaderCopy = (PWB_GRAPHIC) new BYTE[ pOldHeader->length ];
|
|
|
|
if( pOldHeaderCopy == NULL )
|
|
{
|
|
ERROR_OUT( ("Can't copy pOldHeader, can't update drawing") );
|
|
|
|
g_pwbCore->WBP_GraphicRelease(hPage, hGraphic, pOldHeader );
|
|
g_pwbCore->WBP_GraphicUpdateConfirm(hPage, hGraphic);
|
|
return;
|
|
}
|
|
|
|
CopyMemory( (PVOID)pOldHeaderCopy, (CONST VOID *)pOldHeader, pOldHeader->length );
|
|
|
|
// confirm and get the new one
|
|
g_pwbCore->WBP_GraphicRelease(hPage, hGraphic, pOldHeader );
|
|
g_pwbCore->WBP_GraphicUpdateConfirm(hPage, hGraphic);
|
|
|
|
pNewHeader = PG_GetData(hPage, hGraphic);
|
|
|
|
// This update might affect painting. See if old and new are visually different
|
|
if( HasGraphicChanged( pOldHeaderCopy, (const PWB_GRAPHIC)pNewHeader ) )
|
|
{
|
|
// they're different, invalidate/erase old graphic's bounding rect
|
|
pOldGraphic = DCWbGraphic::ConstructGraphic(hPage, hGraphic, pOldHeaderCopy );
|
|
m_drawingArea.GraphicUpdated( pOldGraphic, FALSE, TRUE );
|
|
|
|
// draw new graphic (don't need to erase)
|
|
pNewGraphic = DCWbGraphic::ConstructGraphic(hPage, hGraphic, pNewHeader );
|
|
g_pwbCore->WBP_GraphicRelease(hPage, hGraphic, pNewHeader );
|
|
m_drawingArea.GraphicUpdated( pNewGraphic, TRUE, FALSE );
|
|
|
|
// If the graphic is selected, ensure the attributes bar is up to date
|
|
if (m_drawingArea.GraphicSelected())
|
|
{
|
|
DCWbGraphic* pSelectedGraphic = m_drawingArea.GetSelection();
|
|
if ((pSelectedGraphic != NULL) &&
|
|
(pSelectedGraphic->Handle() == hGraphic))
|
|
{
|
|
m_pCurrentTool->SelectGraphic(pNewGraphic);
|
|
OnUpdateAttributes();
|
|
}
|
|
}
|
|
|
|
delete pOldGraphic;
|
|
delete pNewGraphic;
|
|
}
|
|
else
|
|
{
|
|
g_pwbCore->WBP_GraphicRelease(hPage, hGraphic, pNewHeader);
|
|
}
|
|
|
|
delete [] pOldHeaderCopy;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPGraphicReplaceInd
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_GRAPHIC_REPLACE_IND
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPGraphicReplaceInd
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic
|
|
)
|
|
{
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPGraphicReplaceInd");
|
|
|
|
if(hGraphic->flags & DELETED)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Retrieve the graphic that has been replaced
|
|
DCWbGraphic* pGraphic = DCWbGraphic::ConstructGraphic(hPage, hGraphic);
|
|
|
|
if (pGraphic->IsGraphicTool() == enumGraphicFreeHand)
|
|
{
|
|
// Confirm the replace - the graphic reads its new details
|
|
pGraphic->ReplaceConfirm();
|
|
|
|
// Only redraw the graphic if it is on the current page
|
|
if (hPage == m_hCurrentPage)
|
|
{
|
|
// Redraw the graphic
|
|
m_drawingArea.GraphicFreehandUpdated(pGraphic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We make two updates to the drawing area - one with the graphic in its
|
|
// current state and one after the update is confirmed. The first one
|
|
// invalidates the rectangle that the graphic now occupies. The second one
|
|
// invalidates the new rectangle. This ensures that the graphic is
|
|
// correctly redrawn.
|
|
|
|
// If the graphic is on the current page...
|
|
if (hPage == m_hCurrentPage)
|
|
{
|
|
// Update the drawing area for the old version of the graphic
|
|
m_drawingArea.GraphicUpdated(pGraphic, FALSE);
|
|
}
|
|
|
|
// Confirm the replace - the graphic reads its new details
|
|
pGraphic->ReplaceConfirm();
|
|
|
|
// If the graphic is on the current page...
|
|
if (hPage == m_hCurrentPage)
|
|
{
|
|
// Update the drawing area for the new version of the graphic
|
|
m_drawingArea.GraphicUpdated(pGraphic, TRUE);
|
|
}
|
|
}
|
|
|
|
// If the graphic is selected, ensure the attributes bar is up to date
|
|
if (m_drawingArea.GraphicSelected())
|
|
{
|
|
DCWbGraphic* pSelectedGraphic = m_drawingArea.GetSelection();
|
|
if ((pSelectedGraphic != NULL) &&
|
|
(pSelectedGraphic->Handle() == hGraphic))
|
|
{
|
|
m_pCurrentTool->SelectGraphic(pGraphic);
|
|
OnUpdateAttributes();
|
|
}
|
|
}
|
|
|
|
// Free the graphic
|
|
delete pGraphic;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPGraphicDeleteInd
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_GRAPHIC_DELETE_IND
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPGraphicDeleteInd
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic
|
|
)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPGraphicDeleteInd");
|
|
|
|
// if the graphic was copied into the clipboard and was delayed,
|
|
// then save it in case we are asked to render it later
|
|
if ( (CLP_LastCopiedPage() == hPage)
|
|
&& (CLP_LastCopiedGraphic() == hGraphic))
|
|
{
|
|
CLP_SaveDelayedGraphic();
|
|
}
|
|
|
|
// Retrieve the graphic that is to be deleted
|
|
DCWbGraphic* pGraphic = DCWbGraphic::ConstructGraphic(hPage, hGraphic);
|
|
|
|
// If the graphic is on the current page...
|
|
if (hPage == m_hCurrentPage)
|
|
{
|
|
// Update the drawing area
|
|
m_drawingArea.GraphicDeleted(pGraphic);
|
|
}
|
|
|
|
// Confirm the delete
|
|
g_pwbCore->WBP_GraphicDeleteConfirm(hPage, hGraphic);
|
|
|
|
// Free the graphic
|
|
delete pGraphic;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: UserJoined
|
|
//
|
|
// Purpose: Make updates necessary for a new user joining the call
|
|
//
|
|
//
|
|
void WbMainWindow::UserJoined(WbUser* pUser)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::UserJoined");
|
|
|
|
// Get the user's remote pointer
|
|
ASSERT(pUser);
|
|
DCWbGraphicPointer* pPointer = pUser->GetPointer();
|
|
|
|
// If the pointer is active and on the current page...
|
|
ASSERT(pPointer);
|
|
|
|
if ( (pPointer->IsActive())
|
|
&& (pPointer->Page() == m_hCurrentPage))
|
|
{
|
|
// Update the drawing area
|
|
m_drawingArea.PointerUpdated(pPointer);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPUserJoined
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_PERSON_JOINED
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPUserJoined(POM_OBJECT hUser)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPUserJoined");
|
|
|
|
// Create a user object from the handle
|
|
WbUser* pUser = WB_GetUser(hUser);
|
|
if (!pUser)
|
|
{
|
|
WARNING_OUT(("Can't handle OnWBPUserJoined; can't create user object for 0x%08x", hUser));
|
|
}
|
|
else
|
|
{
|
|
// Make the necessary updates
|
|
UserJoined(pUser);
|
|
}
|
|
|
|
// Update the title bar to reflect the number of users. Do this here,
|
|
// rather than in UserJoined because we go through this function for
|
|
// remote users only, but through UserJoined for the local user too.
|
|
|
|
UpdateWindowTitle();
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPUserLeftInd
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_PERSON_LEFT
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPUserLeftInd(POM_OBJECT hUser)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPUserLeft");
|
|
|
|
// Create a user object from the handle
|
|
WbUser* pUser = WB_GetUser(hUser);
|
|
|
|
if (!pUser)
|
|
{
|
|
WARNING_OUT(("Can't handle OnWBPUserLeftInd; can't get user object for 0x%08x", hUser));
|
|
}
|
|
else
|
|
{
|
|
// Get the user's remote pointer
|
|
DCWbGraphicPointer* pPointer = pUser->GetPointer();
|
|
ASSERT(pPointer);
|
|
|
|
// If the pointer is on the current page...
|
|
if (pPointer->Page() == m_hCurrentPage)
|
|
{
|
|
// Update the drawing area
|
|
m_drawingArea.PointerRemoved(pPointer);
|
|
}
|
|
}
|
|
|
|
// Confirm the update.
|
|
g_pwbCore->WBP_PersonLeftConfirm(hUser);
|
|
|
|
//
|
|
// Get this dude out of our list
|
|
//
|
|
if (pUser != NULL)
|
|
{
|
|
ASSERT(g_pUsers);
|
|
|
|
POSITION position = g_pUsers->GetHeadPosition();
|
|
|
|
WbUser * pRemovedUser;
|
|
|
|
while (position)
|
|
{
|
|
POSITION savedPosition = position;
|
|
pRemovedUser = (WbUser*)g_pUsers->GetNext(position);
|
|
if (pRemovedUser == pUser)
|
|
{
|
|
g_pUsers->RemoveAt(savedPosition);
|
|
position = NULL;
|
|
}
|
|
}
|
|
|
|
delete pUser;
|
|
}
|
|
|
|
// Update the title bar to reflect the number of users
|
|
UpdateWindowTitle();
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPUserUpdateInd
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_PERSON_UPDATE
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPUserUpdateInd(POM_OBJECT hUser, BOOL bReplace)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPUserUpdateInd");
|
|
BOOL bActiveOld, bActiveNew;
|
|
WB_PAGE_HANDLE hPointerPageOld, hPointerPageNew;
|
|
POINT pointOld, pointNew;
|
|
WB_PAGE_HANDLE hUserPageOld, hUserPageNew;
|
|
BOOL syncOld, syncNew;
|
|
DCWbGraphicPointer * pPointer = NULL;
|
|
|
|
// Get the user object associated with the handle, and the remote pointer
|
|
WbUser* pUser = WB_GetUser(hUser);
|
|
|
|
if (!pUser)
|
|
{
|
|
WARNING_OUT(("Can't handle OnWBPUserUpdatedInd; can't get user object for 0x%08x", hUser));
|
|
}
|
|
else
|
|
{
|
|
pPointer = pUser->GetPointer();
|
|
ASSERT(pPointer);
|
|
|
|
//
|
|
// Save the interesting bits of the user's state before the change.
|
|
//
|
|
bActiveOld = pPointer->IsActive();
|
|
hPointerPageOld = pPointer->Page();
|
|
pPointer->GetPosition(&pointOld);
|
|
hUserPageOld = pUser->Page();
|
|
syncOld = pUser->IsSynced();
|
|
}
|
|
|
|
//
|
|
// Confirm the change
|
|
//
|
|
if (bReplace)
|
|
{
|
|
g_pwbCore->WBP_PersonReplaceConfirm(hUser);
|
|
}
|
|
else
|
|
{
|
|
g_pwbCore->WBP_PersonUpdateConfirm(hUser);
|
|
}
|
|
|
|
if (pUser != NULL)
|
|
{
|
|
pUser->Refresh();
|
|
|
|
//
|
|
// We do nothing for the local user; since we made the updates locally,
|
|
// we should have already accounted for them.
|
|
//
|
|
if (pUser == m_pLocalUser)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the state after the change.
|
|
//
|
|
pPointer = pUser->GetPointer();
|
|
ASSERT(pPointer);
|
|
|
|
bActiveNew = pPointer->IsActive();
|
|
hPointerPageNew = pPointer->Page();
|
|
pPointer->GetPosition(&pointNew);
|
|
hUserPageNew = pUser->Page();
|
|
syncNew = pUser->IsSynced();
|
|
|
|
|
|
// Check whether anything in the pointer has changed
|
|
if ( (bActiveNew != bActiveOld)
|
|
|| (hPointerPageNew != hPointerPageOld)
|
|
|| (!EqualPoint(pointNew, pointOld)))
|
|
{
|
|
// Check that at least one of the pages is the current page
|
|
if ( (hPointerPageNew == m_hCurrentPage)
|
|
|| (hPointerPageOld == m_hCurrentPage))
|
|
{
|
|
m_drawingArea.PointerUpdated(pPointer);
|
|
}
|
|
}
|
|
|
|
if (syncOld != syncNew)
|
|
{
|
|
// ensure the page button enable/disable state is correct
|
|
UpdatePageButtons();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnWBPSyncPositionUpdated
|
|
//
|
|
// Purpose: Handler for WBP_EVENT_SYNC_POSITION_UPDATED
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPSyncPositionUpdated(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPSyncPositionUpdated");
|
|
|
|
//
|
|
// Dont do anythig if we don't have a local user.
|
|
//
|
|
if (m_pLocalUser == NULL)
|
|
{
|
|
ERROR_OUT(("Got a WBP_EVENT_SYNC_POSITION_UPDATED event and pLocaUser is NULL "));
|
|
return;
|
|
}
|
|
|
|
// If the local user is synced, change the current page/position
|
|
if (m_pLocalUser->IsSynced())
|
|
{
|
|
GotoSyncPosition();
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnSize
|
|
//
|
|
// Purpose: The window has been resized.
|
|
//
|
|
//
|
|
void WbMainWindow::OnSize(UINT nType, int cx, int cy )
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnSize");
|
|
|
|
|
|
// Only process this message if the window is not minimized
|
|
if (nType != SIZE_MINIMIZED)
|
|
{
|
|
// Hide the statusbar to avoid drawing problems
|
|
if (m_bStatusBarOn)
|
|
{
|
|
::ShowWindow(m_hwndSB, SW_HIDE);
|
|
}
|
|
|
|
// Resize the subpanes of the window
|
|
ResizePanes();
|
|
|
|
// Show it again
|
|
if (m_bStatusBarOn)
|
|
{
|
|
::ShowWindow(m_hwndSB, SW_SHOW);
|
|
}
|
|
}
|
|
|
|
// The user's view has changed
|
|
PositionUpdated();
|
|
|
|
// If the status has changed, set the option
|
|
if (m_uiWindowSize != nType)
|
|
{
|
|
m_uiWindowSize = nType;
|
|
|
|
// Write the new option values to file
|
|
OPT_SetBooleanOption(OPT_MAIN_MAXIMIZED,
|
|
(m_uiWindowSize == SIZE_MAXIMIZED));
|
|
OPT_SetBooleanOption(OPT_MAIN_MINIMIZED,
|
|
(m_uiWindowSize == SIZE_MINIMIZED));
|
|
}
|
|
|
|
// If this is setting the window to a new normal size,
|
|
// save the new position.
|
|
if (nType == SIZE_RESTORED)
|
|
{
|
|
SaveWindowPosition();
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: SaveWindowPosition
|
|
//
|
|
// Purpose: Save the current window position to the options file.
|
|
//
|
|
//
|
|
void WbMainWindow::SaveWindowPosition(void)
|
|
{
|
|
RECT rectWindow;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::SaveWindowPosition");
|
|
|
|
// Get the new window rectangle
|
|
::GetWindowRect(m_hwnd, &rectWindow);
|
|
|
|
// Write the new option values to file
|
|
OPT_SetWindowRectOption(OPT_MAIN_MAINWINDOWRECT, &rectWindow);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnMove
|
|
//
|
|
// Purpose: The window has been moved.
|
|
//
|
|
//
|
|
void WbMainWindow::OnMove(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnMove");
|
|
|
|
// If we are not maximized
|
|
if (!::IsZoomed(m_hwnd) && !::IsIconic(m_hwnd))
|
|
{
|
|
// Save the new position of the window
|
|
SaveWindowPosition();
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: ResizePanes
|
|
//
|
|
// Purpose: Resize the subpanes of the main window.
|
|
//
|
|
//
|
|
void WbMainWindow::ResizePanes(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::ResizePanes");
|
|
|
|
//
|
|
//
|
|
// The client area is organized as follows:
|
|
//
|
|
// -------------------------------------
|
|
// | | |
|
|
// | T | |
|
|
// | o | Drawing Area |
|
|
// | o | |
|
|
// | l | |
|
|
// | s | |
|
|
// |---| |
|
|
// | W | |
|
|
// | i | |
|
|
// | d | |
|
|
// | t | |
|
|
// | h | |
|
|
// | s | |
|
|
// |-----------------------------------|
|
|
// | Attributes (colors) | Pages |
|
|
// |-----------------------------------|
|
|
// | Status |
|
|
// -------------------------------------
|
|
//
|
|
//
|
|
|
|
RECT clientRect;
|
|
RECT rectStatusBar;
|
|
RECT rectToolBar;
|
|
RECT rectWG;
|
|
RECT rectAG;
|
|
RECT rectDraw;
|
|
SIZE size;
|
|
SIZE sizeAG;
|
|
|
|
// Get the client rectangle
|
|
::GetClientRect(m_hwnd, &clientRect);
|
|
rectStatusBar = clientRect;
|
|
|
|
// Resize the help bar and progress meter
|
|
if (m_bStatusBarOn)
|
|
{
|
|
rectStatusBar.top = rectStatusBar.bottom - STATUSBAR_HEIGHT;
|
|
|
|
::MoveWindow(m_hwndSB, rectStatusBar.left, rectStatusBar.top,
|
|
rectStatusBar.right - rectStatusBar.left,
|
|
rectStatusBar.bottom - rectStatusBar.top, TRUE);
|
|
}
|
|
else
|
|
{
|
|
// Status bar is off - set it's height to zero
|
|
rectStatusBar.top = rectStatusBar.bottom;
|
|
}
|
|
|
|
// Resize the tool and width windows
|
|
m_TB.GetNaturalSize(&size);
|
|
rectToolBar.left = 0;
|
|
rectToolBar.right = rectToolBar.left + size.cx;
|
|
rectToolBar.top = 0;
|
|
rectToolBar.bottom = rectToolBar.top + size.cy;
|
|
|
|
m_WG.GetNaturalSize(&size);
|
|
rectWG.left = rectToolBar.left;
|
|
rectWG.top = rectToolBar.bottom;
|
|
rectWG.bottom = rectWG.top + size.cy;
|
|
|
|
if (!m_bToolBarOn)
|
|
{
|
|
// Toolbar is either off or floating - set its width to zero
|
|
rectToolBar.right = rectToolBar.left;
|
|
}
|
|
rectWG.right = rectToolBar.right;
|
|
|
|
// Position attribute group
|
|
m_AG.GetNaturalSize(&sizeAG);
|
|
|
|
::MoveWindow(m_AG.m_hwnd, rectToolBar.left, rectStatusBar.top - sizeAG.cy,
|
|
clientRect.right - rectToolBar.left, sizeAG.cy, TRUE);
|
|
|
|
// finish fiddling with tools and widths bars
|
|
if (m_bToolBarOn)
|
|
{
|
|
//
|
|
// We make the toolbar, which includes the width bar, extend all
|
|
// down the left side.
|
|
//
|
|
rectToolBar.bottom = rectStatusBar.top - sizeAG.cy;
|
|
rectWG.left += TOOLBAR_MARGINX;
|
|
rectWG.right -= 2*TOOLBAR_MARGINX;
|
|
|
|
::MoveWindow(m_TB.m_hwnd, rectToolBar.left,
|
|
rectToolBar.top, rectToolBar.right - rectToolBar.left,
|
|
rectToolBar.bottom - rectToolBar.top, TRUE);
|
|
|
|
::MoveWindow(m_WG.m_hwnd, rectWG.left, rectWG.top,
|
|
rectWG.right - rectWG.left, rectWG.bottom - rectWG.top, TRUE);
|
|
|
|
::BringWindowToTop(m_WG.m_hwnd);
|
|
}
|
|
|
|
// Resize the drawing pane
|
|
rectDraw = clientRect;
|
|
rectDraw.bottom = rectStatusBar.top - sizeAG.cy;
|
|
rectDraw.left = rectToolBar.right;
|
|
::MoveWindow(m_drawingArea.m_hwnd, rectDraw.left, rectDraw.top,
|
|
rectDraw.right - rectDraw.left, rectDraw.bottom - rectDraw.top, TRUE);
|
|
|
|
// Check to see if Width group is overlapping Attributes group. This can happen if
|
|
// the menu bar has wrapped because the window isn't wide enough (bug 424)
|
|
RECT crWidthWnd;
|
|
RECT crAttrWnd;
|
|
|
|
::GetWindowRect(m_WG.m_hwnd, &crWidthWnd);
|
|
::GetWindowRect(m_AG.m_hwnd, &crAttrWnd);
|
|
|
|
if (crAttrWnd.top < crWidthWnd.bottom)
|
|
{
|
|
// the menu bar has wrapped and our height placements are wrong. Adjust window
|
|
// by difference and try again
|
|
RECT crMainWnd;
|
|
|
|
::GetWindowRect(m_hwnd, &crMainWnd);
|
|
crMainWnd.bottom += (crWidthWnd.bottom - crAttrWnd.top + ::GetSystemMetrics(SM_CYFIXEDFRAME));
|
|
|
|
::MoveWindow(m_hwnd, crMainWnd.left, crMainWnd.top,
|
|
crMainWnd.right - crMainWnd.left, crMainWnd.bottom - crMainWnd.top,
|
|
FALSE);
|
|
|
|
// this is going to recurse but the adjustment will happen only once.....
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: WbMainWindow::OnGetMinMaxInfo
|
|
//
|
|
// Purpose: Set the minimum and maximum tracking sizes of the window
|
|
//
|
|
//
|
|
void WbMainWindow::OnGetMinMaxInfo(LPMINMAXINFO lpmmi)
|
|
{
|
|
if (m_TB.m_hwnd == NULL)
|
|
return; // not ready to do this yet
|
|
|
|
SIZE csFrame;
|
|
SIZE csSeparator;
|
|
SIZE csAG;
|
|
SIZE csToolBar;
|
|
SIZE csWidthBar;
|
|
SIZE csStatusBar;
|
|
RECT rectStatusBar;
|
|
SIZE csMaxSize;
|
|
SIZE csScrollBars;
|
|
|
|
csFrame.cx = ::GetSystemMetrics(SM_CXSIZEFRAME);
|
|
csFrame.cy = ::GetSystemMetrics(SM_CYSIZEFRAME);
|
|
|
|
csSeparator.cx = ::GetSystemMetrics(SM_CXEDGE);
|
|
csSeparator.cy = ::GetSystemMetrics(SM_CYEDGE);
|
|
|
|
csScrollBars.cx = ::GetSystemMetrics(SM_CXVSCROLL);
|
|
csScrollBars.cy = ::GetSystemMetrics(SM_CYHSCROLL);
|
|
|
|
m_AG.GetNaturalSize(&csAG);
|
|
|
|
m_TB.GetNaturalSize(&csToolBar);
|
|
m_WG.GetNaturalSize(&csWidthBar);
|
|
|
|
csStatusBar.cx = 0;
|
|
if (m_bStatusBarOn)
|
|
{
|
|
csStatusBar.cy = STATUSBAR_HEIGHT;
|
|
}
|
|
else
|
|
{
|
|
csStatusBar.cy = 0;
|
|
}
|
|
|
|
// Set the minimum width and height of the window
|
|
lpmmi->ptMinTrackSize.x =
|
|
csFrame.cx + csAG.cx + csFrame.cx;
|
|
|
|
lpmmi->ptMinTrackSize.y =
|
|
csFrame.cy +
|
|
GetSystemMetrics( SM_CYCAPTION ) +
|
|
GetSystemMetrics( SM_CYMENU ) +
|
|
csToolBar.cy +
|
|
csWidthBar.cy +
|
|
csSeparator.cy +
|
|
csAG.cy +
|
|
csSeparator.cy +
|
|
csStatusBar.cy +
|
|
csFrame.cy ;
|
|
|
|
//
|
|
// Retrieves the size of the work area on the primary display monitor. The work
|
|
// area is the portion of the screen not obscured by the system taskbar or by
|
|
// application desktop toolbars
|
|
//
|
|
RECT rcWorkArea;
|
|
::SystemParametersInfo( SPI_GETWORKAREA, 0, (&rcWorkArea), NULL );
|
|
csMaxSize.cx = rcWorkArea.right - rcWorkArea.left;
|
|
csMaxSize.cy = rcWorkArea.bottom - rcWorkArea.top;
|
|
|
|
lpmmi->ptMaxPosition.x = 0;
|
|
lpmmi->ptMaxPosition.y = 0;
|
|
lpmmi->ptMaxSize.x = csMaxSize.cx;
|
|
lpmmi->ptMaxSize.y = csMaxSize.cy;
|
|
lpmmi->ptMaxTrackSize.x = csMaxSize.cx;
|
|
lpmmi->ptMaxTrackSize.y = csMaxSize.cy;
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: WbMainWindow::CreateContextMenus
|
|
//
|
|
// Purpose: Create the pop-up context menus: used within the application
|
|
// drawing area.
|
|
//
|
|
//
|
|
BOOL WbMainWindow::CreateContextMenus(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::CreateContextMenus");
|
|
|
|
m_hContextMenuBar = ::LoadMenu(g_hInstance, MAKEINTRESOURCE(CONTEXTMENU));
|
|
if (!m_hContextMenuBar)
|
|
{
|
|
ERROR_OUT(("Failed to create context menu"));
|
|
DefaultExceptionHandler(WBFE_RC_WINDOWS, 0);
|
|
return FALSE;
|
|
}
|
|
m_hContextMenu = ::GetSubMenu(m_hContextMenuBar, 0);
|
|
|
|
m_hGrobjContextMenuBar = ::LoadMenu(g_hInstance, MAKEINTRESOURCE(GROBJMENU));
|
|
if (!m_hGrobjContextMenuBar)
|
|
{
|
|
ERROR_OUT(("Failed to create grobj context menu"));
|
|
DefaultExceptionHandler(WBFE_RC_WINDOWS, 0);
|
|
return FALSE;
|
|
}
|
|
m_hGrobjContextMenu = ::GetSubMenu(m_hGrobjContextMenuBar, 0);
|
|
|
|
// make parts of m_hGrobjContextMenu be owner draw
|
|
::ModifyMenu(m_hGrobjContextMenu, IDM_WIDTH_1, MF_ENABLED | MF_OWNERDRAW,
|
|
IDM_WIDTH_1, NULL);
|
|
::ModifyMenu(m_hGrobjContextMenu, IDM_WIDTH_2, MF_ENABLED | MF_OWNERDRAW,
|
|
IDM_WIDTH_2, NULL);
|
|
::ModifyMenu(m_hGrobjContextMenu, IDM_WIDTH_3, MF_ENABLED | MF_OWNERDRAW,
|
|
IDM_WIDTH_3, NULL);
|
|
::ModifyMenu(m_hGrobjContextMenu, IDM_WIDTH_4, MF_ENABLED | MF_OWNERDRAW,
|
|
IDM_WIDTH_4, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: WbMainWindow::InitializeMenus
|
|
//
|
|
// Purpose: Initialise the menus: set up owner-drawn menu items and
|
|
// those read from options file.
|
|
//
|
|
//
|
|
void WbMainWindow::InitializeMenus(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::InitializeMenus");
|
|
|
|
// Make the width menu ownerdraw
|
|
HMENU hMenu = GetMenuWithItem(::GetMenu(m_hwnd), IDM_WIDTH_1);
|
|
if (hMenu != NULL)
|
|
{
|
|
// Change each entry to be ownerdraw (loop until failure)
|
|
int iIndex;
|
|
UINT uiId;
|
|
int iCount = ::GetMenuItemCount(hMenu);
|
|
|
|
for (iIndex = 0; iIndex < iCount; iIndex++)
|
|
{
|
|
uiId = ::GetMenuItemID(hMenu, iIndex);
|
|
::ModifyMenu(hMenu, iIndex,
|
|
MF_BYPOSITION
|
|
| MF_ENABLED
|
|
| MF_OWNERDRAW,
|
|
uiId,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: WbMainWindow::OnMeasureItem
|
|
//
|
|
// Purpose: Return the size of an item in the widths menu
|
|
//
|
|
//
|
|
void WbMainWindow::OnMeasureItem
|
|
(
|
|
int nIDCtl,
|
|
LPMEASUREITEMSTRUCT measureStruct
|
|
)
|
|
{
|
|
// Check that this is for a color menu item
|
|
if ( (measureStruct->itemID >= IDM_WIDTHS_START)
|
|
&& (measureStruct->itemID < IDM_WIDTHS_END))
|
|
{
|
|
measureStruct->itemWidth = ::GetSystemMetrics(SM_CXMENUCHECK) +
|
|
(2 * CHECKMARK_BORDER_X) + COLOR_MENU_WIDTH;
|
|
measureStruct->itemHeight = ::GetSystemMetrics(SM_CYMENUCHECK) +
|
|
(2 * CHECKMARK_BORDER_Y);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: WbMainWindow::OnDrawItem
|
|
//
|
|
// Purpose: Draw an item in the color menu
|
|
//
|
|
//
|
|
void WbMainWindow::OnDrawItem
|
|
(
|
|
int nIDCtl,
|
|
LPDRAWITEMSTRUCT drawStruct
|
|
)
|
|
{
|
|
COLORREF crMenuBackground;
|
|
COLORREF crMenuText;
|
|
HPEN hOldPen;
|
|
HBRUSH hOldBrush;
|
|
COLORREF crOldBkgnd;
|
|
COLORREF crOldText;
|
|
int nOldBkMode;
|
|
HBITMAP hbmp = NULL;
|
|
BITMAP bitmap;
|
|
UINT uiCheckWidth;
|
|
UINT uiCheckHeight;
|
|
RECT rect;
|
|
RECT rectCheck;
|
|
RECT rectLine;
|
|
HDC hMemDC;
|
|
UINT uiWidthIndex;
|
|
UINT uiWidth;
|
|
HPEN hPenMenu;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnDrawItem");
|
|
|
|
// Check that this is a width menu item
|
|
if( (drawStruct->itemID < IDM_WIDTHS_START) ||
|
|
(drawStruct->itemID >= IDM_WIDTHS_END) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// get menu item colors
|
|
if( (drawStruct->itemState & ODS_SELECTED) ||
|
|
((drawStruct->itemState & (ODS_SELECTED |ODS_CHECKED)) ==
|
|
(ODS_SELECTED |ODS_CHECKED))
|
|
)
|
|
{
|
|
crMenuBackground = COLOR_HIGHLIGHT;
|
|
crMenuText = COLOR_HIGHLIGHTTEXT;
|
|
}
|
|
else if( drawStruct->itemState & ODS_GRAYED)
|
|
{
|
|
crMenuBackground = COLOR_MENU;
|
|
crMenuText = COLOR_GRAYTEXT;
|
|
}
|
|
else
|
|
{
|
|
crMenuBackground = COLOR_MENU;
|
|
crMenuText = COLOR_MENUTEXT;
|
|
}
|
|
|
|
hPenMenu = ::CreatePen(PS_SOLID, 0, ::GetSysColor(crMenuBackground));
|
|
if (!hPenMenu)
|
|
{
|
|
TRACE_MSG(("Failed to create penMenu"));
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WINDOWS, 0);
|
|
goto bail_out;
|
|
}
|
|
|
|
rect = drawStruct->rcItem;
|
|
|
|
// Fill the whole box with current menu background color
|
|
hOldPen = SelectPen(drawStruct->hDC, hPenMenu);
|
|
hOldBrush = SelectBrush(drawStruct->hDC, GetSysColorBrush(crMenuBackground));
|
|
|
|
::Rectangle(drawStruct->hDC, rect.left, rect.top, rect.right, rect.bottom);
|
|
|
|
SelectBrush(drawStruct->hDC, hOldBrush);
|
|
SelectPen(drawStruct->hDC, hOldPen);
|
|
|
|
if( (hbmp = (HBITMAP)LoadImage( NULL, MAKEINTRESOURCE( OBM_CHECK ), IMAGE_BITMAP,
|
|
0,0, 0 ))
|
|
== NULL )
|
|
{
|
|
TRACE_MSG(("Failed to create check image"));
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WINDOWS, 0);
|
|
goto bail_out;
|
|
}
|
|
|
|
// Get the width and height of the bitmap (allowing some border)
|
|
::GetObject(hbmp, sizeof(BITMAP), &bitmap);
|
|
uiCheckWidth = bitmap.bmWidth + (2 * CHECKMARK_BORDER_X);
|
|
uiCheckHeight = bitmap.bmHeight;
|
|
|
|
// Draw in a checkmark (if needed)
|
|
if (drawStruct->itemState & ODS_CHECKED)
|
|
{
|
|
hMemDC = ::CreateCompatibleDC(drawStruct->hDC);
|
|
if (!hMemDC)
|
|
{
|
|
ERROR_OUT(("Failed to create memDC"));
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WINDOWS, 0);
|
|
goto bail_out;
|
|
}
|
|
|
|
crOldBkgnd = ::SetBkColor(drawStruct->hDC, GetSysColor( crMenuBackground ) );
|
|
crOldText = ::SetTextColor(drawStruct->hDC, GetSysColor( crMenuText ) );
|
|
nOldBkMode = ::SetBkMode(drawStruct->hDC, OPAQUE );
|
|
|
|
HBITMAP hOld = SelectBitmap(hMemDC, hbmp);
|
|
|
|
if (hOld != NULL)
|
|
{
|
|
rectCheck = rect;
|
|
rectCheck.top += ((rectCheck.bottom - rectCheck.top)/2 - uiCheckHeight/2);
|
|
rectCheck.right = rectCheck.left + uiCheckWidth;
|
|
rectCheck.bottom = rectCheck.top + uiCheckHeight;
|
|
|
|
::BitBlt(drawStruct->hDC, rectCheck.left,
|
|
rectCheck.top,
|
|
rectCheck.right - rectCheck.left,
|
|
rectCheck.bottom - rectCheck.top,
|
|
hMemDC,
|
|
0,
|
|
0,
|
|
SRCCOPY);
|
|
|
|
SelectBitmap(hMemDC, hOld);
|
|
}
|
|
|
|
::SetBkMode(drawStruct->hDC, nOldBkMode);
|
|
::SetTextColor(drawStruct->hDC, crOldText);
|
|
::SetBkColor(drawStruct->hDC, crOldBkgnd);
|
|
|
|
::DeleteDC(hMemDC);
|
|
}
|
|
|
|
DeleteBitmap(hbmp);
|
|
|
|
// Allow room for the checkmark to the left of the color
|
|
rect.left += uiCheckWidth;
|
|
|
|
uiWidthIndex = drawStruct->itemID - IDM_WIDTHS_START;
|
|
uiWidth = g_PenWidths[uiWidthIndex];
|
|
|
|
// If pens are very wide they can be larger than the allowed rectangle.
|
|
// So we reduce the clipping rectangle here. We save the DC so that we
|
|
// can restore it - getting the clip region back.
|
|
if (::SaveDC(drawStruct->hDC) == 0)
|
|
{
|
|
ERROR_OUT(("Failed to save DC"));
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WINDOWS, 0);
|
|
goto bail_out;
|
|
}
|
|
|
|
if (::IntersectClipRect(drawStruct->hDC, rect.left, rect.top,
|
|
rect.right, rect.bottom) == ERROR)
|
|
{
|
|
ERROR_OUT(("Failed to set clip rect"));
|
|
|
|
::RestoreDC(drawStruct->hDC, -1);
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WINDOWS, 0);
|
|
goto bail_out;
|
|
}
|
|
|
|
hOldPen = SelectPen(drawStruct->hDC, hPenMenu);
|
|
hOldBrush = SelectBrush(drawStruct->hDC, GetSysColorBrush(crMenuText));
|
|
|
|
rectLine.left = rect.left;
|
|
rectLine.top = rect.top + ((rect.bottom - rect.top) / 2) - uiWidth/2;
|
|
rectLine.right= rect.right - ((rect.right - rect.left) / 6);
|
|
rectLine.bottom = rectLine.top + uiWidth + 2;
|
|
|
|
::Rectangle(drawStruct->hDC, rectLine.left, rectLine.top,
|
|
rectLine.right, rectLine.bottom);
|
|
|
|
SelectBrush(drawStruct->hDC, hOldBrush);
|
|
SelectPen(drawStruct->hDC, hOldPen);
|
|
|
|
::RestoreDC(drawStruct->hDC, -1);
|
|
|
|
bail_out:
|
|
if (hPenMenu != NULL)
|
|
{
|
|
::DeletePen(hPenMenu);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnSetFocus
|
|
//
|
|
// Purpose: The window is getting the focus
|
|
//
|
|
//
|
|
void WbMainWindow::OnSetFocus(void)
|
|
{
|
|
// We pass the focus on to the main drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: UpdateStatus
|
|
//
|
|
// Purpose: Set the text in the status bar
|
|
//
|
|
//
|
|
void WbMainWindow::UpdateStatus()
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::UpdateStatus");
|
|
|
|
//
|
|
// Update the current and last page numbers
|
|
//
|
|
m_AG.SetCurrentPageNumber(g_pwbCore->WBP_PageNumberFromHandle(m_hCurrentPage));
|
|
m_AG.SetLastPageNumber(g_pwbCore->WBP_ContentsCountPages());
|
|
|
|
//
|
|
// Update the user information with the page.
|
|
//
|
|
if (m_pLocalUser != NULL)
|
|
{
|
|
m_pLocalUser->SetPage(m_hCurrentPage);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: SetMenuState
|
|
//
|
|
// Purpose: Sets menu contents to their correct enabled/disabled state
|
|
//
|
|
//
|
|
void WbMainWindow::SetMenuStates(HMENU hInitMenu)
|
|
{
|
|
BOOL bLocked;
|
|
BOOL bPageOrderLocked;
|
|
BOOL bPresentationMode;
|
|
UINT uiEnable;
|
|
UINT uiCountPages;
|
|
BOOL bIdle;
|
|
BOOL bSelected;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::SetMenuStates");
|
|
|
|
//
|
|
// Check menu exists
|
|
//
|
|
if (hInitMenu == NULL)
|
|
{
|
|
WARNING_OUT(("Menu doesn't exist"));
|
|
return;
|
|
}
|
|
|
|
HMENU hMainMenu = ::GetMenu(m_hwnd);
|
|
|
|
// Get the window's main menu and check that the menu
|
|
// now being popped up is one on the top-level. (We do not
|
|
// seem to be able to associate the index number passed with
|
|
// sub-menus easily.)
|
|
if ((hInitMenu != m_hContextMenu) && (hInitMenu != m_hGrobjContextMenu))
|
|
{
|
|
BOOL bTopLevel = FALSE;
|
|
|
|
int nCount = ::GetMenuItemCount(hMainMenu);
|
|
|
|
for (int nNext = 0; nNext < nCount; nNext++)
|
|
{
|
|
HMENU hNextMenu = ::GetSubMenu(hMainMenu, nNext);
|
|
if (hNextMenu != NULL)
|
|
{
|
|
if (hNextMenu == hInitMenu)
|
|
{
|
|
bTopLevel = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// not a top level, so leave the function now
|
|
if (!bTopLevel)
|
|
{
|
|
TRACE_DEBUG(("Not top-level menu"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Get the lock and selection states:
|
|
// If we are joining a call, we cannot assume that the contents
|
|
// and user/client details have been created yet, so just set the
|
|
// locked state to true.
|
|
bIdle = IsIdle();
|
|
bSelected = m_drawingArea.GraphicSelected();
|
|
TRACE_DEBUG(("m_uiState %d", m_uiState));
|
|
if ((m_uiState == STARTING) || (m_uiState == JOINING))
|
|
{
|
|
TRACE_DEBUG(("Not initilalised yet"));
|
|
bLocked = TRUE;
|
|
bPageOrderLocked = TRUE;
|
|
bPresentationMode = TRUE;
|
|
uiCountPages = 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Note that bLocked and bPageOrderLocked are always true when
|
|
// we're not in idle state.
|
|
//
|
|
uiCountPages = g_pwbCore->WBP_ContentsCountPages();
|
|
bLocked = (WB_Locked() || !bIdle);
|
|
bPageOrderLocked = (WB_Locked() || !bIdle);
|
|
bPresentationMode = (((m_uiState == IN_CALL) &&
|
|
(WB_PresentationMode()))
|
|
|| (!bIdle));
|
|
}
|
|
|
|
//
|
|
// Functions which are disabled when contents is locked
|
|
//
|
|
uiEnable = MF_BYCOMMAND | (bLocked ? MF_GRAYED : MF_ENABLED);
|
|
|
|
::EnableMenuItem(hInitMenu, IDM_OPEN, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_SAVE, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_SAVE_AS, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_PRINT, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_GRAB_AREA, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_GRAB_WINDOW, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_SELECTALL, uiEnable);
|
|
|
|
::EnableMenuItem(hInitMenu, IDM_SELECT, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_PEN, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_HIGHLIGHT, uiEnable);
|
|
|
|
// Don't allow editing in zoom mode
|
|
if( m_drawingArea.Zoomed() )
|
|
::EnableMenuItem(hInitMenu, IDM_TEXT, MF_GRAYED);
|
|
else
|
|
::EnableMenuItem(hInitMenu, IDM_TEXT, uiEnable);
|
|
|
|
::EnableMenuItem(hInitMenu, IDM_CLEAR_PAGE, uiEnable);
|
|
|
|
::EnableMenuItem(hInitMenu, IDM_ERASER, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_LINE, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_BOX, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_FILLED_BOX, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_ELLIPSE, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_FILLED_ELLIPSE, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_ZOOM, uiEnable);
|
|
|
|
|
|
// So toolbar will follow menu (MFC-auto-update is broken for this)
|
|
EnableToolbar( !bLocked );
|
|
|
|
|
|
//
|
|
// File/New is disabled if page order is locked, or not in a call,
|
|
// or a new is already in progress.
|
|
//
|
|
::EnableMenuItem(hInitMenu, IDM_NEW, MF_BYCOMMAND |
|
|
(bPageOrderLocked ? MF_GRAYED : MF_ENABLED));
|
|
|
|
//
|
|
// Paste enabled only if not locked, and there's something in the
|
|
// clipboard
|
|
//
|
|
uiEnable = MF_BYCOMMAND | MF_ENABLED;
|
|
if ( (CLP_AcceptableClipboardFormat() == NULL)
|
|
|| (bLocked))
|
|
{
|
|
// No acceptable format available, or the contents
|
|
// are locked by another user - gray the Paste command.
|
|
uiEnable = MF_BYCOMMAND | MF_GRAYED;
|
|
}
|
|
::EnableMenuItem(hInitMenu, IDM_PASTE, uiEnable);
|
|
|
|
//
|
|
// Functions which require a graphic to be selected
|
|
//
|
|
uiEnable = MF_BYCOMMAND | MF_ENABLED;
|
|
if( !m_drawingArea.TextEditActive() )
|
|
{
|
|
if (!bSelected || bLocked)
|
|
{
|
|
// No acceptable format available - gray the menu item
|
|
uiEnable = MF_BYCOMMAND | MF_GRAYED;
|
|
}
|
|
}
|
|
|
|
::EnableMenuItem(hInitMenu, IDM_CUT, uiEnable);
|
|
|
|
// don't do textedit delete for now
|
|
if( m_drawingArea.TextEditActive() )
|
|
::EnableMenuItem(hInitMenu, IDM_DELETE, MF_BYCOMMAND | MF_GRAYED);
|
|
else
|
|
::EnableMenuItem(hInitMenu, IDM_DELETE, uiEnable);
|
|
|
|
::EnableMenuItem(hInitMenu, IDM_BRING_TO_TOP, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_SEND_TO_BACK, uiEnable);
|
|
|
|
//
|
|
// Can copy even if contents are locked
|
|
//
|
|
//COMMENT BY RAND - To fix 556 I changed !bIdle to bIdle like the current
|
|
// 16bit code does.
|
|
::EnableMenuItem(hInitMenu, IDM_COPY, MF_BYCOMMAND |
|
|
(m_drawingArea.TextEditActive()||(bSelected && bIdle)
|
|
? MF_ENABLED : MF_GRAYED)); //CHANGED BY RAND for 556
|
|
|
|
//
|
|
// Object to undelete?
|
|
//
|
|
::EnableMenuItem(hInitMenu, IDM_UNDELETE, MF_BYCOMMAND |
|
|
((m_LastDeletedGraphic.GotTrash() &&
|
|
(m_LastDeletedGraphic.Page() == m_hCurrentPage) &&
|
|
(!bLocked)) ? MF_ENABLED : MF_GRAYED));
|
|
|
|
//
|
|
// Page functions depend on number of pages
|
|
//
|
|
::EnableMenuItem(hInitMenu, IDM_DELETE_PAGE, MF_BYCOMMAND |
|
|
((bPageOrderLocked ||
|
|
(uiCountPages == 1)||
|
|
(!m_bUnlockStateSettled))
|
|
? MF_GRAYED : MF_ENABLED));
|
|
|
|
uiEnable = MF_BYCOMMAND | MF_ENABLED;
|
|
if ((bPageOrderLocked) ||
|
|
(uiCountPages == WB_MAX_PAGES)||
|
|
(!m_bUnlockStateSettled))
|
|
{
|
|
uiEnable = MF_BYCOMMAND | MF_GRAYED;
|
|
}
|
|
::EnableMenuItem(hInitMenu, IDM_PAGE_INSERT_BEFORE, uiEnable);
|
|
::EnableMenuItem(hInitMenu, IDM_PAGE_INSERT_AFTER, uiEnable);
|
|
|
|
//
|
|
// Can't bring up page sorter if locked
|
|
//
|
|
::EnableMenuItem(hInitMenu, IDM_PAGE_SORTER, MF_BYCOMMAND |
|
|
(bPresentationMode ? MF_GRAYED : MF_ENABLED));
|
|
|
|
// Enable page controls
|
|
m_AG.EnablePageCtrls(!bPresentationMode);
|
|
|
|
//
|
|
// Lock enabled only if not already locked
|
|
//
|
|
::EnableMenuItem(hInitMenu, IDM_LOCK, MF_BYCOMMAND |
|
|
(bPageOrderLocked ? MF_GRAYED : MF_ENABLED));
|
|
|
|
//
|
|
// Enable sync if not in "presentation" mode
|
|
//
|
|
::EnableMenuItem(hInitMenu, IDM_SYNC, MF_BYCOMMAND |
|
|
(((!bPresentationMode) && bIdle) ? MF_ENABLED : MF_GRAYED));
|
|
|
|
//
|
|
// Gray font/color/widths if inappropriate for current tool.
|
|
//
|
|
::EnableMenuItem(hInitMenu, IDM_FONT, MF_BYCOMMAND |
|
|
(!bLocked && m_pCurrentTool->HasFont() ? MF_ENABLED : MF_GRAYED));
|
|
|
|
::EnableMenuItem(hInitMenu, IDM_EDITCOLOR, MF_BYCOMMAND |
|
|
(!bLocked && m_pCurrentTool->HasColor() ? MF_ENABLED : MF_GRAYED));
|
|
|
|
|
|
// enable width menu (bug 433)
|
|
HMENU hOptionsMenu = ::GetSubMenu(hMainMenu, MENUPOS_OPTIONS);
|
|
uiEnable = (!bLocked && m_pCurrentTool->HasWidth())?MF_ENABLED:MF_GRAYED;
|
|
|
|
if (hOptionsMenu == hInitMenu )
|
|
::EnableMenuItem(hOptionsMenu, OPTIONSPOS_WIDTH, MF_BYPOSITION | uiEnable );
|
|
|
|
UINT i;
|
|
UINT uIdmCurWidth = 0;
|
|
if( uiEnable == MF_ENABLED )
|
|
uIdmCurWidth = m_pCurrentTool->GetWidthIndex() + IDM_WIDTH_1;
|
|
|
|
// set width state(bug 426)
|
|
for( i=IDM_WIDTH_1; i<=IDM_WIDTH_4; i++ )
|
|
{
|
|
::EnableMenuItem(hInitMenu, i, uiEnable );
|
|
|
|
if( uiEnable == MF_ENABLED )
|
|
{
|
|
if( uIdmCurWidth == i )
|
|
::CheckMenuItem(hInitMenu, i, MF_CHECKED );
|
|
else
|
|
::CheckMenuItem(hInitMenu, i, MF_UNCHECKED );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnInitMenuPopup
|
|
//
|
|
// Purpose: Process a WM_INITMENUPOPUP event
|
|
//
|
|
//
|
|
void WbMainWindow::OnInitMenuPopup
|
|
(
|
|
HMENU hMenu,
|
|
UINT uiIndex,
|
|
BOOL bSystemMenu
|
|
)
|
|
{
|
|
|
|
// 1/2 of fix for strange MFC4.2 build bug that clogs up DCL's message pipe.
|
|
// The other 1/2 and a better comment are in LoadFile().
|
|
if( m_bIsWin95 )
|
|
{
|
|
if( GetSubState() == SUBSTATE_LOADING )
|
|
{
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// Ignore the event if it relates to the system menu
|
|
if (!bSystemMenu)
|
|
{
|
|
if (hMenu)
|
|
{
|
|
SetMenuStates(hMenu);
|
|
m_hInitMenu = hMenu;
|
|
}
|
|
else
|
|
{
|
|
m_hInitMenu = NULL;
|
|
}
|
|
|
|
// Save the last menu we handled, so that we can alter its state
|
|
// if necessary whilst it is still visible
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function : OnMenuSelect
|
|
//
|
|
// Purpose : Update the text in the help bar
|
|
//
|
|
//
|
|
void WbMainWindow::OnMenuSelect(UINT uiItemID, UINT uiFlags, HMENU hSysMenu)
|
|
{
|
|
UINT firstMenuId;
|
|
UINT statusId;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnMenuSelect");
|
|
|
|
//
|
|
// Work out the help ID for the menu item. We have to store this now
|
|
// because when the user presses F1 from a menu item, we can't tell
|
|
// which item it was.
|
|
//
|
|
if (uiFlags == (UINT)-1)
|
|
{
|
|
//
|
|
// The menu has been dismissed
|
|
//
|
|
m_hInitMenu = NULL;
|
|
statusId = IDS_DEFAULT;
|
|
|
|
if( hSysMenu == 0 )
|
|
{
|
|
// Menu was dismissed, check cursor loc.
|
|
DCWbGraphic *pGraphic;
|
|
|
|
POINT surfacePos;
|
|
::GetCursorPos( &surfacePos );
|
|
::ScreenToClient(m_drawingArea.m_hwnd, &surfacePos);
|
|
m_drawingArea.ClientToSurface(&surfacePos );
|
|
|
|
if( (pGraphic = m_drawingArea.GetHitObject( surfacePos )) == NULL )
|
|
{
|
|
// we clicked dead air, don't lose current selection (bug 426)
|
|
m_drawingArea.SetLClickIgnore( TRUE );
|
|
}
|
|
else
|
|
delete pGraphic; // plug leak
|
|
}
|
|
}
|
|
else if ((uiFlags & MF_POPUP) && (uiFlags & MF_SYSMENU))
|
|
{
|
|
//
|
|
// System menu selected
|
|
//
|
|
statusId = IDS_MENU_SYSTEM;
|
|
}
|
|
else if (uiFlags & MF_POPUP)
|
|
{
|
|
// get popup menu handle and first item (bug NM4db:463)
|
|
HMENU hPopup = ::GetSubMenu( hSysMenu, uiItemID );
|
|
firstMenuId = ::GetMenuItemID( hPopup, 0 );
|
|
|
|
// figure out which popup it is so we can display the right help text
|
|
switch (firstMenuId)
|
|
{
|
|
case IDM_NEW:
|
|
statusId = IDS_MENU_FILE;
|
|
break;
|
|
|
|
case IDM_DELETE:
|
|
statusId = IDS_MENU_EDIT;
|
|
break;
|
|
|
|
case IDM_TOOL_BAR_TOGGLE:
|
|
statusId = IDS_MENU_VIEW;
|
|
break;
|
|
|
|
case IDM_EDITCOLOR:
|
|
// The first item in the options menu is the color popup
|
|
// menu - popup menus have Id -1
|
|
statusId = IDS_MENU_OPTIONS;
|
|
break;
|
|
|
|
case IDM_TOOLS_START:
|
|
statusId = IDS_MENU_TOOLS;
|
|
break;
|
|
|
|
case IDM_HELP:
|
|
statusId = IDS_MENU_HELP;
|
|
break;
|
|
|
|
case IDM_WIDTH_1: // (added for bug NM4db:463)
|
|
statusId = IDS_MENU_WIDTH;
|
|
break;
|
|
|
|
default:
|
|
statusId = IDS_DEFAULT;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// A normal menu item has been selected
|
|
//
|
|
statusId = uiItemID;
|
|
}
|
|
|
|
// Set the new help text
|
|
TCHAR szStatus[256];
|
|
|
|
if (::LoadString(g_hInstance, statusId, szStatus, 256))
|
|
{
|
|
::SetWindowText(m_hwndSB, szStatus);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnParentNotfiy
|
|
//
|
|
// Purpose: Process a message coming from a child window
|
|
//
|
|
//
|
|
void WbMainWindow::OnParentNotify(UINT uiMessage)
|
|
{
|
|
switch (uiMessage)
|
|
{
|
|
// Scroll message from the drawing area. These are sent when the user
|
|
// scrolls the area using the scroll bars. We queue an update of the
|
|
// current sync position.
|
|
case WM_HSCROLL:
|
|
case WM_VSCROLL:
|
|
// The user's view has changed
|
|
PositionUpdated();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: QuerySaveRequired
|
|
//
|
|
// Purpose: Check whether the drawing pane contents are to be saved
|
|
// before a destructive function is performed.
|
|
//
|
|
//
|
|
int WbMainWindow::QuerySaveRequired(BOOL bCancelBtn)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::QuerySaveRequired");
|
|
|
|
// Default the response to "no save required"
|
|
int iResult = IDNO;
|
|
|
|
//
|
|
// If we are already displaying a "Save As" dialog, dismiss it.
|
|
//
|
|
if (m_hwndQuerySaveDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndQuerySaveDlg, WM_COMMAND,
|
|
MAKELONG(IDCANCEL, BN_CLICKED), 0);
|
|
ASSERT(m_hwndQuerySaveDlg == NULL);
|
|
}
|
|
|
|
// If any of the pages has changed - ask the user if they want to
|
|
// save the contents of the Whiteboard.
|
|
if (g_pwbCore->WBP_ContentsChanged())
|
|
{
|
|
::SetForegroundWindow(m_hwnd); //bring us to the top first
|
|
|
|
// SetForegroundWindow() does not work properly in Memphis when its called during a
|
|
// SendMessage handler, specifically, when conf calls me to shutdown. The window activation
|
|
// state is messed up or something and my window does not pop to the top. So I have to
|
|
// force my window to the top using SetWindowPos. But even after that the titlebar is not
|
|
// highlighted properly. I tried combinations of SetActiveWindow, SetFocus, etc but to no
|
|
// avail. But, at least the dialog is visible so you can clear it thus fixing the
|
|
// bug (NM4db:2103). SetForegroundWindow() works ok for Win95 and NT here without
|
|
// having to use SetWindowPos (it doesn't hurt anyting to do it anyway so I didn't
|
|
// do a platform check).
|
|
::SetWindowPos(m_hwnd, HWND_TOPMOST, 0,0, 0,0, SWP_NOMOVE | SWP_NOSIZE ); // force to top
|
|
::SetWindowPos(m_hwnd, HWND_NOTOPMOST, 0,0, 0,0, SWP_NOMOVE | SWP_NOSIZE ); // let go of topmost
|
|
|
|
//
|
|
// Display a dialog box with the relevant question
|
|
// LOWORD of user data is "cancel command is allowed"
|
|
// HIWORD of user data is "disable cancel button"
|
|
//
|
|
iResult = (int)DialogBoxParam(g_hInstance,
|
|
bCancelBtn ? MAKEINTRESOURCE(QUERYSAVEDIALOGCANCEL)
|
|
: MAKEINTRESOURCE(QUERYSAVEDIALOG),
|
|
m_hwnd,
|
|
QuerySaveDlgProc,
|
|
MAKELONG(bCancelBtn, FALSE));
|
|
}
|
|
|
|
return iResult;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// QuerySaveDlgProc()
|
|
// Handler for query save dialogs. We save some flags in GWL_USER
|
|
//
|
|
INT_PTR CALLBACK QuerySaveDlgProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fHandled = FALSE;
|
|
|
|
switch (uMessage)
|
|
{
|
|
case WM_INITDIALOG:
|
|
//
|
|
// Save away our HWND so this dialog can be cancelled if necessary
|
|
//
|
|
g_pMain->m_hwndQuerySaveDlg = hwnd;
|
|
|
|
// Remember the flags we passed
|
|
::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
|
|
|
|
// Should the cancel button be disabled?
|
|
if (HIWORD(lParam))
|
|
::EnableWindow(::GetDlgItem(hwnd, IDCANCEL), FALSE);
|
|
|
|
// Bring us to the front
|
|
::SetForegroundWindow(hwnd);
|
|
|
|
fHandled = TRUE;
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
// Even if the cancel button is disabled, kill the dialog
|
|
::PostMessage(hwnd, WM_COMMAND, IDCANCEL, 0);
|
|
fHandled = TRUE;
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDCANCEL:
|
|
//
|
|
// If a dialog doesn't have a cancel button or it's
|
|
// disabled and the user pressed the close btn, we can
|
|
// get here.
|
|
//
|
|
if (!LOWORD(::GetWindowLongPtr(hwnd, GWLP_USERDATA)))
|
|
wParam = MAKELONG(IDNO, HIWORD(wParam));
|
|
// FALL THRU
|
|
|
|
case IDYES:
|
|
case IDNO:
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
|
|
{
|
|
g_pMain->m_hwndQuerySaveDlg = NULL;
|
|
|
|
::EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
fHandled = TRUE;
|
|
break;
|
|
}
|
|
|
|
return(fHandled);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnNew
|
|
//
|
|
// Purpose: Clear the workspace and associated filenames
|
|
//
|
|
//
|
|
void WbMainWindow::OnNew(void)
|
|
{
|
|
int iDoNew;
|
|
|
|
if( UsersMightLoseData( NULL, NULL ) ) // bug NM4db:418
|
|
return;
|
|
|
|
|
|
// check state before proceeding - if we're already doing a new, then abort
|
|
if ( (m_uiState != IN_CALL)
|
|
|| (m_uiSubState == SUBSTATE_NEW_IN_PROGRESS))
|
|
{
|
|
// post an error message indicating the whiteboard is busy
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_BUSY);
|
|
goto OnNewCleanup;
|
|
}
|
|
// if we're currently loading, then cancel the load and proceed (don't
|
|
// prompt to save).
|
|
else if (m_uiSubState == SUBSTATE_LOADING)
|
|
{
|
|
// cancel load, not releasing the page order lock, because
|
|
// we need it immediately afterwards
|
|
CancelLoad(FALSE);
|
|
iDoNew = IDNO;
|
|
}
|
|
// otherwise prompt to save if necessary
|
|
else
|
|
{
|
|
// Get confirmation for the new
|
|
iDoNew = QuerySaveRequired(TRUE);
|
|
}
|
|
|
|
if (iDoNew == IDYES)
|
|
{
|
|
// Save the changes
|
|
iDoNew = OnSave(FALSE);
|
|
}
|
|
|
|
// If the user did not cancel the operation, clear the drawing area
|
|
if (iDoNew != IDCANCEL)
|
|
{
|
|
// Go to the first page, as this won't be deleted - stops flashing
|
|
// with locking contents for each page delete
|
|
OnFirstPage();
|
|
GotoPosition(0, 0);
|
|
|
|
// lock the drawing area
|
|
LockDrawingArea();
|
|
|
|
// Save the current lock status
|
|
SaveLock();
|
|
|
|
// Get the Page Order Lock (with an invisible dialog)
|
|
BOOL bGotLock = GetLock(WB_LOCK_TYPE_PAGE_ORDER, SW_HIDE);
|
|
if (!bGotLock)
|
|
{
|
|
RestoreLock();
|
|
}
|
|
else
|
|
{
|
|
UINT uiReturn;
|
|
|
|
// Remove all the pages
|
|
uiReturn = g_pwbCore->WBP_ContentsDelete();
|
|
if (uiReturn != 0)
|
|
{
|
|
DefaultExceptionHandler(WBFE_RC_WB, uiReturn);
|
|
return;
|
|
}
|
|
|
|
// if there is only one page, the new is implemented just as a page-
|
|
// clear, so we don't need to go into NEW_IN_PROGRESS substate.
|
|
if (g_pwbCore->WBP_ContentsCountPages() > 1)
|
|
{
|
|
// set substate to show we're doing a new
|
|
SetSubstate(SUBSTATE_NEW_IN_PROGRESS);
|
|
}
|
|
else
|
|
{
|
|
// Restore the lock status
|
|
RestoreLock();
|
|
}
|
|
|
|
// Clear the associated file name
|
|
ZeroMemory(m_strFileName, sizeof(m_strFileName));
|
|
|
|
// Update the window title with no file name
|
|
UpdateWindowTitle();
|
|
}
|
|
}
|
|
|
|
OnNewCleanup:
|
|
|
|
// unlock the drawing area if the new is not asynchronous
|
|
if ( (m_uiSubState != SUBSTATE_NEW_IN_PROGRESS)
|
|
&& (!WB_ContentsLocked()))
|
|
{
|
|
UnlockDrawingArea();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnNextPage
|
|
//
|
|
// Purpose: Move to the next worksheet in the pages list
|
|
//
|
|
//
|
|
void WbMainWindow::OnNextPage(void)
|
|
{
|
|
// ignore this command if in presentation mode
|
|
if ( (m_uiState == IN_CALL)
|
|
&& (!WB_PresentationMode()))
|
|
{
|
|
// Go to the next page
|
|
GotoPage(PG_GetNextPage(m_hCurrentPage));
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnPrevPage
|
|
//
|
|
// Purpose: Move to the previous worksheet in the pages list
|
|
//
|
|
//
|
|
void WbMainWindow::OnPrevPage(void)
|
|
{
|
|
// ignore this command if in presentation mode
|
|
if ( (m_uiState == IN_CALL)
|
|
&& (!WB_PresentationMode()))
|
|
{
|
|
// Go to the previous page
|
|
GotoPage(PG_GetPreviousPage(m_hCurrentPage));
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnFirstPage
|
|
//
|
|
// Purpose: Move to the first worksheet in the pages list
|
|
//
|
|
//
|
|
void WbMainWindow::OnFirstPage(void)
|
|
{
|
|
// ignore this command if in presentation mode
|
|
if ( (m_uiState == IN_CALL)
|
|
&& (!WB_PresentationMode()))
|
|
{
|
|
// Go to the first page
|
|
WB_PAGE_HANDLE hPage;
|
|
|
|
g_pwbCore->WBP_PageHandle(WB_PAGE_HANDLE_NULL, PAGE_FIRST, &hPage);
|
|
GotoPage(hPage);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnLastPage
|
|
//
|
|
// Purpose: Move to the last worksheet in the pages list
|
|
//
|
|
//
|
|
void WbMainWindow::OnLastPage(void)
|
|
{
|
|
// ignore this command if in presentation mode
|
|
if ( (m_uiState == IN_CALL)
|
|
&& (!WB_PresentationMode()))
|
|
{
|
|
// Go to the last page
|
|
WB_PAGE_HANDLE hPage;
|
|
|
|
g_pwbCore->WBP_PageHandle(WB_PAGE_HANDLE_NULL, PAGE_LAST, &hPage);
|
|
GotoPage(hPage);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnGotoPage
|
|
//
|
|
// Purpose: Move to the specified page (if it exists)
|
|
//
|
|
//
|
|
void WbMainWindow::OnGotoPage(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnGotoPage");
|
|
|
|
// ignore this command if in presentation mode
|
|
if ( (m_uiState == IN_CALL)
|
|
&& (!WB_PresentationMode()))
|
|
{
|
|
// Get the requested page number from the pages group
|
|
UINT uiPageNumber = m_AG.GetCurrentPageNumber();
|
|
|
|
// Goto the page
|
|
GotoPageNumber(uiPageNumber);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: GotoPage
|
|
//
|
|
// Purpose: Move to the specified page
|
|
//
|
|
//
|
|
void WbMainWindow::GotoPage(WB_PAGE_HANDLE hPageNew)
|
|
{
|
|
BOOL inEditField;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::GotoPage");
|
|
|
|
inEditField = m_AG.IsChildEditField(::GetFocus());
|
|
|
|
// If we are changing page
|
|
if (hPageNew != m_hCurrentPage)
|
|
{
|
|
m_drawingArea.CancelDrawingMode();
|
|
|
|
// Attach the new page to the drawing area
|
|
m_hCurrentPage = hPageNew;
|
|
m_drawingArea.Attach(m_hCurrentPage);
|
|
|
|
// Update the local user information with the new page
|
|
if (m_pLocalUser != NULL)
|
|
m_pLocalUser->SetPage(m_hCurrentPage);
|
|
|
|
// Show that we need to update the sync position
|
|
m_bSyncUpdateNeeded = TRUE;
|
|
|
|
PAGE_POSITION *mapob = NULL;
|
|
POSITION position = m_pageToPosition.GetHeadPosition();
|
|
BOOL bFound = FALSE;
|
|
while (position && !bFound)
|
|
{
|
|
mapob = (PAGE_POSITION *)m_pageToPosition.GetNext(position);
|
|
if ( mapob && mapob->hPage == hPageNew)
|
|
{
|
|
bFound = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!bFound)
|
|
{
|
|
// page not in map, so go to the top-left
|
|
//CHANGED BY RAND - to fix memory leak
|
|
GotoPosition( 0, 0);
|
|
}
|
|
else
|
|
{
|
|
if(mapob)
|
|
{
|
|
GotoPosition(mapob->position.x, mapob->position.y);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the status display
|
|
UpdateStatus();
|
|
|
|
// set the focus back to the drawing area
|
|
if (!inEditField)
|
|
{
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: GotoPageNumber
|
|
//
|
|
// Purpose: Move to the specified page
|
|
//
|
|
//
|
|
void WbMainWindow::GotoPageNumber(UINT uiPageNumber)
|
|
{
|
|
GotoPage(PG_GetPageNumber(uiPageNumber));
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: GotoPosition
|
|
//
|
|
// Purpose: Move to the specified position within the page
|
|
//
|
|
//
|
|
void WbMainWindow::GotoPosition(int x, int y)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::GotoPosition");
|
|
|
|
// Move the drawing area to the new position
|
|
m_drawingArea.GotoPosition(x, y);
|
|
|
|
// The user's view has changed
|
|
PositionUpdated();
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: GotoSyncPosition
|
|
//
|
|
// Purpose: Move to the the current sync position
|
|
//
|
|
//
|
|
void WbMainWindow::GotoSyncPosition(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::GotoSyncPosition");
|
|
|
|
//
|
|
// Get the local user to determine the new position.
|
|
//
|
|
if (!m_pLocalUser)
|
|
{
|
|
ERROR_OUT(("Skipping GotoSyncPosition; no local user object"));
|
|
return;
|
|
}
|
|
|
|
m_pLocalUser->GetSyncPosition();
|
|
|
|
//
|
|
// If the page is different to where we are currently, get the number
|
|
// of the page and select the current page
|
|
//
|
|
if (m_pLocalUser->Page() != m_hCurrentPage)
|
|
{
|
|
GotoPageNumber(g_pwbCore->WBP_PageNumberFromHandle(m_pLocalUser->Page()));
|
|
}
|
|
|
|
// Get the requested position from the user
|
|
RECT rectVisibleUser;
|
|
m_pLocalUser->GetVisibleRect(&rectVisibleUser);
|
|
|
|
// Scroll to the required position
|
|
GotoPosition(rectVisibleUser.left, rectVisibleUser.top);
|
|
|
|
// Make sure we are zoomed / not zoomed as appropriate
|
|
if ((m_pLocalUser->GetZoom()) != m_drawingArea.Zoomed())
|
|
{
|
|
OnZoom();
|
|
}
|
|
|
|
//
|
|
// Reset the sync position update flag that will have been turned on by
|
|
// the calls above. We do not want to change the current sync position
|
|
// when we are merely changing our position to match that set by
|
|
// another user in the call.
|
|
//
|
|
m_bSyncUpdateNeeded = FALSE;
|
|
|
|
// Inform the other users that we have changed position
|
|
m_pLocalUser->Update();
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnGotoUserPosition
|
|
//
|
|
// Purpose: Move to the the current position of the specified user
|
|
//
|
|
//
|
|
void WbMainWindow::OnGotoUserPosition(LPARAM lParam)
|
|
{
|
|
UINT uiPageNumber = 1;
|
|
WB_PAGE_HANDLE hPage;
|
|
WbUser * pUser;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnGotoUserPosition");
|
|
|
|
//
|
|
// If the drawing area is busy, ignore this command. This is unlikely
|
|
// since this command is generated by selecting a menu entry on a user
|
|
// icon. The user should not therefore be drawing on the page by the
|
|
// time we get the message.
|
|
//
|
|
if (m_drawingArea.IsBusy())
|
|
{
|
|
TRACE_DEBUG(("drawing area is busy just now.."));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get a user object (throws an exception if the handle specified is no
|
|
// longer valid).
|
|
//
|
|
pUser = WB_GetUser((POM_OBJECT) lParam);
|
|
if (!pUser)
|
|
{
|
|
WARNING_OUT(("Can't handle OnGotoUserPosition; can't get user object for 0x%08x", lParam));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the requested page from the user.
|
|
//
|
|
hPage = pUser->Page();
|
|
|
|
//
|
|
// Quit if the requested page is not valid locally.
|
|
//
|
|
if (hPage == WB_PAGE_HANDLE_NULL)
|
|
{
|
|
TRACE_DEBUG(("Page is not valid locally"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Don't go to user's position if it's on another page and we're in
|
|
// presentation mode (this shouldn't normally happen, since we should
|
|
// all be on the same page, but there is a window at the start-up of
|
|
// presentation mode.
|
|
//
|
|
if ( (hPage == m_hCurrentPage) ||
|
|
(!WB_PresentationMode()) )
|
|
{
|
|
//
|
|
// If the page is different to where we are currently, get the
|
|
// number of the page and select the current page.
|
|
//
|
|
if (hPage != m_hCurrentPage)
|
|
{
|
|
uiPageNumber = g_pwbCore->WBP_PageNumberFromHandle(hPage);
|
|
GotoPageNumber(uiPageNumber);
|
|
}
|
|
|
|
//
|
|
// Get the requested position from the user and scroll to it.
|
|
//
|
|
RECT rectVisibleUser;
|
|
pUser->GetVisibleRect(&rectVisibleUser);
|
|
GotoPosition(rectVisibleUser.left, rectVisibleUser.top);
|
|
|
|
//
|
|
// Zoom/unzoom if the sync zoom state is different to our current
|
|
// zoom state.
|
|
//
|
|
if ( (m_pLocalUser->GetZoom()) != (m_drawingArea.Zoomed()) )
|
|
{
|
|
TRACE_DEBUG(("Change zoom state"));
|
|
OnZoom();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnGotoUserPointer
|
|
//
|
|
// Purpose: Move to the pointer position of the specified user
|
|
//
|
|
//
|
|
void WbMainWindow::OnGotoUserPointer(LPARAM lParam)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnGotoUserPointer");
|
|
|
|
// If the drawing area is busy, ignore this command.
|
|
// This is unlikely since this command is generated by selecting
|
|
// a menu entry on a user icon. The user should not therefore be
|
|
// drawing on the page by the time we get the message.
|
|
if (!m_drawingArea.IsBusy())
|
|
{
|
|
// Get a user object (throws an exception if the
|
|
// handle specified is no longer valid).
|
|
WbUser* pUser = WB_GetUser((POM_OBJECT) lParam);
|
|
|
|
if (!pUser)
|
|
{
|
|
WARNING_OUT(("Can't handle OnGotoUserPointer; can't get user object for 0x%08x", lParam));
|
|
return;
|
|
}
|
|
|
|
DCWbGraphicPointer* pPointer = pUser->GetPointer();
|
|
ASSERT(pPointer != NULL);
|
|
|
|
// Continue only if the user is using the pointer
|
|
if (pPointer->IsActive())
|
|
{
|
|
// Get the requested page from the user
|
|
WB_PAGE_HANDLE hPage = pPointer->Page();
|
|
|
|
// Check that the requested page is valid locally
|
|
if (hPage != WB_PAGE_HANDLE_NULL)
|
|
{
|
|
// If the pointer is on a different page, change to the
|
|
// correct page.
|
|
if (hPage != m_hCurrentPage)
|
|
{
|
|
GotoPageNumber(g_pwbCore->WBP_PageNumberFromHandle(hPage));
|
|
}
|
|
|
|
// Move within the page if the pointer is not wholly visible
|
|
// in the drawing area window.
|
|
RECT rectPointer;
|
|
RECT rcVis;
|
|
RECT rcT;
|
|
|
|
pPointer->GetBoundsRect(&rectPointer);
|
|
m_drawingArea.GetVisibleRect(&rcVis);
|
|
|
|
::IntersectRect(&rcT, &rcVis, &rectPointer);
|
|
if (!::EqualRect(&rcT, &rectPointer))
|
|
{
|
|
// Adjust the position so that the pointer is shown
|
|
// in the centre of the window.
|
|
POINT position;
|
|
SIZE size;
|
|
|
|
position.x = rectPointer.left;
|
|
position.y = rectPointer.top;
|
|
|
|
size.cx = (rcVis.right - rcVis.left) - (rectPointer.right - rectPointer.left);
|
|
size.cy = (rcVis.bottom - rcVis.top) - (rectPointer.bottom - rectPointer.top);
|
|
|
|
position.x += -size.cx / 2;
|
|
position.y += -size.cy / 2;
|
|
|
|
// Scroll to the required position
|
|
GotoPosition(position.x, position.y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: LoadFile
|
|
//
|
|
// Purpose: Load a metafile into the application. Errors are reported
|
|
// to the caller by the return code.
|
|
//
|
|
//
|
|
void WbMainWindow::LoadFile
|
|
(
|
|
LPCSTR szLoadFileName
|
|
)
|
|
{
|
|
UINT uRes;
|
|
|
|
// Check we're in idle state
|
|
if (!IsIdle())
|
|
{
|
|
// post an error message indicating the whiteboard is busy
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_BUSY);
|
|
goto UserPointerCleanup;
|
|
}
|
|
|
|
if (*szLoadFileName)
|
|
{
|
|
// Change the cursor to "wait"
|
|
::SetCursor(::LoadCursor(NULL, IDC_WAIT));
|
|
|
|
// Save the current lock
|
|
SaveLock();
|
|
|
|
// Get the Page Order Lock (with an invisible dialog)
|
|
BOOL bGotLock = GetLock(WB_LOCK_TYPE_PAGE_ORDER, SW_HIDE);
|
|
|
|
if (!bGotLock)
|
|
{
|
|
RestoreLock();
|
|
goto UserPointerCleanup;
|
|
}
|
|
|
|
// Load the file
|
|
uRes = g_pwbCore->WBP_ContentsLoad(szLoadFileName);
|
|
if (uRes != 0)
|
|
{
|
|
DefaultExceptionHandler(WBFE_RC_WB, uRes);
|
|
return;
|
|
}
|
|
|
|
// Set the window title to the new file name
|
|
lstrcpy(m_strFileName, szLoadFileName);
|
|
|
|
// Update the window title with the new file name
|
|
UpdateWindowTitle();
|
|
|
|
// Set the state to say that we are loading a file
|
|
SetSubstate(SUBSTATE_LOADING);
|
|
}
|
|
|
|
UserPointerCleanup:
|
|
|
|
// Restore the cursor
|
|
::SetCursor(::LoadCursor(NULL, IDC_ARROW));
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnDropFiles
|
|
//
|
|
// Purpose: Files have been dropped onto the Whiteboard window
|
|
//
|
|
//
|
|
void WbMainWindow::OnDropFiles(HDROP hDropInfo)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnDropFiles");
|
|
|
|
UINT uiFilesDropped = 0;
|
|
UINT eachfile;
|
|
|
|
// Get the total number of files dropped
|
|
uiFilesDropped = ::DragQueryFile(hDropInfo, (UINT) -1, NULL, (UINT) 0);
|
|
|
|
// release mouse capture in case we report any errors (message boxes
|
|
// won't repsond to mouse clicks if we don't)
|
|
ReleaseCapture();
|
|
|
|
if( UsersMightLoseData( NULL, NULL ) ) // bug NM4db:418
|
|
goto bail_out;
|
|
|
|
// Don't prompt to save file if we're already loading
|
|
int iOnSave;
|
|
if( m_uiSubState != SUBSTATE_LOADING )
|
|
{
|
|
// Check whether there are changes to be saved
|
|
iOnSave = QuerySaveRequired(TRUE);
|
|
}
|
|
else
|
|
{
|
|
goto bail_out;
|
|
}
|
|
|
|
if( iOnSave == IDYES )
|
|
{
|
|
// User wants to save the drawing area contents
|
|
int iResult = OnSave(TRUE);
|
|
|
|
if( iResult == IDOK )
|
|
{
|
|
// Update the window title with the new file name
|
|
UpdateWindowTitle();
|
|
}
|
|
else
|
|
{
|
|
// cancelled out of save, so cancel the open operation
|
|
goto bail_out;
|
|
}
|
|
}
|
|
|
|
// see if user canceled the whole drop
|
|
if( iOnSave == IDCANCEL )
|
|
goto bail_out;
|
|
|
|
for (eachfile = 0; eachfile < uiFilesDropped; eachfile++)
|
|
{
|
|
// Retrieve each file name
|
|
char szDropFileName[256];
|
|
|
|
::DragQueryFile(hDropInfo, eachfile,
|
|
szDropFileName, 256);
|
|
|
|
TRACE_MSG(("Loading file: %s", szDropFileName));
|
|
|
|
// Load the file
|
|
// If this is a valid whiteboard file, the action is simply to load it
|
|
if (g_pwbCore->WBP_ValidateFile(szDropFileName, NULL) == 0)
|
|
{
|
|
LoadFile(szDropFileName);
|
|
}
|
|
else
|
|
{
|
|
::Message(NULL, IDS_MSG_CAPTION,IDS_MSG_BAD_FILE_FORMAT);
|
|
}
|
|
}
|
|
|
|
bail_out:
|
|
::DragFinish(hDropInfo);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnOpen
|
|
//
|
|
// Purpose: Load a metafile into the application.
|
|
//
|
|
//
|
|
void WbMainWindow::OnOpen(void)
|
|
{
|
|
int iOnSave;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnOpen");
|
|
|
|
if( UsersMightLoseData( NULL, NULL ) ) // bug NM4db:418
|
|
return;
|
|
|
|
// Check we're in idle state
|
|
if ( (m_uiState != IN_CALL) || (m_uiSubState == SUBSTATE_NEW_IN_PROGRESS))
|
|
{
|
|
// post an error message indicating the whiteboard is busy
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_BUSY);
|
|
return;
|
|
}
|
|
|
|
// Don't prompt to save file if we're already loading
|
|
if (m_uiSubState != SUBSTATE_LOADING)
|
|
{
|
|
// Check whether there are changes to be saved
|
|
iOnSave = QuerySaveRequired(TRUE);
|
|
}
|
|
else
|
|
{
|
|
iOnSave = IDNO;
|
|
}
|
|
|
|
if (iOnSave == IDYES)
|
|
{
|
|
// User wants to save the drawing area contents
|
|
int iResult = OnSave(TRUE);
|
|
|
|
if (iResult == IDOK)
|
|
{
|
|
UpdateWindowTitle();
|
|
}
|
|
else
|
|
{
|
|
// cancelled out of Save As, so cancel the open operation
|
|
iOnSave = IDCANCEL;
|
|
}
|
|
}
|
|
|
|
// Only continue if the user has not cancelled the operation
|
|
if (iOnSave != IDCANCEL)
|
|
{
|
|
OPENFILENAME ofn;
|
|
TCHAR szFileName[_MAX_PATH];
|
|
TCHAR szFileTitle[64];
|
|
TCHAR strLoadFilter[2*_MAX_PATH];
|
|
TCHAR strDefaultExt[_MAX_PATH];
|
|
TCHAR strDefaultPath[2*_MAX_PATH];
|
|
TCHAR * pStr;
|
|
UINT strSize = 0;
|
|
UINT totalSize;
|
|
|
|
// Build the filter for loadable files
|
|
pStr = strLoadFilter;
|
|
totalSize = 2*_MAX_PATH;
|
|
|
|
// These must be NULL separated, with a double NULL at the end
|
|
strSize = ::LoadString(g_hInstance, IDS_FILTER_WHT, pStr, totalSize) + 1;
|
|
pStr += strSize;
|
|
ASSERT(totalSize > strSize);
|
|
totalSize -= strSize;
|
|
|
|
strSize = ::LoadString(g_hInstance, IDS_FILTER_WHT_SPEC, pStr, totalSize) + 1;
|
|
pStr += strSize;
|
|
ASSERT(totalSize > strSize);
|
|
totalSize -= strSize;
|
|
|
|
strSize = ::LoadString(g_hInstance, IDS_FILTER_ALL, pStr, totalSize) + 1;
|
|
pStr += strSize;
|
|
ASSERT(totalSize > strSize);
|
|
totalSize -= strSize;
|
|
|
|
strSize = ::LoadString(g_hInstance, IDS_FILTER_ALL_SPEC, pStr, totalSize) + 1;
|
|
pStr += strSize;
|
|
ASSERT(totalSize > strSize);
|
|
totalSize -= strSize;
|
|
|
|
*pStr = 0;
|
|
|
|
//
|
|
// Setup the OPENFILENAME struct
|
|
//
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = m_hwnd;
|
|
|
|
// No file name supplied to begin with
|
|
szFileName[0] = 0;
|
|
ofn.lpstrFile = szFileName;
|
|
ofn.nMaxFile = _MAX_PATH;
|
|
|
|
// Default Extension: .WHT
|
|
::LoadString(g_hInstance, IDS_EXT_WHT, strDefaultExt, sizeof(strDefaultExt));
|
|
ofn.lpstrDefExt = strDefaultExt;
|
|
|
|
// Default file title is empty
|
|
szFileTitle[0] = 0;
|
|
ofn.lpstrFileTitle = szFileTitle;
|
|
ofn.nMaxFileTitle = 64;
|
|
|
|
// Open flags
|
|
ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_EXPLORER;
|
|
ofn.hInstance = g_hInstance;
|
|
|
|
// Filter
|
|
ofn.lpstrFilter = strLoadFilter;
|
|
|
|
// Default path
|
|
if (GetDefaultPath(strDefaultPath, sizeof(strDefaultPath)))
|
|
ofn.lpstrInitialDir = strDefaultPath;
|
|
|
|
// Get user input, continue only if the user selects the OK button
|
|
if (::GetOpenFileName(&ofn))
|
|
{
|
|
// Change the cursor to "wait"
|
|
::SetCursor(::LoadCursor(NULL, IDC_WAIT));
|
|
|
|
// if we're currently loading a file, cancel it, not releasing
|
|
// the page order lock, because we need it immediately afterwards
|
|
if (m_uiSubState == SUBSTATE_LOADING)
|
|
{
|
|
CancelLoad(FALSE);
|
|
}
|
|
|
|
// Load the file
|
|
LoadFile(ofn.lpstrFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: GetFileName
|
|
//
|
|
// Purpose: Get a file name for saving the contents
|
|
//
|
|
//
|
|
int WbMainWindow::GetFileName(void)
|
|
{
|
|
OPENFILENAME ofn;
|
|
int iResult;
|
|
TCHAR szFileTitle[64];
|
|
TCHAR strSaveFilter[2*_MAX_PATH];
|
|
TCHAR strDefaultExt[_MAX_PATH];
|
|
TCHAR strDefaultPath[2 * _MAX_PATH];
|
|
TCHAR szFileName[2*_MAX_PATH];
|
|
TCHAR * pStr;
|
|
UINT strSize = 0;
|
|
UINT totalSize;
|
|
|
|
//
|
|
// If we are already displaying a "Save As" dialog, dismiss it and create
|
|
// a new one. This can happen if Win95 shuts down whilst WB is
|
|
// displaying the "Save As" dialog and the use selects "Yes" when asked
|
|
// whether they want to save the contents - a second "Save As dialog
|
|
// appears on top of the first.
|
|
//
|
|
if (m_bInSaveDialog)
|
|
{
|
|
CancelSaveDialog();
|
|
}
|
|
|
|
// Build the filter for save files
|
|
pStr = strSaveFilter;
|
|
totalSize = 2*_MAX_PATH;
|
|
|
|
// These must be NULL separated, with a double NULL at the end
|
|
strSize = ::LoadString(g_hInstance, IDS_FILTER_WHT, pStr, totalSize) + 1;
|
|
pStr += strSize;
|
|
ASSERT(totalSize > strSize);
|
|
totalSize -= strSize;
|
|
|
|
strSize = ::LoadString(g_hInstance, IDS_FILTER_WHT_SPEC, pStr, totalSize) + 1;
|
|
pStr += strSize;
|
|
ASSERT(totalSize > strSize);
|
|
totalSize -= strSize;
|
|
|
|
strSize = ::LoadString(g_hInstance, IDS_FILTER_ALL, pStr, totalSize) + 1;
|
|
pStr += strSize;
|
|
ASSERT(totalSize > strSize);
|
|
totalSize -= strSize;
|
|
|
|
strSize = ::LoadString(g_hInstance, IDS_FILTER_ALL_SPEC, pStr, totalSize) + 1;
|
|
pStr += strSize;
|
|
ASSERT(totalSize > strSize);
|
|
totalSize -= strSize;
|
|
|
|
*pStr = 0;
|
|
|
|
//
|
|
// Setup the OPENFILENAME struct
|
|
//
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = m_hwnd;
|
|
|
|
lstrcpy(szFileName, m_strFileName);
|
|
ofn.lpstrFile = szFileName;
|
|
ofn.nMaxFile = _MAX_PATH;
|
|
|
|
// Build the default extension string
|
|
::LoadString(g_hInstance, IDS_EXT_WHT, strDefaultExt, sizeof(strDefaultExt));
|
|
ofn.lpstrDefExt = strDefaultExt;
|
|
|
|
szFileTitle[0] = 0;
|
|
ofn.lpstrFileTitle = szFileTitle;
|
|
ofn.nMaxFileTitle = 64;
|
|
|
|
// Save flags
|
|
ofn.Flags = OFN_HIDEREADONLY | OFN_NOREADONLYRETURN |
|
|
OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
|
|
ofn.hInstance = g_hInstance;
|
|
|
|
// Filter
|
|
ofn.lpstrFilter = strSaveFilter;
|
|
|
|
// Default path
|
|
if (GetDefaultPath(strDefaultPath, sizeof(strDefaultPath)))
|
|
ofn.lpstrInitialDir = strDefaultPath;
|
|
|
|
m_bInSaveDialog = TRUE;
|
|
|
|
if (::GetSaveFileName(&ofn))
|
|
{
|
|
// The user selected OK
|
|
iResult = IDOK;
|
|
lstrcpy(m_strFileName, szFileName);
|
|
}
|
|
else
|
|
{
|
|
iResult = IDCANCEL;
|
|
}
|
|
|
|
m_bInSaveDialog = FALSE;
|
|
|
|
return iResult;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnSave
|
|
//
|
|
// Purpose: Save the contents of the Whiteboard using the current file
|
|
// name (or prompting for a new name if there is no current).
|
|
//
|
|
//
|
|
int WbMainWindow::OnSave(BOOL bPrompt)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnSave");
|
|
|
|
int iResult = IDOK;
|
|
|
|
// save the old file name in case there's an error
|
|
TCHAR *strOldName;
|
|
UINT fileNameSize = lstrlen(m_strFileName);
|
|
strOldName = new TCHAR[fileNameSize+1];
|
|
|
|
if (!strOldName)
|
|
{
|
|
ERROR_OUT(("OnSave: failed to allocate strOldName TCHAR array, fail"));
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_BUSY);
|
|
return(iResult);
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(strOldName, m_strFileName);
|
|
}
|
|
|
|
BOOL bNewName = FALSE;
|
|
|
|
if (!IsIdle())
|
|
{
|
|
// post an error message indicating the whiteboard is busy
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_BUSY);
|
|
return(iResult);
|
|
}
|
|
|
|
// Check whether there is a filename available for use
|
|
if (!fileNameSize || bPrompt)
|
|
{
|
|
// Get user input, continue only if the user selects the OK button
|
|
iResult = GetFileName();
|
|
|
|
if (iResult == IDOK)
|
|
{
|
|
// entering a blank file name is treated as cancelling the save
|
|
if (!lstrlen(m_strFileName))
|
|
{
|
|
lstrcpy(m_strFileName, strOldName);
|
|
iResult = IDCANCEL;
|
|
}
|
|
else
|
|
{
|
|
// flag that we've changed the contents file name
|
|
bNewName = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now save the file
|
|
if ((iResult == IDOK) && lstrlen(m_strFileName))
|
|
{
|
|
WIN32_FIND_DATA findFileData;
|
|
HANDLE hFind;
|
|
|
|
// Get attributes
|
|
hFind = ::FindFirstFile(m_strFileName, &findFileData);
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
{
|
|
::FindClose(hFind);
|
|
|
|
// This is a read-only file; we can't change its contents
|
|
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
|
|
{
|
|
WARNING_OUT(("Dest file %s is read only", m_strFileName));
|
|
::Message(NULL, IDS_SAVE, IDS_SAVE_READ_ONLY);
|
|
|
|
// If the file name was changed for this save then undo
|
|
// the change
|
|
if (bNewName)
|
|
{
|
|
lstrcpy(m_strFileName, strOldName);
|
|
bNewName = FALSE;
|
|
}
|
|
|
|
// Change the return code to indicate no save was made
|
|
iResult = IDCANCEL;
|
|
return(iResult);
|
|
}
|
|
}
|
|
|
|
// Change the cursor to "wait"
|
|
::SetCursor(::LoadCursor(NULL,IDC_WAIT));
|
|
|
|
// Write the file
|
|
if (g_pwbCore->WBP_ContentsSave(m_strFileName) != 0)
|
|
{
|
|
// Show that an error occurred saving the file.
|
|
WARNING_OUT(("Error saving file"));
|
|
::Message(NULL, IDS_SAVE, IDS_SAVE_ERROR);
|
|
|
|
// If the file name was changed for this save then undo
|
|
// the change
|
|
if (bNewName)
|
|
{
|
|
lstrcpy(m_strFileName, strOldName);
|
|
bNewName = FALSE;
|
|
}
|
|
|
|
// Change the return code to indicate no save was made
|
|
iResult = IDCANCEL;
|
|
}
|
|
|
|
// Restore the cursor
|
|
::SetCursor(::LoadCursor(NULL,IDC_ARROW));
|
|
}
|
|
|
|
// if the contents file name has changed as a result of the save then
|
|
// update the window title
|
|
if (bNewName)
|
|
{
|
|
UpdateWindowTitle();
|
|
}
|
|
|
|
delete [] strOldName;
|
|
return(iResult);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// CancelSaveDialog()
|
|
// This cancels the save as dialog if up and we need to kill it to continue.
|
|
// We walk back up the owner chain in case the save dialog puts up help or
|
|
// other owned windows.
|
|
//
|
|
void WbMainWindow::CancelSaveDialog(void)
|
|
{
|
|
WBFINDDIALOG wbf;
|
|
|
|
ASSERT(m_bInSaveDialog);
|
|
|
|
wbf.hwndOwner = m_hwnd;
|
|
wbf.hwndDialog = NULL;
|
|
EnumThreadWindows(::GetCurrentThreadId(), WbFindCurrentDialog, (LPARAM)&wbf);
|
|
|
|
if (wbf.hwndDialog)
|
|
{
|
|
// Found it!
|
|
::SendMessage(wbf.hwndDialog, WM_COMMAND, IDCANCEL, 0);
|
|
}
|
|
|
|
m_bInSaveDialog = FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL CALLBACK WbFindCurrentDialog(HWND hwndNext, LPARAM lParam)
|
|
{
|
|
WBFINDDIALOG * pwbf = (WBFINDDIALOG *)lParam;
|
|
|
|
// Is this a dialog, owned by the main window?
|
|
if ((::GetClassLong(hwndNext, GCW_ATOM) == 0x8002) &&
|
|
(::GetWindow(hwndNext, GW_OWNER) == pwbf->hwndOwner))
|
|
{
|
|
pwbf->hwndDialog = hwndNext;
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnClose
|
|
//
|
|
// Purpose: Close the Whiteboard
|
|
//
|
|
//
|
|
void WbMainWindow::OnClose()
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnClose");
|
|
|
|
int iOnSave = IDOK;
|
|
|
|
KillInitDlg();
|
|
|
|
m_drawingArea.CancelDrawingMode();
|
|
m_drawingArea.RemoveMarker(NULL);
|
|
m_drawingArea.GetMarker()->DeleteAllMarkers( NULL );
|
|
|
|
m_AG.SaveSettings();
|
|
|
|
// If we got here, by way of OnDestroy from the DCL cores or
|
|
// by system shutdown, then assume that user responded already to the
|
|
// save-changes dialog that would have poped up during conf's global shutdown
|
|
// message. We don't need to ask 'em again. What tangled webs......
|
|
if ((!m_bQuerySysShutdown) && (IsIdle()))
|
|
{
|
|
// Check whether there are changes to be saved
|
|
iOnSave = QuerySaveRequired(TRUE);
|
|
if (iOnSave == IDYES)
|
|
{
|
|
// User wants to save the drawing area contents
|
|
iOnSave = OnSave(TRUE);
|
|
}
|
|
}
|
|
|
|
// If the exit was not cancelled, close the application
|
|
if (iOnSave != IDCANCEL)
|
|
{
|
|
// Mark state as closing - stops any queued events being processed
|
|
m_uiState = CLOSING;
|
|
|
|
//PUTBACK BY RAND - the progress timer meter is kinda the heart beat
|
|
// of this thing which I ripped out when I removed the
|
|
// progress meter. I put it back to fix 1476.
|
|
if (m_bTimerActive)
|
|
{
|
|
::KillTimer(m_hwnd, TIMERID_PROGRESS_METER);
|
|
m_bTimerActive = FALSE;
|
|
}
|
|
|
|
m_drawingArea.ShutDownDC();
|
|
|
|
// Close the application
|
|
::PostQuitMessage(0);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnClearPage
|
|
//
|
|
// Purpose: Clear the Whiteboard drawing area. The user is prompted to
|
|
// choose clearing of foreground, background or both.
|
|
//
|
|
//
|
|
void WbMainWindow::OnClearPage(void)
|
|
{
|
|
int iResult;
|
|
BOOL bWasPosted;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnClearPage");
|
|
|
|
if( UsersMightLoseData( &bWasPosted, NULL ) ) // bug NM4db:418
|
|
return;
|
|
|
|
if( bWasPosted )
|
|
iResult = IDYES;
|
|
else
|
|
iResult = ::Message(NULL, IDS_CLEAR_CAPTION, IDS_CLEAR_MESSAGE, MB_YESNO | MB_ICONQUESTION);
|
|
|
|
|
|
if (iResult == IDYES)
|
|
{
|
|
TRACE_MSG(("User requested clear of page"));
|
|
|
|
// lock the drawing area
|
|
LockDrawingArea();
|
|
|
|
// Save the current lock status
|
|
SaveLock();
|
|
|
|
// Get the Page Order Lock (with an invisible dialog)
|
|
BOOL bGotLock = GetLock(WB_LOCK_TYPE_PAGE_ORDER, SW_HIDE);
|
|
|
|
if( bGotLock )
|
|
{
|
|
// clear only if we got the page lock (NM4db:470)
|
|
m_drawingArea.Clear();
|
|
GotoPosition(0, 0);
|
|
}
|
|
|
|
RestoreLock();
|
|
UnlockDrawingArea();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnDelete
|
|
//
|
|
// Purpose: Delete the current selection
|
|
//
|
|
//
|
|
void WbMainWindow::OnDelete()
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnDelete");
|
|
|
|
DCWbGraphic* pGraphicCopy = NULL;
|
|
|
|
// cleanup select logic in case object context menu called us (bug 426)
|
|
m_drawingArea.SetLClickIgnore( FALSE );
|
|
|
|
// If the user currently has a graphic selected
|
|
if (m_drawingArea.GraphicSelected())
|
|
{
|
|
m_LastDeletedGraphic.BurnTrash();
|
|
|
|
// Delete the currently selected graphic and add to m_LastDeletedGraphic
|
|
m_drawingArea.DeleteSelection();
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnUndelete
|
|
//
|
|
// Purpose: Undo the last delete operation
|
|
//
|
|
//
|
|
void WbMainWindow::OnUndelete()
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnUndelete");
|
|
|
|
// If there is a deleted graphic to restore
|
|
if ( m_LastDeletedGraphic.GotTrash() )
|
|
{
|
|
// If the deleted graphic belongs to the current page
|
|
if (m_LastDeletedGraphic.Page() == m_hCurrentPage)
|
|
{
|
|
// Add the graphic back into the current page
|
|
m_LastDeletedGraphic.AddToPageLast(m_hCurrentPage);
|
|
|
|
// if the current tool is a select tool then select the new
|
|
// graphic, otherwise forget it.
|
|
if (m_pCurrentTool->ToolType() == TOOLTYPE_SELECT)
|
|
{
|
|
m_LastDeletedGraphic.SelectTrash();
|
|
m_LastDeletedGraphic.EmptyTrash();
|
|
}
|
|
else
|
|
{
|
|
// Free the local copy
|
|
m_LastDeletedGraphic.BurnTrash();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void WbMainWindow::OnSelectAll( void )
|
|
{
|
|
// turn off any selections
|
|
// cleanup select logic in case object context menu called us (bug 426)
|
|
m_drawingArea.SetLClickIgnore( FALSE );
|
|
|
|
// inhibit normal select-tool action
|
|
m_bSelectAllInProgress = TRUE;
|
|
|
|
//put us in select-tool mode first
|
|
OnSelectTool(IDM_SELECT);
|
|
|
|
// back to normal
|
|
m_bSelectAllInProgress = FALSE;
|
|
|
|
// now, select everything
|
|
m_drawingArea.SelectMarkerFromRect( NULL );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: DoCopy
|
|
//
|
|
// Purpose: Copy the current selection to the clipboard
|
|
//
|
|
//
|
|
BOOL WbMainWindow::DoCopy(BOOL bRenderNow)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
DCWbGraphicMarker *pMarker;
|
|
|
|
DCWbGraphic* pGraphic = m_drawingArea.GetSelection();
|
|
if (pGraphic != NULL)
|
|
{
|
|
pMarker = m_drawingArea.GetMarker();
|
|
if( pMarker->GetNumMarkers() > 1 )
|
|
{
|
|
// more objs than just pGraphic, do a multi-object-to-clipboard
|
|
// operation.
|
|
pGraphic = pMarker;
|
|
}
|
|
//else if == 1 then pMarker contains just pGraphic already
|
|
// so we do a single-object-to-clipboard operation.
|
|
|
|
// Copy the graphic (or multiple marker objects) to the clipboard
|
|
bResult = CLP_Copy(pGraphic, bRenderNow);
|
|
|
|
// If an error occurred during the copy, report it now
|
|
if (!bResult)
|
|
{
|
|
::Message(NULL, IDS_COPY, IDS_COPY_ERROR);
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnCut
|
|
//
|
|
// Purpose: Cut the current selection
|
|
//
|
|
//
|
|
void WbMainWindow::OnCut()
|
|
{
|
|
// cleanup select logic in case object context menu called us (bug 426)
|
|
m_drawingArea.SetLClickIgnore( FALSE );
|
|
|
|
if (m_drawingArea.TextEditActive())
|
|
{
|
|
m_drawingArea.TextEditCut();
|
|
return;
|
|
}
|
|
|
|
if (DoCopy(TRUE))
|
|
{
|
|
// Graphic copied to the clipboard OK - delete it
|
|
m_drawingArea.DeleteSelection();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// OnCopy()
|
|
// Purpose: Copy the current selection to the clipboard
|
|
//
|
|
//
|
|
void WbMainWindow::OnCopy(void)
|
|
{
|
|
// cleanup select logic in case object context menu called us (bug 426)
|
|
m_drawingArea.SetLClickIgnore( FALSE );
|
|
|
|
if( m_drawingArea.TextEditActive() )
|
|
{
|
|
m_drawingArea.TextEditCopy();
|
|
return;
|
|
}
|
|
|
|
DoCopy(TRUE);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnPaste
|
|
//
|
|
// Purpose: Paste the contents of the clipboard into the drawing pane
|
|
//
|
|
//
|
|
void WbMainWindow::OnPaste()
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnPaste");
|
|
|
|
// cleanup select logic in case object context menu called us (bug 426)
|
|
m_drawingArea.SetLClickIgnore( FALSE );
|
|
|
|
if (m_drawingArea.TextEditActive())
|
|
{
|
|
m_drawingArea.TextEditPaste();
|
|
return;
|
|
}
|
|
|
|
// Get the data from the clipboard
|
|
DCWbGraphic* pGraphic = CLP_Paste();
|
|
if (pGraphic != NULL)
|
|
{
|
|
TRACE_MSG(("Got graphic object from clipboard OK"));
|
|
|
|
//CHANGED BY RAND - have to handle marker sperately,
|
|
// marker objects are already added to
|
|
// m_hCurrentPage and positioned
|
|
if( pGraphic->IsGraphicTool() == enumGraphicMarker)
|
|
{
|
|
((DCWbGraphicMarker *)pGraphic)->Update();
|
|
if( m_pCurrentTool->ToolType() == TOOLTYPE_SELECT )
|
|
{
|
|
// marker is already setup, just draw it
|
|
m_drawingArea.PutMarker(NULL);
|
|
}
|
|
else
|
|
{
|
|
// don't select anything, dump marker
|
|
m_drawingArea.RemoveMarker(NULL);
|
|
}
|
|
}
|
|
else // not a marker, deal with single object
|
|
{
|
|
RECT rcVis;
|
|
|
|
// Position the graphic at the top left of the visible area of the
|
|
// drawing area
|
|
m_drawingArea.GetVisibleRect(&rcVis);
|
|
pGraphic->MoveTo(rcVis.left, rcVis.top);
|
|
|
|
// Add the graphic to the page
|
|
pGraphic->AddToPageLast(m_hCurrentPage);
|
|
|
|
// if the current tool is a select tool then select the new
|
|
// object, otherwise forget it.
|
|
if( m_pCurrentTool->ToolType() == TOOLTYPE_SELECT )
|
|
m_drawingArea.SelectGraphic(pGraphic);
|
|
else
|
|
{
|
|
// Free the graphic
|
|
delete pGraphic;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE_MSG(("Could not get graphic from clipboard"));
|
|
// display error message instead of throwing exception
|
|
::Message(NULL, IDS_PASTE, IDS_PASTE_ERROR);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnRenderAllFormats
|
|
//
|
|
// Purpose: Render all formats of the graphic last copied to the
|
|
// CLP_
|
|
//
|
|
//
|
|
void WbMainWindow::OnRenderAllFormats(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnRenderAllFormats");
|
|
//
|
|
// only render something if we have not done it already
|
|
//
|
|
if (CLP_LastCopiedPage() != WB_PAGE_HANDLE_NULL)
|
|
{
|
|
if (!CLP_RenderAllFormats())
|
|
{
|
|
// An error occurred rendering the formats
|
|
ERROR_OUT(("Error rendering all formats"));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: CheckMenuItem
|
|
//
|
|
// Purpose: Check an item on the application menus (main and context
|
|
// menu.)
|
|
//
|
|
//
|
|
void WbMainWindow::CheckMenuItem(UINT uiId)
|
|
{
|
|
CheckMenuItemRecursive(::GetMenu(m_hwnd), uiId, MF_BYCOMMAND | MF_CHECKED);
|
|
CheckMenuItemRecursive(m_hContextMenu, uiId, MF_BYCOMMAND | MF_CHECKED);
|
|
CheckMenuItemRecursive(m_hGrobjContextMenu, uiId, MF_BYCOMMAND | MF_CHECKED); // bug 426
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: UncheckMenuItem
|
|
//
|
|
// Purpose: Uncheck an item on the application menus (main and context
|
|
// menus.)
|
|
//
|
|
//
|
|
void WbMainWindow::UncheckMenuItem(UINT uiId)
|
|
{
|
|
CheckMenuItemRecursive(::GetMenu(m_hwnd), uiId, MF_BYCOMMAND | MF_UNCHECKED);
|
|
CheckMenuItemRecursive(m_hContextMenu, uiId, MF_BYCOMMAND | MF_UNCHECKED);
|
|
CheckMenuItemRecursive(m_hGrobjContextMenu, uiId, MF_BYCOMMAND | MF_UNCHECKED); // bug 426
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: CheckMenuItemRecursive
|
|
//
|
|
// Purpose: Check or uncheck an item on the any of the Whiteboard menus.
|
|
// This function recursively searches through the menus until
|
|
// it finds the specified item. The menu item Ids must be
|
|
// unique for this function to work.
|
|
//
|
|
//
|
|
BOOL WbMainWindow::CheckMenuItemRecursive(HMENU hMenu,
|
|
UINT uiId,
|
|
BOOL bCheck)
|
|
{
|
|
UINT uiNumItems = ::GetMenuItemCount(hMenu);
|
|
|
|
// Attempt to check the menu item
|
|
UINT uiCheck = MF_BYCOMMAND | (bCheck ? MF_CHECKED : MF_UNCHECKED);
|
|
|
|
// A return code of -1 from CheckMenuItem implies that
|
|
// the menu item was not found
|
|
BOOL bChecked = ((::CheckMenuItem(hMenu, uiId, uiCheck) == -1) ? FALSE : TRUE);
|
|
if (bChecked)
|
|
{
|
|
//
|
|
// If this item is on the active menu, ensure it's redrawn now
|
|
//
|
|
if (hMenu == m_hInitMenu)
|
|
{
|
|
InvalidateActiveMenu();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UINT uiPos;
|
|
HMENU hSubMenu;
|
|
|
|
// Recurse through the submenus of the specified menu
|
|
for (uiPos = 0; uiPos < uiNumItems; uiPos++)
|
|
{
|
|
// Assume that the next item is a submenu
|
|
// and try to get a pointer to it
|
|
hSubMenu = ::GetSubMenu(hMenu, (int)uiPos);
|
|
|
|
// NULL return implies the item is a not submenu
|
|
if (hSubMenu != NULL)
|
|
{
|
|
// Item is a submenu, make recursive call to search it
|
|
bChecked = CheckMenuItemRecursive(hSubMenu, uiId, bCheck);
|
|
if (bChecked)
|
|
{
|
|
// We have found the item
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bChecked;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: GetMenuWithItem
|
|
//
|
|
// Purpose: Return the menu which contains the specified item.
|
|
//
|
|
//
|
|
HMENU WbMainWindow::GetMenuWithItem(HMENU hMenu, UINT uiID)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::GetMenuWithItem");
|
|
|
|
ASSERT(hMenu != NULL);
|
|
|
|
HMENU hMenuResult = NULL;
|
|
|
|
// Get the number ofitems in the menu
|
|
UINT uiNumItems = ::GetMenuItemCount(hMenu);
|
|
UINT uiPos;
|
|
UINT uiNextID;
|
|
|
|
// Look for the item through the menu
|
|
for (uiPos = 0; uiPos < uiNumItems; uiPos++)
|
|
{
|
|
// Get the ID of the item at this position
|
|
uiNextID = ::GetMenuItemID(hMenu, uiPos);
|
|
|
|
if (uiNextID == uiID)
|
|
{
|
|
// We have found the item
|
|
hMenuResult = hMenu;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we have not yet found the item
|
|
if (hMenuResult == NULL)
|
|
{
|
|
// Look through each of the submenus of the current menu
|
|
HMENU hSubMenu;
|
|
|
|
for (uiPos = 0; uiPos < uiNumItems; uiPos++)
|
|
{
|
|
// Get the ID of the item at this position
|
|
uiNextID = ::GetMenuItemID(hMenu, uiPos);
|
|
|
|
// If the item is a submenu
|
|
if (uiNextID == -1)
|
|
{
|
|
// Get the submenu
|
|
hSubMenu = ::GetSubMenu(hMenu, (int) uiPos);
|
|
|
|
// Search the submenu
|
|
hMenuResult = GetMenuWithItem(hSubMenu, uiID);
|
|
if (hMenuResult != NULL)
|
|
{
|
|
// We have found the menu with the requested item
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hMenuResult;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnScrollAccelerator
|
|
//
|
|
// Purpose: Called when a scroll accelerator is used
|
|
//
|
|
//
|
|
void WbMainWindow::OnScrollAccelerator(UINT uiMenuId)
|
|
{
|
|
int iScroll;
|
|
|
|
// Locate the scroll messages to be sent in the conversion table
|
|
for (iScroll = 0; iScroll < ARRAYSIZE(s_MenuToScroll); iScroll++)
|
|
{
|
|
if (s_MenuToScroll[iScroll].uiMenuId == uiMenuId)
|
|
{
|
|
// Found it;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Send the messages
|
|
if (iScroll < ARRAYSIZE(s_MenuToScroll))
|
|
{
|
|
while ((s_MenuToScroll[iScroll].uiMenuId == uiMenuId) && (iScroll < ARRAYSIZE(s_MenuToScroll)))
|
|
{
|
|
// Tell the drawing pane to scroll
|
|
::PostMessage(m_drawingArea.m_hwnd, s_MenuToScroll[iScroll].uiMessage,
|
|
s_MenuToScroll[iScroll].uiScrollCode, 0);
|
|
|
|
iScroll++;
|
|
}
|
|
|
|
// Indicate that scrolling has completed (in both directions)
|
|
::PostMessage(m_drawingArea.m_hwnd, WM_HSCROLL, SB_ENDSCROLL, 0L);
|
|
::PostMessage(m_drawingArea.m_hwnd, WM_VSCROLL, SB_ENDSCROLL, 0L);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnZoom
|
|
//
|
|
// Purpose: Zoom or unzoom the drawing area
|
|
//
|
|
//
|
|
void WbMainWindow::OnZoom()
|
|
{
|
|
// If the drawing area is currently zoomed
|
|
if (m_drawingArea.Zoomed())
|
|
{
|
|
// Remove the zoomed check mark
|
|
UncheckMenuItem(IDM_ZOOM);
|
|
|
|
// Tell the tool bar of the new selection
|
|
m_TB.PopUp(IDM_ZOOM);
|
|
|
|
// Inform the local user of the zoom state
|
|
if (m_pLocalUser != NULL)
|
|
m_pLocalUser->Unzoom();
|
|
}
|
|
else
|
|
{
|
|
// Set the zoomed check mark
|
|
CheckMenuItem(IDM_ZOOM);
|
|
|
|
// Tell the tool bar of the new selection
|
|
m_TB.PushDown(IDM_ZOOM);
|
|
|
|
// Inform the local user of the zoom state
|
|
if (m_pLocalUser != NULL)
|
|
m_pLocalUser->Zoom();
|
|
}
|
|
|
|
// Zoom/unzoom the drawing area
|
|
m_drawingArea.Zoom();
|
|
|
|
// Restore the focus to the drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnSelectTool
|
|
//
|
|
// Purpose: Select the current tool
|
|
//
|
|
//
|
|
void WbMainWindow::OnSelectTool(UINT uiMenuId)
|
|
{
|
|
UINT uiIndex;
|
|
|
|
UncheckMenuItem(m_currentMenuTool);
|
|
CheckMenuItem( uiMenuId);
|
|
|
|
// Save the new menu Id
|
|
m_currentMenuTool = uiMenuId;
|
|
|
|
// Tell the tool bar of the new selection
|
|
m_TB.PushDown(m_currentMenuTool);
|
|
|
|
// Get the new tool
|
|
m_pCurrentTool = m_ToolArray[TOOL_INDEX(m_currentMenuTool)];
|
|
|
|
// Set the current attributes
|
|
if( !m_bSelectAllInProgress )
|
|
{
|
|
m_AG.SetChoiceColor(m_pCurrentTool->GetColor() );
|
|
|
|
::SendMessage(m_hwnd, WM_COMMAND, IDM_COLOR, 0L);
|
|
::SendMessage(m_hwnd, WM_COMMAND, IDM_WIDTHS_START + m_pCurrentTool->GetWidthIndex(), 0L);//CHANGED BY RAND
|
|
}
|
|
|
|
// Report the change of tool to the attributes group
|
|
m_AG.DisplayTool(m_pCurrentTool);
|
|
|
|
// Select the new tool into the drawing area
|
|
m_drawingArea.SelectTool(m_pCurrentTool);
|
|
|
|
// Restore the focus to the drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnSelectColor
|
|
//
|
|
// Purpose: Set the current color
|
|
//
|
|
//
|
|
void WbMainWindow::OnSelectColor(void)
|
|
{
|
|
// Tell the attributes group of the new selection and get the
|
|
// new color value selected ino the current tool.
|
|
m_AG.SelectColor(m_pCurrentTool);
|
|
|
|
// Select the changed tool into the drawing area
|
|
m_drawingArea.SelectTool(m_pCurrentTool);
|
|
|
|
// If we are using a select tool, change the color of the selected object
|
|
if (m_pCurrentTool->ToolType() == TOOLTYPE_SELECT)
|
|
{
|
|
// If there is an object marked for changing
|
|
if (m_drawingArea.GraphicSelected())
|
|
{
|
|
// Update the object
|
|
m_drawingArea.SetSelectionColor(m_pCurrentTool->GetColor());
|
|
}
|
|
}
|
|
|
|
// if currently editing a text object then change its color
|
|
if ( (m_pCurrentTool->ToolType() == TOOLTYPE_TEXT)
|
|
&& (m_drawingArea.TextEditActive()))
|
|
{
|
|
m_drawingArea.SetSelectionColor(m_pCurrentTool->GetColor());
|
|
}
|
|
|
|
// Restore the focus to the drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnSelectWidth
|
|
//
|
|
// Purpose: Set the current nib width
|
|
//
|
|
//
|
|
void WbMainWindow::OnSelectWidth(UINT uiMenuId)
|
|
{
|
|
// cleanup select logic in case object context menu called us (bug 426)
|
|
m_drawingArea.SetLClickIgnore( FALSE );
|
|
|
|
// Move the check mark on the menu
|
|
UncheckMenuItem(m_currentMenuWidth);
|
|
CheckMenuItem(uiMenuId);
|
|
|
|
// Save the new pen width
|
|
m_currentMenuWidth = uiMenuId;
|
|
|
|
// Tell the attributes display of the new selection
|
|
m_WG.PushDown(uiMenuId - IDM_WIDTHS_START);
|
|
|
|
if (m_pCurrentTool != NULL)
|
|
{
|
|
m_pCurrentTool->SetWidthIndex(uiMenuId - IDM_WIDTHS_START);
|
|
}
|
|
|
|
// Tell the drawing pane of the new selection
|
|
m_drawingArea.SelectTool(m_pCurrentTool);
|
|
|
|
// If we are using a select tool, change the color of the selected object
|
|
if (m_pCurrentTool->ToolType() == TOOLTYPE_SELECT)
|
|
{
|
|
// If there is an object marked for changing
|
|
if (m_drawingArea.GraphicSelected())
|
|
{
|
|
// Update the object
|
|
m_drawingArea.SetSelectionWidth(m_pCurrentTool->GetWidth());
|
|
}
|
|
}
|
|
|
|
// Restore the focus to the drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnChooseFont
|
|
//
|
|
// Purpose: Let the user select a font
|
|
//
|
|
//
|
|
void WbMainWindow::OnChooseFont(void)
|
|
{
|
|
HDC hdc;
|
|
LOGFONT lfont;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnChooseFont");
|
|
|
|
// cleanup select logic in case object context menu called us (bug 426)
|
|
m_drawingArea.SetLClickIgnore( FALSE );
|
|
|
|
// It is only really sensible to be here when a text tool is selected.
|
|
// This is achieved by graying the Font selection menu entry when
|
|
// anything other than a text tool is in use.
|
|
|
|
// Get the font details from the current tool
|
|
::GetObject(m_pCurrentTool->GetFont(), sizeof(LOGFONT), &lfont);
|
|
lfont.lfClipPrecision |= CLIP_DFA_OVERRIDE;
|
|
|
|
//
|
|
// The Font dialog is passed a LOGFONT structure which it uses to
|
|
// initialize all of its fields (face name, weight etc).
|
|
//
|
|
// The face name passed in the LOGFONT structure is checked by the dialog
|
|
// against the facenames of all available fonts. If the name does not
|
|
// match one of the available fonts, no name is displayed.
|
|
//
|
|
// WB stores the LOGFONT structure specifying the font used for a text
|
|
// object in the object. This LOGFONT is selected into a DC where the
|
|
// GDIs font mapper decides which physical font most closely matches the
|
|
// required logical font. On boxes where the original font is not
|
|
// supported the font is substituted for the closest matching font
|
|
// available.
|
|
//
|
|
// So, if we pass the LOGFONT structure for a font which is not supported
|
|
// into the Font dialog, no facename is displayed. To bypass this we
|
|
//
|
|
// - select the logical font into a DC
|
|
//
|
|
// - determine the textmetrics and get the face name of the physical font
|
|
// chosen by the Font Mapper
|
|
//
|
|
// - use these textmetrics to create a LOGFONT structure which matches
|
|
// the substituted font!
|
|
//
|
|
// The resulting LOGFONT will have the correct weight, dimensions and
|
|
// facename for the substituted font.
|
|
//
|
|
hdc = ::CreateCompatibleDC(NULL);
|
|
if (hdc != NULL)
|
|
{
|
|
TEXTMETRIC tm;
|
|
HFONT hFont;
|
|
HFONT hOldFont;
|
|
|
|
hFont = ::CreateFontIndirect(&lfont);
|
|
|
|
//
|
|
// Get the face name and text metrics of the selected font.
|
|
//
|
|
hOldFont = SelectFont(hdc, hFont);
|
|
if (hOldFont == NULL)
|
|
{
|
|
WARNING_OUT(("Failed to select font into DC"));
|
|
}
|
|
else
|
|
{
|
|
::GetTextMetrics(hdc, &tm);
|
|
::GetTextFace(hdc, LF_FACESIZE, lfont.lfFaceName);
|
|
|
|
//
|
|
// Restore the old font back into the DC.
|
|
//
|
|
SelectFont(hdc, hOldFont);
|
|
|
|
//
|
|
// Create a LOGFONT structure which matches the Text metrics
|
|
// of the font used by the DC so that the font dialog manages
|
|
// to initialise all of its fields properly, even for
|
|
// substituted fonts...
|
|
//
|
|
lfont.lfHeight = tm.tmHeight;
|
|
lfont.lfWidth = tm.tmAveCharWidth;
|
|
lfont.lfWeight = tm.tmWeight;
|
|
lfont.lfItalic = tm.tmItalic;
|
|
lfont.lfUnderline = tm.tmUnderlined;
|
|
lfont.lfStrikeOut = tm.tmStruckOut;
|
|
lfont.lfCharSet = tm.tmCharSet;
|
|
|
|
//ADDED BY RAND - to make lfHeight be a char height. This makes
|
|
// the font dlg show the same pt size that is
|
|
// displayed in the sample font toolbar
|
|
if( lfont.lfHeight > 0 )
|
|
{
|
|
lfont.lfHeight = -(lfont.lfHeight - tm.tmInternalLeading);
|
|
}
|
|
}
|
|
|
|
::DeleteDC(hdc);
|
|
|
|
if (hFont != NULL)
|
|
{
|
|
::DeleteFont(hFont);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING_OUT(("Failed to get DC to select font into"));
|
|
}
|
|
|
|
CHOOSEFONT cf;
|
|
TCHAR szStyleName[64];
|
|
|
|
ZeroMemory(&cf, sizeof(cf));
|
|
ZeroMemory(szStyleName, sizeof(szStyleName));
|
|
|
|
cf.lStructSize = sizeof(cf);
|
|
cf.lpszStyle = szStyleName;
|
|
cf.rgbColors = m_pCurrentTool->GetColor() & 0x00ffffff; // blow off palette bits (NM4db:2304)
|
|
cf.Flags = CF_EFFECTS | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS |
|
|
CF_NOVERTFONTS;
|
|
cf.lpLogFont = &lfont;
|
|
cf.hwndOwner = m_hwnd;
|
|
|
|
// Call up the ChooseFont dialog from COM DLG
|
|
if (::ChooseFont(&cf))
|
|
{
|
|
lfont.lfClipPrecision |= CLIP_DFA_OVERRIDE;
|
|
|
|
//ADDED BY RAND - set color selected in dialog.
|
|
m_pCurrentTool->SetColor(cf.rgbColors);
|
|
m_AG.DisplayTool( m_pCurrentTool );
|
|
|
|
::SendMessage(m_hwnd, WM_COMMAND,
|
|
(WPARAM)MAKELONG( IDM_COLOR, BN_CLICKED ),
|
|
(LPARAM)0 );
|
|
|
|
// Inform the drawing pane of the new selection
|
|
HFONT hNewFont;
|
|
|
|
hNewFont = ::CreateFontIndirect(&lfont);
|
|
if (!hNewFont)
|
|
{
|
|
ERROR_OUT(("Failed to create font"));
|
|
DefaultExceptionHandler(WBFE_RC_WINDOWS, 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We need to set the text editor font after inserting it in the DC
|
|
// and querying the metrics, otherwise we may get a font with different
|
|
// metrics in zoomed mode
|
|
//
|
|
HFONT hNewFont2;
|
|
HDC hDC = m_drawingArea.GetCachedDC();
|
|
TEXTMETRIC textMetrics;
|
|
|
|
m_drawingArea.PrimeFont(hDC, hNewFont, &textMetrics);
|
|
lfont.lfHeight = textMetrics.tmHeight;
|
|
lfont.lfWidth = textMetrics.tmAveCharWidth;
|
|
lfont.lfPitchAndFamily = textMetrics.tmPitchAndFamily;
|
|
::GetTextFace(hDC, sizeof(lfont.lfFaceName),
|
|
lfont.lfFaceName);
|
|
TRACE_MSG(("Font face name %s", lfont.lfFaceName));
|
|
|
|
// Inform the drawing pane of the new selection
|
|
hNewFont2 = ::CreateFontIndirect(&lfont);
|
|
if (!hNewFont2)
|
|
{
|
|
ERROR_OUT(("Failed to create font"));
|
|
DefaultExceptionHandler(WBFE_RC_WINDOWS, 0);
|
|
return;
|
|
}
|
|
|
|
|
|
m_drawingArea.SetSelectionColor(cf.rgbColors);
|
|
|
|
m_drawingArea.SetSelectionFont(hNewFont2);
|
|
|
|
if (m_pCurrentTool != NULL)
|
|
{
|
|
m_pCurrentTool->SetFont(hNewFont2);
|
|
}
|
|
m_drawingArea.SelectTool(m_pCurrentTool);
|
|
|
|
//
|
|
// discard the new font
|
|
//
|
|
m_drawingArea.UnPrimeFont( hDC );
|
|
|
|
// Delete the fonts we created--everybody above makes copies
|
|
::DeleteFont(hNewFont2);
|
|
::DeleteFont(hNewFont);
|
|
}
|
|
|
|
// Restore the focus to the drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnToolBarToggle
|
|
//
|
|
// Purpose: Let the user toggle the tool bar on/off
|
|
//
|
|
//
|
|
void WbMainWindow::OnToolBarToggle(void)
|
|
{
|
|
RECT rectWnd;
|
|
|
|
// Toggle the flag
|
|
m_bToolBarOn = !m_bToolBarOn;
|
|
|
|
// Make the necessary updates
|
|
if (m_bToolBarOn)
|
|
{
|
|
// The tool bar was hidden, so show it
|
|
::ShowWindow(m_TB.m_hwnd, SW_SHOW);
|
|
|
|
// The tool window is fixed so we must resize the other panes in
|
|
// the window to make room for it
|
|
ResizePanes();
|
|
|
|
// Check the associated menu item
|
|
CheckMenuItem(IDM_TOOL_BAR_TOGGLE);
|
|
}
|
|
else
|
|
{
|
|
// The tool bar was visible, so hide it
|
|
::ShowWindow(m_TB.m_hwnd, SW_HIDE);
|
|
|
|
// Uncheck the associated menu item
|
|
UncheckMenuItem(IDM_TOOL_BAR_TOGGLE);
|
|
|
|
ResizePanes();
|
|
}
|
|
|
|
// Make sure things reflect current tool
|
|
m_AG.DisplayTool(m_pCurrentTool);
|
|
|
|
// Write the new option value to the options file
|
|
OPT_SetBooleanOption(OPT_MAIN_TOOLBARVISIBLE,
|
|
m_bToolBarOn);
|
|
|
|
::GetWindowRect(m_hwnd, &rectWnd);
|
|
::MoveWindow(m_hwnd, rectWnd.left, rectWnd.top,
|
|
rectWnd.right - rectWnd.left, rectWnd.bottom - rectWnd.top, TRUE);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnStatusBarToggle
|
|
//
|
|
// Purpose: Let the user toggle the help bar on/off
|
|
//
|
|
//
|
|
void WbMainWindow::OnStatusBarToggle(void)
|
|
{
|
|
RECT rectWnd;
|
|
|
|
// Toggle the flag
|
|
m_bStatusBarOn = !m_bStatusBarOn;
|
|
|
|
// Make the necessary updates
|
|
if (m_bStatusBarOn)
|
|
{
|
|
// Resize the panes to give room for the help bar
|
|
ResizePanes();
|
|
|
|
// The help bar was hidden, so show it
|
|
::ShowWindow(m_hwndSB, SW_SHOW);
|
|
|
|
// Check the associated menu item
|
|
CheckMenuItem(IDM_STATUS_BAR_TOGGLE);
|
|
}
|
|
else
|
|
{
|
|
// The help bar was visible, so hide it
|
|
::ShowWindow(m_hwndSB, SW_HIDE);
|
|
|
|
// Uncheck the associated menu item
|
|
UncheckMenuItem(IDM_STATUS_BAR_TOGGLE);
|
|
|
|
// Resize the panes to take up the help bar space
|
|
ResizePanes();
|
|
}
|
|
|
|
// Write the new option value to the options file
|
|
OPT_SetBooleanOption(OPT_MAIN_STATUSBARVISIBLE, m_bStatusBarOn);
|
|
|
|
::GetWindowRect(m_hwnd, &rectWnd);
|
|
::MoveWindow(m_hwnd, rectWnd.left, rectWnd.top,
|
|
rectWnd.right - rectWnd.left, rectWnd.bottom - rectWnd.top, TRUE);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnAbout
|
|
//
|
|
// Purpose: Show the about box for the Whiteboard application. This
|
|
// method is called whenever a WM_COMMAND with IDM_ABOUT
|
|
// is issued by Windows.
|
|
//
|
|
//
|
|
void WbMainWindow::OnAbout()
|
|
{
|
|
::DialogBoxParam(g_hInstance, MAKEINTRESOURCE(ABOUTBOX), m_hwnd,
|
|
AboutDlgProc, 0);
|
|
}
|
|
|
|
|
|
INT_PTR AboutDlgProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fHandled = FALSE;
|
|
|
|
switch (uMessage)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
TCHAR szFormat[256];
|
|
TCHAR szVersion[512];
|
|
|
|
::GetDlgItemText(hwnd, IDC_ABOUTVERSION, szFormat, 256);
|
|
wsprintf(szVersion, szFormat, VER_PRODUCTRELEASE_STR,
|
|
VER_PRODUCTVERSION_STR);
|
|
::SetDlgItemText(hwnd, IDC_ABOUTVERSION, szVersion);
|
|
|
|
fHandled = TRUE;
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
switch (GET_WM_COMMAND_CMD(wParam, lParam))
|
|
{
|
|
case BN_CLICKED:
|
|
::EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
fHandled = TRUE;
|
|
break;
|
|
}
|
|
|
|
return(fHandled);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: SelectWindow
|
|
//
|
|
// Purpose: Let the user select a window for grabbing
|
|
//
|
|
//
|
|
HWND WbMainWindow::SelectWindow(void)
|
|
{
|
|
POINT mousePos; // Mouse position
|
|
HWND hwndSelected = NULL; // Window clicked on
|
|
MSG msg; // Current message
|
|
|
|
// Load the grabbing cursors
|
|
HCURSOR hGrabCursor = ::LoadCursor(g_hInstance, MAKEINTRESOURCE( GRABCURSOR ) );
|
|
|
|
// Capture the mouse
|
|
UT_CaptureMouse(m_hwnd);
|
|
|
|
// Ensure we receive all keyboard messages.
|
|
::SetFocus(m_hwnd);
|
|
|
|
// Reset the CancelMode state
|
|
ResetCancelMode();
|
|
|
|
// Change to the grab cursor
|
|
HCURSOR hOldCursor = ::SetCursor(hGrabCursor);
|
|
|
|
// Trap all mouse messages until a WM_LBUTTONUP is received
|
|
for ( ; ; )
|
|
{
|
|
// Wait for the next message
|
|
::WaitMessage();
|
|
|
|
|
|
// Cancel if we have been sent a WM_CANCELMODE message
|
|
if (CancelModeSent())
|
|
{
|
|
break;
|
|
}
|
|
|
|
// If it is a mouse message, process it
|
|
if (::PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
|
|
{
|
|
if (msg.message == WM_LBUTTONUP)
|
|
{
|
|
// Get mouse position
|
|
mousePos.x = (short)LOWORD(msg.lParam);
|
|
mousePos.y = (short)HIWORD(msg.lParam);
|
|
|
|
// Convert to screen coordinates
|
|
::ClientToScreen(m_hwnd, &mousePos);
|
|
|
|
// Get the window under the mouse
|
|
hwndSelected = ::WindowFromPoint(mousePos);
|
|
|
|
// Leave the loop
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Cancel if ESCAPE is pressed.
|
|
// or if another window receives the focus
|
|
else if (::PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
|
|
{
|
|
if (msg.wParam == VK_ESCAPE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Release the mouse
|
|
UT_ReleaseMouse(m_hwnd);
|
|
|
|
// Restore the cursor
|
|
::SetCursor(hOldCursor);
|
|
|
|
return(hwndSelected);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnGrabWindow
|
|
//
|
|
// Purpose: Allows the user to grab a bitmap of a window
|
|
//
|
|
//
|
|
void WbMainWindow::OnGrabWindow(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnGrabWindow");
|
|
|
|
if (::DialogBoxParam(g_hInstance, MAKEINTRESOURCE(WARNSELECTWINDOW),
|
|
m_hwnd, WarnSelectWindowDlgProc, 0) != IDOK)
|
|
{
|
|
// User cancelled; bail out
|
|
return;
|
|
}
|
|
|
|
// Hide the application windows
|
|
::ShowWindow(m_hwnd, SW_HIDE);
|
|
|
|
// Get window selection from the user
|
|
HWND hwndSelected = SelectWindow();
|
|
|
|
if (hwndSelected != NULL)
|
|
{
|
|
// Walk back to the find the 'real' window ancestor
|
|
HWND hwndParent;
|
|
|
|
// The following piece of code attempts to find the frame window
|
|
// enclosing the selected window. This allows us to bring the
|
|
// enclosing window to the top, bringing the child window with it.
|
|
DWORD dwStyle;
|
|
|
|
while ((hwndParent = ::GetParent(hwndSelected)) != NULL)
|
|
{
|
|
// If we have reached a stand-alone window, stop the search
|
|
dwStyle = ::GetWindowLong(hwndSelected, GWL_STYLE);
|
|
|
|
if ( ((dwStyle & WS_POPUP) == WS_POPUP)
|
|
|| ((dwStyle & WS_THICKFRAME) == WS_THICKFRAME)
|
|
|| ((dwStyle & WS_DLGFRAME) == WS_DLGFRAME))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Move up to the parent window
|
|
hwndSelected = hwndParent;
|
|
}
|
|
|
|
// Bring the selected window to the top
|
|
::BringWindowToTop(hwndSelected);
|
|
::UpdateWindow(hwndSelected);
|
|
|
|
// Get an image copy of the window
|
|
RECT areaRect;
|
|
|
|
::GetWindowRect(hwndSelected, &areaRect);
|
|
|
|
DCWbGraphicDIB dib;
|
|
dib.FromScreenArea(&areaRect);
|
|
|
|
// Add the new grabbed bitmap
|
|
AddCapturedImage(dib);
|
|
|
|
// Force the selection tool to be selected
|
|
::PostMessage(m_hwnd, WM_COMMAND, IDM_TOOLS_START, 0L);
|
|
}
|
|
|
|
// Show the windows again
|
|
::ShowWindow(m_hwnd, SW_SHOW);
|
|
|
|
// Restore the focus to the drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
|
|
//
|
|
// WarnSelectWindowDlgProc()
|
|
// This puts up the warning/explanation dialog. We use the default settings
|
|
// or whatever the user chose last time this dialog was up.
|
|
//
|
|
INT_PTR CALLBACK WarnSelectWindowDlgProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fHandled = FALSE;
|
|
|
|
switch (uMessage)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
if (OPT_GetBooleanOption( OPT_MAIN_SELECTWINDOW_NOTAGAIN,
|
|
DFLT_MAIN_SELECTWINDOW_NOTAGAIN))
|
|
{
|
|
// End this right away, the user doesn't want a warning
|
|
::EndDialog(hwnd, IDOK);
|
|
}
|
|
|
|
fHandled = TRUE;
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDOK:
|
|
switch (GET_WM_COMMAND_CMD(wParam, lParam))
|
|
{
|
|
case BN_CLICKED:
|
|
//
|
|
// Update settings -- note that we don't have to write out
|
|
// FALSE--we wouldn't be in the dialog in the first place
|
|
// if the current setting weren't already FALSE.
|
|
//
|
|
if (::IsDlgButtonChecked(hwnd, IDC_SWWARN_NOTAGAIN))
|
|
{
|
|
OPT_SetBooleanOption(OPT_MAIN_SELECTWINDOW_NOTAGAIN, TRUE);
|
|
}
|
|
|
|
::EndDialog(hwnd, IDOK);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
switch (GET_WM_COMMAND_CMD(wParam, lParam))
|
|
{
|
|
case BN_CLICKED:
|
|
::EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
fHandled = TRUE;
|
|
break;
|
|
}
|
|
|
|
return(fHandled);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: ShowAllWindows
|
|
//
|
|
// Purpose: Show or hide the main window and associated windows
|
|
//
|
|
//
|
|
void WbMainWindow::ShowAllWindows(int iShow)
|
|
{
|
|
// Show/hide the main window
|
|
::ShowWindow(m_hwnd, iShow);
|
|
|
|
// Show/hide the tool window
|
|
if (m_bToolBarOn)
|
|
{
|
|
::ShowWindow(m_TB.m_hwnd, iShow);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnGrabArea
|
|
//
|
|
// Purpose: Allows the user to grab a bitmap of an area of the screen
|
|
//
|
|
//
|
|
void WbMainWindow::OnGrabArea(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnGrabArea");
|
|
|
|
if (::DialogBoxParam(g_hInstance, MAKEINTRESOURCE(WARNSELECTAREA),
|
|
m_hwnd, WarnSelectAreaDlgProc, 0) != IDOK)
|
|
{
|
|
// User cancelled, so bail out
|
|
return;
|
|
}
|
|
|
|
// Hide the application windows
|
|
::ShowWindow(m_hwnd, SW_HIDE);
|
|
|
|
// Load the grabbing cursors
|
|
HCURSOR hGrabCursor = ::LoadCursor(g_hInstance, MAKEINTRESOURCE( PENCURSOR ) );
|
|
|
|
// Capture the mouse
|
|
UT_CaptureMouse(m_hwnd);
|
|
|
|
// Ensure we receive all keyboard messages.
|
|
::SetFocus(m_hwnd);
|
|
|
|
// Reset the CancelMode status
|
|
ResetCancelMode();
|
|
|
|
// Change to the grab cursor
|
|
HCURSOR hOldCursor = ::SetCursor(hGrabCursor);
|
|
|
|
// Let the user select the area to be grabbed
|
|
RECT rect;
|
|
int tmp;
|
|
|
|
GetGrabArea(&rect);
|
|
|
|
// Normalize coords
|
|
if (rect.right < rect.left)
|
|
{
|
|
tmp = rect.left;
|
|
rect.left = rect.right;
|
|
rect.right = tmp;
|
|
}
|
|
|
|
if (rect.bottom < rect.top)
|
|
{
|
|
tmp = rect.top;
|
|
rect.top = rect.bottom;
|
|
rect.bottom = tmp;
|
|
}
|
|
|
|
DCWbGraphicDIB dib;
|
|
if (!::IsRectEmpty(&rect))
|
|
{
|
|
// Get a bitmap copy of the screen area
|
|
dib.FromScreenArea(&rect);
|
|
}
|
|
|
|
// Show the windows again now - if we do it later we get the bitmap to
|
|
// be added re-drawn twice (once on the window show and once when the
|
|
// graphic added indication arrives).
|
|
::ShowWindow(m_hwnd, SW_SHOW);
|
|
::UpdateWindow(m_hwnd);
|
|
|
|
if (!::IsRectEmpty(&rect))
|
|
{
|
|
// Add the bitmap
|
|
AddCapturedImage(dib);
|
|
|
|
// Force the selection tool to be selected
|
|
::PostMessage(m_hwnd, WM_COMMAND, IDM_TOOLS_START, 0L);
|
|
}
|
|
|
|
// Release the mouse
|
|
UT_ReleaseMouse(m_hwnd);
|
|
|
|
// Restore the cursor
|
|
::SetCursor(hOldCursor);
|
|
|
|
// Restore the focus to the drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WarnSelectArea dialog handler
|
|
//
|
|
INT_PTR CALLBACK WarnSelectAreaDlgProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fHandled = FALSE;
|
|
|
|
switch (uMessage)
|
|
{
|
|
case WM_INITDIALOG:
|
|
if (OPT_GetBooleanOption(OPT_MAIN_SELECTAREA_NOTAGAIN,
|
|
DFLT_MAIN_SELECTAREA_NOTAGAIN))
|
|
{
|
|
// End this right away, the user doesn't want a warning
|
|
::EndDialog(hwnd, IDOK);
|
|
}
|
|
|
|
fHandled = TRUE;
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDOK:
|
|
switch (GET_WM_COMMAND_CMD(wParam, lParam))
|
|
{
|
|
case BN_CLICKED:
|
|
//
|
|
// Update settings -- note that we don't have to write out
|
|
// FALSE--we wouldn't be in the dialog in the first place
|
|
// if the current setting weren't already FALSE.
|
|
//
|
|
if (::IsDlgButtonChecked(hwnd, IDC_SAWARN_NOTAGAIN))
|
|
{
|
|
OPT_SetBooleanOption(OPT_MAIN_SELECTAREA_NOTAGAIN, TRUE);
|
|
}
|
|
|
|
::EndDialog(hwnd, IDOK);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
switch (GET_WM_COMMAND_CMD(wParam, lParam))
|
|
{
|
|
case BN_CLICKED:
|
|
::EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
fHandled = TRUE;
|
|
break;
|
|
}
|
|
|
|
return(fHandled);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: GetGrabArea
|
|
//
|
|
// Purpose: Allows the user to grab a bitmap of an area of the screen
|
|
//
|
|
//
|
|
void WbMainWindow::GetGrabArea(LPRECT lprect)
|
|
{
|
|
POINT mousePos; // Mouse position
|
|
MSG msg; // Current message
|
|
BOOL tracking = FALSE; // Flag indicating mouse button is down
|
|
HDC hDC = NULL;
|
|
POINT grabStartPoint; // Start point (when mouse button is pressed)
|
|
POINT grabEndPoint; // End point (when mouse button is released)
|
|
POINT grabCurrPoint; // Current mouse position
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::GetGrabArea");
|
|
|
|
// Set the result to an empty rectangle
|
|
::SetRectEmpty(lprect);
|
|
|
|
// Create the rectangle to be used for tracking
|
|
DCWbGraphicTrackingRectangle rectangle;
|
|
rectangle.SetColor(RGB(0, 0, 0));
|
|
rectangle.SetPenWidth(1);
|
|
rectangle.SetPenStyle(PS_DOT);
|
|
|
|
// Get the DC for tracking
|
|
HWND hDesktopWnd = ::GetDesktopWindow();
|
|
hDC = ::GetWindowDC(hDesktopWnd);
|
|
if (hDC == NULL)
|
|
{
|
|
WARNING_OUT(("NULL desktop DC"));
|
|
goto GrabAreaCleanup;
|
|
}
|
|
|
|
// Trap all mouse messages until a WM_LBUTTONUP is received
|
|
for ( ; ; )
|
|
{
|
|
// Wait for the next message
|
|
::WaitMessage();
|
|
|
|
// Cancel if we have been sent a WM_CANCELMODE message
|
|
if (CancelModeSent())
|
|
{
|
|
TRACE_MSG(("canceling grab"));
|
|
|
|
// Erase the last tracking rectangle
|
|
if (!EqualPoint(grabStartPoint, grabEndPoint))
|
|
{
|
|
rectangle.SetRectPts(grabStartPoint, grabEndPoint);
|
|
rectangle.Draw(hDC);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// If it is a mouse message, process it
|
|
if (::PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
|
|
{
|
|
// Get mouse position
|
|
TRACE_MSG( ("msg = %x, lParam = %0x", msg.message, msg.lParam) );
|
|
mousePos.x = (short)LOWORD(msg.lParam);
|
|
mousePos.y = (short)HIWORD(msg.lParam);
|
|
|
|
TRACE_MSG( ("mousePos = %d,%d", mousePos.x, mousePos.y) );
|
|
|
|
// Convert to screen coordinates
|
|
::ClientToScreen(m_hwnd, &mousePos);
|
|
grabCurrPoint = mousePos;
|
|
|
|
switch (msg.message)
|
|
{
|
|
// Starting the grab
|
|
case WM_LBUTTONDOWN:
|
|
// Save the starting position
|
|
TRACE_MSG(("grabbing start position"));
|
|
grabStartPoint = mousePos;
|
|
grabEndPoint = mousePos;
|
|
tracking = TRUE;
|
|
break;
|
|
|
|
// Completing the rectangle
|
|
case WM_LBUTTONUP:
|
|
{
|
|
tracking = FALSE;
|
|
// Check that there is an area to capture
|
|
TRACE_MSG(("grabbing end position"));
|
|
if (EqualPoint(grabStartPoint, grabCurrPoint))
|
|
{
|
|
TRACE_MSG(("start == end, skipping grab"));
|
|
goto GrabAreaCleanup;
|
|
}
|
|
|
|
// Erase the last tracking rectangle
|
|
if (!EqualPoint(grabStartPoint, grabEndPoint))
|
|
{
|
|
rectangle.SetRectPts(grabStartPoint, grabEndPoint);
|
|
rectangle.Draw(hDC);
|
|
}
|
|
|
|
// Update the rectangle object
|
|
rectangle.SetRectPts(grabStartPoint, grabCurrPoint);
|
|
*lprect = *rectangle.GetRect();
|
|
|
|
// We are done
|
|
goto GrabAreaCleanup;
|
|
}
|
|
break;
|
|
|
|
// Continuing the rectangle
|
|
case WM_MOUSEMOVE:
|
|
if (tracking)
|
|
{
|
|
TRACE_MSG(("tracking grab"));
|
|
|
|
// Erase the last tracking rectangle
|
|
if (!EqualPoint(grabStartPoint, grabEndPoint))
|
|
{
|
|
rectangle.SetRectPts(grabStartPoint, grabEndPoint);
|
|
rectangle.Draw(hDC);
|
|
}
|
|
|
|
// Draw the new rectangle
|
|
if (!EqualPoint(grabStartPoint, grabCurrPoint))
|
|
{
|
|
// Save the new box end point
|
|
grabEndPoint = grabCurrPoint;
|
|
|
|
// Draw the rectangle
|
|
TRACE_MSG( ("grabStartPoint = %d,%d",
|
|
grabStartPoint.x, grabStartPoint.y) );
|
|
TRACE_MSG( ("grabEndPoint = %d,%d",
|
|
grabEndPoint.x, grabEndPoint.y) );
|
|
|
|
rectangle.SetRectPts(grabStartPoint, grabEndPoint);
|
|
rectangle.Draw(hDC);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// Cancel if ESCAPE is pressed.
|
|
else if (::PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
|
|
{
|
|
if( ((msg.message == WM_KEYUP)||(msg.message == WM_SYSKEYUP))&&
|
|
(msg.wParam == VK_ESCAPE) )
|
|
{
|
|
TRACE_MSG(("grab cancelled by ESC"));
|
|
|
|
// Erase the last tracking rectangle
|
|
if (!EqualPoint(grabStartPoint, grabEndPoint))
|
|
{
|
|
rectangle.SetRectPts(grabStartPoint, grabEndPoint);
|
|
rectangle.Draw(hDC);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
GrabAreaCleanup:
|
|
|
|
// Release the device context (if we have it)
|
|
if (hDC != NULL)
|
|
{
|
|
::ReleaseDC(hDesktopWnd, hDC);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: AddCapturedImage
|
|
//
|
|
// Purpose: Add a bitmap to the contents (adding a new page for it
|
|
// if necessary).
|
|
//
|
|
//
|
|
void WbMainWindow::AddCapturedImage(DCWbGraphicDIB& dib)
|
|
{
|
|
// Position the grabbed object at the top left of the currently visible
|
|
// area.
|
|
RECT rcVis;
|
|
m_drawingArea.GetVisibleRect(&rcVis);
|
|
dib.MoveTo(rcVis.left, rcVis.top);
|
|
|
|
// Add the new grabbed bitmap
|
|
dib.AddToPageLast(m_hCurrentPage);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnPrint
|
|
//
|
|
// Purpose: Print the contents of the drawing pane
|
|
//
|
|
//
|
|
void WbMainWindow::OnPrint()
|
|
{
|
|
BOOL bPrintError = FALSE;
|
|
PRINTDLG pd;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnPrint");
|
|
|
|
if (!IsIdle())
|
|
{
|
|
// post an error message indicating the whiteboard is busy
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_BUSY);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialize the PRINTDLG structure
|
|
//
|
|
ZeroMemory(&pd, sizeof(pd));
|
|
pd.lStructSize = sizeof(pd);
|
|
pd.hInstance = g_hInstance;
|
|
pd.hwndOwner = m_hwnd;
|
|
pd.Flags = PD_ALLPAGES | PD_RETURNDC | PD_PAGENUMS |
|
|
PD_HIDEPRINTTOFILE | PD_NOSELECTION;
|
|
|
|
pd.nMinPage = 1;
|
|
pd.nMaxPage = (WORD)g_pwbCore->WBP_ContentsCountPages();
|
|
pd.nFromPage = pd.nMinPage;
|
|
pd.nToPage = pd.nMaxPage;
|
|
|
|
// Put up the COMMDLG print dialog
|
|
if (::PrintDlg(&pd))
|
|
{
|
|
int nStartPage, nEndPage;
|
|
|
|
// Get the start and end page numbers to be printed
|
|
if (pd.Flags & PD_PAGENUMS)
|
|
{
|
|
nStartPage = pd.nFromPage;
|
|
nEndPage = pd.nToPage;
|
|
}
|
|
else
|
|
{
|
|
nStartPage = pd.nMinPage;
|
|
nEndPage = pd.nMaxPage;
|
|
}
|
|
|
|
// Check whether any pages are to be printed
|
|
if (nStartPage <= pd.nMaxPage)
|
|
{
|
|
// Ensure that the start and end pages lie within range.
|
|
nStartPage = max(nStartPage, pd.nMinPage);
|
|
nEndPage = min(nEndPage, pd.nMaxPage);
|
|
|
|
// Get the printer and output port names.
|
|
// These are written to the dialog for the user to see
|
|
// in the OnInitDialog member.
|
|
TCHAR szDeviceName[2*_MAX_PATH];
|
|
LPDEVNAMES lpDev;
|
|
|
|
// Device name
|
|
if (pd.hDevNames == NULL)
|
|
{
|
|
szDeviceName[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
lpDev = (LPDEVNAMES)::GlobalLock(pd.hDevNames);
|
|
|
|
wsprintf(szDeviceName, "%s %s",
|
|
(LPCTSTR)lpDev + lpDev->wDeviceOffset,
|
|
(LPCTSTR)lpDev + lpDev->wOutputOffset);
|
|
|
|
::GlobalUnlock(pd.hDevNames);
|
|
}
|
|
|
|
//
|
|
// Tell the printer we are starting the print.
|
|
// Note that the printer object handles the cancellation dialog.
|
|
WbPrinter printer(szDeviceName);
|
|
|
|
TCHAR szJobName[_MAX_PATH];
|
|
::LoadString(g_hInstance, IDS_PRINT_NAME, szJobName, _MAX_PATH);
|
|
|
|
int nPrintResult = printer.StartDoc(pd.hDC, szJobName, nStartPage);
|
|
if (nPrintResult < 0)
|
|
{
|
|
WARNING_OUT(("Print result %d", nPrintResult));
|
|
bPrintError = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Find out how many copies to print
|
|
int copyNum;
|
|
|
|
copyNum = 0;
|
|
while ((copyNum < pd.nCopies) && !bPrintError)
|
|
{
|
|
// Loop through all pages
|
|
int nPrintPage;
|
|
WB_PAGE_HANDLE hPage;
|
|
|
|
for (nPrintPage = nStartPage; nPrintPage <= nEndPage; nPrintPage++)
|
|
{
|
|
// Get the first page
|
|
hPage = PG_GetPageNumber((WORD) nPrintPage);
|
|
|
|
// Only print the page if there are some objects on it
|
|
if (g_pwbCore->WBP_PageCountGraphics(hPage) > 0)
|
|
{
|
|
// Tell the printer we are starting a new page
|
|
printer.StartPage(pd.hDC, nPrintPage);
|
|
if (!printer.Error())
|
|
{
|
|
RECT rectArea;
|
|
|
|
rectArea.left = 0;
|
|
rectArea.top = 0;
|
|
rectArea.right = DRAW_WIDTH;
|
|
rectArea.bottom = DRAW_HEIGHT;
|
|
|
|
// Print the page
|
|
PG_Print(hPage, pd.hDC, &rectArea);
|
|
|
|
// Inform the printer that the page is complete
|
|
printer.EndPage(pd.hDC);
|
|
}
|
|
else
|
|
{
|
|
bPrintError = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
copyNum++;
|
|
}
|
|
|
|
// The print has completed
|
|
nPrintResult = printer.EndDoc(pd.hDC);
|
|
if (nPrintResult < 0)
|
|
{
|
|
WARNING_OUT(("Print result %d", nPrintResult));
|
|
bPrintError = TRUE;
|
|
}
|
|
|
|
// reset the error if the user cancelled the print
|
|
if (printer.Aborted())
|
|
{
|
|
WARNING_OUT(("User cancelled print"));
|
|
bPrintError = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Inform the user if an error occurred
|
|
if (bPrintError)
|
|
{
|
|
// display a message informing the user the job terminated
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_PRINTER, 0);
|
|
}
|
|
|
|
//
|
|
// Cleanup the hDevMode, hDevNames, and hdc blocks if allocated
|
|
//
|
|
if (pd.hDevMode != NULL)
|
|
{
|
|
::GlobalFree(pd.hDevMode);
|
|
pd.hDevMode = NULL;
|
|
}
|
|
|
|
if (pd.hDevNames != NULL)
|
|
{
|
|
::GlobalFree(pd.hDevNames);
|
|
pd.hDevNames = NULL;
|
|
}
|
|
|
|
if (pd.hDC != NULL)
|
|
{
|
|
::DeleteDC(pd.hDC);
|
|
pd.hDC = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnPageSorter
|
|
//
|
|
// Purpose: Re-order the pages
|
|
//
|
|
//
|
|
void WbMainWindow::OnPageSorter()
|
|
{
|
|
// don't call up page sorter if another user is presenting (has the contents
|
|
// locked and sync on)
|
|
if ( (m_uiState == IN_CALL)
|
|
&& (!WB_PresentationMode()))
|
|
{
|
|
PAGESORT ps;
|
|
|
|
m_drawingArea.CancelDrawingMode();
|
|
|
|
// Save the lock state (in case the Page Sorter gets it)
|
|
SaveLock();
|
|
|
|
//
|
|
// Fill in the initial values
|
|
//
|
|
ZeroMemory(&ps, sizeof(ps));
|
|
ps.hCurPage = m_hCurrentPage;
|
|
ps.fPageOpsAllowed = (m_uiSubState == SUBSTATE_IDLE);
|
|
|
|
//
|
|
// Put up the dialog
|
|
//
|
|
ASSERT(m_hwndPageSortDlg == NULL);
|
|
|
|
::DialogBoxParam(g_hInstance, MAKEINTRESOURCE(PAGESORTERDIALOG),
|
|
m_hwnd, PageSortDlgProc, (LPARAM)&ps);
|
|
|
|
ASSERT(m_hwndPageSortDlg == NULL);
|
|
|
|
// Restore the lock state
|
|
RestoreLock();
|
|
|
|
// Set up the new current page pointer
|
|
if ((ps.hCurPage != m_hCurrentPage) || ps.fChanged)
|
|
{
|
|
GotoPage((WB_PAGE_HANDLE)ps.hCurPage);
|
|
}
|
|
|
|
// Update the page number display,
|
|
// the number of the current page may have changed.
|
|
UpdateStatus();
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnInsertPageBefore
|
|
//
|
|
// Purpose: Insert a new page before the current page
|
|
//
|
|
//
|
|
void WbMainWindow::OnInsertPageBefore()
|
|
{
|
|
if (!m_bUnlockStateSettled)
|
|
{
|
|
// Disable insert button code so crazed users can't insert again before
|
|
// the conference wide page-lock status has settled. If we ask for the
|
|
// page-lock again before the last unlock has finished then something
|
|
// happens to the lock-event from the cores and we hang waiting for it
|
|
// (until our wait-timeout runs out). This arguably could be called a
|
|
// DCL core bug but I couldn't generate any convincing proof of that
|
|
// so I just fixed it on Whiteboard's end by preventing asking for the
|
|
// lock too soon.
|
|
//
|
|
// RestoreLock() will eventually set m_bUnlockStateSettled to TRUE (in
|
|
// OnWBPUnlocked() by way of the WBP_EVENT_UNLOCKED event)
|
|
MessageBeep( 0xffffffff );
|
|
return;
|
|
}
|
|
|
|
// check state before allowing action
|
|
if (!IsIdle())
|
|
{
|
|
// post an error message indicating the whiteboard is busy
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_BUSY);
|
|
return;
|
|
}
|
|
|
|
// Save the current lock status
|
|
SaveLock();
|
|
|
|
// Catch exceptions so that we can restore the lock state
|
|
// Get the Page Order Lock (with an invisible dialog)
|
|
BOOL bGotLock = GetLock(WB_LOCK_TYPE_PAGE_ORDER, SW_HIDE);
|
|
if (bGotLock)
|
|
{
|
|
UINT uiRet;
|
|
WB_PAGE_HANDLE hPage;
|
|
|
|
// Set flag to prevent any more inserts until
|
|
// we have completely released the page-lock
|
|
m_bUnlockStateSettled = FALSE;
|
|
|
|
// Add the new page to the list (throws an exception on failure)
|
|
uiRet = g_pwbCore->WBP_PageAddBefore(m_hCurrentPage, &hPage);
|
|
if (uiRet != 0)
|
|
{
|
|
DefaultExceptionHandler(WBFE_RC_WB, uiRet);
|
|
return;
|
|
}
|
|
|
|
// Go to the inserted page
|
|
GotoPage(hPage);
|
|
}
|
|
|
|
//CHANGED BY RAND
|
|
// Restore the lock status. This will eventually set m_bUnlockStateSettled
|
|
// to TRUE (in OnWBPUnlocked() by way of the WBP_EVENT_UNLOCKED event)
|
|
// and enable this function after the conference wide lock-status
|
|
// has settled.
|
|
RestoreLock();
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: InsertPageAfter
|
|
//
|
|
// Purpose: Insert a new page after the specified page.
|
|
//
|
|
//
|
|
void WbMainWindow::InsertPageAfter(WB_PAGE_HANDLE hPageAfter)
|
|
{
|
|
if (!m_bUnlockStateSettled)
|
|
{
|
|
// Disable insert button code so crazed users can't insert again before
|
|
// the conference wide page-lock status has settled. If we ask for the
|
|
// page-lock again before the last unlock has finished then something
|
|
// happens to the lock-event from the cores and we hang waiting for it
|
|
// (until our wait-timeout runs out). This arguably could be called a
|
|
// DCL core bug but I couldn't generate any convincing proof of that
|
|
// so I just fixed it on Whiteboard's end by preventing asking for the
|
|
// lock too soon.
|
|
//
|
|
// RestoreLock() will eventually set m_bUnlockStateSettled to TRUE (in
|
|
// OnWBPUnlocked() by way of the WBP_EVENT_UNLOCKED event)
|
|
MessageBeep( 0xffffffff );
|
|
return;
|
|
}
|
|
|
|
|
|
// check state before allowing action
|
|
if (!IsIdle())
|
|
{
|
|
// post an error message indicating the whiteboard is busy
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_BUSY);
|
|
return;
|
|
}
|
|
|
|
// Save the current lock status
|
|
SaveLock();
|
|
|
|
// Catch exceptions so that we can restore the lock state
|
|
BOOL bGotLock = GetLock(WB_LOCK_TYPE_PAGE_ORDER, SW_HIDE);
|
|
if (bGotLock)
|
|
{
|
|
UINT uiRet;
|
|
WB_PAGE_HANDLE hPage;
|
|
|
|
// Set flag to prevent any more inserts until
|
|
// we have completely released the page-lock
|
|
m_bUnlockStateSettled = FALSE;
|
|
|
|
uiRet = g_pwbCore->WBP_PageAddAfter(hPageAfter, &hPage);
|
|
if (uiRet != 0)
|
|
{
|
|
DefaultExceptionHandler(WBFE_RC_WB, uiRet);
|
|
return;
|
|
}
|
|
|
|
// Move to the added page
|
|
GotoPage(hPage);
|
|
|
|
}
|
|
|
|
//CHANGED BY RAND
|
|
// Restore the lock status. This will eventually set m_bUnlockStateSettled
|
|
// to TRUE (in OnWBPUnlocked() by way of the WBP_EVENT_UNLOCKED event)
|
|
// and enable this function after the conference wide lock-status
|
|
// has settled.
|
|
RestoreLock();
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnInsertPageAfter
|
|
//
|
|
// Purpose: Insert a new page after the current page
|
|
//
|
|
//
|
|
void WbMainWindow::OnInsertPageAfter()
|
|
{
|
|
// Insert the new page
|
|
InsertPageAfter(m_hCurrentPage);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnDeletePage
|
|
//
|
|
// Purpose: Delete the current page
|
|
//
|
|
//
|
|
void WbMainWindow::OnDeletePage()
|
|
{
|
|
int iResult;
|
|
BOOL bWasPosted;
|
|
|
|
if (!m_bUnlockStateSettled)
|
|
{
|
|
// Disable delete button code so crazed users can't delete again before
|
|
// the conference wide page-lock status has settled. If we ask for the
|
|
// page-lock again before the last unlock has finished then something
|
|
// happens to the lock-event from the cores and we hang waiting for it
|
|
// (until our wait-timeout runs out). This arguably could be called a
|
|
// DCL core bug but I couldn't generate any convincing proof of that
|
|
// so I just fixed it on Whiteboard's end by preventing asking for the
|
|
// lock too soon.
|
|
//
|
|
// RestoreLock() will eventually set m_bUnlockStateSettled to TRUE (in
|
|
// OnWBPUnlocked() by way of the WBP_EVENT_UNLOCKED event)
|
|
MessageBeep( 0xffffffff );
|
|
return;
|
|
}
|
|
|
|
// check state
|
|
if (!IsIdle())
|
|
{
|
|
// post an error message indicating the whiteboard is busy
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_BUSY);
|
|
return;
|
|
}
|
|
|
|
// Display a message box with the relevant question
|
|
if( UsersMightLoseData( &bWasPosted, NULL ) ) // bug NM4db:418
|
|
return;
|
|
|
|
if( bWasPosted )
|
|
iResult = IDYES;
|
|
else
|
|
iResult = ::Message(NULL, IDS_DELETE_PAGE, IDS_DELETE_PAGE_MESSAGE, MB_YESNO | MB_ICONQUESTION);
|
|
|
|
|
|
// If the user wants to continue with the delete
|
|
if (iResult == IDYES)
|
|
{
|
|
// If this is the only page
|
|
if (g_pwbCore->WBP_ContentsCountPages() == 1)
|
|
{
|
|
// Just clear it. The core does handle this
|
|
// case but it is better not to get the lock unnecessarily, the
|
|
// lock is required to call the core delete page function.
|
|
m_drawingArea.Clear();
|
|
}
|
|
else
|
|
{
|
|
// Lock the drawing area - this ensures we cannot draw to a bad page
|
|
// It will normally be unlocked when we get the corresponding page
|
|
// delete indication
|
|
// - moved until after we have got the page order lock
|
|
//LockDrawingArea();
|
|
|
|
// Save the current lock status
|
|
SaveLock();
|
|
|
|
// Catch exceptions so that we can restore the lock state
|
|
// Get the Page Order Lock (with an invisible dialog)
|
|
BOOL bGotLock = GetLock(WB_LOCK_TYPE_PAGE_ORDER, SW_HIDE);
|
|
if (bGotLock)
|
|
{
|
|
UINT uiRet;
|
|
|
|
// Set flag to prevent any more inserts until
|
|
// we have completely released the page-lock
|
|
m_bUnlockStateSettled = FALSE;
|
|
|
|
// Lock the drawing area - this ensures we cannot draw to a bad page
|
|
// It will normally be unlocked when we get the corresponding page
|
|
// delete indication
|
|
LockDrawingArea();
|
|
|
|
// Delete the page. The page is not deleted immediately but a
|
|
// WBP_EVENT_PAGE_DELETED event is generated.
|
|
uiRet = g_pwbCore->WBP_PageDelete(m_hCurrentPage);
|
|
if (uiRet != 0)
|
|
{
|
|
DefaultExceptionHandler(WBFE_RC_WB, uiRet);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//CHANGED BY RAND
|
|
// Restore the lock status. This will eventually set m_bUnlockStateSettled
|
|
// to TRUE (in OnWBPUnlocked() by way of the WBP_EVENT_UNLOCKED event)
|
|
// and enable this function after the conference wide lock-status
|
|
// has settled.
|
|
RestoreLock();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnRemotePointer
|
|
//
|
|
// Purpose: Create a remote pointer
|
|
//
|
|
//
|
|
void WbMainWindow::OnRemotePointer(void)
|
|
{
|
|
if (!m_pLocalUser)
|
|
return;
|
|
|
|
DCWbGraphicPointer* pPointer = m_pLocalUser->GetPointer();
|
|
|
|
// This function toggles the presence of the user's remote pointer
|
|
ASSERT(pPointer != NULL);
|
|
if (pPointer->IsActive())
|
|
{
|
|
// Turn off the pointer in the user information
|
|
pPointer->SetInactive();
|
|
|
|
// Tell the drawing area of the change
|
|
m_drawingArea.PointerUpdated(pPointer);
|
|
|
|
// Set the check mark on the menu item
|
|
UncheckMenuItem(IDM_REMOTE);
|
|
|
|
// Pop up the sync button
|
|
m_TB.PopUp(IDM_REMOTE);
|
|
}
|
|
else
|
|
{
|
|
// Calculate a position at which to drop the pointer. The centre of the
|
|
// remote pointer is placed in the centre of the currently visible
|
|
// area of the surface (the centre of the drawing area window).
|
|
RECT rectVisible;
|
|
RECT rectPointer;
|
|
POINT ptCenter;
|
|
|
|
m_drawingArea.GetVisibleRect(&rectVisible);
|
|
pPointer->GetBoundsRect(&rectPointer);
|
|
|
|
ptCenter.x = (rectVisible.left + rectVisible.right) / 2;
|
|
ptCenter.x -= ((rectPointer.right - rectPointer.left) / 2);
|
|
ptCenter.y = (rectVisible.top + rectVisible.bottom) / 2;
|
|
ptCenter.y -= ((rectPointer.bottom - rectPointer.top) / 2);
|
|
|
|
// Turn on the pointer in the user information
|
|
pPointer->SetActive(m_hCurrentPage, ptCenter);
|
|
|
|
// Tell the drawing area of the change
|
|
m_drawingArea.PointerUpdated(pPointer);
|
|
|
|
// Set the synced check mark
|
|
CheckMenuItem(IDM_REMOTE);
|
|
|
|
// Pop up the sync button
|
|
m_TB.PushDown(IDM_REMOTE);
|
|
|
|
// Force the selection tool to be selected
|
|
::PostMessage(m_hwnd, WM_COMMAND, IDM_TOOLS_START, 0L);
|
|
}
|
|
|
|
// Restore the focus to the drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnSync
|
|
//
|
|
// Purpose: Sync or unsync the Whiteboard with other users
|
|
//
|
|
//
|
|
void WbMainWindow::OnSync(void)
|
|
{
|
|
// disabled if in presentation mode (another user has lock & sync on)
|
|
if (!WB_PresentationMode())
|
|
{
|
|
if (m_pLocalUser != NULL)
|
|
{
|
|
// Determine whether we are currently synced
|
|
if (m_pLocalUser->IsSynced())
|
|
{
|
|
// currently synced, so unsync
|
|
Unsync();
|
|
}
|
|
else
|
|
{
|
|
// currently unsynced, so sync
|
|
Sync();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Restore the focus to the drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: Sync
|
|
//
|
|
// Purpose: Sync the Whiteboard with other users
|
|
//
|
|
//
|
|
void WbMainWindow::Sync(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::Sync");
|
|
|
|
//
|
|
// Dont do anything if the local user is already synced.
|
|
//
|
|
if (!m_pLocalUser || m_pLocalUser->IsSynced())
|
|
{
|
|
TRACE_DEBUG(("User already synced"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update the local user's position information, to make sure it's up
|
|
// to date.
|
|
//
|
|
RECT rcVisDraw;
|
|
RECT rcVisUser;
|
|
|
|
m_drawingArea.GetVisibleRect(&rcVisDraw);
|
|
|
|
m_pLocalUser->SetVisibleRect(&rcVisDraw);
|
|
|
|
//
|
|
// We are not currently synced - sync now (if we have the contents
|
|
// lock, or are the first to sync, it will put our sync position).
|
|
//
|
|
m_pLocalUser->Sync();
|
|
|
|
//
|
|
// Set the synced check mark and pop up the sync button.
|
|
//
|
|
CheckMenuItem(IDM_SYNC);
|
|
m_TB.PushDown(IDM_SYNC);
|
|
|
|
//
|
|
// If the sync position (or zoom state) chosen was not where we are
|
|
// now, move to the current sync position (we are joining a set of
|
|
// synced users).
|
|
//
|
|
m_drawingArea.GetVisibleRect(&rcVisDraw);
|
|
|
|
m_pLocalUser->GetVisibleRect(&rcVisUser);
|
|
|
|
if ( (m_pLocalUser->Page() != m_hCurrentPage) ||
|
|
(!::EqualRect(&rcVisUser, &rcVisDraw)) ||
|
|
(m_pLocalUser->GetZoom() != m_drawingArea.Zoomed()) ) //CHANGED BY RAND
|
|
{
|
|
TRACE_DEBUG(("Move to new sync pos/state"));
|
|
::PostMessage(m_hwnd, WM_USER_GOTO_USER_POSITION, 0, (LPARAM)m_pLocalUser->Handle());
|
|
}
|
|
} // Sync
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: Unsync
|
|
//
|
|
// Purpose: Unsync the Whiteboard with other users
|
|
//
|
|
//
|
|
void WbMainWindow::Unsync(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::Unsync");
|
|
|
|
//
|
|
// Dont do anythig if we are already unsynced.
|
|
//
|
|
if (!m_pLocalUser || !m_pLocalUser->IsSynced())
|
|
{
|
|
TRACE_DEBUG(("Already unsynced"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Unsync.
|
|
// Set the synced check mark and pop up the sync button.
|
|
//
|
|
m_pLocalUser->Unsync();
|
|
UncheckMenuItem(IDM_SYNC);
|
|
m_TB.PopUp(IDM_SYNC);
|
|
|
|
} // Unsync
|
|
|
|
//
|
|
//
|
|
// Function: SaveLock
|
|
//
|
|
// Purpose: Save the current lock type
|
|
//
|
|
//
|
|
void WbMainWindow::SaveLock(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::SaveLock");
|
|
|
|
m_uiSavedLockType = WB_LOCK_TYPE_NONE;
|
|
|
|
// If we have the contents lock
|
|
if (WB_GotContentsLock())
|
|
{
|
|
TRACE_MSG(("Saved contents lock"));
|
|
m_uiSavedLockType = WB_LOCK_TYPE_CONTENTS;
|
|
}
|
|
else
|
|
{
|
|
// If we have the page order lock
|
|
if (WB_GotLock())
|
|
{
|
|
TRACE_MSG(("Saved page order lock"));
|
|
m_uiSavedLockType = WB_LOCK_TYPE_PAGE_ORDER;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: RestoreLock
|
|
//
|
|
// Purpose: Restore the current lock type (SaveLock must have been
|
|
// called previously.
|
|
//
|
|
//
|
|
void WbMainWindow::RestoreLock(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::RestoreLock");
|
|
|
|
switch(m_uiSavedLockType)
|
|
{
|
|
case WB_LOCK_TYPE_CONTENTS:
|
|
|
|
// If we do not have the contents lock
|
|
if (!WB_GotContentsLock())
|
|
{
|
|
// Get the contents lock (with invisible dialog)
|
|
TRACE_MSG(("Restoring contents lock"));
|
|
GetLock(WB_LOCK_TYPE_CONTENTS, SW_HIDE);
|
|
|
|
}
|
|
|
|
// we really own the lock, clear settled flag so page buttons don't hang
|
|
m_bUnlockStateSettled = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
case WB_LOCK_TYPE_PAGE_ORDER:
|
|
|
|
if (!WB_GotLock() || WB_GotContentsLock())
|
|
{
|
|
// Get the page order lock (with invisible dialog)
|
|
TRACE_MSG(("Restoring page order lock"));
|
|
GetLock(WB_LOCK_TYPE_PAGE_ORDER, SW_HIDE);
|
|
|
|
}
|
|
|
|
//ADDED BY RAND- we really own the lock, clear settled flag
|
|
// so page buttons don't hang
|
|
m_bUnlockStateSettled = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
case WB_LOCK_TYPE_NONE:
|
|
|
|
// If we have the lock
|
|
if (WB_GotLock())
|
|
{
|
|
// Release the lock
|
|
TRACE_MSG(("Restoring no lock"));
|
|
|
|
// Let WBP_EVENT_LOCKED handle m_bUnlockStateSettled flag
|
|
g_pwbCore->WBP_Unlock();
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
// We have saved an invalid lock type
|
|
ERROR_OUT(("Bad saved lock type"));
|
|
|
|
//ADDED BY RAND- somethings broken, clear settled flag
|
|
// so page buttons don't hang
|
|
m_bUnlockStateSettled = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: GetLock
|
|
//
|
|
// Purpose: Get the Page Order Lock (synchronously)
|
|
//
|
|
//
|
|
BOOL WbMainWindow::GetLock(UINT uiLockType, UINT uiHide)
|
|
{
|
|
BOOL bGotRequiredLock = FALSE;
|
|
BOOL bCancelled = FALSE;
|
|
UINT uiDialogReturn = 0;
|
|
UINT lDialogDelay = 1;
|
|
UINT lTimeout = 0;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::GetLock");
|
|
|
|
switch(uiLockType)
|
|
{
|
|
case WB_LOCK_TYPE_PAGE_ORDER:
|
|
|
|
TRACE_DEBUG(("WB_LOCK_TYPE_PAGE_ORDER"));
|
|
if (WB_GotLock())
|
|
{
|
|
TRACE_DEBUG(("Already got it"));
|
|
bGotRequiredLock = TRUE;
|
|
goto RestoreLockCleanup;
|
|
}
|
|
break;
|
|
|
|
case WB_LOCK_TYPE_CONTENTS:
|
|
|
|
TRACE_DEBUG(("WB_LOCK_TYPE_CONTENTS"));
|
|
if (WB_GotContentsLock())
|
|
{
|
|
TRACE_DEBUG(("Already got it"));
|
|
bGotRequiredLock = TRUE;
|
|
goto RestoreLockCleanup;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Invalid lock type requested"));
|
|
break;
|
|
}
|
|
|
|
if (WB_Locked())
|
|
{
|
|
TRACE_DEBUG(("Contents already locked"));
|
|
goto RestoreLockCleanup;
|
|
}
|
|
|
|
|
|
// check for any object locks
|
|
BOOL bAnObjectIsLocked;
|
|
WB_PAGE_HANDLE hPage;
|
|
DCWbGraphic* pGraphic;
|
|
|
|
bAnObjectIsLocked = FALSE;
|
|
hPage = m_drawingArea.Page();
|
|
if (hPage != WB_PAGE_HANDLE_NULL)
|
|
{
|
|
WB_GRAPHIC_HANDLE hStart;
|
|
|
|
pGraphic = PG_First(hPage, &hStart);
|
|
while (pGraphic != NULL)
|
|
{
|
|
// get object lock
|
|
bAnObjectIsLocked = pGraphic->Locked();
|
|
|
|
// Release the current graphic
|
|
delete pGraphic;
|
|
|
|
// check object lock
|
|
if( bAnObjectIsLocked )
|
|
break;
|
|
|
|
// Get the next one
|
|
pGraphic = PG_Next(hPage, &hStart, NULL);
|
|
}
|
|
}
|
|
|
|
if( bAnObjectIsLocked )
|
|
{
|
|
Message(NULL, IDS_LOCK, IDS_OBJECTSARELOCKED);
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// If we get this far then we need to get the lock.
|
|
//
|
|
if (uiLockType == WB_LOCK_TYPE_PAGE_ORDER)
|
|
{
|
|
g_pwbCore->WBP_PageOrderLock();
|
|
}
|
|
else
|
|
{
|
|
g_pwbCore->WBP_ContentsLock();
|
|
}
|
|
|
|
//
|
|
// Bring up a dialog to wait for the response. This dialog is
|
|
// cancelled by the event handler code when the lock response event is
|
|
// received.
|
|
//
|
|
ASSERT(m_hwndWaitForLockDlg == NULL);
|
|
|
|
TMDLG tmdlg;
|
|
|
|
ZeroMemory(&tmdlg, sizeof(tmdlg));
|
|
tmdlg.bLockNotEvent = TRUE;
|
|
tmdlg.uiMaxDisplay = MAIN_LOCK_TIMEOUT;
|
|
|
|
if (uiHide == SW_SHOW)
|
|
{
|
|
tmdlg.bVisible = TRUE;
|
|
|
|
uiDialogReturn = (UINT)::DialogBoxParam(g_hInstance, MAKEINTRESOURCE(LOCKDIALOG),
|
|
m_hwnd, TimedDlgProc, (LPARAM)&tmdlg);
|
|
}
|
|
else
|
|
{
|
|
tmdlg.bVisible = FALSE;
|
|
|
|
uiDialogReturn = (UINT)::DialogBoxParam(g_hInstance, MAKEINTRESOURCE(INVISIBLEDIALOG),
|
|
m_hwnd, TimedDlgProc, (LPARAM)&tmdlg);
|
|
}
|
|
|
|
ASSERT(m_hwndWaitForLockDlg == NULL);
|
|
|
|
if (uiDialogReturn == IDCANCEL)
|
|
{
|
|
// The user cancelled the lock request or it timed out
|
|
TRACE_MSG(("User cancelled lock request"));
|
|
bCancelled = TRUE;
|
|
//
|
|
// If we havent already got the lock then unlock here.
|
|
//
|
|
if (!WB_GotLock())
|
|
{
|
|
TRACE_DEBUG(("Havent got lock confirmation yet - cancel it"));
|
|
g_pwbCore->WBP_Unlock();
|
|
}
|
|
|
|
goto RestoreLockCleanup;
|
|
}
|
|
|
|
switch(uiLockType)
|
|
{
|
|
case WB_LOCK_TYPE_PAGE_ORDER:
|
|
|
|
if (WB_GotLock())
|
|
{
|
|
bGotRequiredLock = TRUE;
|
|
}
|
|
break;
|
|
|
|
case WB_LOCK_TYPE_CONTENTS:
|
|
|
|
if (WB_GotContentsLock())
|
|
{
|
|
bGotRequiredLock = TRUE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// can't get here - trapped at top.
|
|
ERROR_OUT(("Invalid lock type - internal error"));
|
|
break;
|
|
}
|
|
|
|
RestoreLockCleanup:
|
|
|
|
if (!bGotRequiredLock)
|
|
{
|
|
if( !bCancelled )
|
|
{
|
|
// post error only if user didn't cancel (bug NM4db:429)
|
|
TRACE_MSG(("Failed to get the lock"));
|
|
// post an error message indicating the failure to get the lock
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_LOCKED);
|
|
}
|
|
}
|
|
|
|
return(bGotRequiredLock);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: OnLock
|
|
//
|
|
// Purpose: Lock or unlock the Whiteboard
|
|
//
|
|
//
|
|
void WbMainWindow::OnLock(void)
|
|
{
|
|
// If we have the lock, this is an unlock request
|
|
if (WB_GotContentsLock())
|
|
{
|
|
// if currently loading or doing a new, then restore page order lock
|
|
if (!IsIdle())
|
|
{
|
|
GetLock(WB_LOCK_TYPE_PAGE_ORDER, SW_HIDE);
|
|
}
|
|
else
|
|
{
|
|
// Release the lock
|
|
g_pwbCore->WBP_Unlock();
|
|
}
|
|
|
|
// Set the locked check mark
|
|
UncheckMenuItem(IDM_LOCK);
|
|
|
|
// Pop up the lock button
|
|
m_TB.PopUp(IDM_LOCK);
|
|
}
|
|
else
|
|
{
|
|
// If another user has the lock.
|
|
// We should not usually get here if another user has the lock because
|
|
// the Lock menu entry (and button) will be grayed.
|
|
if (WB_ContentsLocked())
|
|
{
|
|
// Display a message
|
|
Message(NULL, IDS_LOCK, IDS_LOCK_ERROR);
|
|
}
|
|
else
|
|
{
|
|
// Save the current lock state (in case the user cancels the request)
|
|
SaveLock();
|
|
|
|
// Catch exceptions raised during the lock request
|
|
// Request the lock
|
|
BOOL bGotLock = GetLock(WB_LOCK_TYPE_CONTENTS, SW_SHOW);
|
|
if (!bGotLock)
|
|
{
|
|
RestoreLock();
|
|
}
|
|
else
|
|
{
|
|
// Turn sync on and write our sync position
|
|
Sync();
|
|
m_pLocalUser->PutSyncPosition();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Restore the focus to the drawing area
|
|
::SetFocus(m_drawingArea.m_hwnd);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function : OnWBPLoadComplete
|
|
//
|
|
// Purpose : Finished loading a file
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPLoadComplete(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPLoadComplete");
|
|
if (m_uiSubState == SUBSTATE_LOADING)
|
|
{
|
|
TRACE_MSG(("Load has completed OK"));
|
|
SetSubstate(SUBSTATE_IDLE);
|
|
if (WB_GotLock())
|
|
{
|
|
}
|
|
ReleasePageOrderLock();
|
|
}
|
|
else
|
|
{
|
|
TRACE_MSG(("Unexpected WBP_EVENT_LOAD_COMPLETE event ignored"));
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function : OnWBPLoadFailed
|
|
//
|
|
// Purpose : Finished loading a file
|
|
//
|
|
//
|
|
void WbMainWindow::OnWBPLoadFailed(void)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnWBPLoadFailed");
|
|
|
|
if (m_uiSubState == SUBSTATE_LOADING)
|
|
{
|
|
::PostMessage(m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WB, WB_RC_BAD_FILE_FORMAT);
|
|
|
|
TRACE_MSG(("Load has failed - tell the user about it..."));
|
|
SetSubstate(SUBSTATE_IDLE);
|
|
ReleasePageOrderLock();
|
|
}
|
|
else
|
|
{
|
|
TRACE_MSG(("Unexpected WBP_EVENT_LOAD_FAILED event ignored"));
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: GetWindowTitle
|
|
//
|
|
// Purpose: Return a string for the window title
|
|
//
|
|
//
|
|
TCHAR * WbMainWindow::GetWindowTitle()
|
|
{
|
|
|
|
// Calculate the size we will need
|
|
int strSize=0;
|
|
if( m_pLockOwner != NULL )
|
|
{
|
|
strSize = lstrlen(m_pLockOwner->Name());
|
|
}
|
|
|
|
// This is the worst scenario, the total size would be less than 2*_MAX_FNAME
|
|
// but we give a lot of space for localization.
|
|
int totalSize = 2*(_MAX_FNAME)
|
|
+ strSize + 1
|
|
+3*(_MAX_FNAME); // account for the following strings, the total is probably < 200
|
|
// IDS_UNTITLED
|
|
// IDS_TITLE_SEPARATOR
|
|
// IDS_DEFAULT
|
|
// IDS_IN_CALL
|
|
// IDS_IN_CALL_OTHERS
|
|
// IDS_JOINING
|
|
// IDS_INITIALIZING
|
|
// IDS_NOT_IN_CALL
|
|
// IDS_LOCKEDTITLE
|
|
|
|
|
|
TCHAR *pTitle = new TCHAR[totalSize];
|
|
if (!pTitle)
|
|
{
|
|
ERROR_OUT(("GetWindowTitle: failed to allocate TCHAR array"));
|
|
return(NULL);
|
|
}
|
|
TCHAR inUseBy[_MAX_PATH];
|
|
|
|
TCHAR *pStartTitle = pTitle;
|
|
|
|
// Set title to either the "Untitled" string, or the loaded file name
|
|
if( (!lstrlen(m_strFileName))||
|
|
(GetFileTitle( m_strFileName, pTitle, 2*_MAX_FNAME ) != 0) )
|
|
{
|
|
strSize = ::LoadString(g_hInstance, IDS_UNTITLED, pTitle, totalSize );
|
|
pTitle+=strSize;
|
|
ASSERT(totalSize>strSize);
|
|
totalSize -=strSize;
|
|
}
|
|
else
|
|
{
|
|
strSize = lstrlen(pTitle);
|
|
pTitle +=strSize;;
|
|
ASSERT(totalSize>strSize);
|
|
totalSize -=strSize;
|
|
}
|
|
|
|
// Get the separator from resources
|
|
strSize = ::LoadString(g_hInstance, IDS_TITLE_SEPARATOR, pTitle, totalSize);
|
|
pTitle+=strSize;;
|
|
ASSERT(totalSize>strSize);
|
|
totalSize -=strSize;
|
|
|
|
// Get the application title from options
|
|
strSize = ::LoadString(g_hInstance, IDS_DEFAULT, pTitle, totalSize );
|
|
pTitle+=strSize;
|
|
ASSERT(totalSize>strSize);
|
|
totalSize -=strSize;
|
|
|
|
// Add either "In Call" or "Not in Call", or "Initialising" or
|
|
// "Joining a call"
|
|
strSize = ::LoadString(g_hInstance, IDS_TITLE_SEPARATOR, pTitle, totalSize);
|
|
pTitle+=strSize;
|
|
ASSERT(totalSize>strSize);
|
|
totalSize -=strSize;
|
|
|
|
if ((m_uiState == IN_CALL) && m_bCallActive)
|
|
{
|
|
UINT count;
|
|
|
|
count = g_pwbCore->WBP_PersonCountInCall();
|
|
|
|
strSize = ::LoadString(g_hInstance, IDS_IN_CALL, inUseBy, totalSize);
|
|
|
|
strSize=wsprintf(pTitle, inUseBy, (count-1));
|
|
pTitle+=strSize;
|
|
ASSERT(totalSize>strSize);
|
|
totalSize -=strSize;
|
|
|
|
}
|
|
else if ((m_uiState == JOINING) ||
|
|
((m_uiState == JOINED) && !m_bCallActive) ||
|
|
((m_uiState == IN_CALL) && (m_dwDomain != OM_NO_CALL) && !m_bCallActive))
|
|
{
|
|
strSize = ::LoadString(g_hInstance, IDS_JOINING, pTitle, totalSize );
|
|
pTitle+=strSize;
|
|
ASSERT(totalSize>strSize);
|
|
totalSize -=strSize;
|
|
}
|
|
else if (m_uiState == STARTING)
|
|
{
|
|
strSize = ::LoadString(g_hInstance, IDS_INITIALIZING, pTitle, totalSize);
|
|
pTitle+=strSize;
|
|
ASSERT(totalSize>strSize);
|
|
totalSize -=strSize;
|
|
}
|
|
else
|
|
{
|
|
strSize = ::LoadString(g_hInstance, IDS_NOT_IN_CALL, pTitle, totalSize);
|
|
pTitle+=strSize;
|
|
ASSERT(totalSize>strSize);
|
|
totalSize -=strSize;
|
|
}
|
|
|
|
|
|
// add lock info
|
|
if( m_pLockOwner != NULL )
|
|
{
|
|
strSize = ::LoadString(g_hInstance, IDS_LOCKEDTITLE, pTitle, totalSize);
|
|
ASSERT(totalSize>strSize);
|
|
pTitle+=strSize;
|
|
lstrcpy(pTitle, m_pLockOwner->Name());
|
|
}
|
|
|
|
// Return the complete title string
|
|
return pStartTitle;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LRESULT WbMainWindow::OnConfShutdown( WPARAM, LPARAM )
|
|
{
|
|
if (OnQueryEndSession())
|
|
{
|
|
::SendMessage(m_hwnd, WM_CLOSE, 0, 0); // do close immediately
|
|
// :
|
|
// DON'T DO ANYTHING else at this point except for exit.
|
|
return( 0 );// tell conf ok to shutdown
|
|
}
|
|
else
|
|
return( (LRESULT)g_cuEndSessionAbort ); // don't shutdown
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: OnQueryEndSession
|
|
//
|
|
// Purpose: Ensure user is prompted to save changes when windows is
|
|
// ended.
|
|
//
|
|
//
|
|
LRESULT WbMainWindow::OnQueryEndSession(void)
|
|
{
|
|
HWND hwndPopup;
|
|
|
|
if ((hwndPopup = ::GetLastActivePopup(m_hwnd)) != m_hwnd)
|
|
{
|
|
Message(NULL, IDS_DEFAULT, IDS_CANTCLOSE );
|
|
::BringWindowToTop(hwndPopup);
|
|
return( FALSE );
|
|
}
|
|
|
|
// If changes are required then prompt the user to save
|
|
int iDoNew = IDOK;
|
|
|
|
if (IsIdle())
|
|
{
|
|
iDoNew = QuerySaveRequired(TRUE);
|
|
if (iDoNew == IDYES)
|
|
{
|
|
// Save the changes
|
|
iDoNew = OnSave(FALSE);
|
|
}
|
|
}
|
|
|
|
// remember what we did so OnClose can act appropriately
|
|
m_bQuerySysShutdown = (iDoNew != IDCANCEL);
|
|
|
|
// If the user did not cancel, let windows exit
|
|
return( m_bQuerySysShutdown );
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: Recover
|
|
//
|
|
// Purpose: Ensure the whiteboard is not left partly registered.
|
|
//
|
|
//
|
|
//
|
|
void WbMainWindow::Recover()
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::Recover");
|
|
|
|
// If the error occurred during start-up, then quit immediately
|
|
if (m_uiState == STARTING)
|
|
{
|
|
TRACE_MSG(("error during startup - exiting"));
|
|
::PostMessage(m_hwnd, WM_CLOSE, FALSE, 0L);
|
|
}
|
|
else
|
|
{
|
|
// ensure the drawing area is locked while we are in a bad state
|
|
LockDrawingArea();
|
|
|
|
// disable remote pointer while we are handling this join failure (bug 4767)
|
|
m_TB.Disable(IDM_REMOTE);
|
|
|
|
// set state to starting - ensures we don't get in an infinite loop,
|
|
// because if an error occurs then we will quit if we try to recover
|
|
m_uiState = STARTING;
|
|
TRACE_MSG(("Attempting to recover after join call failure - state set to STARTING"));
|
|
|
|
// state changed: update page buttons
|
|
UpdatePageButtons();
|
|
|
|
// see if there is a call active
|
|
CM_STATUS cmStatus;
|
|
|
|
// if there's a call available, try to join it
|
|
if (!CMS_GetStatus(&cmStatus))
|
|
cmStatus.callID = OM_NO_CALL;
|
|
|
|
::PostMessage(m_hwnd, WM_USER_JOIN_CALL, FALSE, (LONG)cmStatus.callID);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: UnlockDrawingArea
|
|
//
|
|
// Purpose: Unlock the drawing area and enable the appropriate buttons
|
|
//
|
|
//
|
|
//
|
|
void WbMainWindow::UnlockDrawingArea()
|
|
{
|
|
m_drawingArea.Unlock();
|
|
|
|
// Enable tool-bar buttons that can now be used
|
|
if (WB_Locked() || !IsIdle())
|
|
{
|
|
EnableToolbar( FALSE );
|
|
}
|
|
else
|
|
{
|
|
EnableToolbar( TRUE );
|
|
}
|
|
|
|
//
|
|
// Show the tool attributes group.
|
|
//
|
|
m_AG.DisplayTool(m_pCurrentTool);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: LockDrawingArea
|
|
//
|
|
// Purpose: Lock the drawing area and enable the appropriate buttons
|
|
//
|
|
//
|
|
//
|
|
void WbMainWindow::LockDrawingArea()
|
|
{
|
|
m_drawingArea.Lock();
|
|
|
|
// Disable tool-bar buttons that cannot be used while locked
|
|
if (WB_Locked() || !IsIdle())
|
|
{
|
|
EnableToolbar( FALSE );
|
|
}
|
|
else
|
|
{
|
|
EnableToolbar( TRUE );
|
|
}
|
|
|
|
//
|
|
// Hide the tool attributes
|
|
//
|
|
if (m_WG.m_hwnd != NULL)
|
|
{
|
|
::ShowWindow(m_WG.m_hwnd, SW_HIDE);
|
|
}
|
|
m_AG.Hide();
|
|
}
|
|
|
|
|
|
void WbMainWindow::EnableToolbar( BOOL bEnable )
|
|
{
|
|
if (bEnable)
|
|
{
|
|
m_TB.Enable(IDM_SELECT);
|
|
|
|
// don't allow text editing in zoom mode
|
|
if( m_drawingArea.Zoomed() )
|
|
m_TB.Disable(IDM_TEXT);
|
|
else
|
|
m_TB.Enable(IDM_TEXT);
|
|
|
|
m_TB.Enable(IDM_PEN);
|
|
m_TB.Enable(IDM_HIGHLIGHT);
|
|
|
|
m_TB.Enable(IDM_LINE);
|
|
m_TB.Enable(IDM_ZOOM);
|
|
m_TB.Enable(IDM_BOX);
|
|
m_TB.Enable(IDM_FILLED_BOX);
|
|
m_TB.Enable(IDM_ELLIPSE);
|
|
m_TB.Enable(IDM_FILLED_ELLIPSE);
|
|
m_TB.Enable(IDM_ERASER);
|
|
|
|
m_TB.Enable(IDM_GRAB_AREA);
|
|
m_TB.Enable(IDM_GRAB_WINDOW);
|
|
m_TB.Enable(IDM_LOCK);
|
|
m_TB.Enable(IDM_SYNC);
|
|
|
|
// enable remote pointer incase it was disabled handling
|
|
// join failures (bug 4767)
|
|
m_TB.Enable(IDM_REMOTE);
|
|
}
|
|
else
|
|
{
|
|
m_TB.Disable(IDM_SELECT);
|
|
m_TB.Disable(IDM_PEN);
|
|
m_TB.Disable(IDM_HIGHLIGHT);
|
|
m_TB.Disable(IDM_TEXT);
|
|
m_TB.Disable(IDM_LINE);
|
|
m_TB.Disable(IDM_ZOOM);
|
|
m_TB.Disable(IDM_BOX);
|
|
m_TB.Disable(IDM_FILLED_BOX);
|
|
m_TB.Disable(IDM_ELLIPSE);
|
|
m_TB.Disable(IDM_FILLED_ELLIPSE);
|
|
m_TB.Disable(IDM_ERASER);
|
|
|
|
m_TB.Disable(IDM_GRAB_AREA);
|
|
m_TB.Disable(IDM_GRAB_WINDOW);
|
|
m_TB.Disable(IDM_LOCK);
|
|
m_TB.Disable(IDM_SYNC);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: UpdatePageButtons
|
|
//
|
|
// Purpose: Enable or disable the page buttons, according to the current
|
|
// state.
|
|
//
|
|
//
|
|
//
|
|
void WbMainWindow::UpdatePageButtons()
|
|
{
|
|
// Disable page buttons if not in a call, or doing a new, or another user
|
|
// has the lock and is synced.
|
|
if ( (m_uiState != IN_CALL) ||
|
|
(m_uiSubState == SUBSTATE_NEW_IN_PROGRESS) ||
|
|
(WB_PresentationMode()))
|
|
{
|
|
m_AG.EnablePageCtrls(FALSE);
|
|
|
|
// when the page buttons are disabled, we do not allow the page sorter
|
|
// dialog to be displayed
|
|
if (m_hwndPageSortDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndPageSortDlg, WM_COMMAND, MAKELONG(IDOK, BN_CLICKED),
|
|
0);
|
|
ASSERT(m_hwndPageSortDlg == NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_AG.EnablePageCtrls(TRUE);
|
|
}
|
|
|
|
if (WB_Locked() || !IsIdle() )
|
|
{
|
|
EnableToolbar( FALSE );
|
|
}
|
|
else
|
|
{
|
|
EnableToolbar( TRUE );
|
|
}
|
|
|
|
|
|
//
|
|
// If the page sorter is up, inform it of the state change
|
|
//
|
|
if (m_hwndPageSortDlg != NULL)
|
|
{
|
|
::SendMessage(m_hwndPageSortDlg, WM_PS_ENABLEPAGEOPS,
|
|
(m_uiSubState == SUBSTATE_IDLE), 0);
|
|
}
|
|
|
|
//
|
|
// Enable the insert-page button if the page order's not locked
|
|
//
|
|
m_AG.EnableInsert( ((m_uiState == IN_CALL) &&
|
|
(m_uiSubState == SUBSTATE_IDLE) &&
|
|
(g_pwbCore->WBP_ContentsCountPages() < WB_MAX_PAGES) &&
|
|
(!WB_Locked())));
|
|
|
|
//
|
|
// Ensure the currently active menu (if any) is correctly enabled
|
|
//
|
|
InvalidateActiveMenu();
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: InvalidateActiveMenu
|
|
//
|
|
// Purpose: If a menu is currently active, gray items according to
|
|
// the current state, and force it to redraw.
|
|
//
|
|
//
|
|
void WbMainWindow::InvalidateActiveMenu()
|
|
{
|
|
if (m_hInitMenu != NULL)
|
|
{
|
|
// A menu is displayed, so set the state appropriately and force a
|
|
// repaint to show the new state
|
|
SetMenuStates(m_hInitMenu);
|
|
|
|
::RedrawWindow(::GetTopWindow(::GetDesktopWindow()),
|
|
NULL, NULL,
|
|
RDW_FRAME | RDW_INVALIDATE | RDW_ERASE |
|
|
RDW_ERASENOW | RDW_ALLCHILDREN);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: CancelLoad
|
|
//
|
|
// Purpose: Cancel any load in progress
|
|
//
|
|
//
|
|
void WbMainWindow::CancelLoad(BOOL bReleaseLock)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::CancelLoad");
|
|
|
|
// Cancel the load
|
|
g_pwbCore->WBP_CancelLoad();
|
|
|
|
// reset file name to untitled
|
|
ZeroMemory(m_strFileName, sizeof(m_strFileName));
|
|
|
|
UpdateWindowTitle();
|
|
|
|
// reset the whiteboard substate
|
|
SetSubstate(SUBSTATE_IDLE);
|
|
|
|
if (bReleaseLock)
|
|
{
|
|
ReleasePageOrderLock();
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: ReleasePageOrderLock
|
|
//
|
|
// Purpose: Releases the page order lock, unless the user has got the
|
|
// contents locked, in which case it has no effect. Called
|
|
// after asynchronous functions requiring the page order lock
|
|
// (file/new, file/open) have completed.
|
|
//
|
|
//
|
|
void WbMainWindow::ReleasePageOrderLock()
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::ReleasePageOrderLock");
|
|
|
|
//
|
|
// Only release the page order lock if:
|
|
// - the contents are not also locked (if they are then releasing
|
|
// the page order lock has no effect).
|
|
// - we actually have the page order locked in the first place.
|
|
//
|
|
if ( (!WB_GotContentsLock()) &&
|
|
(WB_GotLock()) )
|
|
{
|
|
g_pwbCore->WBP_Unlock();
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: IsIdle
|
|
//
|
|
// Purpose: Returns true if the main window is idle (in a call and not
|
|
// loading a file/performing a new)
|
|
//
|
|
//
|
|
BOOL WbMainWindow::IsIdle()
|
|
{
|
|
|
|
return((m_uiState == IN_CALL) && (m_uiSubState == SUBSTATE_IDLE));
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: SetSubstate
|
|
//
|
|
// Purpose: Sets the substate, informing the page sorter dialog of the
|
|
// change, if necessary.
|
|
//
|
|
//
|
|
void WbMainWindow::SetSubstate(UINT newSubState)
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::SetSubstate");
|
|
|
|
// substate only valid if in a call
|
|
if ( (m_uiState != IN_CALL)
|
|
|| (newSubState != m_uiSubState))
|
|
{
|
|
m_uiSubState = newSubState;
|
|
|
|
// Trace the substate change
|
|
switch (m_uiSubState)
|
|
{
|
|
case SUBSTATE_IDLE:
|
|
TRACE_DEBUG(("set substate to IDLE"));
|
|
break;
|
|
|
|
case SUBSTATE_LOADING:
|
|
TRACE_DEBUG(("set substate to LOADING"));
|
|
break;
|
|
|
|
case SUBSTATE_NEW_IN_PROGRESS:
|
|
TRACE_DEBUG(("set substate to NEW_IN_PROGRESS"));
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Unknown substate %hd",m_uiSubState));
|
|
break;
|
|
}
|
|
|
|
// update the page buttons (may have become enabled/disabled)
|
|
UpdatePageButtons();
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: PositionUpdated
|
|
//
|
|
// Purpose: Called when the drawing area position has changed.
|
|
// change, if necessary.
|
|
//
|
|
//
|
|
void WbMainWindow::PositionUpdated()
|
|
{
|
|
RECT rectDraw;
|
|
|
|
m_drawingArea.GetVisibleRect(&rectDraw);
|
|
|
|
if (m_pLocalUser != NULL)
|
|
{
|
|
// Set the new position from the drawing area
|
|
m_pLocalUser->SetVisibleRect(&rectDraw);
|
|
|
|
// Show that an update of the sync position is pending
|
|
m_bSyncUpdateNeeded = TRUE;
|
|
}
|
|
|
|
// If the current page is a valid one then store the user's position on
|
|
// that page.
|
|
if (m_hCurrentPage != WB_PAGE_HANDLE_NULL)
|
|
{
|
|
// Store position of this page
|
|
WORD pageIndex = (WORD)m_hCurrentPage;
|
|
|
|
|
|
PAGE_POSITION *mapob;
|
|
POSITION position = m_pageToPosition.GetHeadPosition();
|
|
BOOL bFound = FALSE;
|
|
while (position && !bFound)
|
|
{
|
|
mapob = (PAGE_POSITION *)m_pageToPosition.GetNext(position);
|
|
if ( mapob->hPage == pageIndex)
|
|
{
|
|
bFound = TRUE;
|
|
}
|
|
}
|
|
|
|
// If we're replacing an existing entry, then free the old entry.
|
|
if (bFound)
|
|
{
|
|
mapob->position.x = rectDraw.left;
|
|
mapob->position.y = rectDraw.top;
|
|
}
|
|
else
|
|
{
|
|
mapob = new PAGE_POSITION;
|
|
|
|
if (!mapob)
|
|
{
|
|
ERROR_OUT(("PositionUpdated failing; couldn't allocate PAGE_POSITION object"));
|
|
}
|
|
else
|
|
{
|
|
mapob->hPage = pageIndex;
|
|
mapob->position.x = rectDraw.left;
|
|
mapob->position.y = rectDraw.top;
|
|
m_pageToPosition.AddTail(mapob);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function : OnALSLoadResult
|
|
//
|
|
// Purpose : Deal with an ALS_LOAD_RESULT event
|
|
//
|
|
//
|
|
void WbMainWindow::OnALSLoadResult(UINT reason)
|
|
{
|
|
|
|
int errorMsg = 0;
|
|
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnALSLoadResult");
|
|
|
|
switch (reason)
|
|
{
|
|
case AL_LOAD_FAIL_NO_FP:
|
|
WARNING_OUT(("Remote WB load failed - no FP"));
|
|
errorMsg = IDS_MSG_LOAD_FAIL_NO_FP;
|
|
break;
|
|
|
|
case AL_LOAD_FAIL_NO_EXE:
|
|
WARNING_OUT(("Remote WB load failed - no exe"));
|
|
errorMsg = IDS_MSG_LOAD_FAIL_NO_EXE;
|
|
break;
|
|
|
|
case AL_LOAD_FAIL_BAD_EXE:
|
|
WARNING_OUT(("Remote WB load failed - bad exe"));
|
|
errorMsg = IDS_MSG_LOAD_FAIL_BAD_EXE;
|
|
break;
|
|
|
|
case AL_LOAD_FAIL_LOW_MEM:
|
|
WARNING_OUT(("Remote WB load failed - low mem"));
|
|
errorMsg = IDS_MSG_LOAD_FAIL_LOW_MEM;
|
|
break;
|
|
|
|
default:
|
|
WARNING_OUT(("Bad ALSLoadResult reason %d", reason));
|
|
break;
|
|
}
|
|
|
|
|
|
if (errorMsg)
|
|
{
|
|
//
|
|
// Put up an error message
|
|
//
|
|
Message(NULL, IDS_MSG_CAPTION, errorMsg);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function : OnEndSession
|
|
//
|
|
// Purpose : Called when Windows is exiting
|
|
//
|
|
//
|
|
void WbMainWindow::OnEndSession(BOOL bEnding)
|
|
{
|
|
if (bEnding)
|
|
{
|
|
::PostQuitMessage(0);
|
|
}
|
|
else
|
|
{
|
|
m_bQuerySysShutdown = FALSE; // never mind, cancel OnClose special handling
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Function: OnCancelMode()
|
|
//
|
|
// Purpose: Called whenever a WM_CANCELMODE message is sent to the frame
|
|
// window.
|
|
// WM_CANCELMODE is sent when another app or dialog receives the
|
|
// input focus. The frame simply records that a WM_CANCELMODE
|
|
// message has been sent. This fact is used by the SelectWindow
|
|
// code to determine if it should cancel the selecting of a
|
|
// window
|
|
//
|
|
//
|
|
void WbMainWindow::OnCancelMode()
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::OnCancelMode");
|
|
|
|
m_cancelModeSent = TRUE;
|
|
|
|
//
|
|
// Note: Not passed to the default handler as the default action on
|
|
// WM_CANCELMODE is to release mouse capture - we shall do this
|
|
// explicitly.
|
|
//
|
|
|
|
|
|
// blow off any dragging that might be in progress (bug 573)
|
|
POINT pt;
|
|
::GetCursorPos( &pt );
|
|
::ScreenToClient(m_drawingArea.m_hwnd, &pt);
|
|
::SendMessage(m_drawingArea.m_hwnd, WM_LBUTTONUP, 0, MAKELONG( pt.x, pt.y ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void WbMainWindow::LoadCmdLine(LPCSTR szFilename)
|
|
{
|
|
int iOnSave;
|
|
|
|
if (szFilename && *szFilename)
|
|
{
|
|
if( UsersMightLoseData( NULL, NULL ) ) // bug NM4db:418
|
|
return;
|
|
|
|
// Don't prompt to save file if we're already loading
|
|
if (m_uiSubState != SUBSTATE_LOADING )
|
|
{
|
|
// Check whether there are changes to be saved
|
|
iOnSave = QuerySaveRequired(TRUE);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (iOnSave == IDYES)
|
|
{
|
|
// User wants to save the drawing area contents
|
|
int iResult = OnSave(TRUE);
|
|
|
|
if( iResult == IDOK )
|
|
{
|
|
UpdateWindowTitle();
|
|
}
|
|
else
|
|
{
|
|
// cancelled out of save, so cancel the open operation
|
|
return;
|
|
}
|
|
}
|
|
|
|
// load filename
|
|
if( iOnSave != IDCANCEL )
|
|
LoadFile(szFilename);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OnNotify()
|
|
// Handles TTN_NEEDTEXTA and TTN_NEEDTEXTW
|
|
//
|
|
void WbMainWindow::OnNotify(UINT id, NMHDR * pNM)
|
|
{
|
|
UINT nID;
|
|
HWND hwnd = NULL;
|
|
POINT ptCurPos;
|
|
UINT nTipStringID;
|
|
|
|
if (!pNM)
|
|
return;
|
|
|
|
if (pNM->code == TTN_NEEDTEXTA)
|
|
{
|
|
TOOLTIPTEXTA *pTA = (TOOLTIPTEXTA *)pNM;
|
|
|
|
// get id and hwnd
|
|
if( pTA->uFlags & TTF_IDISHWND )
|
|
{
|
|
// idFrom is actually the HWND of the tool
|
|
hwnd = (HWND)pNM->idFrom;
|
|
nID = ::GetDlgCtrlID(hwnd);
|
|
}
|
|
else
|
|
{
|
|
nID = (UINT)pNM->idFrom;
|
|
}
|
|
|
|
// get tip string id
|
|
nTipStringID = GetTipId(hwnd, nID);
|
|
if (nTipStringID == 0)
|
|
return;
|
|
|
|
// give it to em
|
|
pTA->lpszText = MAKEINTRESOURCE( nTipStringID );
|
|
pTA->hinst = g_hInstance;
|
|
}
|
|
else if (pNM->code == TTN_NEEDTEXTW)
|
|
{
|
|
TOOLTIPTEXTW *pTW = (TOOLTIPTEXTW *)pNM;
|
|
|
|
// get id and hwnd
|
|
if( pTW->uFlags & TTF_IDISHWND )
|
|
{
|
|
// idFrom is actually the HWND of the tool
|
|
hwnd = (HWND)pNM->idFrom;
|
|
nID = ::GetDlgCtrlID(hwnd);
|
|
}
|
|
else
|
|
{
|
|
nID = (UINT)pNM->idFrom;
|
|
}
|
|
|
|
// get tip string id
|
|
nTipStringID = GetTipId(hwnd, nID );
|
|
if (nTipStringID == 0)
|
|
return;
|
|
|
|
// give it to em
|
|
pTW->lpszText = (LPWSTR) MAKEINTRESOURCE( nTipStringID );
|
|
pTW->hinst = g_hInstance;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// GetTipId()
|
|
// Finds the tooltip for a control in Whiteboard
|
|
//
|
|
UINT WbMainWindow::GetTipId(HWND hwndTip, UINT nID)
|
|
{
|
|
WbTool * pTool;
|
|
BOOL bCheckedState;
|
|
int nTipID;
|
|
int nTipStringID;
|
|
int i;
|
|
|
|
// find tip stuff relevant for nID
|
|
nTipID = -1;
|
|
for( i=0; i<((sizeof g_tipIDsArray)/(sizeof (TIPIDS) )); i++ )
|
|
{
|
|
if( g_tipIDsArray[i].nID == nID )
|
|
{
|
|
nTipID = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// valid?
|
|
if( nTipID < 0 )
|
|
return( 0 );
|
|
|
|
// get checked state
|
|
switch( g_tipIDsArray[ nTipID ].nCheck )
|
|
{
|
|
case TB:
|
|
bCheckedState =
|
|
(::SendMessage(m_TB.m_hwnd, TB_ISBUTTONCHECKED, nID, 0) != 0);
|
|
break;
|
|
|
|
case BT:
|
|
if (hwndTip != NULL)
|
|
{
|
|
bCheckedState =
|
|
(::SendMessage(hwndTip, BM_GETSTATE, 0, 0) & 0x0003) == 1;
|
|
}
|
|
else
|
|
bCheckedState = FALSE;
|
|
|
|
break;
|
|
|
|
case NA:
|
|
default:
|
|
bCheckedState = FALSE;
|
|
break;
|
|
}
|
|
|
|
// get tip string id
|
|
if( bCheckedState )
|
|
nTipStringID = g_tipIDsArray[ nTipID ].nDownTipID;
|
|
else
|
|
nTipStringID = g_tipIDsArray[ nTipID ].nUpTipID;
|
|
|
|
// done
|
|
return( nTipStringID );
|
|
}
|
|
|
|
|
|
|
|
// gets default path if no saves or opens have been done yet
|
|
// Returns FALSE if last default should be reused
|
|
BOOL WbMainWindow::GetDefaultPath(LPTSTR csDefaultPath , UINT size)
|
|
{
|
|
DWORD dwType;
|
|
DWORD dwBufLen = size;
|
|
HKEY hDefaultKey = NULL;
|
|
BOOL bRet =FALSE;
|
|
|
|
if( !lstrlen(m_strFileName) )
|
|
{
|
|
// a name has not been picked yet in this session, use path
|
|
// to "My Documents"
|
|
if( (RegOpenKeyEx( HKEY_CURRENT_USER,
|
|
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
|
|
0,
|
|
KEY_READ,
|
|
&hDefaultKey )
|
|
!= ERROR_SUCCESS) ||
|
|
(RegQueryValueEx( hDefaultKey,
|
|
"Personal",
|
|
NULL,
|
|
&dwType,
|
|
(BYTE *)csDefaultPath,
|
|
&dwBufLen )
|
|
!= ERROR_SUCCESS))
|
|
{
|
|
// reg failed, use desktop
|
|
LPITEMIDLIST pidl;
|
|
if(SUCCEEDED (SHGetSpecialFolderLocation(GetDesktopWindow(),CSIDL_DESKTOPDIRECTORY,&pidl)))
|
|
|
|
{
|
|
bRet= SHGetPathFromIDList(pidl,csDefaultPath);
|
|
}
|
|
|
|
if( hDefaultKey != NULL )
|
|
RegCloseKey( hDefaultKey );
|
|
}
|
|
else
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WbMainWindow::OnSysColorChange( void )
|
|
{
|
|
if (m_drawingArea.Page() != WB_PAGE_HANDLE_NULL)
|
|
{
|
|
PG_ReinitPalettes();
|
|
|
|
::InvalidateRect(m_hwnd, NULL, TRUE );
|
|
::UpdateWindow(m_hwnd);
|
|
}
|
|
|
|
m_TB.RecolorButtonImages();
|
|
m_AG.RecolorButtonImages();
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// posts a do-you-wana-do-that message if other users are in the conference
|
|
//
|
|
BOOL WbMainWindow::UsersMightLoseData( BOOL *pbWasPosted, HWND hwnd )
|
|
{
|
|
if ( (m_uiState == IN_CALL) && m_bCallActive )
|
|
{
|
|
UINT count;
|
|
|
|
count = g_pwbCore->WBP_PersonCountInCall();
|
|
|
|
if (count > 1)
|
|
{
|
|
if( pbWasPosted != NULL )
|
|
*pbWasPosted = TRUE;
|
|
|
|
return( ::Message(hwnd, IDS_DEFAULT, IDS_MSG_USERSMIGHTLOSE, MB_YESNO | MB_ICONEXCLAMATION ) != IDYES );
|
|
}
|
|
}
|
|
|
|
if( pbWasPosted != NULL )
|
|
*pbWasPosted = FALSE;
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
|
|
BOOL WbMainWindow::HasGraphicChanged( PWB_GRAPHIC pOldHeaderCopy, const PWB_GRAPHIC pNewHeader )
|
|
{
|
|
MLZ_EntryOut(ZONE_FUNCTION, "WbMainWindow::HasGraphicChanged");
|
|
|
|
|
|
// If nothing is different but the lock state and some misc in a WBP_EVENT_GRAPHIC_UPDATE_IND then
|
|
// the graphics are visually the same.
|
|
//
|
|
// NOTE: This does not check ZORDER. ZORDER changes are handled by WBP_EVENT_GRAPHIC_MOVED
|
|
|
|
// if objects aren't the same length, they are different
|
|
if( pOldHeaderCopy->length != pNewHeader->length )
|
|
return( TRUE );
|
|
|
|
// temporarialy set pOldHeaderCopy's locked state + misc to same as pNewHeader so we can do an
|
|
// object compare.
|
|
UINT uOldLocked = pOldHeaderCopy->locked;
|
|
pOldHeaderCopy->locked = pNewHeader->locked;
|
|
|
|
OM_OBJECT_ID oldlockPersonID = pOldHeaderCopy->lockPersonID;
|
|
pOldHeaderCopy->lockPersonID = pNewHeader->lockPersonID;
|
|
|
|
UINT oldloadedFromFile = pOldHeaderCopy->loadedFromFile;
|
|
pOldHeaderCopy->loadedFromFile = pNewHeader->loadedFromFile;
|
|
|
|
NET_UID oldloadingClientID = pOldHeaderCopy->loadingClientID;
|
|
pOldHeaderCopy->loadingClientID = pNewHeader->loadingClientID;
|
|
|
|
// compare objects
|
|
BOOL bChanged = FALSE;
|
|
if( memcmp( pOldHeaderCopy, pNewHeader, pOldHeaderCopy->length ) != 0 )
|
|
bChanged = TRUE;
|
|
|
|
|
|
// restore lock state + misc
|
|
pOldHeaderCopy->locked = (TSHR_UINT8)uOldLocked;
|
|
pOldHeaderCopy->lockPersonID = oldlockPersonID;
|
|
pOldHeaderCopy->loadedFromFile = (TSHR_UINT16)oldloadedFromFile;
|
|
pOldHeaderCopy->loadingClientID = oldloadingClientID;
|
|
|
|
return( bChanged );
|
|
}
|
|
|
|
|
|
|
|
void WbMainWindow::UpdateWindowTitle(void)
|
|
{
|
|
TCHAR *pTitle = GetWindowTitle();
|
|
if (pTitle != NULL)
|
|
{
|
|
::SetWindowText(m_hwnd, pTitle);
|
|
delete pTitle;
|
|
}
|
|
}
|
|
|