4856 lines
135 KiB
C
4856 lines
135 KiB
C
/*-----------------------------------------------------------------------------+
|
|
| MPLAYER.C |
|
|
| |
|
|
| This file contains the code that implements the "MPlayer" (main) dialog box. |
|
|
| |
|
|
| (C) Copyright Microsoft Corporation 1991. All rights reserved. |
|
|
| |
|
|
| Revision History |
|
|
| Oct-1992 MikeTri Ported to WIN32 / WIN16 common code |
|
|
| |
|
|
+-----------------------------------------------------------------------------*/
|
|
|
|
/* include files */
|
|
#include "nocrap.h"
|
|
#include "stdio.h"
|
|
|
|
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
#include <shellapi.h>
|
|
#include <windowsx.h>
|
|
#include <htmlhelp.h>
|
|
#include <tchar.h>
|
|
#define INCGUID
|
|
#include "mpole.h"
|
|
|
|
#include "mplayer.h"
|
|
#include "toolbar.h"
|
|
#include "fixreg.h"
|
|
#include "helpids.h"
|
|
|
|
//These include files for WM_DEVICECHANGE messages from the mixer
|
|
#include <dbt.h>
|
|
#include <cfgmgr32.h>
|
|
#include <initguid.h>
|
|
#include <mmddk.h>
|
|
#include <ks.h>
|
|
#include <ksmedia.h>
|
|
|
|
HDEVNOTIFY MixerEventContext = NULL; //Event Context for WM_DEVICECHANGE messages related to mixer
|
|
|
|
BOOL DeviceChange_Init(HWND hWnd);
|
|
void DeviceChange_Cleanup();
|
|
|
|
//extern int FAR PASCAL ShellAbout(HWND hWnd, LPCTSTR szApp, LPCTSTR szOtherStuff, HICON hIcon);
|
|
|
|
/* in server.c, but not in a header file like it should be... */
|
|
extern PTSTR FAR FileName(LPCTSTR szPath);
|
|
|
|
/* globals */
|
|
|
|
|
|
// Used in converting units from pixels to Himetric and vice-versa
|
|
int giXppli = 0; // pixels per logical inch along width
|
|
int giYppli = 0; // pixels per logical inch along height
|
|
|
|
// Since this is a not an MDI app, there can be only one server and one doc.
|
|
CLSID clsid;
|
|
SRVR srvrMain;
|
|
DOC docMain;
|
|
LPMALLOC lpMalloc;
|
|
|
|
TCHAR szClient[cchFilenameMax];
|
|
TCHAR szClientDoc[cchFilenameMax];
|
|
|
|
// Has the user made changes to the document?
|
|
BOOL fDocChanged = FALSE;
|
|
|
|
/*********************************************************************
|
|
** OLE2NOTE: the very last thing an app must be do is properly shut
|
|
** down OLE. This call MUST be guarded! it is only allowable to
|
|
** call OleUninitialize if OleInitialize has been called.
|
|
*********************************************************************/
|
|
|
|
// Has OleInitialize been called? assume not.
|
|
BOOL gfOleInitialized = FALSE;
|
|
|
|
// Clipboard formats
|
|
CLIPFORMAT cfNative;
|
|
CLIPFORMAT cfEmbedSource;
|
|
CLIPFORMAT cfObjectDescriptor;
|
|
CLIPFORMAT cfMPlayer;
|
|
|
|
LPWSTR sz1Ole10Native = L"\1Ole10Native";
|
|
|
|
/* in server.c, but not in a header file like it should be... */
|
|
extern LPTSTR FAR FileName(LPCTSTR szPath);
|
|
/* in init.c */
|
|
extern PTSTR gpchFilter;
|
|
//extern HMREGNOTIFY ghmrn;
|
|
|
|
/* globals */
|
|
|
|
DWORD gwPlatformId;
|
|
UINT gwPlaybarHeight=TOOLBAR_HEIGHT;/* Taken from server.c */
|
|
UINT gwOptions; /* The object options from the dlg box */
|
|
BOOL gfEmbeddedObject; // TRUE if editing embedded OLE object
|
|
BOOL gfRunWithEmbeddingFlag; // TRUE if we are run with "-Embedding"
|
|
BOOL gfPlayingInPlace; // TRUE if playing in place
|
|
BOOL gfParentWasEnabled; // TRUE if parent was enabled
|
|
BOOL gfShowWhilePlaying; //
|
|
BOOL gfDirty; //
|
|
int gfErrorBox; // TRUE if we have a message box active
|
|
BOOL gfErrorDeath;
|
|
BOOL gfWinIniChange;
|
|
|
|
HHOOK hHookMouse; // Mouse hook handle.
|
|
HOOKPROC fpMouseHook; // Mouse hook proc address.
|
|
|
|
HWND ghwndFocusSave; // saved focus window
|
|
|
|
BOOL gfOpenDialog = FALSE; // If TRUE, put up open dialog
|
|
BOOL gfCloseAfterPlaying = FALSE;// TRUE if we are to hide after play
|
|
HICON hiconApp; /* app icon */
|
|
HMENU ghMenu; /* handle to the dialog's main menu */
|
|
HMENU ghDeviceMenu; /* handle to the Device popup menu */
|
|
HWND ghwndApp; /* handle to the MPlayer (main) dialog box*/
|
|
HWND ghwndMap; /* handle to the track map window */
|
|
HWND ghwndStatic; /* handle to the static text window */
|
|
HBRUSH ghbrFillPat; /* The selection fill pattern. */
|
|
HWND ghwndToolbar; /* handle of the toolbar */
|
|
HWND ghwndMark; /* handle of the mark buttons toolbar */
|
|
HWND ghwndFSArrows; /* handle of the arrows to the scrollbar */
|
|
HWND ghwndTrackbar; /* handle to the trackbar window */
|
|
UINT gwStatus = (UINT)(-1); /* device status (if <gwDeviceID> != NULL)*/
|
|
DWORD gdwSeekPosition; /* Place to seek to next */
|
|
BOOL gfValidMediaInfo; /* are we displaying valid media info? */
|
|
BOOL gfValidCaption; /* are we displaying a valid caption? */
|
|
BOOL gfScrollTrack; /* is user dragging the scrollbar thumb? */
|
|
BOOL gfPlayOnly; /* play only window? */
|
|
BOOL gfJustPlayed = FALSE; /* Just sent a PlayMCI() command */
|
|
BOOL gfJustPlayedSel = FALSE; /* Just sent a ID_PLAYSEL command. */
|
|
BOOL gfUserStopped = FALSE; /* user pressed stop - didn't happen itslf*/
|
|
DWORD_PTR dwLastPageUpTime; /* time of last page-left operation */
|
|
UINT gwCurScale = ID_NONE; /* current scale style */
|
|
LONG glSelStart = -1; /* See if selection changes (dirty object)*/
|
|
LONG glSelEnd = -1; /* See if selection changes (dirty object)*/
|
|
|
|
int gInc; /* how much to inc/dec spin arrows by */
|
|
|
|
BOOL gfAppActive = FALSE; /* Are we the active application? */
|
|
UINT gwHeightAdjust;
|
|
HWND ghwndFocus = NULL; /* Who had the focus when we went inactive*/
|
|
BOOL gfInClose = FALSE; /* ack?*/
|
|
BOOL gfCurrentCDChecked = FALSE; /* TRUE if we've checked whether it can play */
|
|
BOOL gfCurrentCDNotAudio = FALSE;/* TRUE when we have a CD that we can't play */
|
|
|
|
extern BOOL gfInPlayMCI;
|
|
|
|
LPDATAOBJECT gpClipboardDataObject = NULL; /* If non-NULL, call OleFlushClipboard on exit */
|
|
|
|
HPALETTE ghpalApp;
|
|
|
|
static sfSeekExact; // last state
|
|
|
|
UINT gwCurDevice = 0; /* current device */
|
|
UINT gwNumDevices = 0; /* number of available media devices */
|
|
MCIDEVICE garMciDevices[MAX_MCI_DEVICES]; /* array with info about a device */
|
|
|
|
|
|
/* strings which get loaded in InitMplayerDialog in init.c, English version shown here
|
|
All the sizes are much larger than needed, probably. Maybe could save nearly 100 bytes!! :)
|
|
*/
|
|
extern TCHAR gszFrames[40]; /* "frames" */
|
|
extern TCHAR gszHrs[20]; /* "hrs" */
|
|
extern TCHAR gszMin[20]; /* "min" */
|
|
extern TCHAR gszSec[20]; /* "sec" */
|
|
extern TCHAR gszMsec[20]; /* "msec" */
|
|
|
|
|
|
static SZCODE aszNULL[] = TEXT("");
|
|
static BOOL sfInLayout = FALSE; // don't let Layout get re-entered
|
|
|
|
static SZCODE szSndVol32[] = TEXT("sndvol32.exe");
|
|
|
|
|
|
static SZCODE aszTitleFormat[] = TEXT("%"TS" - %"TS"");
|
|
|
|
HANDLE ghInst; /* handle to the application instance */
|
|
HFONT ghfontMap; /* handle to the font used for drawing
|
|
the track map */
|
|
LPTSTR gszCmdLine; /* string holding the command line parms */
|
|
int giCmdShow; /* command show */
|
|
TCHAR gachFileDevice[MAX_PATH]; /* string holding the curr file or device */
|
|
TCHAR gachWindowTitle[MAX_PATH]; /* string holding name we will display */
|
|
TCHAR gachCaption[MAX_PATH]; /* string holding name we will display */
|
|
|
|
HACCEL hAccel;
|
|
int gcAccelEntries;
|
|
|
|
typedef struct _POS
|
|
{
|
|
int x;
|
|
int y;
|
|
int cx; /* This field is non-0 if we're currently sizing/moving */
|
|
int cy;
|
|
}
|
|
POS, *PPOS;
|
|
|
|
POS posSizeMove = {0,0,0,0}; /* POS we want during size/move operations */
|
|
|
|
|
|
|
|
STRING_TO_ID_MAP DevToIconIDMap[] =
|
|
{
|
|
{ szCDAudio, IDI_DCDA },
|
|
{ szVideoDisc, IDI_DDEFAULT },
|
|
{ szSequencer, IDI_DMIDI },
|
|
{ szVCR, IDI_DDEFAULT },
|
|
{ szWaveAudio, IDI_DSOUND },
|
|
{ szAVIVideo, IDI_DVIDEO }
|
|
};
|
|
|
|
|
|
//CDA file processing///////////////////////////////////////////////////
|
|
//The following structure taken from deluxecd. This is used in processing
|
|
typedef struct {
|
|
DWORD dwRIFF; // 'RIFF'
|
|
DWORD dwSize; // Chunk size = (file size - 8)
|
|
DWORD dwCDDA; // 'CDDA'
|
|
DWORD dwFmt; // 'fmt '
|
|
DWORD dwCDDASize; // Chunk size of 'fmt ' = 24
|
|
WORD wFormat; // Format tag
|
|
WORD wTrack; // Track number
|
|
DWORD DiscID; // Unique disk id
|
|
DWORD lbnTrackStart; // Track starting sector (LBN)
|
|
DWORD lbnTrackLength; // Track length (LBN count)
|
|
DWORD msfTrackStart; // Track starting sector (MSF)
|
|
DWORD msfTrackLength; // Track length (MSF)
|
|
} RIFFCDA;
|
|
|
|
void HandleCDAFile(TCHAR *szFile);
|
|
BOOL IsTrackFileNameValid(LPTSTR lpstFileName, UINT *pUiTrackIndex);
|
|
void JumpToCDTrack(UINT trackno);
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
/* private function prototypes */
|
|
|
|
//int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int iCmdShow);
|
|
void CleanUpClipboard();
|
|
int GetHeightAdjust(HWND hwnd);
|
|
HANDLE PASCAL GetDib (VOID);
|
|
|
|
|
|
static HHOOK fpfnOldMsgFilter;
|
|
static HOOKPROC fpfnMsgHook;
|
|
//Data used for supporting context menu help
|
|
BOOL bF1InMenu=FALSE; //If true F1 was pressed on a menu item.
|
|
UINT currMenuItem=0; //The current menu item if any.
|
|
|
|
|
|
typedef void (FAR PASCAL *PENWINREGISTERPROC)(UINT, BOOL);
|
|
|
|
/* Define some constants to make parameters to CreateEvent a tad less obscure:
|
|
*/
|
|
#define EVENT_DEFAULT_SECURITY NULL
|
|
#define EVENT_RESET_MANUAL TRUE
|
|
#define EVENT_RESET_AUTOMATIC FALSE
|
|
#define EVENT_INITIAL_STATE_SIGNALED TRUE
|
|
#define EVENT_INITIAL_STATE_NOT_SIGNALED FALSE
|
|
#define EVENT_NO_NAME NULL
|
|
|
|
HANDLE heventCmdLineScanned; /* Event will be signaled when command line scanned */
|
|
HANDLE heventDeviceMenuBuilt; /* Event will be signaled when device menu complete */
|
|
|
|
#ifdef LATER
|
|
SCALE gscaleInitXY[2] = { 0, 0, 0, 0 }; // Initial scale to use for inserting OLE objects
|
|
#endif
|
|
|
|
|
|
|
|
/*------------------------------------------------------+
|
|
| HelpMsgFilter - filter for F1 key in dialogs |
|
|
| |
|
|
+------------------------------------------------------*/
|
|
|
|
DWORD FAR PASCAL HelpMsgFilter(int nCode, DWORD_PTR wParam, DWORD_PTR lParam)
|
|
{
|
|
if (nCode >= 0){
|
|
LPMSG msg = (LPMSG)lParam;
|
|
|
|
if ((msg->message == WM_KEYDOWN) && (msg->wParam == VK_F1))
|
|
{
|
|
if(nCode == MSGF_MENU)
|
|
bF1InMenu = TRUE;
|
|
SendMessage(ghwndApp, WM_COMMAND, (WPARAM)IDM_HELPTOPICS, 0L);
|
|
}
|
|
}
|
|
// return DefHookProc(nCode, wParam, lParam, (HHOOK FAR *)&fpfnOldMsgFilter);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CHICAGO_PRODUCT
|
|
|
|
BOOL IsBadSegmentedCodePtr(LPARAM lpPtr)
|
|
{
|
|
#define DSC_PRESENT 0x80
|
|
#define DSC_CODE_BIT 0x08
|
|
#define DSC_RW_BIT 0x02
|
|
#define DSC_DISCARDABLE 0x10
|
|
|
|
WORD wSel;
|
|
WORD wOff;
|
|
BOOL fRet;
|
|
|
|
wSel = HIWORD(lpPtr);
|
|
wOff = LOWORD(lpPtr);
|
|
|
|
_asm {
|
|
mov ax, [wSel];
|
|
lar bx, ax;
|
|
jnz ValidDriverCallback_Failure ; //Return TRUE for error
|
|
|
|
mov ch, DSC_CODE_BIT or DSC_RW_BIT or DSC_PRESENT ;
|
|
and bh, ch;
|
|
cmp bh, ch;
|
|
jne ValidDriverCallback_Failure ; //Not executable segment
|
|
|
|
test bl, DSC_DISCARDABLE ;
|
|
jnz ValidDriverCallback_Failure ; //Not fixed segment
|
|
|
|
lsl cx, ax; ; //Get segment limit
|
|
mov bx, [wOff];
|
|
cmp bx, cx;
|
|
jb ValidDriverCallback_Success ; //Valid offset
|
|
|
|
jne ValidDriverCallback_Failure ; //Not executable segment
|
|
|
|
ValidDriverCallback_Failure:
|
|
mov eax, 1;
|
|
jmp ValidDriverCallback_Return;
|
|
ValidDriverCallback_Success:
|
|
xor eax, eax;
|
|
ValidDriverCallback_Return:
|
|
mov [fRet], eax;
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* RouteKeyPresses
|
|
*
|
|
* Reroutes cursor keys etc to track bar.
|
|
*/
|
|
void RouteKeyPresses(PMSG pMsg)
|
|
{
|
|
/* Hack for PowerPoint
|
|
*
|
|
* Mail from PaulWa:
|
|
*
|
|
* --------
|
|
* Here's a problem you might consider fixing.
|
|
* Launching Media Player with certain keystrokes
|
|
* doesn't work right (e.g. arrow keys, page up/down,
|
|
* etc.).
|
|
*
|
|
* The problem is due to the fact that Media Player
|
|
* handles key up events. We use the key down event
|
|
* to launch the server in slideshow, but then the key
|
|
* up event is passed to the server. It would probably
|
|
* be best for Media Player to ignore key up events
|
|
* unless it had previously received a key down.
|
|
* If this is very difficult to fix in Media Player,
|
|
* then we can fix it in PP by launching servers on
|
|
* key up rather than key down. However, other container
|
|
* apps will see the same problem.
|
|
* --------
|
|
*
|
|
* OK, in the spirit of cooperation, let's hack things
|
|
* so our PowerPoint friends can carry on with their
|
|
* dubious practices.
|
|
*/
|
|
static WPARAM LastVKeyDown;
|
|
|
|
/* On key down when we're embedded, remember what is was:
|
|
*/
|
|
if (gfRunWithEmbeddingFlag && (pMsg->message == WM_KEYDOWN))
|
|
LastVKeyDown = pMsg->wParam;
|
|
|
|
/* Don't reroute if it's a key up that doesn't match
|
|
* the last key down; this effectively ignores it:
|
|
*/
|
|
if (gfRunWithEmbeddingFlag &&
|
|
(pMsg->message == WM_KEYUP) && (pMsg->wParam != LastVKeyDown))
|
|
{
|
|
DPF0("Ignoring WM_KEYUP, since it doesn't match last WM_KEYDOWN.\n");
|
|
}
|
|
else
|
|
{
|
|
switch(pMsg->wParam)
|
|
{
|
|
case VK_UP:
|
|
case VK_LEFT:
|
|
case VK_DOWN:
|
|
case VK_RIGHT:
|
|
case VK_NEXT:
|
|
case VK_PRIOR:
|
|
case VK_HOME:
|
|
case VK_END:
|
|
pMsg->hwnd = ghwndTrackbar;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pMsg->message == WM_KEYUP)
|
|
LastVKeyDown = 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* WinMain(hInst, hPrev, szCmdLine, iCmdShow)
|
|
*
|
|
* This is the main procedure for the application. It performs initialization
|
|
* and then enters a message-processing loop, where it remains until it
|
|
* receives a WM_QUIT message (meaning the app was closed). This function
|
|
* always returns TRUE..
|
|
*
|
|
*/
|
|
int WINAPI WinMain( HINSTANCE hInst /* handle to the current instance of the application */
|
|
, HINSTANCE hPrev /* handle to the previous instance of the application */
|
|
, LPSTR szCmdLine /* null-terminated string holding the command line params */
|
|
, int iCmdShow /* how the window should be initially displayed */
|
|
)
|
|
{
|
|
MSG rMsg; /* variable used for holding a message */
|
|
HWND hwndFocus;
|
|
HWND hwndP;
|
|
|
|
/* call the Pen Windows extensions to allow them to subclass our
|
|
edit controls if they so wish
|
|
*/
|
|
|
|
OSVERSIONINFO OSVersionInfo;
|
|
|
|
#ifdef UNICODE
|
|
LPTSTR szUnicodeCmdLine;
|
|
|
|
szUnicodeCmdLine = AllocateUnicodeString(szCmdLine);
|
|
#endif
|
|
|
|
heventCmdLineScanned = CreateEvent( EVENT_DEFAULT_SECURITY,
|
|
EVENT_RESET_MANUAL,
|
|
EVENT_INITIAL_STATE_NOT_SIGNALED,
|
|
EVENT_NO_NAME );
|
|
|
|
heventDeviceMenuBuilt = CreateEvent( EVENT_DEFAULT_SECURITY,
|
|
EVENT_RESET_MANUAL,
|
|
EVENT_INITIAL_STATE_NOT_SIGNALED,
|
|
EVENT_NO_NAME );
|
|
|
|
if (!heventCmdLineScanned || !heventDeviceMenuBuilt)
|
|
return FALSE;
|
|
|
|
OSVersionInfo.dwOSVersionInfoSize = sizeof OSVersionInfo;
|
|
|
|
GetVersionEx(&OSVersionInfo);
|
|
|
|
gwPlatformId = OSVersionInfo.dwPlatformId;
|
|
|
|
|
|
giCmdShow = iCmdShow;
|
|
|
|
#ifdef UNICODE
|
|
if (!AppInit(hInst,hPrev,szUnicodeCmdLine))
|
|
#else
|
|
if (!AppInit(hInst,hPrev,szCmdLine))
|
|
#endif
|
|
return FALSE;
|
|
|
|
/* Device Menu Initialization:
|
|
*
|
|
* If the user has requested an Open dialog (by supplying the /open
|
|
* flag with no file name), we've already built the Device menu,
|
|
* since the list of devices is required up front.
|
|
*
|
|
* If we're just playing in tiny mode, we don't need the device list.
|
|
* It will be built if the user switches to full mode and then accesses
|
|
* the Device menu or selects File.Open.
|
|
*
|
|
* Otherwise go for it. The main window's already up now, so we
|
|
* can build the list on a background thread. Don't forget to wait
|
|
* for the event to be signaled when the appropriate menu is accessed.
|
|
*/
|
|
if (!gfOpenDialog && !gfPlayOnly)
|
|
InitDeviceMenu();
|
|
|
|
#ifdef UNICODE
|
|
// ScanCmdLine mangles it, so forget it
|
|
// FreeUnicodeString(szUnicodeCmdLine);
|
|
#endif
|
|
|
|
/* setup the message filter to handle grabbing F1 for this task */
|
|
fpfnMsgHook = (HOOKPROC)MakeProcInstance((FARPROC)HelpMsgFilter, ghInst);
|
|
fpfnOldMsgFilter = (HHOOK)SetWindowsHook(WH_MSGFILTER, fpfnMsgHook);
|
|
|
|
#ifdef DEBUG
|
|
GdiSetBatchLimit(1);
|
|
#endif
|
|
|
|
for (;;)
|
|
{
|
|
/* If we're ever still around after being destroyed, DIE! */
|
|
if (!IsWindow(ghwndApp))
|
|
break;
|
|
|
|
/* call the server code and let it unblock the server */
|
|
#ifdef OLE1_HACK
|
|
ServerUnblock();
|
|
#endif /* OLE1_HACK */
|
|
|
|
/* Polling messages from event queue */
|
|
|
|
if (!GetMessage(&rMsg, NULL, 0, 0))
|
|
break;
|
|
|
|
if (gfPlayingInPlace) {
|
|
|
|
// If focus ever gets to the client during play in place,
|
|
// be really nasty and force focus to us. (Aldus BUG!!!!)
|
|
// Aldus Persuasion won't play in place without this.
|
|
|
|
hwndFocus = GetFocus();
|
|
hwndP = GetParent(ghwndApp);
|
|
|
|
if (!ghwndIPHatch && hwndFocus && hwndP &&
|
|
GetWindowTask(hwndP) == GetWindowTask(hwndFocus))
|
|
PostCloseMessage();
|
|
}
|
|
|
|
/* Hack: post END_SCROLL messages with lParam == -1 */
|
|
|
|
if ((rMsg.hwnd==ghwndApp)
|
|
|| (rMsg.hwnd && GetParent(rMsg.hwnd)==ghwndApp))
|
|
{
|
|
/* Reroute arrow keys etc to track bar:
|
|
*/
|
|
if (rMsg.message == WM_KEYDOWN || rMsg.message == WM_KEYUP)
|
|
RouteKeyPresses(&rMsg);
|
|
}
|
|
|
|
|
|
if (IsWindow(ghwndApp)) {
|
|
|
|
if (gfRunWithEmbeddingFlag
|
|
&& docMain.lpIpData
|
|
&& docMain.lpIpData->lpFrame
|
|
&& !IsAccelerator(hAccel, gcAccelEntries, &rMsg, NULL)
|
|
&& OleTranslateAccelerator(docMain.lpIpData->lpFrame,
|
|
&docMain.lpIpData->frameInfo, &rMsg) == NOERROR) {
|
|
continue;
|
|
}
|
|
|
|
if (hAccel && TranslateAccelerator(ghwndApp, hAccel, &rMsg))
|
|
continue;
|
|
|
|
}
|
|
|
|
if (rMsg.message == WM_TIMER && rMsg.hwnd == NULL) {
|
|
#ifdef CHICAGO_PRODUCT
|
|
/* The reason for requiring the following test is now lost
|
|
* in the mists of time. Now this app is 32-bit, these
|
|
* bogus timer callbacks (if they really do still occur)
|
|
* could be 16-bit, so we need to add yet more ugliness
|
|
* in the form of assembler to an app which is already
|
|
* hardly a paragon of pulchritude.
|
|
*
|
|
* A plea:
|
|
*
|
|
* If you add some obscure code such as below, to this or
|
|
* any other app, even if it has only the teeniest chance
|
|
* of being less blindingly obvious to someone else than
|
|
* it is to you at the time of writing, please please please
|
|
* add a f***ing comment.
|
|
*
|
|
* Respectfully,
|
|
* A Developer
|
|
*/
|
|
if (IsBadSegmentedCodePtr(rMsg.lParam))
|
|
#else
|
|
if (IsBadCodePtr((FARPROC)rMsg.lParam))
|
|
#endif /* ~CHICAGO_PRODUCT */
|
|
{
|
|
DPF0("Bad function pointer (%08lx) in WM_TIMER message\n", rMsg.lParam);
|
|
rMsg.message = WM_NULL;
|
|
}
|
|
}
|
|
if (rMsg.message == WM_SYSCOMMAND
|
|
&& (((0xFFF0 & rMsg.wParam) == SC_MOVE)|| ((0xFFF0 & rMsg.wParam) == SC_SIZE)) ) {
|
|
// If ANY window owned by our thread is going into a modal
|
|
// size or move loop then we need to force some repainting to
|
|
// take place. The cost of not doing so is that garbage can
|
|
// be left lying around on the trackbar, e.g. bits of system
|
|
// menu, or partially drawn sliders.
|
|
UpdateWindow(ghwndApp);
|
|
}
|
|
TranslateMessage(&rMsg);
|
|
DispatchMessage(&rMsg);
|
|
}
|
|
|
|
ghwndApp = NULL;
|
|
|
|
/* Delete the track map font that we created earlier. */
|
|
|
|
if (ghfontMap != NULL) {
|
|
DeleteObject(ghfontMap);
|
|
ghfontMap = NULL;
|
|
}
|
|
|
|
if (ghbrFillPat)
|
|
DeleteObject(ghbrFillPat);
|
|
|
|
if (ghpalApp)
|
|
DeleteObject(ghpalApp);
|
|
|
|
/* if the message hook was installed, remove it and free */
|
|
/* up our proc instance for it. */
|
|
if (fpfnOldMsgFilter){
|
|
UnhookWindowsHook(WH_MSGFILTER, fpfnMsgHook);
|
|
}
|
|
|
|
ControlCleanup();
|
|
|
|
// TermServer();
|
|
|
|
/*********************************************************************
|
|
** OLE2NOTE: the very last thing an app must be do is properly shut
|
|
** down OLE. This call MUST be guarded! it is only allowable to
|
|
** call OleUninitialize if OleInitialize has been called.
|
|
*********************************************************************/
|
|
|
|
// Clean shutdown for OLE
|
|
DPFI("*before oleunint");
|
|
if (gfOleInitialized) {
|
|
if (gpClipboardDataObject)
|
|
CleanUpClipboard();
|
|
(void)OleUninitialize();
|
|
IMalloc_Release(lpMalloc);
|
|
lpMalloc = NULL;
|
|
gfOleInitialized = FALSE;
|
|
}
|
|
|
|
|
|
if (hOLE32)
|
|
FreeLibrary(hOLE32);
|
|
|
|
/* End of program */
|
|
|
|
return((int)rMsg.wParam);
|
|
}
|
|
|
|
void CleanUpClipboard()
|
|
{
|
|
/* Check whether the DATAOBJECT we put on the clipboard is still there:
|
|
*/
|
|
if (OleIsCurrentClipboard(gpClipboardDataObject) == S_OK)
|
|
{
|
|
LPDATAOBJECT pIDataObject;
|
|
|
|
if (OleGetClipboard(&pIDataObject) == S_OK)
|
|
{
|
|
OleFlushClipboard();
|
|
IDataObject_Release(pIDataObject);
|
|
}
|
|
else
|
|
{
|
|
DPF0("OleGetClipboard failed\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(ghClipData)
|
|
GLOBALFREE(ghClipData);
|
|
if(ghClipMetafile)
|
|
GLOBALFREE(ghClipMetafile);
|
|
if(ghClipDib)
|
|
GLOBALFREE(ghClipDib);
|
|
}
|
|
}
|
|
|
|
//
|
|
// cancel any active menus and close the app.
|
|
//
|
|
void PostCloseMessage()
|
|
{
|
|
HWND hwnd;
|
|
|
|
hwnd = GetWindowMCI();
|
|
if (hwnd != NULL)
|
|
SendMessage(hwnd, WM_CANCELMODE, 0, 0);
|
|
SendMessage(ghwndApp, WM_CANCELMODE, 0, 0);
|
|
PostMessage(ghwndApp, WM_CLOSE, 0, 0);
|
|
}
|
|
|
|
//
|
|
// If we have a dialog box up (gfErrorBox is set) or we're disabled (we have
|
|
// a dialog box up) or the MCI device's default window is disabled (it has a
|
|
// dialog box up) then closing us would result in our deaths.
|
|
//
|
|
BOOL ItsSafeToClose(void)
|
|
{
|
|
HWND hwnd;
|
|
|
|
if (gfErrorBox)
|
|
return FALSE;
|
|
if (!IsWindowEnabled(ghwndApp))
|
|
return FALSE;
|
|
hwnd = GetWindowMCI();
|
|
if (hwnd && !IsWindowEnabled(hwnd))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* ResolveLink
|
|
*
|
|
* This routine is called when the user drags and drops a shortcut
|
|
* onto Media Player. If it succeeds, it returns the full path
|
|
* of the actual file in szResolved.
|
|
*/
|
|
BOOL ResolveLink(LPTSTR szPath, LPTSTR szResolved, LONG cbSize)
|
|
{
|
|
IShellLink *psl = NULL;
|
|
HRESULT hres;
|
|
|
|
if (!InitOLE(&gfOleInitialized, &lpMalloc))
|
|
{
|
|
DPF0("Initialization of OLE FAILED!! Can't resolve link.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
hres = (HRESULT)CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC,
|
|
&IID_IShellLink, &psl);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
IPersistFile *ppf;
|
|
|
|
psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf);
|
|
|
|
if (ppf)
|
|
{
|
|
WCHAR wszPath[MAX_PATH];
|
|
#ifdef UNICODE
|
|
lstrcpy (wszPath, szPath);
|
|
#else
|
|
AnsiToUnicodeString(szPath, wszPath, UNKNOWN_LENGTH);
|
|
#endif
|
|
hres = ppf->lpVtbl->Load(ppf, wszPath, 0);
|
|
ppf->lpVtbl->Release(ppf);
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
psl->lpVtbl->Release(psl);
|
|
psl = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psl->lpVtbl->Release(psl);
|
|
psl = NULL;
|
|
}
|
|
}
|
|
|
|
if (psl)
|
|
{
|
|
psl->lpVtbl->Resolve(psl, NULL, SLR_NO_UI);
|
|
psl->lpVtbl->GetPath(psl, szResolved, cbSize, NULL, 0);
|
|
psl->lpVtbl->Release(psl);
|
|
}
|
|
|
|
return SUCCEEDED(hres);
|
|
}
|
|
|
|
|
|
/* ResolveIfLink
|
|
*
|
|
* Called to check whether a given file name is a shortcut
|
|
* on Windows 95.
|
|
*
|
|
* Copies the resolved file name into the buffer provided,
|
|
* overwriting the original name.
|
|
*
|
|
* Returns TRUE if the function succeeded, whether or not the
|
|
* file name was changed. FALSE indicates that an error occurred.
|
|
*
|
|
* Andrew Bell, 16 February 1995
|
|
*/
|
|
BOOL ResolveIfLink(PTCHAR szFileName)
|
|
{
|
|
SHFILEINFO sfi;
|
|
BOOL rc = TRUE;
|
|
|
|
if ((SHGetFileInfo(szFileName, 0, &sfi, sizeof sfi, SHGFI_ATTRIBUTES) == 1)
|
|
&& ((sfi.dwAttributes & SFGAO_LINK) == SFGAO_LINK))
|
|
{
|
|
TCHAR szResolvedLink[MAX_PATH];
|
|
|
|
if (ResolveLink(szFileName, szResolvedLink, CHAR_COUNT(szResolvedLink)))
|
|
lstrcpy(szFileName, szResolvedLink);
|
|
else
|
|
rc = FALSE;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* JumpToCDTrack()
|
|
*
|
|
* Jumps to the appropriate track on the CD and updates the UI accordingly
|
|
*
|
|
*/
|
|
void JumpToCDTrack(UINT trackno)
|
|
{
|
|
//If the track number is invalid just ignore.
|
|
//Let the default behaviour take place, There is no need to give a message box
|
|
//saying we couldn't jump to track.
|
|
if(trackno > gwNumTracks)
|
|
return;
|
|
|
|
/* We MUST use PostMessage because the */
|
|
/* SETPOS and ENDTRACK must happen one */
|
|
/* immediately after the other */
|
|
PostMessage(ghwndTrackbar, TBM_SETPOS, (WPARAM)TRUE, gadwTrackStart[trackno]);
|
|
PostMessage(ghwndApp, WM_HSCROLL, (WPARAM)TB_ENDTRACK, (LPARAM)ghwndTrackbar);
|
|
}
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* IsTrackFileNameValid
|
|
*
|
|
* This routine copied from deluxecd and modified
|
|
*
|
|
* This function returns true if the specified filename is a valid CD track.
|
|
|
|
* On NT track filenames must be of the form:
|
|
* d:\track(n).cda where d: is the CD-Rom device and \track(n).cda
|
|
* is the index of the track to be played (starting from 1).
|
|
*
|
|
* On Chicago the track filename is actually a riff CDDA file which contains
|
|
* the track info that we require.
|
|
*
|
|
* If the filename is valid the function true and sets
|
|
* piTrackIndex to the correct value.
|
|
*
|
|
* History:
|
|
* 29-09-94 - StephenE - Created
|
|
*
|
|
\**************************************************************************/
|
|
BOOL
|
|
IsTrackFileNameValid(
|
|
LPTSTR lpstFileName,
|
|
UINT *puiTrackIndex
|
|
)
|
|
{
|
|
#define RIFF_RIFF 0x46464952
|
|
#define RIFF_CDDA 0x41444443
|
|
|
|
|
|
RIFFCDA cda;
|
|
HANDLE hFile;
|
|
int i;
|
|
DWORD cbRead;
|
|
BOOL fRead;
|
|
|
|
// Open file and read in CDA info
|
|
hFile = CreateFile (lpstFileName, GENERIC_READ,
|
|
FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, 0, NULL);
|
|
if (INVALID_HANDLE_VALUE == hFile) {
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory (&cda, sizeof (cda));
|
|
fRead = ReadFile(hFile, &cda, sizeof(cda), &cbRead, NULL);
|
|
CloseHandle (hFile);
|
|
|
|
if (!fRead)
|
|
return FALSE;
|
|
|
|
//
|
|
// Make sure its a RIFF CDDA file
|
|
//
|
|
if ( (cda.dwRIFF != RIFF_RIFF) || (cda.dwCDDA != RIFF_CDDA) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
*puiTrackIndex = cda.wTrack - 1;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* HandleCDAFile()
|
|
*
|
|
* Checks to see if the opened file is a CDA file and tries to jump to the appropriate track.
|
|
*
|
|
*/
|
|
void HandleCDAFile(TCHAR *szFile)
|
|
{
|
|
UINT trackno;
|
|
if(IsTrackFileNameValid(szFile, &trackno))
|
|
{
|
|
JumpToCDTrack(trackno);
|
|
}
|
|
}
|
|
|
|
|
|
/* Process file drop/drag options. */
|
|
void PASCAL NEAR doDrop(HWND hwnd, HDROP hDrop)
|
|
{
|
|
RECT rc;
|
|
|
|
if(DragQueryFile(hDrop,(UINT)(~0),NULL,0)){/* # of files dropped */
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
/* If user dragged/dropped a file regardless of keys pressed
|
|
* at the time, open the first selected file from file
|
|
* manager.
|
|
*/
|
|
DragQueryFile(hDrop,0,szPath,sizeof(szPath)/sizeof(TCHAR));
|
|
SetActiveWindow(hwnd);
|
|
|
|
ResolveIfLink(szPath);
|
|
|
|
if (OpenMciDevice(szPath, NULL)) {
|
|
SubClassMCIWindow();
|
|
PostMessage(hwnd, WM_COMMAND, (WPARAM)ID_PLAY, 0);
|
|
DirtyObject(FALSE); // we're dirty now!
|
|
gfCloseAfterPlaying = FALSE; // stay up from now on
|
|
|
|
//If the CD Audio device was opened it must have been a *.cda file.
|
|
//Try to jump to the track corresponding to the file opened.
|
|
if ((gwDeviceType & DTMCI_DEVICE) == DTMCI_CDAUDIO)
|
|
{
|
|
HandleCDAFile(szPath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gwCurDevice = 0;// force next file open dialog to say
|
|
// "all files" because CloseMCI won't.
|
|
gwCurScale = ID_NONE; // uncheck all scale types
|
|
Layout(); // Make window snap back to smaller size
|
|
}
|
|
|
|
SetMPlayerIcon();
|
|
|
|
/* Force WM_GETMINMAXINFO to be called so we'll snap to a */
|
|
/* proper size. */
|
|
GetWindowRect(ghwndApp, &rc);
|
|
MoveWindow(ghwndApp, rc.left, rc.top, rc.right - rc.left,
|
|
rc.bottom - rc.top, TRUE);
|
|
}
|
|
DragFinish(hDrop); /* Delete structure alocated for WM_DROPFILES*/
|
|
}
|
|
|
|
/* Change the number in dwPosition to the proper format. szNum contains the */
|
|
/* formatted number only "01 45:10" while szBuf contains units such as */
|
|
/* "01 45:10 (min:sec)" */
|
|
/* If fRound is set, it will not always display millisecond accuracy, but */
|
|
/* choose something useful like second accuracy or hundreth sec accuracy. */
|
|
void FAR PASCAL FormatTime(DWORD_PTR dwPosition, LPTSTR szNum, LPTSTR szBuf, BOOL fRound)
|
|
{
|
|
UINT w;
|
|
UINT hrs;
|
|
UINT min;
|
|
UINT sec;
|
|
UINT hsec;
|
|
UINT msec;
|
|
DWORD dwMaxSize = gdwMediaLength;
|
|
static TCHAR framestr[40] = TEXT("");
|
|
static TCHAR sec_str[40] = TEXT("");
|
|
static TCHAR min_str[40] = TEXT("");
|
|
static TCHAR hrs_str[40] = TEXT("");
|
|
static TCHAR msec_str[40] = TEXT("");
|
|
|
|
static SZCODE aszLongDecimal[] = TEXT("%ld");
|
|
static SZCODE aszFrameFormat[] = TEXT("%"TS" %ld");
|
|
static SZCODE asz02Decimal[] = TEXT("%02d ");
|
|
static SZCODE aszTimeFormat1[] = TEXT("%02d%c%02d%c%02d");
|
|
static SZCODE aszTimeFormat2[] = TEXT("%02d%c%02d%c%02d%c%03d");
|
|
static SZCODE aszTimeFormat3[] = TEXT("%02d%c%02d%c%02d (%"TS"%c%"TS"%c%"TS")");
|
|
static SZCODE aszTimeFormat4[] = TEXT("%02d%c%02d%c%02d%c%03d (%"TS"%c%"TS"%c%"TS"%c%"TS")");
|
|
static SZCODE aszTimeFormat5[] = TEXT("%02d%c%02d");
|
|
static SZCODE aszTimeFormat6[] = TEXT("%02d%c%02d%c%03d");
|
|
static SZCODE aszTimeFormat7[] = TEXT("%02d%c%02d (%"TS"%c%"TS")");
|
|
static SZCODE aszTimeFormat8[] = TEXT("%02d%c%02d%c%03d (%"TS"%c%"TS"%c%"TS")");
|
|
static SZCODE aszTimeFormat9[] = TEXT("%c%02d");
|
|
static SZCODE aszTimeFormat10[] = TEXT("%c%03d");
|
|
static SZCODE aszTimeFormat11[] = TEXT("%02d%c%03d");
|
|
static SZCODE aszTimeFormat12[] = TEXT("%c%02d (%"TS")");
|
|
static SZCODE aszTimeFormat13[] = TEXT("%02d%c%02d (%"TS")");
|
|
static SZCODE aszTimeFormat14[] = TEXT("%c%03d (%"TS"%c%"TS")");
|
|
static SZCODE aszTimeFormat15[] = TEXT("%02d%c%03d (%"TS"%c%"TS")");
|
|
|
|
|
|
//!!! LoadStrings at init time, dont hardcode...
|
|
|
|
#define ONE_HOUR (60ul*60ul*1000ul)
|
|
#define ONE_MINUTE (60ul*1000ul)
|
|
#define ONE_SECOND (1000ul)
|
|
|
|
if (szBuf)
|
|
*szBuf = 0;
|
|
if (szNum)
|
|
*szNum = 0;
|
|
|
|
if (gwDeviceID == (UINT)0)
|
|
return;
|
|
|
|
if (gwStatus == MCI_MODE_NOT_READY || gwStatus == MCI_MODE_OPEN)
|
|
return;
|
|
|
|
switch (gwCurScale) {
|
|
|
|
case ID_FRAMES:
|
|
if (!STRLEN(framestr))
|
|
LOADSTRING(IDS_FRAME,framestr);
|
|
if (szNum)
|
|
wsprintf(szNum, aszLongDecimal, (long)dwPosition);
|
|
if (szBuf)
|
|
wsprintf(szBuf, aszFrameFormat, framestr, (long)dwPosition);
|
|
gInc = 1; // spin arrow inc/dec by one frame
|
|
break;
|
|
|
|
case ID_TRACKS:
|
|
//
|
|
// find the track that contains this position
|
|
// also, find the longest track so we know if we should display
|
|
// hh:mm:ss or mm:ss or ss.sss or whatever.
|
|
//
|
|
|
|
if (gwNumTracks == 0)
|
|
return;
|
|
|
|
dwMaxSize = 0;
|
|
|
|
for (w=0; w<gwNumTracks-1; w++) {
|
|
|
|
if (gadwTrackStart[w+1] - gadwTrackStart[w] > dwMaxSize)
|
|
dwMaxSize = gadwTrackStart[w+1] - gadwTrackStart[w];
|
|
|
|
/* When a CD is stopped, it's still spinning, and after we */
|
|
/* seek to the beginning of a track, it may return a value */
|
|
/* slightly less than the track start everyonce in a while.*/
|
|
/* So if we're within 200ms of the track start, let's just */
|
|
/* pretend we're exactly on the start of the track. */
|
|
|
|
if (dwPosition < gadwTrackStart[w+1] &&
|
|
gadwTrackStart[w+1] - dwPosition < 200)
|
|
dwPosition = gadwTrackStart[w+1];
|
|
|
|
if (gadwTrackStart[w+1] > dwPosition)
|
|
break;
|
|
}
|
|
|
|
if (szNum) {
|
|
wsprintf(szNum, asz02Decimal, gwFirstTrack + w);
|
|
szNum += 3;
|
|
}
|
|
if (szBuf) {
|
|
wsprintf(szBuf, asz02Decimal, gwFirstTrack + w);
|
|
szBuf += 3;
|
|
}
|
|
|
|
dwPosition -= gadwTrackStart[w];
|
|
|
|
for (; w < gwNumTracks - 1; w++) {
|
|
if (gadwTrackStart[w+1] - gadwTrackStart[w] > dwMaxSize)
|
|
dwMaxSize = gadwTrackStart[w+1] - gadwTrackStart[w];
|
|
}
|
|
|
|
// fall through
|
|
|
|
case ID_TIME:
|
|
if (!STRLEN(sec_str))
|
|
{
|
|
LOADSTRING(IDS_SEC,sec_str);
|
|
LOADSTRING(IDS_HRS,hrs_str);
|
|
LOADSTRING(IDS_MIN,min_str);
|
|
LOADSTRING(IDS_MSEC,msec_str);
|
|
}
|
|
|
|
min = (UINT)((dwPosition / ONE_MINUTE) % 60);
|
|
sec = (UINT)((dwPosition / ONE_SECOND) % 60);
|
|
msec = (UINT)(dwPosition % 1000);
|
|
|
|
if (dwMaxSize > ONE_HOUR) {
|
|
|
|
hrs = (UINT)(dwPosition / ONE_HOUR);
|
|
|
|
if (szNum && fRound) {
|
|
wsprintf(szNum, aszTimeFormat1,
|
|
hrs, chTime, min, chTime, sec);
|
|
} else if (szNum) {
|
|
wsprintf(szNum, aszTimeFormat2,
|
|
hrs, chTime, min, chTime, sec, chDecimal, msec);
|
|
}
|
|
|
|
if (szBuf && fRound) {
|
|
wsprintf(szBuf, aszTimeFormat3,
|
|
hrs, chTime, min, chTime, sec, hrs_str,
|
|
chTime, min_str, chTime, sec_str);
|
|
} else if (szBuf) {
|
|
wsprintf(szBuf,
|
|
aszTimeFormat4,
|
|
hrs, chTime, min, chTime, sec, chDecimal, msec,
|
|
hrs_str,chTime, min_str,chTime,
|
|
sec_str, chDecimal, msec_str);
|
|
}
|
|
|
|
gInc = 1000; // spin arrow inc/dec by seconds
|
|
|
|
} else if (dwMaxSize > ONE_MINUTE) {
|
|
|
|
if (szNum && fRound) {
|
|
wsprintf(szNum, aszTimeFormat5, min, chTime, sec);
|
|
} else if (szNum) {
|
|
wsprintf(szNum, aszTimeFormat6, min, chTime, sec,
|
|
chDecimal, msec);
|
|
}
|
|
|
|
if (szBuf && fRound) {
|
|
wsprintf(szBuf, aszTimeFormat7, min, chTime, sec,
|
|
min_str,chTime,sec_str);
|
|
} else if (szBuf) {
|
|
wsprintf(szBuf, aszTimeFormat8,
|
|
min, chTime, sec, chDecimal, msec,
|
|
min_str,chTime,sec_str, chDecimal,
|
|
msec_str);
|
|
}
|
|
|
|
gInc = 1000; // spin arrow inc/dec by seconds
|
|
|
|
} else {
|
|
|
|
hsec = (UINT)((dwPosition % 1000) / 10);
|
|
|
|
if (szNum && fRound) {
|
|
if (!sec && chLzero == TEXT('0'))
|
|
wsprintf(szNum, aszTimeFormat9, chDecimal, hsec);
|
|
else
|
|
wsprintf(szNum, aszTimeFormat5, sec, chDecimal, hsec);
|
|
|
|
} else if (szNum) {
|
|
if (!sec && chLzero == TEXT('0'))
|
|
wsprintf(szNum, aszTimeFormat10, chDecimal, msec);
|
|
else
|
|
wsprintf(szNum, aszTimeFormat11, sec, chDecimal, msec);
|
|
}
|
|
|
|
if (szBuf && fRound) {
|
|
if (!sec && chLzero == TEXT('0'))
|
|
wsprintf(szBuf, aszTimeFormat12, chDecimal, hsec, sec_str);
|
|
else
|
|
wsprintf(szBuf, aszTimeFormat13, sec, chDecimal, hsec, sec_str);
|
|
|
|
} else if (szBuf) {
|
|
if (!sec && chLzero == TEXT('0'))
|
|
wsprintf(szBuf, aszTimeFormat14, chDecimal,
|
|
msec, sec_str,chDecimal,msec_str);
|
|
else
|
|
wsprintf(szBuf, aszTimeFormat15, sec, chDecimal,
|
|
msec, sec_str,chDecimal,msec_str);
|
|
}
|
|
|
|
gInc = 100; // spin arrow inc/dec by 1/10 second
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL IsCdromDataOnly();
|
|
|
|
|
|
BOOL UpdateWindowText(HWND hwnd, LPTSTR Text)
|
|
{
|
|
TCHAR CurrentText[80];
|
|
|
|
GetWindowText(hwnd, CurrentText, CHAR_COUNT(CurrentText));
|
|
|
|
if(lstrcmp(Text, CurrentText))
|
|
return SetWindowText(hwnd, Text);
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* UpdateDisplay()
|
|
*
|
|
* Update the scrollbar, buttons, etc. If the media information (media
|
|
* length, no. tracks, etc.) is not currently valid, then update it first.
|
|
*
|
|
* The following table shows how the current status (value of <gwStatus>)
|
|
* affects which windows are enabled:
|
|
*
|
|
* Play Pause Stop Eject
|
|
* MCI_MODE_STOP ENABLE n/a ENABLE
|
|
* MCI_MODE_PAUSE ENABLE n/a ENABLE ENABLE
|
|
* MCI_MODE_PLAY n/a ENABLE ENABLE ENABLE
|
|
* MCI_MODE_OPEN n/a ENABLE
|
|
* MCI_MODE_RECORD ?????? ?????? ?????? ??????
|
|
* MCI_MODE_SEEK ENABLE n/a ENABLE ENABLE
|
|
*
|
|
* MCI_MODE_NOT_READY ALL DISABLED
|
|
*
|
|
* The eject button is always enabled if the medium can be ejected and
|
|
* disabled otherwise.
|
|
*
|
|
* In open mode, either Play or Eject will cause the media door to close,
|
|
* but Play will also begin play. In any mode, Eject always does an
|
|
* implicit Stop first.
|
|
*
|
|
* If <gwDeviceID> is NULL, then there is no current device and all four
|
|
* of these buttons are disabled.
|
|
*
|
|
*/
|
|
void FAR PASCAL UpdateDisplay(void)
|
|
{
|
|
DWORD_PTR dwPosition; /* the current position within the medium */
|
|
UINT wStatusMCI; /* status of the device according to MCI */
|
|
#if 0
|
|
TOOLBUTTON tb;
|
|
#endif
|
|
static BOOL sfBlock = FALSE; // keep SeekMCI from causing infinite loop
|
|
|
|
/* Don't even think about updating the display if the trackbar's scrolling: */
|
|
if (gfScrollTrack)
|
|
return;
|
|
|
|
/* We've been re-entered */
|
|
if (sfBlock)
|
|
return;
|
|
|
|
/*
|
|
* if for some reason we were closed, close now!
|
|
*/
|
|
if (gfErrorDeath) {
|
|
DPF("*** Trying to close window now!\n");
|
|
PostMessage(ghwndApp, gfErrorDeath, 0, 0);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the track information is not valid (e.g. a CD was just inserted),
|
|
* then update it.
|
|
*
|
|
*/
|
|
|
|
if (!gfValidMediaInfo)
|
|
UpdateMCI(); /* update the appropriate global variables*/
|
|
|
|
/*
|
|
* Determine the current position and status ( stopped, playing, etc. )
|
|
* as MCI believes them to be.
|
|
*
|
|
*/
|
|
|
|
wStatusMCI = StatusMCI(&dwPosition);
|
|
|
|
|
|
|
|
/* The deal here is that the user can insert CDs, any of which may not be
|
|
* playable because they contain no audio tracks. So, as soon as we detect
|
|
* that we have a CD we haven't checked, make sure we can play it.
|
|
* If the current device is CD, and the door isn't open, check it.
|
|
*
|
|
*/
|
|
if (((gwDeviceType & DTMCI_DEVICE) == DTMCI_CDAUDIO) &&
|
|
(wStatusMCI != MCI_MODE_OPEN))
|
|
{
|
|
if (!gfCurrentCDChecked)
|
|
{
|
|
if (IsCdromDataOnly())
|
|
{
|
|
gfCurrentCDNotAudio = TRUE;
|
|
gwCurScale = ID_NONE;
|
|
Error(ghwndApp, IDS_INSERTAUDIODISC);
|
|
}
|
|
else
|
|
gfCurrentCDNotAudio = FALSE;
|
|
|
|
gfCurrentCDChecked = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gfCurrentCDChecked = FALSE; // Otherwise, make sure it gets cleared.
|
|
gfCurrentCDNotAudio = FALSE;
|
|
}
|
|
|
|
|
|
/* Here's the problem: If the medium is short, we'll send a Play command */
|
|
/* but it'll stop before we notice it was ever playing. So if we know */
|
|
/* that we just sent a PlayMCI command, but the status isn't PLAY, then */
|
|
/* force the last command to be PLAY. Also, once we notice we are playing*/
|
|
/* we can clear gfJustPlayed. */
|
|
|
|
if (wStatusMCI == MCI_MODE_PLAY && gfJustPlayed)
|
|
gfJustPlayed = FALSE;
|
|
if (((wStatusMCI == MCI_MODE_STOP) || (wStatusMCI == MCI_MODE_SEEK)) && gfJustPlayed) {
|
|
gwStatus = MCI_MODE_PLAY;
|
|
gfJustPlayed = FALSE;
|
|
}
|
|
|
|
if (wStatusMCI == MCI_MODE_SEEK) {
|
|
// The second major problem is this. During rewind the status
|
|
// is SEEK. If we detect MODE_SEEK we will not restart the play,
|
|
// and it looks like the auto replay simply ended. Seeking back to
|
|
// the beginning can take a significant amount of time. We allow
|
|
// ourselves to wait for up to half a second to give the device,
|
|
// particularly AVI from a CD or over the network, a chance to
|
|
// catch up. Any slower response and the autorepeat will terminate.
|
|
dwPosition = gdwLastSeekToPosition;
|
|
if (!gfUserStopped && (gwOptions&OPT_AUTOREP)) {
|
|
UINT n=15;
|
|
for (; n; --n) {
|
|
|
|
Sleep(32);
|
|
// If autorepeating and device is seeking, try the status
|
|
// again in case it has got back to the beginning
|
|
wStatusMCI = StatusMCI(&dwPosition);
|
|
|
|
if (wStatusMCI != MCI_MODE_SEEK) {
|
|
wStatusMCI = MCI_MODE_STOP;
|
|
break; // Exit the FOR loop
|
|
} else {
|
|
dwPosition = gdwLastSeekToPosition;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The current device status has
|
|
* changed from the way MPlayer last perceived it, so update the display
|
|
* and make MPlayer agree with MCI again.
|
|
*
|
|
*/
|
|
|
|
// After we close, our last timer msg must gray stuff and execute this //
|
|
if (!gwDeviceID || wStatusMCI != gwStatus) {
|
|
DWORD dwEndMedia, dwStartSel, dwEndSel, dwEndSelDelta;
|
|
|
|
/* Auto-repeat and Rewind happen if you stop at the end of the media */
|
|
/* (rewind to beginning) or if you stop at the end of the selection */
|
|
/* (rewind to beginning of selection). */
|
|
|
|
dwEndMedia = MULDIV32(gdwMediaLength + gdwMediaStart, 99, 100L);
|
|
dwStartSel = (DWORD)SendMessage(ghwndTrackbar, TBM_GETSELSTART, 0, 0);
|
|
dwEndSel = (DWORD)SendMessage(ghwndTrackbar, TBM_GETSELEND, 0, 0);
|
|
if (dwEndSel != -1) {
|
|
dwEndSelDelta = MULDIV32(dwEndSel, 99, 100L);
|
|
} else {
|
|
dwEndSelDelta = 0; // force (dwPosition >= dwEndSelDelta) to FALSE
|
|
}
|
|
|
|
if ((wStatusMCI == MCI_MODE_STOP || wStatusMCI == MCI_MODE_PAUSE)
|
|
&& ((dwPosition >= dwEndMedia) || (dwPosition==0) ||
|
|
(dwPosition >= dwEndSelDelta && gfJustPlayedSel))
|
|
&& dwPosition >= gdwMediaStart // dwPosition may == the beginning
|
|
&& !gfScrollTrack
|
|
&& (gwStatus == MCI_MODE_PLAY || gwStatus == MCI_MODE_SEEK)) {
|
|
|
|
DPF("End of medium\n");
|
|
|
|
/* We're at the end of the entire media or at the end of */
|
|
/* our selection now, and stopped automatically (not */
|
|
/* by the user). We were playing or seeking. So */
|
|
/* we can check the Auto Repeat and Auto Rewind flags. */
|
|
/* CD players seem to return a length that's too big, so */
|
|
/* we check for > 99% done. Use semaphore to keep from */
|
|
/* causing an infinite loop. */
|
|
|
|
if (!gfUserStopped && (gwOptions & OPT_AUTOREP)) {
|
|
DPF("Auto-Repeat\n");
|
|
sfBlock = TRUE; // calls UpdateDisplay which will
|
|
// re-enter this code just before mode
|
|
|
|
/* Repeat either the selection or whole thing. */
|
|
/* NOTE: Must send message while gwStatus is STOPPED.*/
|
|
|
|
gwStatus = wStatusMCI; // old status no longer valid
|
|
if (gfJustPlayedSel && dwPosition >= dwEndSelDelta)
|
|
{
|
|
SeekMCI(dwStartSel); // MCICDA doen't go to start w/out this.
|
|
SendMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_PLAYSEL, 0);
|
|
}
|
|
else
|
|
{
|
|
SeekToStartMCI();
|
|
SendMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_PLAY, 0);
|
|
}
|
|
|
|
sfBlock = FALSE; // switches to SEEK.
|
|
gwStatus = (UINT)(-1); // old status no longer valid
|
|
return; // because we are switching modes
|
|
|
|
} else if (!gfCloseAfterPlaying && !gfUserStopped &&
|
|
(gwOptions & OPT_AUTORWD)) {
|
|
DPF("Auto-Rewind to media start\n");
|
|
//
|
|
// set gwStatus so SeekMCI will just seek!
|
|
sfBlock = TRUE; // calls UpdateDisplay which will
|
|
// re-enter this code just before mode
|
|
// switches to SEEK.
|
|
|
|
/* Rewind either the selection or whole thing. */
|
|
gwStatus = wStatusMCI; // or SeekMCI will play, too.
|
|
if (gfJustPlayedSel && dwPosition >= dwEndSelDelta)
|
|
{
|
|
SeekMCI(dwStartSel);
|
|
}
|
|
else
|
|
{
|
|
SeekToStartMCI();
|
|
}
|
|
sfBlock = FALSE;
|
|
gwStatus = (UINT)(-1); // old status no longer valid
|
|
return; // because we are switching modes
|
|
}
|
|
else if (gfCloseAfterPlaying)
|
|
PostCloseMessage();
|
|
}
|
|
|
|
/*
|
|
* Enable or disable the various controls according to the new status,
|
|
* following the rules given in the header to this function.
|
|
*
|
|
*/
|
|
|
|
EnableWindow(ghwndTrackbar, TRUE); // Good to always have something enabled
|
|
|
|
/* Show status bar if full mplayer and if device loaded */
|
|
if (ghwndStatic && !gfPlayOnly)
|
|
{
|
|
if (IsWindowVisible(ghwndStatic) != (gwDeviceID ? TRUE : FALSE))
|
|
{
|
|
ShowWindow(ghwndStatic, gwDeviceID ? SW_SHOW : SW_HIDE);
|
|
InvalidateRect(ghwndApp, NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
if (gwDeviceID != (UINT)0 ) {
|
|
|
|
switch (wStatusMCI)
|
|
{
|
|
case MCI_MODE_PLAY:
|
|
toolbarSetFocus(ghwndToolbar,BTN_PAUSE);
|
|
break;
|
|
|
|
case MCI_MODE_PAUSE:
|
|
case MCI_MODE_STOP:
|
|
toolbarSetFocus(ghwndToolbar,BTN_PLAY);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (wStatusMCI == MCI_MODE_OPEN || wStatusMCI == MCI_MODE_NOT_READY ||
|
|
gwDeviceID == (UINT)0 ||
|
|
((gwDeviceType & DTMCI_DEVICE) == DTMCI_CDAUDIO) && gfCurrentCDNotAudio) {
|
|
/* Try to modify both -- one of them should work */
|
|
|
|
toolbarModifyState(ghwndToolbar, BTN_PLAY, TBINDEX_MAIN, BTNST_GRAYED);
|
|
toolbarModifyState(ghwndToolbar, BTN_PAUSE, TBINDEX_MAIN, BTNST_GRAYED);
|
|
|
|
toolbarModifyState(ghwndToolbar, BTN_HOME, TBINDEX_MAIN, BTNST_GRAYED);
|
|
toolbarModifyState(ghwndToolbar, BTN_END, TBINDEX_MAIN, BTNST_GRAYED);
|
|
toolbarModifyState(ghwndToolbar, BTN_RWD, TBINDEX_MAIN, BTNST_GRAYED);
|
|
toolbarModifyState(ghwndToolbar, BTN_FWD, TBINDEX_MAIN, BTNST_GRAYED);
|
|
|
|
SendMessage(ghwndTrackbar, TBM_SETRANGEMIN, (WPARAM)FALSE, 0);
|
|
SendMessage(ghwndTrackbar, TBM_SETRANGEMAX, (WPARAM)FALSE, 0);
|
|
SendMessage(ghwndTrackbar, TBM_CLEARTICS, (WPARAM)FALSE, 0);
|
|
SendMessage(ghwndTrackbar, TBM_CLEARSEL, (WPARAM)TRUE, 0);
|
|
|
|
if (ghwndMark) {
|
|
toolbarModifyState(ghwndMark, BTN_MARKIN, TBINDEX_MARK, BTNST_GRAYED);
|
|
toolbarModifyState(ghwndMark, BTN_MARKOUT, TBINDEX_MARK, BTNST_GRAYED);
|
|
}
|
|
|
|
if (ghwndFSArrows) {
|
|
toolbarModifyState(ghwndFSArrows, ARROW_NEXT, TBINDEX_ARROWS, BTNST_GRAYED);
|
|
toolbarModifyState(ghwndFSArrows, ARROW_PREV, TBINDEX_ARROWS, BTNST_GRAYED);
|
|
}
|
|
|
|
/* Enable transport and Mark buttons if we come from a state where */
|
|
/* they were gray. Layout will then re-gray the ones that */
|
|
/* shouldn't have been enabled because they don't fit. */
|
|
} else if (gwStatus == MCI_MODE_OPEN || gwStatus == MCI_MODE_NOT_READY
|
|
|| gwStatus == -1 ) {
|
|
|
|
/* Only one of these buttons exists */
|
|
toolbarModifyState(ghwndToolbar, BTN_PLAY, TBINDEX_MAIN, BTNST_UP);
|
|
toolbarModifyState(ghwndToolbar, BTN_PAUSE, TBINDEX_MAIN, BTNST_UP);
|
|
|
|
if (!gfPlayOnly || gfOle2IPEditing) {
|
|
toolbarModifyState(ghwndToolbar, BTN_HOME, TBINDEX_MAIN, BTNST_UP);
|
|
toolbarModifyState(ghwndToolbar, BTN_END, TBINDEX_MAIN, BTNST_UP);
|
|
toolbarModifyState(ghwndToolbar, BTN_RWD, TBINDEX_MAIN, BTNST_UP);
|
|
toolbarModifyState(ghwndToolbar, BTN_FWD, TBINDEX_MAIN, BTNST_UP);
|
|
|
|
if (ghwndMark) {
|
|
toolbarModifyState(ghwndMark, BTN_MARKIN, TBINDEX_MARK, BTNST_UP);
|
|
toolbarModifyState(ghwndMark, BTN_MARKOUT, TBINDEX_MARK, BTNST_UP);
|
|
}
|
|
if (ghwndFSArrows) {
|
|
toolbarModifyState(ghwndFSArrows, ARROW_PREV, TBINDEX_ARROWS, BTNST_UP);
|
|
toolbarModifyState(ghwndFSArrows, ARROW_NEXT, TBINDEX_ARROWS, BTNST_UP);
|
|
}
|
|
}
|
|
/* AND we need to call layout to gray the buttons that are too
|
|
* short to fit in this window right now.
|
|
*/
|
|
Layout();
|
|
}
|
|
|
|
//
|
|
// always have the stop button if we are playing in place
|
|
//
|
|
if ((gwDeviceID != (UINT)0) &&
|
|
(wStatusMCI == MCI_MODE_PAUSE ||
|
|
wStatusMCI == MCI_MODE_PLAY ||
|
|
wStatusMCI == MCI_MODE_SEEK || gfPlayingInPlace)) {
|
|
|
|
if (toolbarStateFromButton(ghwndToolbar, BTN_STOP, TBINDEX_MAIN) == BTNST_GRAYED)
|
|
toolbarModifyState(ghwndToolbar, BTN_STOP, TBINDEX_MAIN, BTNST_UP);
|
|
|
|
} else {
|
|
toolbarModifyState(ghwndToolbar, BTN_STOP, TBINDEX_MAIN, BTNST_GRAYED);
|
|
}
|
|
|
|
if (!gfPlayOnly || gfOle2IPEditing) {
|
|
if ((gwDeviceID != (UINT)0) && (gwDeviceType & DTMCI_CANEJECT))
|
|
toolbarModifyState(ghwndToolbar, BTN_EJECT, TBINDEX_MAIN, BTNST_UP);
|
|
else
|
|
toolbarModifyState(ghwndToolbar, BTN_EJECT, TBINDEX_MAIN, BTNST_GRAYED);
|
|
|
|
EnableWindow(ghwndMap, (gwDeviceID != (UINT)0));
|
|
}
|
|
|
|
// WHO had the idea that setting focus back to play every
|
|
// time the status changes was a good idea ??
|
|
/* Only set focus if we won't take activation by doing so */
|
|
//VIJRif (gfAppActive) {
|
|
if (wStatusMCI == MCI_MODE_NOT_READY) {
|
|
//if (gfAppActive)
|
|
//SetFocus(ghwndToolbar); //Setting focus messes up menu access
|
|
//using the ALT key
|
|
} else if (wStatusMCI != MCI_MODE_SEEK &&
|
|
gwStatus != MCI_MODE_SEEK) {
|
|
if (wStatusMCI == MCI_MODE_PLAY) {
|
|
//VIJR SetFocus(ghwndToolbar); // give focus to PAUSE button
|
|
toolbarSetFocus(ghwndToolbar, BTN_PAUSE);
|
|
} else {
|
|
//VIJR SetFocus(ghwndToolbar); // give focus to PLAY button
|
|
toolbarSetFocus(ghwndToolbar, BTN_PLAY);
|
|
if (wStatusMCI == MCI_MODE_OPEN || wStatusMCI == MCI_MODE_NOT_READY ||
|
|
gwDeviceID == (UINT)0) {
|
|
/* Try to modify both -- one of them should work */
|
|
toolbarModifyState(ghwndToolbar, BTN_PLAY, TBINDEX_MAIN, BTNST_GRAYED);
|
|
}
|
|
}
|
|
}
|
|
//VIJR}
|
|
|
|
if (wStatusMCI == MCI_MODE_OPEN || gwStatus == MCI_MODE_OPEN
|
|
|| gwStatus == MCI_MODE_NOT_READY
|
|
|| wStatusMCI == MCI_MODE_NOT_READY) {
|
|
|
|
/* Either the medium was just ejected, or it was just
|
|
* re-inserted -- in either case, the media information (length,
|
|
* # of tracks, etc.) is currently invalid and needs to be updated.
|
|
*/
|
|
|
|
gfValidMediaInfo = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Set <gwStatus> to agree with what MCI tells us, and update the
|
|
* display accordingly.
|
|
*
|
|
*/
|
|
|
|
gwStatus = wStatusMCI;
|
|
gfValidCaption = FALSE;
|
|
}
|
|
|
|
/*
|
|
* The previous code may have invalidated the Media again, so we'll update
|
|
* now instead of waiting for the next UpdateDisplay call.
|
|
*
|
|
*/
|
|
|
|
if (!gfValidMediaInfo)
|
|
UpdateMCI(); /* update the appropriate global variables*/
|
|
|
|
/* If the caption is not valid, then update it */
|
|
|
|
if (!gfValidCaption) {
|
|
|
|
TCHAR ach[_MAX_PATH * 2 + 60]; // string used for the window caption
|
|
TCHAR achWhatToPrint[_MAX_PATH * 2 + 40]; // room for doc title too
|
|
|
|
if (gfPlayOnly) {
|
|
if (gwDeviceID == (UINT)0)
|
|
lstrcpy(ach, gachAppName); /* just use the app name */
|
|
else
|
|
lstrcpy(ach, gachWindowTitle); /* just use device */
|
|
} else {
|
|
/* Latest style guide says title bars should have
|
|
* <object> - <appname>, so do that for anything
|
|
* other than NT:
|
|
*/
|
|
if (gwPlatformId == VER_PLATFORM_WIN32_NT)
|
|
wsprintf(achWhatToPrint, aszTitleFormat, gachAppName,
|
|
gachWindowTitle);
|
|
else
|
|
wsprintf(achWhatToPrint, aszTitleFormat, gachWindowTitle,
|
|
gachAppName);
|
|
|
|
if (gwDeviceID == (UINT)0) {
|
|
lstrcpy(ach, gachAppName); /* just display the app name */
|
|
} else if (gwStatus == MCI_MODE_NOT_READY) {
|
|
wsprintf(ach, aszNotReadyFormat,
|
|
achWhatToPrint); /* the current file / device */
|
|
} else {
|
|
wsprintf(ach, aszReadyFormat,
|
|
achWhatToPrint, /* the current file / device */
|
|
MapModeToStatusString((WORD)wStatusMCI));
|
|
}
|
|
}
|
|
|
|
if (gfEmbeddedObject) {
|
|
if (!SetTitle((LPDOC)&docMain, szClientDoc))
|
|
UpdateWindowText(ghwndApp, ach);
|
|
|
|
SetMPlayerIcon();
|
|
|
|
} else {
|
|
UpdateWindowText(ghwndApp, ach);
|
|
}
|
|
|
|
gfValidCaption = TRUE;
|
|
|
|
}
|
|
|
|
/* Update the scrollbar thumb position unless the user is dragging it */
|
|
/* or the media is current seeking to a previously requested position. */
|
|
|
|
if (!gfScrollTrack && gfValidMediaInfo && wStatusMCI != MCI_MODE_SEEK) {
|
|
TCHAR ach[40];
|
|
|
|
if (ghwndStatic) {
|
|
FormatTime(dwPosition, NULL, ach, TRUE);
|
|
WriteStatusMessage(ghwndStatic, ach);
|
|
}
|
|
SendMessage(ghwndTrackbar, TBM_SETPOS, (WPARAM)TRUE, dwPosition);
|
|
}
|
|
|
|
/* Finish any required window painting immediately */
|
|
|
|
if (gfOle2IPEditing && wStatusMCI == MCI_MODE_STOP &&
|
|
((gwDeviceType & DTMCI_DEVICE) == DTMCI_AVIVIDEO))
|
|
{
|
|
RedrawWindow(ghwndTrackbar, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
|
|
}
|
|
UpdateWindow(ghwndApp);
|
|
}
|
|
|
|
|
|
/*
|
|
* EnableTimer(fEnable)
|
|
*
|
|
* Enable the display-update timer if <fEnable> is TRUE.
|
|
* Disable the timer if <fEnable> is FALSE.
|
|
*
|
|
*/
|
|
|
|
void FAR PASCAL EnableTimer(BOOL fEnable)
|
|
{
|
|
DPF("EnableTimer(%d) %dms\n", fEnable, gfAppActive ? UPDATE_MSEC : UPDATE_INACTIVE_MSEC);
|
|
|
|
if (fEnable)
|
|
SetTimer(ghwndApp, UPDATE_TIMER,
|
|
gfAppActive ? UPDATE_MSEC : UPDATE_INACTIVE_MSEC, NULL);
|
|
else
|
|
KillTimer(ghwndApp, UPDATE_TIMER);
|
|
}
|
|
|
|
|
|
void FAR PASCAL Layout(void)
|
|
{
|
|
RECT rcClient, rc;
|
|
int iYOffset;
|
|
UINT wWidth;
|
|
UINT wFSArrowsWidth = 2 * FSARROW_WIDTH - 1; // 2 arrow buttons wide
|
|
UINT wFSArrowsHeight = FSARROW_HEIGHT;
|
|
UINT wFSTrackHeight = FSTRACK_HEIGHT;
|
|
UINT wFSTrackWidth;
|
|
UINT wToolbarWidth;
|
|
UINT wMinStatusWidth = 0;
|
|
int iYPosition;
|
|
BOOL fShowMark;
|
|
BOOL fShowTrackbar;
|
|
BOOL fShowStatus;
|
|
HDWP hdwp;
|
|
int nState; // The status of the transport buttons (when visible)
|
|
DWORD_PTR dw; // the current position within the medium
|
|
UINT wStatusMCI; // status of the device according to MCI
|
|
UINT wBaseUnits;
|
|
BOOL fRedrawFrame;
|
|
SIZE StatusTextExtent;
|
|
BOOL fRepaintToolbar; // TRUE if we remove or add something to toolbar area
|
|
|
|
/* OK to execute if we're hidden to set ourselves up for being shown */
|
|
|
|
if (sfInLayout || IsIconic(ghwndApp))
|
|
return;
|
|
|
|
if (gfInPlayMCI) {
|
|
DPF("Layout() called when in PlayMCI(). Posting message to Layout() later.\n");
|
|
/* Don't allow this to happen, because Layout() may cause a call to
|
|
* MCI_PUT (via SetWindowPos(ghwndMCI)), which will result in a
|
|
* device-not-ready error, as the MCI_PLAY hasn't completed.
|
|
*/
|
|
PostMessage(ghwndApp, WM_DOLAYOUT, 0, 0);
|
|
return;
|
|
}
|
|
|
|
sfInLayout = TRUE;
|
|
|
|
#ifdef DEBUG
|
|
GetWindowRect(ghwndApp, &rc);
|
|
DPF("***** Layout Window Rect ***** %d %d\n", rc.right - rc.left, rc.bottom - rc.top);
|
|
#endif
|
|
|
|
if (gfPlayOnly) {
|
|
|
|
extern UINT gwPlaybarHeight; // in server.c
|
|
|
|
#define XSLOP 0
|
|
#define XOFF 2
|
|
|
|
|
|
if (gfOle2IPEditing || gfOle2IPPlaying)
|
|
{
|
|
/* Don't call GetClientrect, because the window may have a border,
|
|
* and this will cause us to shrink the window.
|
|
* Note this is a hack to get around the problem of the window
|
|
* changing size when it is in place, making some displays dither
|
|
* the video in a disgusting manner.
|
|
*/
|
|
GetWindowRect(ghwndApp, &rc);
|
|
rc.right -= rc.left;
|
|
rc.bottom -= rc.top;
|
|
rc.left = rc.top = 0;
|
|
}
|
|
else
|
|
GetClientRect(ghwndApp, &rc);
|
|
|
|
rc.bottom -= gwPlaybarHeight;
|
|
|
|
#if 0
|
|
/* If we set WS_MAXIMIZE, user doesn't allow the window to be
|
|
* sized on NT. What's the idea behind this code anyway?
|
|
*/
|
|
|
|
if (ghwndMCI && !EqualRect(&rc, &grcSize))
|
|
fRedrawFrame = SetWS(ghwndApp, WS_MAXIMIZE /* |WS_MAXIMIZEBOX */);
|
|
else if (ghwndMCI)
|
|
fRedrawFrame = //SetWS(ghwndApp, WS_MAXIMIZEBOX) ||
|
|
ClrWS(ghwndApp, WS_MAXIMIZE);
|
|
else
|
|
fRedrawFrame = ClrWS(ghwndApp, WS_MAXIMIZEBOX);
|
|
#endif
|
|
fRedrawFrame = FALSE;
|
|
|
|
/* Here's another horrible hack.
|
|
* When you try to Play an in-place video after an Open (OLE),
|
|
* the toolbar and trackbar don't get drawn correctly.
|
|
* I haven't figured out why this is, but forcing a redraw
|
|
* seems to fix it. This code gets executed only when the window
|
|
* position changes, so it isn't too much of a hit.
|
|
*/
|
|
if (gfOle2IPEditing || gfOle2IPPlaying)
|
|
fRedrawFrame = TRUE;
|
|
|
|
if (fRedrawFrame)
|
|
SetWindowPos(ghwndApp,
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
SWP_DRAWFRAME|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);
|
|
|
|
if (ghwndMCI)
|
|
SetWindowPos(ghwndMCI,
|
|
NULL,
|
|
0,
|
|
0,
|
|
rc.right,
|
|
rc.bottom,
|
|
SWP_NOZORDER|SWP_NOACTIVATE);
|
|
|
|
// If we are inplace editing place controls on the ghwndIPToolWindow
|
|
// and the static window at the bottom of ghwndApp.
|
|
if(gfOle2IPEditing) {
|
|
|
|
SendMessage(ghwndTrackbar, TBM_SHOWTICS, TRUE, FALSE);
|
|
|
|
SetWindowPos(ghwndStatic,
|
|
NULL,
|
|
3,
|
|
rc.bottom + 2 + (gwPlaybarHeight - TOOLBAR_HEIGHT)/2,
|
|
rc.right - rc.left - 8,
|
|
TOOLBAR_HEIGHT-7,
|
|
SWP_NOZORDER|SWP_NOACTIVATE);
|
|
|
|
// Why are we getting the Status here when we have a global that
|
|
// contains it? Because gwStatus is set in UpdateDisplay, but
|
|
// Layout() is called by UpdateDisplay, so the global is not always
|
|
// set properly when this code runs. BUT! We must NOT pass a string
|
|
// to StatusMCI() or it will think UpdateDisplay() called it, and
|
|
// not tell UpdateDisplay() the proper mode next time it asks for it,
|
|
// because it will think that it already knows it.
|
|
|
|
wStatusMCI = StatusMCI(NULL);
|
|
nState = (wStatusMCI == MCI_MODE_OPEN
|
|
|| wStatusMCI == MCI_MODE_NOT_READY
|
|
|| gwDeviceID == (UINT) 0)
|
|
? BTNST_GRAYED
|
|
: BTNST_UP;
|
|
|
|
toolbarModifyState(ghwndToolbar, BTN_HOME, TBINDEX_MAIN, nState);
|
|
toolbarModifyState(ghwndToolbar, BTN_RWD, TBINDEX_MAIN, nState);
|
|
toolbarModifyState(ghwndToolbar, BTN_FWD, TBINDEX_MAIN, nState);
|
|
toolbarModifyState(ghwndToolbar, BTN_END, TBINDEX_MAIN, nState);
|
|
|
|
ShowWindow(ghwndTrackbar, SW_SHOW);
|
|
ShowWindow(ghwndToolbar, SW_SHOW);
|
|
ShowWindow(ghwndStatic, SW_SHOW);
|
|
ShowWindow(ghwndFSArrows, SW_SHOW);
|
|
ShowWindow(ghwndMark, SW_SHOW);
|
|
ShowWindow(ghwndMap, SW_SHOW);
|
|
|
|
if (ghwndIPToolWindow && (ghwndIPToolWindow != GetParent(ghwndTrackbar))
|
|
&& (ghwndIPScrollWindow != GetParent(ghwndTrackbar)))
|
|
{
|
|
SetParent(ghwndTrackbar,ghwndIPToolWindow);
|
|
SetWindowPos(ghwndTrackbar, NULL,4,TOOL_WIDTH+2,
|
|
11*BUTTONWIDTH+3,FSTRACK_HEIGHT,SWP_NOZORDER | SWP_NOACTIVATE);
|
|
SetParent(ghwndMap,ghwndIPToolWindow);
|
|
SetWindowPos(ghwndMap, NULL,4,TOOL_WIDTH+FSTRACK_HEIGHT+2+2,
|
|
11*BUTTONWIDTH+50,MAP_HEIGHT,SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
CalcTicsOfDoom();
|
|
|
|
} else {
|
|
|
|
#define LEFT_MARGIN 1
|
|
|
|
SendMessage(ghwndTrackbar, TBM_SHOWTICS, FALSE, FALSE);
|
|
|
|
SetWindowPos(ghwndToolbar,
|
|
NULL,
|
|
LEFT_MARGIN,
|
|
rc.bottom + 2 + (gwPlaybarHeight - TOOLBAR_HEIGHT)/2,
|
|
XSLOP + 2 * (BUTTONWIDTH - XOFF),
|
|
TOOLBAR_HEIGHT,
|
|
SWP_NOZORDER|SWP_NOACTIVATE);
|
|
|
|
SetWindowPos(ghwndTrackbar,
|
|
NULL,
|
|
XSLOP + 2 * (BUTTONWIDTH - XOFF) + LEFT_MARGIN,
|
|
rc.bottom + (gwPlaybarHeight - TOOLBAR_HEIGHT)/2 + 1,
|
|
rc.right-rc.left-(2 * XSLOP + 2 *(BUTTONWIDTH - XOFF) - LEFT_MARGIN),
|
|
TOOLBAR_HEIGHT - 1,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
// HACK!!!
|
|
// If we aren't visible, officially disable ourselves so that the
|
|
// trackbar shift code won't try and set selection
|
|
|
|
ShowWindow(ghwndTrackbar, gwPlaybarHeight > 0 ? SW_SHOW : SW_HIDE);
|
|
ShowWindow(ghwndToolbar, gwPlaybarHeight > 0 ? SW_SHOW : SW_HIDE);
|
|
ShowWindow(ghwndStatic, SW_HIDE);
|
|
ShowWindow(ghwndFSArrows, SW_HIDE);
|
|
ShowWindow(ghwndMark, SW_HIDE);
|
|
ShowWindow(ghwndMap, SW_HIDE);
|
|
}
|
|
|
|
goto Exit_Layout;
|
|
}
|
|
|
|
fRedrawFrame = ClrWS(ghwndApp, WS_MAXIMIZEBOX);
|
|
|
|
if (fRedrawFrame)
|
|
SetWindowPos(ghwndApp, NULL, 0, 0, 0, 0, SWP_DRAWFRAME|
|
|
SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);
|
|
|
|
if (GetMenu(ghwndApp) != ghMenu)
|
|
SetMenu(ghwndApp, ghMenu);
|
|
|
|
wBaseUnits = LOWORD(GetDialogBaseUnits()); // prop. to size of system font
|
|
|
|
/* If we're bigger than we're allowed to be then shrink us right now */
|
|
GetWindowRect(ghwndApp, &rc);
|
|
|
|
gwHeightAdjust = GetHeightAdjust(ghwndApp);
|
|
|
|
DPF1("Layout: WindowRect = %x, %x, %x, %x\n", rc);
|
|
|
|
if (rc.bottom - rc.top != (int)(MAX_NORMAL_HEIGHT + gwHeightAdjust)) {
|
|
MoveWindow(ghwndApp,
|
|
rc.left,
|
|
rc.top,
|
|
rc.right - rc.left,
|
|
(int)(MAX_NORMAL_HEIGHT + gwHeightAdjust),
|
|
TRUE);
|
|
}
|
|
|
|
|
|
hdwp = BeginDeferWindowPos(6);
|
|
|
|
if (!hdwp)
|
|
goto Exit_Layout;
|
|
|
|
GetClientRect(ghwndApp, &rcClient); // get new size
|
|
|
|
wWidth = rcClient.right;
|
|
|
|
iYOffset = rcClient.bottom - MAX_NORMAL_HEIGHT + 2; // start here
|
|
|
|
/* ??? Hide the trackbar if it can't fit on completely ??? */
|
|
iYPosition = iYOffset >= 0 ? iYOffset :
|
|
((iYOffset >= - 9) ? iYOffset + 9 : 1000);
|
|
|
|
fShowTrackbar = (iYOffset >= - 9);
|
|
|
|
/* Focus in on trackbar which is about to go away */
|
|
if (!fShowTrackbar && GetFocus() == ghwndTrackbar)
|
|
SetFocus(ghwndToolbar);
|
|
|
|
ShowWindow(ghwndToolbar, SW_SHOW);
|
|
|
|
/* The space that COMMCTRL puts to the left of the first toolbar button:
|
|
*/
|
|
#define SLOPLFT 0
|
|
#define XOFF1 8
|
|
|
|
// how long did it end up being?
|
|
wFSTrackWidth = wWidth - SB_XPOS - 1 - wFSArrowsWidth - SLOPLFT;
|
|
|
|
DeferWindowPos(hdwp,
|
|
ghwndTrackbar,
|
|
HWND_TOP,
|
|
SB_XPOS,
|
|
iYPosition,
|
|
wFSTrackWidth,
|
|
wFSTrackHeight,
|
|
SWP_NOZORDER | SWP_NOREDRAW |
|
|
(fShowTrackbar ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
|
|
|
|
|
|
/* Toolbar positioning:
|
|
*
|
|
* If the window is not wide enough to accommodate all the buttons
|
|
* and status bar, here's what we do:
|
|
*
|
|
* If the status bar is invisible, first remove the mark buttons,
|
|
* then use the small control width (with only three buttons).
|
|
*
|
|
* If the status bar is visible, give it priority over the mark
|
|
* buttons and the extra controls, but remove it if there isn't
|
|
* room for it and the small control width.
|
|
*/
|
|
|
|
if (gwDeviceID)
|
|
{
|
|
fShowStatus = TRUE;
|
|
|
|
if (GetStatusTextExtent(ghwndStatic, &StatusTextExtent))
|
|
{
|
|
RECT rc;
|
|
LONG StatusWidth; /* Total width of status window */
|
|
LONG TextAreaWidth; /* Width minus border and size grip */
|
|
|
|
/* Allow for the border around the status window:
|
|
*/
|
|
GetWindowRect(ghwndStatic, &rc);
|
|
StatusWidth = rc.right - rc.left;
|
|
|
|
SendMessage(ghwndStatic, SB_GETRECT, 0, (LPARAM)&rc);
|
|
TextAreaWidth = rc.right - rc.left;
|
|
|
|
wMinStatusWidth = StatusTextExtent.cx + (StatusWidth - TextAreaWidth) + 16;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fShowStatus = FALSE;
|
|
}
|
|
|
|
wToolbarWidth = LARGE_CONTROL_WIDTH + SLOPLFT;
|
|
fShowMark = TRUE;
|
|
|
|
if (wWidth < LARGE_CONTROL_WIDTH + SLOPLFT + MARK_WIDTH + XOFF1 + wMinStatusWidth)
|
|
{
|
|
fShowMark = FALSE;
|
|
|
|
if (wWidth < LARGE_CONTROL_WIDTH + SLOPLFT + wMinStatusWidth)
|
|
wToolbarWidth = SMALL_CONTROL_WIDTH + SLOPLFT;
|
|
|
|
if (wWidth < SMALL_CONTROL_WIDTH + SLOPLFT + wMinStatusWidth)
|
|
fShowStatus = FALSE;
|
|
}
|
|
|
|
fRepaintToolbar = FALSE;
|
|
|
|
/* If we're adding or removing the mark buttons or the status window,
|
|
* make sure we repaint things so that the separator bar corresponds.
|
|
* (It should separate the status window from the buttons, but should
|
|
* go away when the status window does.)
|
|
*/
|
|
if (IsWindowVisible(ghwndStatic) != fShowStatus)
|
|
fRepaintToolbar = TRUE;
|
|
else if (IsWindowVisible(ghwndMark) != fShowMark)
|
|
fRepaintToolbar = TRUE;
|
|
|
|
ShowWindow(ghwndStatic, fShowStatus);
|
|
|
|
/* Turn off the toolbar (for tabbing) if it's not going to be there */
|
|
/* and if we're disabled, we better not keep the focus. */
|
|
if (!fShowMark) {
|
|
if (GetFocus() == ghwndMark)
|
|
SetFocus(ghwndToolbar); // can't give it to SB, might be gone too
|
|
EnableWindow(ghwndMark, FALSE);
|
|
} else
|
|
EnableWindow(ghwndMark, TRUE);
|
|
|
|
DeferWindowPos(hdwp,
|
|
ghwndFSArrows,
|
|
HWND_TOP,
|
|
SB_XPOS + wFSTrackWidth,
|
|
// wWidth - 1 - wFSArrowsWidth,
|
|
iYPosition + 2,
|
|
wFSArrowsWidth + SLOPLFT,
|
|
wFSArrowsHeight + 4, /* Er, 4 because it works */
|
|
SWP_NOZORDER);
|
|
|
|
iYOffset += wFSTrackHeight;
|
|
|
|
DeferWindowPos(hdwp,
|
|
ghwndMap,
|
|
HWND_TOP,
|
|
SB_XPOS,
|
|
iYOffset,
|
|
wWidth - SB_XPOS,
|
|
MAP_HEIGHT,
|
|
SWP_NOZORDER | SWP_NOREDRAW |
|
|
(fShowTrackbar ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
|
|
iYOffset += MAP_HEIGHT;
|
|
|
|
/* Do we show the last four buttons on the main toolbar? */
|
|
/* If not, then disable them for tabs and such. */
|
|
if (wToolbarWidth == LARGE_CONTROL_WIDTH + SLOPLFT)
|
|
{
|
|
|
|
// Why are we getting the Status here when we have a global that
|
|
// contains it? Because gwStatus is set in UpdateDisplay, but
|
|
// Layout() is called by UpdateDisplay, so the global is not always
|
|
// set properly when this code runs. BUT! We must NOT pass a string
|
|
// to StatusMCI() or it will think UpdateDisplay() called it, and
|
|
// not tell UpdateDisplay() the proper mode next time it asks for it,
|
|
// because it will think that it already knows it.
|
|
|
|
wStatusMCI = StatusMCI(&dw);
|
|
nState = (wStatusMCI == MCI_MODE_OPEN
|
|
|| wStatusMCI == MCI_MODE_NOT_READY
|
|
|| gwDeviceID == (UINT)0) ? BTNST_GRAYED : BTNST_UP;
|
|
|
|
toolbarModifyState(ghwndToolbar, BTN_HOME, TBINDEX_MAIN, nState);
|
|
toolbarModifyState(ghwndToolbar, BTN_RWD, TBINDEX_MAIN, nState);
|
|
toolbarModifyState(ghwndToolbar, BTN_FWD, TBINDEX_MAIN, nState);
|
|
toolbarModifyState(ghwndToolbar, BTN_END, TBINDEX_MAIN, nState);
|
|
toolbarModifyState(ghwndToolbar, BTN_PLAY, TBINDEX_MAIN, nState);
|
|
}
|
|
else
|
|
{
|
|
toolbarModifyState(ghwndToolbar, BTN_HOME, TBINDEX_MAIN, BTNST_GRAYED);
|
|
toolbarModifyState(ghwndToolbar, BTN_RWD, TBINDEX_MAIN, BTNST_GRAYED);
|
|
toolbarModifyState(ghwndToolbar, BTN_FWD, TBINDEX_MAIN, BTNST_GRAYED);
|
|
toolbarModifyState(ghwndToolbar, BTN_END, TBINDEX_MAIN, BTNST_GRAYED);
|
|
}
|
|
|
|
DeferWindowPos(hdwp,
|
|
ghwndToolbar,
|
|
HWND_TOP,
|
|
2,
|
|
iYOffset + 2,
|
|
wToolbarWidth,
|
|
TOOLBAR_HEIGHT,
|
|
SWP_NOZORDER);
|
|
|
|
DeferWindowPos(hdwp,
|
|
ghwndMark,
|
|
HWND_TOP,
|
|
wToolbarWidth + XOFF1,
|
|
iYOffset + 2,
|
|
MARK_WIDTH,
|
|
TOOLBAR_HEIGHT,
|
|
SWP_NOZORDER | (fShowMark ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
|
|
|
|
#define ARBITRARY_Y_OFFSET 4
|
|
|
|
DeferWindowPos(hdwp,
|
|
ghwndStatic,
|
|
HWND_TOP,
|
|
wToolbarWidth + (fShowMark ? MARK_WIDTH + XOFF1 : 0) + XOFF1,
|
|
iYOffset + ARBITRARY_Y_OFFSET,
|
|
wWidth - (wToolbarWidth + 3) -
|
|
(fShowMark ? MARK_WIDTH + XOFF1 : 0) - XOFF1,
|
|
TOOLBAR_HEIGHT - 6,
|
|
SWP_NOZORDER);
|
|
|
|
EndDeferWindowPos(hdwp);
|
|
|
|
if (fRepaintToolbar)
|
|
{
|
|
InvalidateRect(ghwndApp, NULL, TRUE);
|
|
}
|
|
|
|
CalcTicsOfDoom();
|
|
|
|
/* These little gems have just cost me about ten hours worth of debugging -
|
|
* note the useful and descriptive comments...
|
|
*
|
|
* The Win32 problem caused by this only arises with CD Audio, when the disk is
|
|
* ejected and then another one is inserted into the drive. At that point
|
|
* the redrawing misses out the Trackmap FSArrows, the borders on the
|
|
* Mark buttons, and various bits of the toolbar.
|
|
*
|
|
* I will leave this here on the assumption that whichever bout
|
|
* on Win16 they are intended to fix still exists - it certainly doesn't on
|
|
* Win32.
|
|
*/
|
|
|
|
|
|
Exit_Layout:
|
|
sfInLayout = FALSE;
|
|
return;
|
|
}
|
|
|
|
|
|
/* What is the previous mark from our current spot? */
|
|
LONG_PTR CalcPrevMark(void)
|
|
{
|
|
LONG_PTR lStart, lEnd, lPos, lTol, lTrack = -1, lTarget;
|
|
LONG_PTR l;
|
|
|
|
lStart = SendMessage(ghwndTrackbar, TBM_GETSELSTART, 0, 0);
|
|
lEnd = SendMessage(ghwndTrackbar, TBM_GETSELEND, 0, 0);
|
|
lPos = SendMessage(ghwndTrackbar, TBM_GETPOS, 0, 0);
|
|
|
|
/* Find the next track we should go to (ignore selection markers) */
|
|
if (gwCurScale == ID_TRACKS) {
|
|
lTol = (LONG)gdwMediaLength / 2000;
|
|
for (l = (LONG)gwNumTracks - 1; l >= 0; l--) {
|
|
if (gadwTrackStart[l] < (DWORD)lPos - lTol) {
|
|
lTrack = gadwTrackStart[l];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* For msec mode: */
|
|
/* Our current position fluctuates randomly and even if we're dead on */
|
|
/* a selection mark, it might say we're a little before or after it. */
|
|
/* So we'll allow a margin for error so that you don't forever stay */
|
|
/* still while you hit PrevMark because it happens to be saying you're*/
|
|
/* always past the mark you're at. The margin of error will be */
|
|
/* half the width of the thumb. */
|
|
|
|
if (gwCurScale == ID_FRAMES)
|
|
lTol = 0L;
|
|
else
|
|
lTol = 0L;//VIJR-TBTrackGetLogThumbWidth(ghwndTrackbar) / 2;
|
|
|
|
if (lEnd != -1 && lPos > lEnd + lTol)
|
|
lTarget = lEnd;
|
|
else if (lStart != -1 && lPos > lStart + lTol)
|
|
lTarget = lStart;
|
|
else
|
|
lTarget = 0;
|
|
|
|
/* go to the either the selection mark or the next track (the closest) */
|
|
if (lTrack != -1 && lTrack > lTarget)
|
|
lTarget = lTrack;
|
|
|
|
return lTarget;
|
|
}
|
|
|
|
/* What is the next mark from our current spot? */
|
|
LONG_PTR CalcNextMark(void)
|
|
{
|
|
LONG_PTR lStart, lEnd, lPos, lTol, lTrack = -1, lTarget;
|
|
UINT_PTR w;
|
|
|
|
lStart = SendMessage(ghwndTrackbar, TBM_GETSELSTART, 0, 0);
|
|
lEnd = SendMessage(ghwndTrackbar, TBM_GETSELEND, 0, 0);
|
|
lPos = SendMessage(ghwndTrackbar, TBM_GETPOS, 0, 0);
|
|
|
|
/* Find the next track we should go to (ignore selection markers) */
|
|
if (gwCurScale == ID_TRACKS) {
|
|
lTol = (LONG)gdwMediaLength / 2000;
|
|
for (w = 0; w < gwNumTracks; w++) {
|
|
if (gadwTrackStart[w] > (DWORD)lPos + lTol) {
|
|
lTrack = gadwTrackStart[w];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* For msec mode: */
|
|
/* Our current position fluctuates randomly and even if we're dead on */
|
|
/* a selection mark, it might say we're a little before or after it. */
|
|
/* So we'll allow a margin for error so that you don't forever stay */
|
|
/* still while you hit NextMark because it happens to be saying you're*/
|
|
/* always before the mark you're at. The margin of error will be */
|
|
/* half the width of the thumb. */
|
|
|
|
if (gwCurScale == ID_FRAMES)
|
|
lTol = 0L;
|
|
else
|
|
lTol = 0L;//VIJR-TBTrackGetLogThumbWidth(ghwndTrackbar) / 2;
|
|
|
|
/* Find the selection mark we should go to */
|
|
if (lStart != -1 && lPos < lStart - lTol)
|
|
lTarget = lStart;
|
|
else if (lEnd != -1 && lPos < lEnd - lTol)
|
|
lTarget = lEnd;
|
|
else
|
|
lTarget = gdwMediaStart + gdwMediaLength;
|
|
|
|
/* go to the either the selection mark or the next track (the closest) */
|
|
if (lTrack != -1 && lTrack < lTarget)
|
|
lTarget = lTrack;
|
|
|
|
return lTarget;
|
|
}
|
|
|
|
|
|
HICON GetIconFromProgID(LPTSTR szProgID)
|
|
{
|
|
DWORD Status;
|
|
HKEY hkeyDefaultIcon;
|
|
BOOL rc = FALSE;
|
|
DWORD Type;
|
|
DWORD Size;
|
|
TCHAR szProgIDDefaultIcon[128];
|
|
TCHAR szDefaultIcon[MAX_PATH+4]; /* <path>,N */
|
|
HICON hicon = NULL;
|
|
LPTSTR pIconIndex;
|
|
UINT IconIndex;
|
|
|
|
wsprintf(szProgIDDefaultIcon, TEXT("%s\\DefaultIcon"), szProgID);
|
|
|
|
Status = RegOpenKeyEx( HKEY_CLASSES_ROOT, szProgIDDefaultIcon, 0,
|
|
KEY_READ, &hkeyDefaultIcon );
|
|
|
|
if (Status == NO_ERROR)
|
|
{
|
|
Size = CHAR_COUNT(szDefaultIcon);
|
|
|
|
Status = RegQueryValueEx( hkeyDefaultIcon,
|
|
aszNULL,
|
|
0,
|
|
&Type,
|
|
(LPBYTE)szDefaultIcon,
|
|
&Size );
|
|
|
|
if (Status == NO_ERROR)
|
|
{
|
|
/* Find a comma in the string. After it comes the icon index:
|
|
*/
|
|
pIconIndex = STRCHR(szDefaultIcon, TEXT(','));
|
|
|
|
if (pIconIndex)
|
|
{
|
|
/* Null terminate the file name:
|
|
*/
|
|
*pIconIndex = TEXT('\0');
|
|
|
|
/* Get the index that comes after the comma:
|
|
*/
|
|
IconIndex = ATOI(pIconIndex+1);
|
|
|
|
DPF1("Extracting icon #%d from %"DTS"\n", IconIndex, szDefaultIcon);
|
|
|
|
hicon = ExtractIcon(ghInst, szDefaultIcon, IconIndex);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
DPF0("Couldn't find Default Icon for %"DTS"\n", szProgID);
|
|
}
|
|
|
|
RegCloseKey(hkeyDefaultIcon);
|
|
}
|
|
|
|
return hicon;
|
|
}
|
|
|
|
|
|
|
|
/* GetIconForCurrentDevice
|
|
*
|
|
* Checks what device is currently selected, and returns a handle to the
|
|
* appropriate icon of the specified size. If there is no current device,
|
|
* returns either the application's icon or the default icon for media
|
|
* documents.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Size - GI_SMALL (for title bar) or GI_LARGE (for in-place icon).
|
|
*
|
|
* DefaultID - Default to use if no current device. APPICON or IDI_DDEFAULT.
|
|
*
|
|
* Return:
|
|
*
|
|
* Icon handle
|
|
*
|
|
*
|
|
* Andrew Bell (andrewbe), 31 March 1995
|
|
*/
|
|
HICON GetIconForCurrentDevice(UINT Size, UINT DefaultID)
|
|
{
|
|
TCHAR DeviceName[256];
|
|
DWORD i;
|
|
LPTSTR ImageID = NULL;
|
|
int cx;
|
|
int cy;
|
|
HICON hIcon;
|
|
|
|
GetDeviceNameMCI(DeviceName, BYTE_COUNT(DeviceName));
|
|
|
|
if (DeviceName[0])
|
|
{
|
|
for (i = 0; i < sizeof DevToIconIDMap / sizeof *DevToIconIDMap; i++)
|
|
{
|
|
if (!lstrcmpi(DeviceName, DevToIconIDMap[i].pString))
|
|
{
|
|
ImageID = MAKEINTRESOURCE(DevToIconIDMap[i].ID);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
if (Size == GI_LARGE)
|
|
{
|
|
|
|
hIcon = GetIconFromProgID(gachProgID);
|
|
|
|
if (hIcon)
|
|
{
|
|
return hIcon;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ImageID == NULL)
|
|
ImageID = MAKEINTRESOURCE(DefaultID);
|
|
|
|
cx = (Size == GI_SMALL ? GetSystemMetrics(SM_CXSMICON) : 0);
|
|
cy = (Size == GI_SMALL ? GetSystemMetrics(SM_CYSMICON) : 0);
|
|
|
|
hIcon = (HICON)LoadImage(ghInst, ImageID, IMAGE_ICON,
|
|
cx, cy, LR_DEFAULTSIZE);
|
|
|
|
return hIcon;
|
|
}
|
|
|
|
|
|
/* SetMPlayerIcon
|
|
*
|
|
* Sets the icon based upon the current device. Uses default document
|
|
* icon if embedded, otherwise the application icon.
|
|
*
|
|
* Andrew Bell (andrewbe), 31 March 1995
|
|
*/
|
|
void SetMPlayerIcon()
|
|
{
|
|
UINT DefaultID;
|
|
|
|
DefaultID = gfEmbeddedObject ? IDI_DDEFAULT : APPICON;
|
|
|
|
SendMessage(ghwndApp, WM_SETICON, FALSE,
|
|
(LPARAM)GetIconForCurrentDevice(GI_SMALL, DefaultID));
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------+
|
|
| AskUpdate - ask the user if they want to update the |
|
|
| object (if we're dirty). |
|
|
| IDYES means yes, go ahead and update please. |
|
|
| IDNO means don't update, but continue. |
|
|
| IDCANCEL means don't update, and cancel what |
|
|
| you were doing. |
|
|
+--------------------------------------------------------------*/
|
|
int NEAR PASCAL AskUpdate(void)
|
|
{
|
|
UINT w;
|
|
|
|
/* Don't update object if no device is loaded into mplayer! */
|
|
if (IsObjectDirty() && gfDirty != -1 && gfEmbeddedObject && gwDeviceID) {
|
|
|
|
if((glCurrentVerb == OLEIVERB_PRIMARY) && !gfOle2IPEditing)
|
|
return IDNO;
|
|
//
|
|
// if we are a hidden MPlayer (most likely doing a Play verb) then
|
|
// update without asking?
|
|
//
|
|
if (!IsWindowVisible(ghwndApp) || gfOle2IPEditing)
|
|
return IDYES;
|
|
|
|
w = ErrorResBox(ghwndApp, ghInst,
|
|
MB_YESNOCANCEL | MB_ICONQUESTION,
|
|
IDS_APPNAME, IDS_UPDATEOBJECT, szClientDoc);
|
|
|
|
} else
|
|
w = IDNO;
|
|
|
|
return w;
|
|
}
|
|
|
|
void SizePlaybackWindow(int dx, int dy)
|
|
{
|
|
RECT rc;
|
|
HWND hwndPlay;
|
|
|
|
if (gfPlayOnly) {
|
|
SetRect(&rc, 0, 0, dx, dy);
|
|
SetMPlayerSize(&rc);
|
|
}
|
|
else {
|
|
if (dx == 0 && dy == 0) {
|
|
SetMPlayerSize(NULL); // size MPlayer to default size
|
|
dx = grcSize.right; // then size the playback window too.
|
|
dy = grcSize.bottom;
|
|
}
|
|
hwndPlay = GetWindowMCI();
|
|
|
|
if (hwndPlay != NULL) {
|
|
|
|
/* make sure that the play window isn't iconized */
|
|
|
|
if (IsIconic(hwndPlay))
|
|
return;
|
|
|
|
GetClientRect(hwndPlay, &rc);
|
|
ClientToScreen(hwndPlay, (LPPOINT)&rc);
|
|
SetRect(&rc, rc.left, rc.top, rc.left+dx, rc.top+dy);
|
|
PutWindowMCI(&rc);
|
|
SetRect(&rc, 0, 0, dx, dy);
|
|
SetDestRectMCI(&rc);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* StartSndVol
|
|
*
|
|
* Kicks off the Sound Volume app asynchronously so we don't hang the UI.
|
|
*/
|
|
VOID StartSndVol( )
|
|
{
|
|
STARTUPINFO StartupInfo;
|
|
PROCESS_INFORMATION ProcessInformation;
|
|
|
|
memset( &StartupInfo, 0, sizeof StartupInfo );
|
|
StartupInfo.cb = sizeof(StartupInfo);
|
|
StartupInfo.wShowWindow = SW_SHOW;
|
|
|
|
CreateProcess( NULL, szSndVol32, NULL, NULL, FALSE, 0,
|
|
NULL, NULL, &StartupInfo, &ProcessInformation );
|
|
|
|
ExitThread( 0 );
|
|
}
|
|
|
|
|
|
/* GetHeightAdjust
|
|
*
|
|
* Finds the real height adjustment needed, by subtracting the client
|
|
* height from the main window height. This allows for menus that
|
|
* have wrapped.
|
|
*/
|
|
int GetHeightAdjust(HWND hwnd)
|
|
{
|
|
RECT rcWindow;
|
|
RECT rcClient;
|
|
int WindowHeight;
|
|
int ClientHeight;
|
|
|
|
GetWindowRect(hwnd, &rcWindow);
|
|
GetClientRect(hwnd, &rcClient);
|
|
WindowHeight = rcWindow.bottom - rcWindow.top;
|
|
ClientHeight = rcClient.bottom - rcClient.top;
|
|
|
|
return WindowHeight - ClientHeight;
|
|
}
|
|
|
|
|
|
/* Message-cracking routines for MPlayerWndProc:
|
|
*/
|
|
|
|
BOOL MPlayer_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
|
|
{
|
|
InitMPlayerDialog(hwnd);
|
|
|
|
/* set off a thread to check that the OLE registry stuff is not corrupted */
|
|
|
|
#ifdef CHICAGO_PRODUCT
|
|
/* If this is the Chicago Media Player, only mess with the registry
|
|
* if we're actually running on that platform.
|
|
* The guy may be running it on NT.
|
|
*/
|
|
if (gwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
|
|
return TRUE;
|
|
#endif
|
|
|
|
if (!IgnoreRegCheck())
|
|
BackgroundRegCheck(hwnd);
|
|
|
|
//Register for WM_DEVICECHANGE notification.
|
|
DeviceChange_Init(hwnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void MPlayer_OnShowWindow(HWND hwnd, BOOL fShow, UINT status)
|
|
{
|
|
if (fShow)
|
|
Layout(); // we're about to be shown and want to set
|
|
}
|
|
|
|
|
|
void MPlayer_OnSize(HWND hwnd, UINT state, int cx, int cy)
|
|
{
|
|
/* Don't waste time Layout()ing if we're not visible */
|
|
if (state != SIZE_RESTORED || IsWindowVisible(hwnd)) {
|
|
|
|
Layout();
|
|
|
|
// If we are inplace editing, our size change must be informed
|
|
// to the container, unless the size change was a result of a
|
|
// OnPosRectChange sent to us by the container.
|
|
if ((gfOle2IPEditing || gfOle2IPPlaying) && ghwndMCI) {
|
|
|
|
RECT rc;
|
|
RECT rcPrev;
|
|
|
|
rcPrev = gInPlacePosRect;
|
|
GetWindowRect(ghwndApp, &gInPlacePosRect);
|
|
gfInPlaceResize = TRUE;
|
|
rc = gInPlacePosRect;
|
|
|
|
/* Check that the previous rect wasn't empty, otherwise we send
|
|
* a bogus OLE_CHANGED on startup.
|
|
*/
|
|
if (!gfPosRectChange /*&& !IsRectEmpty(&rcPrev)*/) {
|
|
|
|
MapWindowPoints(NULL,ghwndCntr,(POINT FAR *)&rc,(UINT)2);
|
|
|
|
DPF("IOleInPlaceSite::OnPosRectChange %d, %d, %d, %d\n", rc);
|
|
if (!gfInPPViewer)
|
|
IOleInPlaceSite_OnPosRectChange(docMain.lpIpData->lpSite, &rc);
|
|
fDocChanged = TRUE;
|
|
SendDocMsg((LPDOC)&docMain, OLE_CHANGED);
|
|
}
|
|
|
|
gfPosRectChange = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL MPlayer_OnWindowPosChanging(HWND hwnd, LPWINDOWPOS lpwpos)
|
|
{
|
|
#define SNAPTOGOODSIZE
|
|
#ifdef SNAPTOGOODSIZE
|
|
BOOL wHeight;
|
|
#endif
|
|
|
|
DPF2("ENTER OnWindowPosChanging: lpwpos = %x, %x, %x, %x\n", *((PPOS)&lpwpos->x));
|
|
|
|
if (IsIconic(hwnd) || gfPlayOnly)
|
|
return TRUE;
|
|
|
|
/* posSizeMove contains the height we want to be when sizing.
|
|
* Don't let the system tell us otherwise.
|
|
*/
|
|
if (posSizeMove.cx != 0)
|
|
{
|
|
lpwpos->cy = posSizeMove.cy;
|
|
posSizeMove = *(PPOS)&lpwpos->x;
|
|
}
|
|
|
|
else if (!(lpwpos->flags & SWP_NOSIZE)) {
|
|
|
|
#ifdef SNAPTOGOODSIZE
|
|
/* We should also do things here to make the window
|
|
** snap to good sizes */
|
|
wHeight = lpwpos->cy - gwHeightAdjust;
|
|
// if (lpwpos->cy >= (int) gwHeightAdjust + MAX_NORMAL_HEIGHT) {
|
|
// } else if (lpwpos->cy < (int) gwHeightAdjust +
|
|
// ((MIN_NORMAL_HEIGHT + MAX_NORMAL_HEIGHT) / 2)) {
|
|
// lpwpos->cy = (int) gwHeightAdjust + MIN_NORMAL_HEIGHT;
|
|
// } else {
|
|
lpwpos->cy = (int) gwHeightAdjust + MAX_NORMAL_HEIGHT;
|
|
// }
|
|
#endif
|
|
}
|
|
|
|
DPF2("EXIT OnWindowPosChanging: lpwpos = %x, %x, %x, %x\n", *((PPOS)&lpwpos->x));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL MPlayer_OnWindowPosChanged(HWND hwnd, LPWINDOWPOS lpwpos)
|
|
{
|
|
if (!IsIconic(hwnd) && !gfPlayOnly && !gfOle2IPEditing && !gfOle2IPPlaying)
|
|
{
|
|
/* The problem here is that we want to modify the height of the
|
|
* window while tracking to take account of the menu height.
|
|
* In its wisdom, the system keeps trying to resize us back to the
|
|
* original height. So, during tracking, we keep hold of the
|
|
* dimensions we want to be and ignore the height that we get
|
|
* passed on WM_WINDOWPOSCHANGING.
|
|
*/
|
|
if (posSizeMove.cx != 0)
|
|
{
|
|
int NewHeightAdjust = GetHeightAdjust(hwnd);
|
|
|
|
if ((int)gwHeightAdjust != NewHeightAdjust)
|
|
{
|
|
/* The total non-client height has changed, so it must
|
|
* be the menu that's wrapped or unwrapped.
|
|
* Modify our height adjustment accordingly and resize
|
|
* the window.
|
|
*/
|
|
DPF("Menu appears to have wrapped. Changing window height.\n");
|
|
|
|
posSizeMove.cy += ( NewHeightAdjust - gwHeightAdjust );
|
|
gwHeightAdjust = NewHeightAdjust;
|
|
MoveWindow(ghwndApp,
|
|
posSizeMove.x, posSizeMove.y,
|
|
posSizeMove.cx, posSizeMove.cy, TRUE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (ghwndStatic && IsWindowVisible(ghwndStatic))
|
|
{
|
|
InvalidateRect(ghwndStatic, NULL, FALSE);
|
|
}
|
|
}
|
|
|
|
FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, DefWindowProc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void MPlayer_OnPaletteChanged(HWND hwnd, HWND hwndPaletteChange)
|
|
{
|
|
if (ghwndMCI && !IsIconic(hwnd))
|
|
FORWARD_WM_PALETTECHANGED(ghwndMCI, hwndPaletteChange, SendMessage);
|
|
}
|
|
|
|
|
|
BOOL MPlayer_OnQueryNewPalette(HWND hwnd)
|
|
{
|
|
HWND hwndT;
|
|
HPALETTE hpal, hpalT;
|
|
HDC hdc;
|
|
UINT PaletteEntries;
|
|
|
|
if (IsIconic(hwnd))
|
|
return FALSE;
|
|
|
|
if (ghwndMCI)
|
|
return FORWARD_WM_QUERYNEWPALETTE(ghwndMCI, SendMessage);
|
|
|
|
hwndT = GetWindowMCI();
|
|
hpal = PaletteMCI();
|
|
|
|
if ((hwndT != NULL) && (hpal != NULL)) {
|
|
hdc = GetDC(hwnd);
|
|
hpalT = SelectPalette(hdc, hpal, FALSE);
|
|
PaletteEntries = RealizePalette(hdc);
|
|
SelectPalette(hdc, hpalT, FALSE);
|
|
ReleaseDC(hwnd, hdc);
|
|
|
|
if (PaletteEntries != GDI_ERROR) {
|
|
InvalidateRect(hwndT, NULL, TRUE);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
HBRUSH MPlayer_OnCtlColor(HWND hwnd, HDC hdc, HWND hwndChild, int type)
|
|
{
|
|
/* Only interested in the CTLCOLOR_STATIC messages.
|
|
* On Win32, type should always equal CTLCOLOR_STATIC:
|
|
*/
|
|
switch( type )
|
|
{
|
|
case CTLCOLOR_STATIC:
|
|
SetBkColor(hdc, rgbButtonFace);
|
|
SetTextColor(hdc, rgbButtonText);
|
|
}
|
|
|
|
return hbrButtonFace;
|
|
}
|
|
|
|
|
|
void MPlayer_OnWinIniChange(HWND hwnd, LPCTSTR lpszSectionName)
|
|
{
|
|
if (!lpszSectionName || !lstrcmpi(lpszSectionName, (LPCTSTR)aszIntl))
|
|
if (GetIntlSpecs())
|
|
InvalidateRect(ghwndMap, NULL, TRUE);
|
|
|
|
if (!gfPlayOnly) {
|
|
|
|
if (gwHeightAdjust != (WORD)(2 * GetSystemMetrics(SM_CYFRAME) +
|
|
GetSystemMetrics(SM_CYCAPTION) +
|
|
GetSystemMetrics(SM_CYBORDER) +
|
|
GetSystemMetrics(SM_CYMENU))) {
|
|
|
|
RECT rc;
|
|
|
|
gwHeightAdjust = 2 * GetSystemMetrics(SM_CYFRAME) +
|
|
GetSystemMetrics(SM_CYCAPTION) +
|
|
GetSystemMetrics(SM_CYBORDER) +
|
|
GetSystemMetrics(SM_CYMENU);
|
|
GetClientRect(hwnd, &rc);
|
|
gfWinIniChange = TRUE;
|
|
SetMPlayerSize(&rc);
|
|
gfWinIniChange = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MPlayer_OnMenuSelect(HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags)
|
|
{
|
|
// Make sure that the container is still not displaying info. about
|
|
// its own menu.
|
|
|
|
if (gfOle2IPEditing && docMain.lpIpData->lpFrame) {
|
|
|
|
//Should have some useful text later.
|
|
IOleInPlaceFrame_SetStatusText(docMain.lpIpData->lpFrame, L"");
|
|
}
|
|
else
|
|
{
|
|
//Keep track of which menu bar item is currently popped up.
|
|
//This will be used for displaying the appropriate help from the mplayer.hlp file
|
|
//when the user presses the F1 key.
|
|
currMenuItem = item;
|
|
}
|
|
}
|
|
|
|
|
|
#define MVSIZEFIRST 1
|
|
#define MVMOVE 9
|
|
void MPlayer_OnNCLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT codeHitTest)
|
|
{
|
|
RECT rc;
|
|
|
|
if (gfPlayOnly && !IsIconic(hwnd) && IsZoomed(hwnd)) {
|
|
|
|
if (codeHitTest >= HTSIZEFIRST && codeHitTest <= HTSIZELAST) {
|
|
|
|
SendMessage(hwnd, WM_SYSCOMMAND,
|
|
// (WPARAM)(SC_SIZE + (codeHitTest - HTSIZEFIRST + MVSIZEFIRST) ),
|
|
(WPARAM)SC_SIZE,
|
|
MAKELPARAM(x, y));
|
|
return;
|
|
}
|
|
|
|
GetWindowRect(hwnd, &rc);
|
|
|
|
if (codeHitTest == HTCAPTION && (rc.left > 0 || rc.top > 0 ||
|
|
rc.right < GetSystemMetrics(SM_CXSCREEN) ||
|
|
rc.bottom < GetSystemMetrics(SM_CYSCREEN))) {
|
|
|
|
SendMessage(hwnd, WM_SYSCOMMAND,
|
|
// (WPARAM)(SC_MOVE | MVMOVE),
|
|
(WPARAM)SC_MOVE,
|
|
MAKELPARAM(x, y));
|
|
return;
|
|
}
|
|
}
|
|
|
|
FORWARD_WM_NCLBUTTONDOWN(hwnd, fDoubleClick, x, y, codeHitTest, DefWindowProc);
|
|
}
|
|
|
|
|
|
void MPlayer_OnNCLButtonDblClk(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT codeHitTest)
|
|
{
|
|
//
|
|
// when the user dbl-clicks on the caption, toggle the play mode.
|
|
//
|
|
if (codeHitTest == HTCAPTION && !IsIconic(hwnd))
|
|
SendMessage(hwnd, WM_COMMAND, (WPARAM)IDM_WINDOW, 0);
|
|
}
|
|
|
|
|
|
void MPlayer_OnInitMenu(HWND hwnd, HMENU hMenu)
|
|
{
|
|
|
|
EnableMenuItem(hMenu, IDM_CLOSE, gwDeviceID ? MF_ENABLED : MF_GRAYED);
|
|
// EnableMenuItem(hMenu, IDM_UPDATE, gwDeviceID && gfEmbeddedObject ? MF_ENABLED : MF_GRAYED);
|
|
|
|
EnableMenuItem(hMenu, IDM_COPY_OBJECT, (gwDeviceID && (gwStatus != MCI_MODE_OPEN) && (gwStatus != MCI_MODE_NOT_READY)) ? MF_ENABLED : MF_GRAYED);
|
|
EnableMenuItem(hMenu, IDM_CONFIG, gwDeviceID && (gwDeviceType & DTMCI_CANCONFIG) ? MF_ENABLED : MF_GRAYED);
|
|
|
|
CheckMenuItem(hMenu, IDM_SCALE + ID_TIME, gwCurScale == ID_TIME ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hMenu, IDM_SCALE + ID_TRACKS, gwCurScale == ID_TRACKS ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hMenu, IDM_SCALE + ID_FRAMES, gwCurScale == ID_FRAMES ? MF_CHECKED : MF_UNCHECKED);
|
|
|
|
EnableMenuItem(hMenu, IDM_SCALE + ID_TIME, gwDeviceID && !gfCurrentCDNotAudio && (gwDeviceType & DTMCI_TIMEMS) ? MF_ENABLED : MF_GRAYED);
|
|
EnableMenuItem(hMenu, IDM_SCALE + ID_FRAMES, gwDeviceID && !gfCurrentCDNotAudio && (gwDeviceType & DTMCI_TIMEFRAMES) ? MF_ENABLED : MF_GRAYED);
|
|
EnableMenuItem(hMenu, IDM_SCALE + ID_TRACKS, gwDeviceID && !gfCurrentCDNotAudio && (gwNumTracks > 1) ? MF_ENABLED : MF_GRAYED);
|
|
|
|
EnableMenuItem(hMenu, IDM_OPTIONS, gwDeviceID ? MF_ENABLED : MF_GRAYED);
|
|
EnableMenuItem(hMenu, IDM_SELECTION, gwDeviceID && gdwMediaLength ? MF_ENABLED : MF_GRAYED);
|
|
|
|
#ifdef DEBUG
|
|
EnableMenuItem(hMenu, IDM_MCISTRING, gwDeviceID ? MF_ENABLED : MF_GRAYED);
|
|
#endif
|
|
|
|
/*
|
|
EnableMenuItem(hMenu, IDM_PASTE_PICTURE , gwDeviceID &&
|
|
(IsClipboardFormatAvailable(CF_METAFILEPICT) ||
|
|
IsClipboardFormatAvailable(CF_BITMAP) ||
|
|
IsClipboardFormatAvailable(CF_DIB))
|
|
? MF_ENABLED : MF_GRAYED);
|
|
|
|
//
|
|
// what is paste frame!
|
|
//
|
|
EnableMenuItem(hMenu, IDM_PASTE_FRAME, gwDeviceID &&
|
|
(gwDeviceType & DTMCI_CANCONFIG) ? MF_ENABLED : MF_GRAYED);
|
|
*/
|
|
}
|
|
|
|
|
|
void MPlayer_OnInitMenuPopup(HWND hwnd, HMENU hMenu, UINT item, BOOL fSystemMenu)
|
|
{
|
|
static BOOL VolumeControlChecked = FALSE;
|
|
|
|
/* Here we look to see whether the menu selected is the Device popup,
|
|
* and, if it is the first time, search for the Sound Volume applet.
|
|
* If we can't find it, grey out the menu item.
|
|
*/
|
|
|
|
/* Caution: When we're in place, there seems to be a discrepancy
|
|
* in the value of parameter UINT item depending on which app is our
|
|
* container. If you use Spy to look at the parameters sent on
|
|
* WM_INITMENUPOPUP, some apps seem to have zero-based menus (e.g.
|
|
* ProgMan, PowerPoint, FileMan), which is what I would expect,
|
|
* but others seem to have one-based menus (e.g. Word, Excel).
|
|
* Why is this? I don't know. But it means that, when the
|
|
* Insert Clip menu item is selected, the item parameter may be
|
|
* either 2 or 3. That's why I'm calling GetSubMenu, since hMenu
|
|
* is always what I would expect.
|
|
*
|
|
* I sent some mail to the User and OLE guys to point this out,
|
|
* but haven't heard anything yet.
|
|
*
|
|
* andrewbe, 28 February 1995
|
|
*/
|
|
|
|
if (hMenu == GetSubMenu(ghMenu, menuposDevice))
|
|
{
|
|
HCURSOR hcurPrev;
|
|
|
|
if(!VolumeControlChecked)
|
|
{
|
|
/*
|
|
** Check to see if the volume controller piglet can be found on
|
|
** the path.
|
|
*/
|
|
{
|
|
TCHAR chBuffer[8];
|
|
LPTSTR lptstr;
|
|
|
|
if( SearchPath( NULL, szSndVol32, NULL, 8, chBuffer, &lptstr ) == 0L )
|
|
EnableMenuItem( hMenu, IDM_VOLUME, MF_GRAYED );
|
|
|
|
VolumeControlChecked = TRUE;
|
|
}
|
|
}
|
|
|
|
/* On Device (or Insert Clip) menu start menu building if necessary
|
|
* (e.g. if we came up in tiny mode then switched to full size),
|
|
* and wait for the separate thread to complete.
|
|
*/
|
|
InitDeviceMenu();
|
|
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
WaitForDeviceMenu();
|
|
SetCursor(hcurPrev);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// This code allows a window to by sized even when in the maximized state
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (gfPlayOnly && !IsIconic(hwnd) && fSystemMenu && IsZoomed(hwnd))
|
|
EnableMenuItem(hMenu, SC_SIZE,
|
|
!IsIconic(hwnd) ? MF_ENABLED : MF_GRAYED);
|
|
}
|
|
|
|
|
|
void MPlayer_OnGetMinMaxInfo(HWND hwnd, LPMINMAXINFO lpMinMaxInfo)
|
|
{
|
|
RECT rc;
|
|
|
|
if (gfPlayOnly) {
|
|
SetRect(&rc, 0, 0, 0, TOOLBAR_HEIGHT);
|
|
AdjustWindowRect(&rc, (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE), FALSE);
|
|
lpMinMaxInfo->ptMinTrackSize.y = rc.bottom - rc.top - 1;
|
|
|
|
if (!gfPlayingInPlace &&
|
|
(gwDeviceID == (UINT)0 || !(gwDeviceType & DTMCI_CANWINDOW)))
|
|
lpMinMaxInfo->ptMaxTrackSize.y = lpMinMaxInfo->ptMinTrackSize.y;
|
|
}
|
|
else {
|
|
lpMinMaxInfo->ptMinTrackSize.y = MAX_NORMAL_HEIGHT + gwHeightAdjust;
|
|
lpMinMaxInfo->ptMaxTrackSize.y = MAX_NORMAL_HEIGHT + gwHeightAdjust;
|
|
}
|
|
}
|
|
|
|
|
|
void MPlayer_OnPaint(HWND hwnd)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
RECT rc;
|
|
int x1, x2, y, y2;
|
|
UINT wParent;
|
|
HBRUSH hbrOld;
|
|
|
|
BeginPaint(hwnd, &ps);
|
|
|
|
if (gfPlayOnly) {
|
|
|
|
extern UINT gwPlaybarHeight; // in server.c
|
|
|
|
/* Separate mci playback window from controls */
|
|
if (gwDeviceType & DTMCI_CANWINDOW) {
|
|
SelectObject(ps.hdc, hbrButtonText);
|
|
GetClientRect(ghwndApp, &rc);
|
|
PatBlt(ps.hdc, 0, rc.bottom - gwPlaybarHeight, rc.right, 1, PATCOPY);
|
|
}
|
|
}
|
|
else {
|
|
hbrOld = SelectObject(ps.hdc, hbrButtonText);
|
|
GetClientRect(ghwndApp, &rc);
|
|
wParent = rc.right;
|
|
|
|
y = rc.bottom - 27; // where to paint borders around toolbar
|
|
|
|
/* Line above trackbar */
|
|
#ifdef CHICAGO_PRODUCT
|
|
y2 = rc.bottom - 74;
|
|
/* This looks bad on NT */
|
|
PatBlt(ps.hdc, 0, y2, wParent, 1, PATCOPY);
|
|
#else
|
|
y2 = rc.bottom - 75;
|
|
#endif
|
|
/* Lines around toolbars */
|
|
PatBlt(ps.hdc, 0, y, wParent, 1, PATCOPY);
|
|
GetClientRect(ghwndToolbar, &rc);
|
|
x1 = rc.right;
|
|
PatBlt(ps.hdc, x1, y, 1, TOOLBAR_HEIGHT + 3, PATCOPY);
|
|
GetWindowRect(ghwndApp, &rc);
|
|
x2 = rc.left;
|
|
|
|
if (IsWindowVisible(ghwndStatic)) {
|
|
GetWindowRect(ghwndStatic, &rc);
|
|
MapWindowPoints(NULL, ghwndApp, (LPPOINT)&rc, 1);
|
|
x2 = rc.left - 2 - GetSystemMetrics(SM_CXFRAME);
|
|
|
|
PatBlt(ps.hdc, x2, y, 1, TOOLBAR_HEIGHT + 3, PATCOPY);
|
|
}
|
|
|
|
SelectObject(ps.hdc, hbrButtonHighLight);
|
|
/* Line above trackbar */
|
|
PatBlt(ps.hdc, 0, y2 + 1, wParent, 1, PATCOPY);
|
|
/* Lines around toolbar */
|
|
PatBlt(ps.hdc, 0, y + 1, wParent, 1, PATCOPY);
|
|
PatBlt(ps.hdc, x1 + 1, y + 1, 1, TOOLBAR_HEIGHT + 2, PATCOPY);
|
|
if (IsWindowVisible(ghwndStatic)) {
|
|
PatBlt(ps.hdc, x2 + 1, y + 1, 1,TOOLBAR_HEIGHT +2, PATCOPY);
|
|
}
|
|
SelectObject(ps.hdc, hbrOld);
|
|
}
|
|
|
|
EndPaint(hwnd, &ps);
|
|
|
|
}
|
|
|
|
|
|
void MPlayer_OnCommand_Toolbar_Play()
|
|
{
|
|
/* This checks to see whether the ALT key is held down.
|
|
* If so, the current selection (if it exists) is played,
|
|
* otherwise the whole shooting match.
|
|
* Note, this does not appear to be documented at present.
|
|
*/
|
|
if (GetKeyState(VK_MENU) < 0)
|
|
PostMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_PLAYSEL, 0);
|
|
|
|
/* On WFW, pressing the play button when in place plays the
|
|
* current selection, if there is one. Do the same if we're
|
|
* playing or editing in place.
|
|
*/
|
|
else if (gfOle2IPPlaying || gfOle2IPEditing)
|
|
PostMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_PLAYSEL, 0);
|
|
else
|
|
PostMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_PLAY, 0);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_Pause()
|
|
{
|
|
PostMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_PAUSE, 0L);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_Stop()
|
|
{
|
|
PostMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_STOP, 0L);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_Eject()
|
|
{
|
|
PostMessage(ghwndApp, WM_COMMAND, (WPARAM)ID_EJECT, 0L);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_Home()
|
|
{
|
|
LONG_PTR lPos = CalcPrevMark();
|
|
|
|
/* We MUST use PostMessage because the */
|
|
/* SETPOS and ENDTRACK must happen one */
|
|
/* immediately after the other */
|
|
|
|
PostMessage(ghwndTrackbar, TBM_SETPOS, (WPARAM)TRUE, lPos);
|
|
|
|
PostMessage(ghwndApp, WM_HSCROLL, (WPARAM)TB_ENDTRACK, (LPARAM)ghwndTrackbar);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_End()
|
|
{
|
|
LONG_PTR lPos = CalcNextMark();
|
|
|
|
/* We MUST use PostMessage because the */
|
|
/* SETPOS and ENDTRACK must happen one */
|
|
/* immediately after the other */
|
|
|
|
PostMessage(ghwndTrackbar, TBM_SETPOS, (WPARAM)TRUE, lPos);
|
|
|
|
PostMessage(ghwndApp, WM_HSCROLL, (WPARAM)TB_ENDTRACK, (LPARAM)ghwndTrackbar);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_Rwd(HWND hwndCtl)
|
|
{
|
|
if (hwndCtl == (HWND)REPEAT_ID)
|
|
{
|
|
if (gwCurScale != ID_TRACKS)
|
|
PostMessage(ghwndApp, WM_HSCROLL, (WPARAM)TB_PAGEUP, 0L);
|
|
else
|
|
PostMessage(ghwndApp, WM_HSCROLL, (WPARAM)TB_LINEUP, 0L);
|
|
}
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_Fwd(HWND hwndCtl)
|
|
{
|
|
if (hwndCtl == (HWND)REPEAT_ID)
|
|
{
|
|
if (gwCurScale != ID_TRACKS)
|
|
PostMessage(ghwndApp, WM_HSCROLL, (WPARAM)TB_PAGEDOWN, 0L);
|
|
else
|
|
PostMessage(ghwndApp, WM_HSCROLL, (WPARAM)TB_LINEDOWN, 0L);
|
|
}
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_MarkIn()
|
|
{
|
|
SendMessage(ghwndTrackbar, TBM_SETSELSTART, (WPARAM)TRUE,
|
|
SendMessage(ghwndTrackbar, TBM_GETPOS, 0, 0));
|
|
|
|
DirtyObject(TRUE);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_MarkOut()
|
|
{
|
|
SendMessage(ghwndTrackbar, TBM_SETSELEND, (WPARAM)TRUE,
|
|
SendMessage(ghwndTrackbar, TBM_GETPOS, 0, 0));
|
|
|
|
DirtyObject(TRUE);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_ArrowPrev(HWND hwndCtl)
|
|
{
|
|
if (hwndCtl == (HWND)REPEAT_ID)
|
|
SendMessage(ghwndApp, WM_HSCROLL, (WPARAM)TB_LINEUP, 0L);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Toolbar_ArrowNext(HWND hwndCtl)
|
|
{
|
|
if (hwndCtl == (HWND)REPEAT_ID)
|
|
SendMessage(ghwndApp, WM_HSCROLL, (WPARAM)TB_LINEDOWN, 0L);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_CopyObject(HWND hwnd)
|
|
{
|
|
if (gfPlayingInPlace)
|
|
{
|
|
DPF0("Mplayer WndProc: Can't cutorcopy\n");
|
|
return;
|
|
}
|
|
|
|
DPF("Mplayer WndProc: Calling cutorcopy\n");
|
|
|
|
if (!InitOLE(&gfOleInitialized, &lpMalloc))
|
|
{
|
|
/* How likely is this? Do we need a dialog box?
|
|
*/
|
|
DPF0("Initialization of OLE FAILED!! Can't do copy.\n");
|
|
}
|
|
|
|
#ifdef OLE1_HACK
|
|
CopyObject(hwnd);
|
|
#endif /* OLE1_HACK */
|
|
CutOrCopyObj(&docMain);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_Config(HWND hwnd)
|
|
{
|
|
RECT rcBefore;
|
|
RECT rcAfter;
|
|
|
|
if (gfPlayingInPlace)
|
|
return;
|
|
|
|
GetDestRectMCI (&rcBefore);
|
|
|
|
ConfigMCI(hwnd);
|
|
|
|
/* If the MCI window size changed, we need to resize */
|
|
/* our reduced mplayer. */
|
|
if (gfPlayOnly)
|
|
{
|
|
GetDestRectMCI (&rcAfter);
|
|
|
|
if (!EqualRect(&rcBefore, &rcAfter) && (!IsRectEmpty(&rcAfter)))
|
|
SetMPlayerSize(&rcAfter);
|
|
}
|
|
}
|
|
|
|
|
|
void MPlayer_OnCommand_Menu_Volume(HWND hwnd)
|
|
{
|
|
HANDLE hThread;
|
|
DWORD dwThreadId;
|
|
|
|
hThread = CreateThread( NULL, 0L,
|
|
(LPTHREAD_START_ROUTINE)StartSndVol,
|
|
NULL, 0L, &dwThreadId );
|
|
|
|
if ( hThread != NULL ) {
|
|
CloseHandle( hThread );
|
|
}
|
|
}
|
|
|
|
|
|
void MPlayer_OnCommand_PlayToggle(HWND hwnd)
|
|
{
|
|
/* This is for the accelerator to toggle play and pause. */
|
|
/* Ordinary play commands better not toggle. */
|
|
|
|
DPF2("MPlayer_OnCommand_PlayToggle: gwStatus == %x\n", gwStatus);
|
|
|
|
switch(gwStatus) {
|
|
|
|
case MCI_MODE_STOP:
|
|
case MCI_MODE_PAUSE:
|
|
case MCI_MODE_SEEK:
|
|
PostMessage(hwnd, WM_COMMAND, (WPARAM)ID_PLAY, 0);
|
|
break;
|
|
|
|
case MCI_MODE_PLAY:
|
|
PostMessage(hwnd, WM_COMMAND, (WPARAM)ID_PAUSE, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MPlayer_OnCommand_PlaySel(HWND hwnd, HWND hwndCtl)
|
|
{
|
|
DWORD_PTR dwPos, dwStart, dwEnd;
|
|
BOOL f;
|
|
dwPos = 0; // Make Prefix Happy..
|
|
|
|
DPF2("MPlayer_OnCommand_PlaySel: gwStatus == %x\n", gwStatus);
|
|
|
|
switch(gwStatus) {
|
|
|
|
case MCI_MODE_OPEN:
|
|
case MCI_MODE_NOT_READY:
|
|
|
|
Error(ghwndApp, IDS_CANTPLAY);
|
|
if (gfCloseAfterPlaying) // get us out now!!
|
|
PostCloseMessage();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* If the shift key's being held down, make this the start
|
|
* of a selection:
|
|
*/
|
|
|
|
if((GetKeyState(VK_SHIFT) < 0)
|
|
&&(toolbarStateFromButton(ghwndMark, BTN_MARKIN, TBINDEX_MARK)
|
|
!= BTNST_GRAYED))
|
|
SendMessage(hwnd, WM_COMMAND, IDT_MARKIN, 0);
|
|
|
|
/* Start playing the medium */
|
|
|
|
StatusMCI(&dwPos); // get the REAL position
|
|
dwStart = SendMessage(ghwndTrackbar, TBM_GETSELSTART, 0, 0);
|
|
dwEnd = SendMessage(ghwndTrackbar, TBM_GETSELEND, 0, 0);
|
|
|
|
/* If there is no valid selection, act like PLAY */
|
|
if (dwStart == -1 || dwEnd == -1 || dwStart == dwEnd)
|
|
hwndCtl = (HWND)ID_PLAY;
|
|
|
|
// Be nice and rewind automatically if we're at the end of the media.
|
|
// Depending on the device, though, the end could be "start + len"
|
|
// or "start + len - 1"
|
|
if (hwndCtl == (HWND)ID_PLAY &&
|
|
dwPos >= gdwMediaStart + gdwMediaLength - 1) {
|
|
if (!SeekMCI(gdwMediaStart))
|
|
break;
|
|
}
|
|
|
|
if (hwndCtl == (HWND)ID_PLAYSEL) {
|
|
f = PlayMCI(dwStart, dwEnd);
|
|
gfJustPlayedSel = TRUE;
|
|
} else {
|
|
f = PlayMCI(0, 0);
|
|
gfJustPlayedSel = FALSE;
|
|
}
|
|
|
|
// get us out NOW!! or focus goes to client
|
|
if (!f && gfCloseAfterPlaying)
|
|
PostCloseMessage();
|
|
|
|
/* No longer needed - reset for next time */
|
|
gfUserStopped = FALSE;
|
|
|
|
gwStatus = (UINT)(-1); // force rewind if needed
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MPlayer_OnCommand_Pause()
|
|
{
|
|
/* Pause the medium, unless we are already paused */
|
|
|
|
DPF2("MPlayer_OnCommand_Pause: gwStatus == %x\n", gwStatus);
|
|
|
|
switch(gwStatus) {
|
|
|
|
case MCI_MODE_PAUSE:
|
|
PlayMCI(0, 0);
|
|
break;
|
|
|
|
case MCI_MODE_PLAY:
|
|
case MCI_MODE_SEEK:
|
|
PauseMCI();
|
|
break;
|
|
|
|
case MCI_MODE_STOP:
|
|
case MCI_MODE_OPEN:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MPlayer_OnCommand_Stop()
|
|
{
|
|
/* Stop the medium */
|
|
|
|
DPF2("MPlayer_OnCommand_Stop: gwStatus == %x\n", gwStatus);
|
|
|
|
switch(gwStatus) {
|
|
|
|
case MCI_MODE_PAUSE:
|
|
case MCI_MODE_PLAY:
|
|
case MCI_MODE_STOP:
|
|
case MCI_MODE_SEEK:
|
|
|
|
StopMCI();
|
|
SeekToStartMCI();
|
|
gfUserStopped = TRUE; // we did this
|
|
gfCloseAfterPlaying = FALSE; //stay up from now on
|
|
|
|
UpdateDisplay();
|
|
|
|
// Focus should go to PLAY button now
|
|
toolbarSetFocus(ghwndToolbar, BTN_PLAY);
|
|
break;
|
|
|
|
case MCI_MODE_OPEN:
|
|
break;
|
|
}
|
|
|
|
if (gfPlayingInPlace)
|
|
PostCloseMessage();
|
|
}
|
|
|
|
|
|
void MPlayer_OnCommand_Eject()
|
|
{
|
|
/*
|
|
* Eject the medium if it currently isn't ejected. If it
|
|
* is currently ejected, then load the new medium into
|
|
* the device.
|
|
*
|
|
*/
|
|
|
|
switch(gwStatus) {
|
|
|
|
case MCI_MODE_PLAY:
|
|
case MCI_MODE_PAUSE:
|
|
|
|
StopMCI();
|
|
EjectMCI(TRUE);
|
|
|
|
break;
|
|
|
|
case MCI_MODE_STOP:
|
|
case MCI_MODE_SEEK:
|
|
case MCI_MODE_NOT_READY:
|
|
|
|
EjectMCI(TRUE);
|
|
|
|
break;
|
|
|
|
case MCI_MODE_OPEN:
|
|
|
|
EjectMCI(FALSE);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void MPlayer_OnCommand_Escape()
|
|
{
|
|
MPlayer_OnCommand_Stop();
|
|
|
|
if( gfOle2IPEditing || gfOle2IPPlaying)
|
|
PostCloseMessage();
|
|
}
|
|
|
|
|
|
void MPlayer_OnCommand_Menu_Open()
|
|
{
|
|
UINT wLastScale;
|
|
UINT wLastDeviceID;
|
|
TCHAR szFile[256];
|
|
RECT rc;
|
|
|
|
wLastScale = gwCurScale; // save old scale
|
|
wLastDeviceID = gwDeviceID;
|
|
if (gfPlayingInPlace || gfOle2IPEditing || gfOle2IPPlaying)
|
|
return;
|
|
|
|
InitDeviceMenu();
|
|
WaitForDeviceMenu();
|
|
|
|
if (OpenDoc(gwCurDevice,szFile))
|
|
{
|
|
DirtyObject(FALSE);
|
|
/* Force WM_GETMINMAXINFO to be called so we'll snap */
|
|
/* to a proper size. */
|
|
GetWindowRect(ghwndApp, &rc);
|
|
MoveWindow(ghwndApp,
|
|
rc.left,
|
|
rc.top,
|
|
rc.right - rc.left,
|
|
rc.bottom - rc.top,
|
|
TRUE);
|
|
|
|
if (gfOpenDialog)
|
|
CompleteOpenDialog(TRUE);
|
|
else
|
|
gfCloseAfterPlaying = FALSE; // stay up from now on
|
|
|
|
//If the CD Audio device was opened it must have been a *.cda file.
|
|
//Try to jump to the track corresponding to the file opened.
|
|
if ((gwDeviceType & DTMCI_DEVICE) == DTMCI_CDAUDIO)
|
|
{
|
|
HandleCDAFile(szFile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (gfOpenDialog)
|
|
CompleteOpenDialog(FALSE);
|
|
|
|
/* The previous device may or may not still be open.
|
|
* If it is, make sure we have the right scale.
|
|
*/
|
|
if (gwDeviceID == wLastDeviceID)
|
|
gwCurScale = wLastScale; // restore to last scale
|
|
|
|
InvalidateRect(ghwndMap, NULL, TRUE); //erase map area
|
|
}
|
|
|
|
// put the focus on the Play button
|
|
SetFocus(ghwndToolbar); // give focus to PLAY button
|
|
toolbarSetFocus(ghwndToolbar, BTN_PLAY);
|
|
|
|
SetMPlayerIcon();
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_Close(HWND hwnd)
|
|
{
|
|
if (gfEmbeddedObject && !gfSeenPBCloseMsg) {
|
|
// this is File.Update
|
|
#ifdef OLE1_HACK
|
|
if( gDocVersion == DOC_VERSION_OLE1 )
|
|
Ole1UpdateObject();
|
|
else
|
|
#endif /* OLE1_HACK */
|
|
UpdateObject();
|
|
}
|
|
else
|
|
{
|
|
// this is File.Close
|
|
gfSeenPBCloseMsg = TRUE;
|
|
|
|
WriteOutOptions();
|
|
InitDoc(TRUE);
|
|
SetMPlayerIcon();
|
|
gwCurDevice = 0;// force next file open dialog to say
|
|
// "all files" because CloseMCI won't.
|
|
|
|
gwCurScale = ID_NONE; // uncheck all scale types
|
|
|
|
Layout(); // Make window snap back to smaller size
|
|
// if it should.
|
|
// Don't leave us closed in play only mode
|
|
|
|
if (gfPlayOnly)
|
|
SendMessage(hwnd, WM_COMMAND, (WPARAM)IDM_WINDOW, 0);
|
|
}
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_Exit()
|
|
{
|
|
PostCloseMessage();
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_Scale(UINT id)
|
|
{
|
|
/*
|
|
* Invalidate the track map window so it will be
|
|
* redrawn with the correct positions, etc.
|
|
*/
|
|
if (gwCurScale != id - IDM_SCALE) {
|
|
|
|
// Restoring the selection doesn't work yet,
|
|
// because UpdateMCI clears the selection,
|
|
// plus we need to do some conversion.
|
|
// int SelStart = SendMessage(ghwndTrackbar, TBM_GETSELSTART, 0, 0);
|
|
// int SelEnd = SendMessage(ghwndTrackbar, TBM_GETSELEND, 0, 0);
|
|
|
|
SendMessage(ghwndTrackbar, TBM_CLEARTICS, (WPARAM)FALSE, 0L);
|
|
if (gwCurScale == ID_FRAMES || id - IDM_SCALE == ID_FRAMES)
|
|
gfValidMediaInfo = FALSE;
|
|
|
|
gwCurScale = id - IDM_SCALE;
|
|
DirtyObject(TRUE); // change scale changes PAGE UP/DOWN
|
|
CalcTicsOfDoom();
|
|
|
|
// SendMessage(ghwndTrackbar, TBM_SETSELSTART, TRUE, SelStart);
|
|
// SendMessage(ghwndTrackbar, TBM_SETSELEND, TRUE, SelEnd);
|
|
}
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_Selection(HWND hwnd)
|
|
{
|
|
if (!gfPlayingInPlace)
|
|
setselDialog(hwnd);
|
|
}
|
|
|
|
|
|
void MPlayer_OnCommand_Menu_Options(HWND hwnd)
|
|
{
|
|
if (!gfPlayingInPlace)
|
|
optionsDialog(hwnd);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_MCIString(HWND hwnd)
|
|
{
|
|
if (!gfPlayingInPlace && gwDeviceID)
|
|
mciDialog(hwnd);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_Window(HWND hwnd)
|
|
{
|
|
//
|
|
// make MPlayer small/big
|
|
//
|
|
//!! dont do this if inside client document !!
|
|
//!! or if we're not visible !!
|
|
|
|
if (!IsWindowVisible(ghwndApp) || gfPlayingInPlace || IsIconic(hwnd)
|
|
|| gfOle2IPEditing)
|
|
return;
|
|
|
|
// allowed to get out of teeny mode when no file is open
|
|
if (gwDeviceID != (UINT)0 || gfPlayOnly) {
|
|
gfPlayOnly = !gfPlayOnly;
|
|
SizeMPlayer();
|
|
}
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_Zoom(HWND hwnd, int id)
|
|
{
|
|
int dx, dy;
|
|
|
|
if (IsIconic(hwnd) ||gfPlayingInPlace || gfOle2IPPlaying || gfOle2IPEditing ||
|
|
!(gwDeviceType & DTMCI_CANWINDOW))
|
|
return;
|
|
|
|
dx = grcSize.right * (id-IDM_ZOOM);
|
|
dy = grcSize.bottom * (id-IDM_ZOOM);
|
|
|
|
//
|
|
// if the playback windows is now larger than the screen
|
|
// maximize MPlayer, this only makes sence for Tiny mode.
|
|
//
|
|
if (gfPlayOnly &&
|
|
(dx >= GetSystemMetrics(SM_CXSCREEN) ||
|
|
dy >= GetSystemMetrics(SM_CYSCREEN))) {
|
|
ClrWS(hwnd, WS_MAXIMIZE);
|
|
DefWindowProc(hwnd, WM_SYSCOMMAND, (WPARAM)SC_MAXIMIZE, 0);
|
|
}
|
|
else {
|
|
SizePlaybackWindow(dx, dy);
|
|
}
|
|
}
|
|
|
|
void DoHtmlHelp()
|
|
{
|
|
//note, using ANSI version of function because UNICODE is foobar in NT5 builds
|
|
char chDst[MAX_PATH];
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, gszHtmlHelpFileName,
|
|
-1, chDst, MAX_PATH, NULL, NULL);
|
|
HtmlHelpA(GetDesktopWindow(), chDst, HH_DISPLAY_TOPIC, 0);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_HelpTopics(HWND hwnd)
|
|
{
|
|
static TCHAR HelpFile[] = TEXT("MPLAYER.HLP");
|
|
|
|
//Handle context menu help
|
|
if(bF1InMenu)
|
|
{
|
|
switch(currMenuItem)
|
|
{
|
|
case IDM_OPEN:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_FILE_OPEN);
|
|
break;
|
|
case IDM_CLOSE:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_FILE_CLOSE);
|
|
break;
|
|
case IDM_EXIT:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_FILE_EXIT);
|
|
break;
|
|
case IDM_COPY_OBJECT:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_EDIT_COPY_OBJECT);
|
|
break;
|
|
case IDM_OPTIONS:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_EDIT_OPTIONS);
|
|
break;
|
|
case IDM_SELECTION:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_EDIT_SELECTION);
|
|
break;
|
|
case IDM_CONFIG:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_DEVICE_PROPERTIES);
|
|
break;
|
|
case IDM_VOLUME:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_DEVICE_VOLUME_CONTROL);
|
|
break;
|
|
case IDM_SCALE + ID_TIME:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_SCALE_TIME);
|
|
break;
|
|
case IDM_SCALE + ID_TRACKS:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_SCALE_TRACKS);
|
|
break;
|
|
case IDM_SCALE + ID_FRAMES:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_SCALE_FRAMES);
|
|
break;
|
|
case IDM_HELPTOPICS:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_HELP_HELP_TOPICS);
|
|
break;
|
|
case IDM_ABOUT:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_MPLYR_CS_MEDIA_PLAYER_HELP_ABOUT);
|
|
break;
|
|
default://In the default case just display the HTML Help.
|
|
DoHtmlHelp();
|
|
}
|
|
bF1InMenu = FALSE; //This flag will be set again if F1 is pressed in a menu.
|
|
}
|
|
else
|
|
DoHtmlHelp();
|
|
}
|
|
|
|
void MPlayer_OnCommand_Menu_About(HWND hwnd)
|
|
{
|
|
ShellAbout(hwnd, gachAppName, aszNULL, hiconApp);
|
|
}
|
|
|
|
void MPlayer_OnCommand_Default(HWND hwnd, int id)
|
|
{
|
|
/*
|
|
* Determine if the user selected one of the entries in
|
|
* the Device menu.
|
|
*
|
|
*/
|
|
|
|
if (id > IDM_DEVICE0 &&
|
|
(id <= (WORD)(IDM_DEVICE0 + gwNumDevices))
|
|
) {
|
|
|
|
BOOL fHasWindow, fHadWindow, fHadDevice;
|
|
|
|
fHadWindow = (gwDeviceID != (UINT)0) && (gwDeviceType & DTMCI_CANWINDOW);
|
|
fHadDevice = (gwDeviceID != (UINT)0);
|
|
|
|
//Choose and open a new device. If we are active inplace we have
|
|
//to consider the effect of the change in device on the visual appearence.
|
|
//For this we have to take into account whether the current and previous
|
|
//device had a playback window or not. We also have to consider
|
|
//whether this is the first device are opening.
|
|
//After all the crazy munging send a messages to the container about
|
|
//the changes.
|
|
if (DoChooseDevice(id-IDM_DEVICE0))
|
|
{
|
|
if (gfOpenDialog)
|
|
CompleteOpenDialog(TRUE);
|
|
|
|
fHasWindow = (gwDeviceID != (UINT)0) && (gwDeviceType & DTMCI_CANWINDOW);
|
|
if(gfOle2IPEditing)
|
|
{
|
|
if (fHasWindow && fHadWindow)
|
|
{
|
|
GetWindowRect(ghwndApp, (LPRECT)&gInPlacePosRect);
|
|
gfInPlaceResize = TRUE;
|
|
SendDocMsg((LPDOC)&docMain, OLE_SIZECHG);
|
|
SendDocMsg((LPDOC)&docMain, OLE_CHANGED);
|
|
}
|
|
|
|
else
|
|
{
|
|
RECT rc;
|
|
RECT rctmp;
|
|
|
|
ClrWS(ghwndApp,
|
|
WS_THICKFRAME|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_BORDER);
|
|
|
|
if (gwOptions & OPT_BORDER)
|
|
SetWS(ghwndApp, WS_BORDER);
|
|
|
|
GetWindowRect(ghwndApp, &rc);
|
|
|
|
if (!(gwDeviceType & DTMCI_CANWINDOW))
|
|
{
|
|
HBITMAP hbm;
|
|
BITMAP bm;
|
|
|
|
if (!fHadDevice)
|
|
GetWindowRect(ghwndIPHatch, &rc);
|
|
hbm = BitmapMCI();
|
|
GetObject(hbm,sizeof(bm),&bm);
|
|
rc.bottom = rc.top + bm.bmHeight;
|
|
rc.right = rc.left + bm.bmWidth;
|
|
DeleteObject(hbm);
|
|
}
|
|
else
|
|
{
|
|
if(!fHadDevice)
|
|
{
|
|
rc.bottom -= (GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CYBORDER));
|
|
gwOptions |= OPT_BAR | OPT_TITLE;
|
|
}
|
|
rc.bottom += gInPlacePosRect.top - rc.top - 4*GetSystemMetrics(SM_CYBORDER) - 4 ;
|
|
rc.right += gInPlacePosRect.left - rc.left- 4*GetSystemMetrics(SM_CXBORDER) - 4 ;
|
|
rc.top = gInPlacePosRect.top;
|
|
rc.left = gInPlacePosRect.left;
|
|
}
|
|
rctmp = gPrevPosRect;
|
|
MapWindowPoints( ghwndCntr, NULL, (LPPOINT)&rctmp,2);
|
|
OffsetRect((LPRECT)&rc, rctmp.left - rc.left, rctmp.top -rc.top);
|
|
gInPlacePosRect = rc;
|
|
gfInPlaceResize = TRUE;
|
|
if(!(gwDeviceType & DTMCI_CANWINDOW) && (gwOptions & OPT_BAR))
|
|
{
|
|
rc.top = rc.bottom - gwPlaybarHeight;
|
|
}
|
|
EditInPlace(ghwndApp,ghwndIPHatch,&rc);
|
|
SendDocMsg((LPDOC)&docMain, OLE_SIZECHG);
|
|
SendDocMsg((LPDOC)&docMain, OLE_CHANGED);
|
|
if (!(gwDeviceType & DTMCI_CANWINDOW) && !(gwOptions &OPT_BAR))
|
|
ShowWindow(ghwndApp, SW_HIDE);
|
|
else
|
|
ShowWindow(ghwndApp, SW_SHOW);
|
|
}
|
|
}
|
|
|
|
DirtyObject(FALSE);
|
|
|
|
if (!gfOpenDialog)
|
|
gfCloseAfterPlaying = FALSE; // stay up from now on
|
|
|
|
SetMPlayerIcon();
|
|
}
|
|
else
|
|
if (gfOpenDialog)
|
|
CompleteOpenDialog(FALSE);
|
|
}
|
|
}
|
|
|
|
#define HANDLE_COMMAND(id, call) case (id): (call); break
|
|
|
|
void MPlayer_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
switch (id) {
|
|
|
|
HANDLE_COMMAND(IDT_PLAY, MPlayer_OnCommand_Toolbar_Play());
|
|
HANDLE_COMMAND(IDT_PAUSE, MPlayer_OnCommand_Toolbar_Pause());
|
|
HANDLE_COMMAND(IDT_STOP, MPlayer_OnCommand_Toolbar_Stop());
|
|
HANDLE_COMMAND(IDT_EJECT, MPlayer_OnCommand_Toolbar_Eject());
|
|
HANDLE_COMMAND(IDT_HOME, MPlayer_OnCommand_Toolbar_Home());
|
|
HANDLE_COMMAND(IDT_END, MPlayer_OnCommand_Toolbar_End());
|
|
HANDLE_COMMAND(IDT_RWD, MPlayer_OnCommand_Toolbar_Rwd(hwndCtl));
|
|
HANDLE_COMMAND(IDT_FWD, MPlayer_OnCommand_Toolbar_Fwd(hwndCtl));
|
|
HANDLE_COMMAND(IDT_MARKIN, MPlayer_OnCommand_Toolbar_MarkIn());
|
|
HANDLE_COMMAND(IDT_MARKOUT, MPlayer_OnCommand_Toolbar_MarkOut());
|
|
HANDLE_COMMAND(IDT_ARROWPREV, MPlayer_OnCommand_Toolbar_ArrowPrev(hwndCtl));
|
|
HANDLE_COMMAND(IDT_ARROWNEXT, MPlayer_OnCommand_Toolbar_ArrowNext(hwndCtl));
|
|
HANDLE_COMMAND(IDM_COPY_OBJECT, MPlayer_OnCommand_Menu_CopyObject(hwnd));
|
|
HANDLE_COMMAND(IDM_CONFIG, MPlayer_OnCommand_Menu_Config(hwnd));
|
|
HANDLE_COMMAND(IDM_VOLUME, MPlayer_OnCommand_Menu_Volume(hwnd));
|
|
HANDLE_COMMAND(ID_PLAYTOGGLE, MPlayer_OnCommand_PlayToggle(hwnd));
|
|
HANDLE_COMMAND(ID_PLAY, MPlayer_OnCommand_PlaySel(hwnd, (HWND)IntToPtr(id)));
|
|
HANDLE_COMMAND(ID_PLAYSEL, MPlayer_OnCommand_PlaySel(hwnd, (HWND)IntToPtr(id)));
|
|
HANDLE_COMMAND(ID_PAUSE, MPlayer_OnCommand_Pause());
|
|
HANDLE_COMMAND(ID_STOP, MPlayer_OnCommand_Stop());
|
|
HANDLE_COMMAND(ID_EJECT, MPlayer_OnCommand_Eject());
|
|
HANDLE_COMMAND(ID_ESCAPE, MPlayer_OnCommand_Escape());
|
|
HANDLE_COMMAND(IDM_OPEN, MPlayer_OnCommand_Menu_Open());
|
|
HANDLE_COMMAND(IDM_CLOSE, MPlayer_OnCommand_Menu_Close(hwnd));
|
|
HANDLE_COMMAND(IDM_EXIT, MPlayer_OnCommand_Menu_Exit());
|
|
HANDLE_COMMAND(IDM_SCALE + ID_TIME, MPlayer_OnCommand_Menu_Scale(id));
|
|
HANDLE_COMMAND(IDM_SCALE + ID_TRACKS, MPlayer_OnCommand_Menu_Scale(id));
|
|
HANDLE_COMMAND(IDM_SCALE + ID_FRAMES, MPlayer_OnCommand_Menu_Scale(id));
|
|
HANDLE_COMMAND(IDM_SELECTION, MPlayer_OnCommand_Menu_Selection(hwnd));
|
|
HANDLE_COMMAND(IDM_OPTIONS, MPlayer_OnCommand_Menu_Options(hwnd));
|
|
HANDLE_COMMAND(IDM_MCISTRING, MPlayer_OnCommand_Menu_MCIString(hwnd));
|
|
HANDLE_COMMAND(IDM_WINDOW, MPlayer_OnCommand_Menu_Window(hwnd));
|
|
HANDLE_COMMAND(IDM_ZOOM1, MPlayer_OnCommand_Menu_Zoom(hwnd, id));
|
|
HANDLE_COMMAND(IDM_ZOOM2, MPlayer_OnCommand_Menu_Zoom(hwnd, id));
|
|
HANDLE_COMMAND(IDM_ZOOM3, MPlayer_OnCommand_Menu_Zoom(hwnd, id));
|
|
HANDLE_COMMAND(IDM_ZOOM4, MPlayer_OnCommand_Menu_Zoom(hwnd, id));
|
|
HANDLE_COMMAND(IDM_HELPTOPICS, MPlayer_OnCommand_Menu_HelpTopics(hwnd));
|
|
HANDLE_COMMAND(IDM_ABOUT, MPlayer_OnCommand_Menu_About(hwnd));
|
|
|
|
default: MPlayer_OnCommand_Default(hwnd, id);
|
|
}
|
|
|
|
UpdateDisplay();
|
|
}
|
|
|
|
void MPlayer_OnClose(HWND hwnd)
|
|
{
|
|
int f;
|
|
|
|
DPF("WM_CLOSE received\n");
|
|
|
|
if (gfInClose) {
|
|
DPF("*** \n");
|
|
DPF("*** Trying to re-enter WM_CLOSE\n");
|
|
DPF("*** \n");
|
|
return;
|
|
}
|
|
|
|
|
|
// Ask if we want to update before we set gfInClose to TRUE or
|
|
// we won't let the dialog box up.
|
|
f = AskUpdate();
|
|
if (f == IDYES)
|
|
UpdateObject();
|
|
if (f == IDCANCEL) {
|
|
gfInClose = FALSE;
|
|
return;
|
|
}
|
|
|
|
gfInClose = TRUE;
|
|
|
|
ExitApplication();
|
|
if (gfPlayingInPlace)
|
|
EndPlayInPlace(hwnd);
|
|
if (gfOle2IPEditing)
|
|
EndEditInPlace(hwnd);
|
|
|
|
if (docMain.lpoleclient)
|
|
IOleClientSite_OnShowWindow(docMain.lpoleclient, FALSE);
|
|
|
|
SendDocMsg(&docMain,OLE_CLOSED);
|
|
DestroyDoc(&docMain);
|
|
ExitApplication();
|
|
|
|
if (hMciOle)
|
|
{
|
|
FreeLibrary(hMciOle);
|
|
hMciOle = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// set either the owner or the WS_CHILD bit so it will
|
|
// not act up because we have the palette bit set and cause the
|
|
// desktop to steal the palette.
|
|
//
|
|
// because we are being run from client apps that dont deal
|
|
// with palettes we dont want the desktop to hose the palette.
|
|
//
|
|
if (gfPlayOnly && gfCloseAfterPlaying && gfRunWithEmbeddingFlag)
|
|
SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LPARAM)GetDesktopWindow() );
|
|
|
|
if (!ItsSafeToClose()) {
|
|
DPF("*** \n");
|
|
DPF("*** Trying to close MPLAYER with a ErrorBox up\n");
|
|
DPF("*** \n");
|
|
gfErrorDeath = WM_CLOSE;
|
|
gfInClose = FALSE;
|
|
return;
|
|
}
|
|
|
|
f = AskUpdate();
|
|
if (f == IDYES)
|
|
UpdateObject();
|
|
if (f == IDCANCEL) {
|
|
gfInClose = FALSE;
|
|
return;
|
|
}
|
|
|
|
PostMessage(ghwndApp, WM_USER_DESTROY, 0, 0);
|
|
DPF("WM_DESTROY message sent\n");
|
|
}
|
|
|
|
void MPlayer_OnEndSession(HWND hwnd, BOOL fEnding)
|
|
{
|
|
if (fEnding) {
|
|
WriteOutPosition();
|
|
WriteOutOptions();
|
|
CloseMCI(FALSE);
|
|
}
|
|
}
|
|
|
|
void MPlayer_OnDestroy(HWND hwnd)
|
|
{
|
|
/*
|
|
* Relinquish control of whatever MCI device we were using (if any). If
|
|
* this device is not shareable, then performing this action allows
|
|
* someone else to gain access to the device.
|
|
*
|
|
*/
|
|
|
|
/* Client might close us if he dies while we're Playing in Place */
|
|
if (gfPlayingInPlace) {
|
|
DPF("****\n");
|
|
DPF("**** Window destroyed while in place!\n");
|
|
DPF("****\n");
|
|
}
|
|
|
|
//Unregister the WM_DEVICECHANGE notification
|
|
DeviceChange_Cleanup();
|
|
|
|
WriteOutOptions();
|
|
CloseMCI(FALSE);
|
|
|
|
SetMenu(hwnd, NULL);
|
|
|
|
if (ghMenu)
|
|
DestroyMenu(ghMenu);
|
|
|
|
ghMenu = NULL;
|
|
|
|
WinHelp(hwnd, gszHelpFileName, HELP_QUIT, 0L);
|
|
|
|
PostQuitMessage(0);
|
|
|
|
if (IsWindow(ghwndFrame))
|
|
SetFocus(ghwndFrame);
|
|
else if (IsWindow(ghwndFocusSave))
|
|
SetFocus(ghwndFocusSave);
|
|
|
|
//Inform OLE that we are not taking any more calls.
|
|
if (gfOleInitialized)
|
|
{
|
|
#ifdef OLE1_HACK
|
|
if( gDocVersion == DOC_VERSION_OLE1 )
|
|
TerminateServer();
|
|
else
|
|
#endif /* OLE1_HACK */
|
|
/* Verify that the server was initialised by checking that one
|
|
* of the fields in docMain is non-null:
|
|
*/
|
|
if( docMain.hwnd )
|
|
CoDisconnectObject((LPUNKNOWN)&docMain, 0);
|
|
else
|
|
DPF0("An instance of the server was never created.\n");
|
|
}
|
|
}
|
|
|
|
|
|
void MPlayer_OnTimer(HWND hwnd, UINT id)
|
|
{
|
|
MSG msg;
|
|
|
|
UpdateDisplay();
|
|
PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE);
|
|
}
|
|
|
|
|
|
#define MARK_START -1
|
|
#define MARK_NONE 0
|
|
#define MARK_END 1
|
|
void UpdateSelection(HWND hwnd, INT_PTR pos, int *pPrevMark)
|
|
{
|
|
INT_PTR SelStart;
|
|
INT_PTR SelEnd;
|
|
|
|
SelStart = SendMessage(ghwndTrackbar, TBM_GETSELSTART, 0, 0);
|
|
SelEnd = SendMessage(ghwndTrackbar, TBM_GETSELEND, 0, 0);
|
|
|
|
if (pos < SelStart)
|
|
{
|
|
SendMessage(hwnd, WM_COMMAND, IDT_MARKIN, 0);
|
|
*pPrevMark = MARK_START;
|
|
}
|
|
else if (pos > SelEnd)
|
|
{
|
|
SendMessage(hwnd, WM_COMMAND, IDT_MARKOUT, 0);
|
|
*pPrevMark = MARK_END;
|
|
}
|
|
else
|
|
{
|
|
if (*pPrevMark == MARK_START)
|
|
SendMessage(hwnd, WM_COMMAND, IDT_MARKIN, 0);
|
|
else
|
|
SendMessage(hwnd, WM_COMMAND, IDT_MARKOUT, 0);
|
|
}
|
|
}
|
|
|
|
|
|
void MPlayer_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
|
|
{
|
|
DWORD_PTR dwPosition; /* player's current position in the medium*/
|
|
DWORD_PTR dwCurTime; /* Time a page up/down is last made */
|
|
TCHAR ach[60];
|
|
static int PrevMark;
|
|
|
|
/* If the media has no size, we can't seek. */
|
|
if (gdwMediaLength == 0L)
|
|
return;
|
|
|
|
dwPosition = SendMessage(ghwndTrackbar, TBM_GETPOS, 0, 0);
|
|
|
|
if (!gfScrollTrack) {
|
|
gfScrollTrack = TRUE;
|
|
|
|
/* If the shift key's being held down, make this the start
|
|
* of a selection:
|
|
*/
|
|
|
|
if((GetKeyState(VK_SHIFT) < 0)
|
|
&&(toolbarStateFromButton(ghwndMark, BTN_MARKIN, TBINDEX_MARK)
|
|
!= BTNST_GRAYED))
|
|
{
|
|
SendMessage(ghwndTrackbar, TBM_CLEARSEL, (WPARAM)TRUE, 0);
|
|
SendMessage(hwnd, WM_COMMAND, IDT_MARKIN, 0);
|
|
SetFocus(ghwndTrackbar); /* So that escape will go to
|
|
the trackbar's subclassed
|
|
winproc. */
|
|
}
|
|
|
|
sfSeekExact = SeekExactMCI(FALSE);
|
|
}
|
|
|
|
switch (code) {
|
|
/*
|
|
* Set the new position within the medium to be
|
|
* slightly before/after the current position if the
|
|
* left/right scroll arrow was clicked on.
|
|
*/
|
|
case TB_LINEUP: /* left scroll arrow */
|
|
dwPosition -= (gwCurScale == ID_FRAMES) ? 1L : SCROLL_GRANULARITY;
|
|
break;
|
|
|
|
case TB_LINEDOWN: /* right scroll arrow */
|
|
dwPosition += (gwCurScale == ID_FRAMES) ? 1L : SCROLL_GRANULARITY;
|
|
break;
|
|
|
|
case TB_PAGEUP: /* page-left */
|
|
|
|
/*
|
|
* If the user just did a page-left a short time ago,
|
|
* then seek to the start of the previous track.
|
|
* Otherwise, seek to the start of this track.
|
|
*
|
|
*/
|
|
if (gwCurScale != ID_TRACKS) {
|
|
dwPosition -= SCROLL_BIGGRAN;
|
|
} else {
|
|
dwCurTime = GetCurrentTime();
|
|
if (dwCurTime - dwLastPageUpTime < SKIPTRACKDELAY_MSEC)
|
|
SkipTrackMCI(-1);
|
|
else
|
|
SkipTrackMCI(0);
|
|
|
|
dwLastPageUpTime = dwCurTime;
|
|
goto BreakOut; // avoid SETPOS
|
|
}
|
|
|
|
break;
|
|
|
|
case TB_PAGEDOWN: /* page-right */
|
|
|
|
if (gwCurScale != ID_TRACKS) {
|
|
dwPosition += SCROLL_BIGGRAN;
|
|
} else {
|
|
/* Seek to the start of the next track */
|
|
SkipTrackMCI(1);
|
|
// Ensure next PageUp can't possibly do SkipTrackMCI(-1)
|
|
// which will skip back too far if you page
|
|
// left, right, left really quickly.
|
|
dwLastPageUpTime = 0;
|
|
goto BreakOut; // avoid SETPOS
|
|
}
|
|
|
|
break;
|
|
|
|
case TB_THUMBTRACK: /* track thumb movement */
|
|
//!!! we should do a "set seek exactly off"
|
|
/* Only seek while tracking for windowed devices that */
|
|
/* aren't currently playing */
|
|
if ((gwDeviceType & DTMCI_CANWINDOW) &&
|
|
!(gwStatus == MCI_MODE_PLAY)) {
|
|
SeekMCI(dwPosition);
|
|
}
|
|
|
|
break;
|
|
|
|
case TB_TOP:
|
|
dwPosition = gdwMediaStart;
|
|
break;
|
|
|
|
case TB_BOTTOM:
|
|
dwPosition = gdwMediaStart + gdwMediaLength;
|
|
break;
|
|
|
|
case TB_THUMBPOSITION: /* thumb has been positioned */
|
|
break;
|
|
|
|
case TB_ENDTRACK: /* user let go of scroll */
|
|
DPF2("TB_ENDTRACK\n");
|
|
|
|
gfScrollTrack = FALSE;
|
|
|
|
/* New as of 2/7/91: Only seek on ENDTRACK */
|
|
|
|
/*
|
|
* Calculate the new position in the medium
|
|
* corresponding to the scrollbar position, and seek
|
|
* to this new position.
|
|
*
|
|
*/
|
|
|
|
/* We really want to update our position */
|
|
if (hwndCtl) {
|
|
if (gdwSeekPosition) {
|
|
dwPosition = gdwSeekPosition;
|
|
gdwSeekPosition = 0;
|
|
}
|
|
|
|
/* Go back to the seek mode we were in before */
|
|
/* we started scrolling. */
|
|
SeekExactMCI(sfSeekExact);
|
|
SeekMCI(dwPosition);
|
|
}
|
|
|
|
PrevMark = MARK_NONE;
|
|
|
|
return;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
SendMessage(ghwndTrackbar, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)dwPosition);
|
|
/* Clamp to a valid range */
|
|
dwPosition = SendMessage(ghwndTrackbar, TBM_GETPOS, 0, 0);
|
|
|
|
BreakOut:
|
|
|
|
if (GetKeyState(VK_SHIFT) < 0)
|
|
UpdateSelection(hwnd, dwPosition, &PrevMark);
|
|
|
|
if (ghwndStatic) {
|
|
FormatTime(dwPosition, NULL, ach, TRUE);
|
|
//VIJR-SBSetWindowText(ghwndStatic, ach);
|
|
WriteStatusMessage(ghwndStatic, ach);
|
|
}
|
|
|
|
// Dirty if you just move the thumb???
|
|
// if (!IsObjectDirty() && !gfCloseAfterPlaying) // don't want playing to dirty
|
|
// DirtyObject();
|
|
}
|
|
|
|
void MPlayer_OnSysCommand(HWND hwnd, UINT cmd, int x, int y)
|
|
{
|
|
RECT rc;
|
|
|
|
// The bottom four bits of wParam contain system information. They
|
|
// must be masked off in order to work out the actual command.
|
|
// See the comments section in the online help for WM_SYSCOMMAND.
|
|
|
|
switch (cmd & 0xFFF0) {
|
|
|
|
case SC_MINIMIZE:
|
|
DPF("minimized -- turn off timer\n");
|
|
ClrWS(hwnd, WS_MAXIMIZE);
|
|
EnableTimer(FALSE);
|
|
break;
|
|
|
|
case SC_MAXIMIZE:
|
|
if (gfPlayOnly && !IsIconic(hwnd)) {
|
|
(void)PostMessage(hwnd, WM_COMMAND, (WPARAM)IDM_ZOOM2, 0);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
case SC_RESTORE:
|
|
if (gfPlayOnly && !IsIconic(hwnd)) {
|
|
GetWindowRect(hwnd, &rc);
|
|
if (rc.left > 0 || rc.top > 0)
|
|
(void)PostMessage(hwnd, WM_COMMAND, (WPARAM)IDM_ZOOM1, 0);
|
|
return;
|
|
}
|
|
|
|
if (gwDeviceID != (UINT)0) {
|
|
DPF("un-minimized -- turn timer back on\n");
|
|
EnableTimer(TRUE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
FORWARD_WM_SYSCOMMAND(hwnd, cmd, x, y, DefWindowProc);
|
|
}
|
|
|
|
|
|
int MPlayer_OnMouseActivate(HWND hwnd, HWND hwndTopLevel, UINT codeHitTest, UINT msg)
|
|
{
|
|
if (gfPlayingInPlace && !gfOle2IPPlaying)
|
|
return MA_NOACTIVATE;
|
|
else
|
|
/* !!! Is this the right thing to do in this case? */
|
|
return FORWARD_WM_MOUSEACTIVATE(hwnd, hwndTopLevel, codeHitTest, msg,
|
|
DefWindowProc);
|
|
}
|
|
|
|
|
|
UINT MPlayer_OnNCHitTest(HWND hwnd, int x, int y)
|
|
{
|
|
UINT Pos;
|
|
|
|
Pos = FORWARD_WM_NCHITTEST(hwnd, x, y, DefWindowProc);
|
|
|
|
if (gfPlayingInPlace && (Pos == HTCLIENT))
|
|
Pos = HTNOWHERE;
|
|
|
|
return Pos;
|
|
}
|
|
|
|
|
|
void MPlayer_OnActivate(HWND hwnd, UINT state, HWND hwndActDeact, BOOL fMinimized)
|
|
{
|
|
HWND hwndT;
|
|
|
|
gfAppActive = (state != WA_INACTIVE);
|
|
|
|
// Put the playback window BEHIND us so it's kinda
|
|
// visible, but not on top of us (annoying).
|
|
if (gfAppActive && !ghwndMCI && !IsIconic(hwnd) &&
|
|
((hwndT = GetWindowMCI()) != NULL))
|
|
{
|
|
SetWindowPos(hwndT, hwnd, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
}
|
|
|
|
if (gwDeviceID != (UINT)0)
|
|
EnableTimer(TRUE);
|
|
|
|
/* Remember who had focus if we're being de-activated. */
|
|
/* Give focus back to him once we're re-activated. */
|
|
/* Don't remember a window that doesn't belong to us, */
|
|
/* or when we give focus back to it, we'll never be */
|
|
/* able to activate! */
|
|
|
|
#if 0
|
|
/* Commenting this out for now. This code looks dubious.
|
|
* wParam (as was) contains state and fMinimized, so, if we're minimized,
|
|
* it will always be non-null.
|
|
*/
|
|
|
|
if (wParam && ghwndFocus) {
|
|
SetFocus(ghwndFocus);
|
|
} else if (!wParam) {
|
|
ghwndFocus = GetFocus();
|
|
}
|
|
#endif
|
|
|
|
FORWARD_WM_ACTIVATE(hwnd, state, hwndActDeact, fMinimized, DefWindowProc);
|
|
}
|
|
|
|
void MPlayer_OnSysColorChange(HWND hwnd)
|
|
{
|
|
ControlCleanup();
|
|
ControlInit(ghInst);
|
|
|
|
FORWARD_WM_SYSCOLORCHANGE(ghwndToolbar, SendMessage);
|
|
FORWARD_WM_SYSCOLORCHANGE(ghwndFSArrows, SendMessage);
|
|
FORWARD_WM_SYSCOLORCHANGE(ghwndMark, SendMessage);
|
|
FORWARD_WM_SYSCOLORCHANGE(ghwndTrackbar, SendMessage);
|
|
}
|
|
|
|
|
|
void MPlayer_OnDropFiles(HWND hwnd, HDROP hdrop)
|
|
{
|
|
doDrop(hwnd, hdrop);
|
|
}
|
|
|
|
|
|
LRESULT MPlayer_OnNotify(HWND hwnd, int idFrom, NMHDR FAR* pnmhdr)
|
|
{
|
|
LPTOOLTIPTEXT pTtt;
|
|
LPTBNOTIFY pTbn;
|
|
TCHAR ach[40];
|
|
|
|
switch(pnmhdr->code) {
|
|
|
|
case TTN_NEEDTEXT:
|
|
|
|
pTtt = (LPTOOLTIPTEXT)pnmhdr;
|
|
|
|
if (gfPlayOnly && (pTtt->hdr.idFrom != IDT_PLAY)
|
|
&& (pTtt->hdr.idFrom != IDT_PAUSE)
|
|
&& (pTtt->hdr.idFrom != IDT_STOP)
|
|
&& !gfOle2IPEditing)
|
|
break;
|
|
switch (pTtt->hdr.idFrom) {
|
|
case IDT_PLAY:
|
|
case IDT_PAUSE:
|
|
case IDT_STOP:
|
|
case IDT_EJECT:
|
|
case IDT_HOME:
|
|
case IDT_END:
|
|
case IDT_FWD:
|
|
case IDT_RWD:
|
|
case IDT_MARKIN:
|
|
case IDT_MARKOUT:
|
|
case IDT_ARROWPREV:
|
|
case IDT_ARROWNEXT:
|
|
LOADSTRING(pTtt->hdr.idFrom, ach);
|
|
lstrcpy(pTtt->szText, ach);
|
|
break;
|
|
default:
|
|
*pTtt->szText = TEXT('\0');
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case TBN_BEGINDRAG:
|
|
pTbn = (LPTBNOTIFY)pnmhdr;
|
|
if(pTbn->iItem == IDT_ARROWPREV || pTbn->iItem == IDT_ARROWNEXT)
|
|
SendMessage(ghwndFSArrows, WM_STARTTRACK, (WPARAM)pTbn->iItem, 0L);
|
|
else
|
|
SendMessage(ghwndToolbar, WM_STARTTRACK, (WPARAM)pTbn->iItem, 0L);
|
|
break;
|
|
|
|
case TBN_ENDDRAG:
|
|
pTbn = (LPTBNOTIFY)pnmhdr;
|
|
if(pTbn->iItem == IDT_ARROWPREV || pTbn->iItem == IDT_ARROWNEXT)
|
|
SendMessage(ghwndFSArrows, WM_ENDTRACK, (WPARAM)pTbn->iItem, 0L);
|
|
else
|
|
SendMessage(ghwndToolbar, WM_ENDTRACK, (WPARAM)pTbn->iItem, 0L);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
// * DeviceChange_Init
|
|
// First time initialization for WM_DEVICECHANGE messages
|
|
// This is specific to NT5
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL DeviceChange_Init(HWND hWnd)
|
|
{
|
|
DEV_BROADCAST_DEVICEINTERFACE dbi;
|
|
|
|
dbi.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
|
|
dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
|
dbi.dbcc_reserved = 0;
|
|
dbi.dbcc_classguid = KSCATEGORY_AUDIO;
|
|
dbi.dbcc_name[0] = TEXT('\0');
|
|
|
|
MixerEventContext = RegisterDeviceNotification(hWnd,
|
|
(PVOID)&dbi,
|
|
DEVICE_NOTIFY_WINDOW_HANDLE);
|
|
if(!MixerEventContext)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
// * DeviceChange_Cleanup
|
|
// Unregister the device notification.
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
void DeviceChange_Cleanup()
|
|
{
|
|
if (MixerEventContext) {
|
|
UnregisterDeviceNotification(MixerEventContext);
|
|
MixerEventContext = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void DisplayNoMciDeviceError()
|
|
{
|
|
DWORD ErrorID;
|
|
|
|
if (!lstrcmpi(gachOpenExtension, aszKeyMID))
|
|
ErrorID = IDS_CANTPLAYMIDI;
|
|
|
|
else if (!lstrcmpi(gachOpenExtension, aszKeyAVI))
|
|
ErrorID = IDS_CANTPLAYVIDEO;
|
|
|
|
else if (!lstrcmpi(gachOpenExtension, aszKeyWAV))
|
|
ErrorID = IDS_CANTPLAYSOUND;
|
|
|
|
else
|
|
ErrorID = IDS_NOMCIDEVICES;
|
|
|
|
Error(ghwndApp, ErrorID);
|
|
}
|
|
|
|
|
|
/*
|
|
* MPlayerWndProc(hwnd, wMsg, wParam, lParam)
|
|
*
|
|
* This is the message processing routine for the MPLAYERBOX (main) dialog.
|
|
*
|
|
*/
|
|
//Harmless message-cracker because the user guys will not fix their
|
|
//windowsx.h macro which cause the irritating rip.
|
|
//This is also a wee bit faster because the message
|
|
//is forwarded only on select and not on deselects. Also we do not care
|
|
//about the params
|
|
#define HANDLE_MPLAYER_WM_MENUSELECT(hwnd, message, fn) \
|
|
case (message): if(lParam) ((fn)((hwnd), (HMENU)(lParam), (UINT)LOWORD(wParam), 0L, 0L )); break;
|
|
|
|
LRESULT FAR PASCAL MPlayerWndProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (wMsg) {
|
|
|
|
HANDLE_MSG(hwnd, WM_CREATE, MPlayer_OnCreate);
|
|
HANDLE_MSG(hwnd, WM_SHOWWINDOW, MPlayer_OnShowWindow);
|
|
HANDLE_MSG(hwnd, WM_SIZE, MPlayer_OnSize);
|
|
HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, MPlayer_OnWindowPosChanging);
|
|
HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, MPlayer_OnWindowPosChanged);
|
|
HANDLE_MSG(hwnd, WM_PALETTECHANGED, MPlayer_OnPaletteChanged);
|
|
HANDLE_MSG(hwnd, WM_QUERYNEWPALETTE, MPlayer_OnQueryNewPalette);
|
|
HANDLE_MSG(hwnd, WM_CTLCOLORSTATIC, MPlayer_OnCtlColor);
|
|
HANDLE_MSG(hwnd, WM_WININICHANGE, MPlayer_OnWinIniChange);
|
|
HANDLE_MPLAYER_WM_MENUSELECT(hwnd, WM_MENUSELECT, MPlayer_OnMenuSelect);
|
|
HANDLE_MSG(hwnd, WM_NCLBUTTONDOWN, MPlayer_OnNCLButtonDown);
|
|
HANDLE_MSG(hwnd, WM_NCLBUTTONDBLCLK, MPlayer_OnNCLButtonDblClk);
|
|
HANDLE_MSG(hwnd, WM_INITMENU, MPlayer_OnInitMenu);
|
|
HANDLE_MSG(hwnd, WM_INITMENUPOPUP, MPlayer_OnInitMenuPopup);
|
|
HANDLE_MSG(hwnd, WM_GETMINMAXINFO, MPlayer_OnGetMinMaxInfo);
|
|
HANDLE_MSG(hwnd, WM_PAINT, MPlayer_OnPaint);
|
|
HANDLE_MSG(hwnd, WM_COMMAND, MPlayer_OnCommand);
|
|
HANDLE_MSG(hwnd, WM_CLOSE, MPlayer_OnClose);
|
|
HANDLE_MSG(hwnd, WM_ENDSESSION, MPlayer_OnEndSession);
|
|
HANDLE_MSG(hwnd, WM_DESTROY, MPlayer_OnDestroy);
|
|
HANDLE_MSG(hwnd, WM_TIMER, MPlayer_OnTimer);
|
|
HANDLE_MSG(hwnd, WM_HSCROLL, MPlayer_OnHScroll);
|
|
HANDLE_MSG(hwnd, WM_SYSCOMMAND, MPlayer_OnSysCommand);
|
|
HANDLE_MSG(hwnd, WM_MOUSEACTIVATE, MPlayer_OnMouseActivate);
|
|
HANDLE_MSG(hwnd, WM_NCHITTEST, MPlayer_OnNCHitTest);
|
|
HANDLE_MSG(hwnd, WM_ACTIVATE, MPlayer_OnActivate);
|
|
HANDLE_MSG(hwnd, WM_SYSCOLORCHANGE, MPlayer_OnSysColorChange);
|
|
HANDLE_MSG(hwnd, WM_DROPFILES, MPlayer_OnDropFiles);
|
|
HANDLE_MSG(hwnd, WM_NOTIFY, MPlayer_OnNotify);
|
|
|
|
/* Other bits of stuff that need tidying up sometime:
|
|
*/
|
|
|
|
case WM_NOMCIDEVICES:
|
|
/* This was posted by the thread building the Device
|
|
* menu to tell us it couldn't find any MCI devices.
|
|
*/
|
|
DisplayNoMciDeviceError();
|
|
PostMessage(ghwndApp, WM_CLOSE, 0, 0);
|
|
break;
|
|
|
|
case WM_GETDIB:
|
|
return (LRESULT)GetDib();
|
|
|
|
case WM_DEVICECHANGE :
|
|
{
|
|
//if plug-and-play sends this, pass it along to the component
|
|
PDEV_BROADCAST_DEVICEINTERFACE bid = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
|
|
|
|
//Check to see if this is a audio message
|
|
if (!MixerEventContext || !bid ||
|
|
bid->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE ||
|
|
!IsEqualGUID(&KSCATEGORY_AUDIO, &bid->dbcc_classguid) ||
|
|
!(*bid->dbcc_name))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
switch(wParam)
|
|
{
|
|
case DBT_DEVICEQUERYREMOVE:
|
|
CloseMCI(TRUE); //Close the MCI device
|
|
break;
|
|
|
|
case DBT_DEVICEREMOVECOMPLETE:
|
|
CloseMCI(TRUE); //Close the MCI device
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
case WM_ENTERSIZEMOVE:
|
|
if (!IsIconic(hwnd) && !gfPlayOnly && !gfOle2IPEditing && !gfOle2IPPlaying)
|
|
{
|
|
/* Save the current window position in x, y, dx, dy format:
|
|
*/
|
|
GetWindowRect(hwnd, (PRECT)&posSizeMove);
|
|
posSizeMove.cx -= posSizeMove.x;
|
|
posSizeMove.cy -= posSizeMove.y;
|
|
}
|
|
break;
|
|
|
|
case WM_EXITSIZEMOVE:
|
|
SetRectEmpty((PRECT)&posSizeMove);
|
|
break;
|
|
|
|
case WM_DOLAYOUT:
|
|
Layout();
|
|
break;
|
|
|
|
case WM_BADREG:
|
|
if ( IDYES == ErrorResBox(hwnd, NULL,
|
|
MB_YESNO | MB_ICONEXCLAMATION, IDS_APPNAME, IDS_BADREG) )
|
|
if (!SetRegValues())
|
|
Error(ghwndApp, IDS_FIXREGERROR);
|
|
break;
|
|
|
|
case WM_SEND_OLE_CHANGE:
|
|
fDocChanged = TRUE;
|
|
SendDocMsg((LPDOC)&docMain,OLE_CHANGED);
|
|
break;
|
|
|
|
case MM_MCINOTIFY:
|
|
#if 0
|
|
//
|
|
// don't do this because, some devices send notify failures
|
|
// where there really is not a error.
|
|
//
|
|
if ((WORD)wParam == MCI_NOTIFY_FAILURE) {
|
|
Error(ghwndApp, IDS_NOTIFYFAILURE);
|
|
}
|
|
#endif
|
|
UpdateDisplay();
|
|
break;
|
|
|
|
#ifdef OLE1_HACK
|
|
/* Actually do the FixLink, SetData and DoVerb we've been putting off */
|
|
/* for so long. */
|
|
case WM_DO_VERB:
|
|
/* This message comes from server.c (and goes back there too) */
|
|
DelayedFixLink(wParam, LOWORD(lParam), HIWORD(lParam)); //OK on NT. LKG
|
|
break;
|
|
#endif /* OLE1_HACK */
|
|
|
|
#ifdef LATER
|
|
// We'll need to call RegisterWindowMessage and provide a message hook proc
|
|
// for this on Win32.
|
|
|
|
case WM_HELP:
|
|
WinHelp(hwnd, TEXT("MPLAYER.HLP"), HELP_PARTIALKEY,
|
|
(DWORD)aszNULL);
|
|
return TRUE;
|
|
#endif /* LATER */
|
|
|
|
case WM_USER_DESTROY:
|
|
DPF("WM_USER_DESTROY received\n");
|
|
|
|
if (gfPlayingInPlace) {
|
|
DPF("****\n");
|
|
DPF("**** Window destroyed while in place!\n");
|
|
DPF("****\n");
|
|
EndPlayInPlace(hwnd);
|
|
}
|
|
|
|
if (gfOle2IPEditing) {
|
|
EndEditInPlace(hwnd);
|
|
}
|
|
|
|
if (!ItsSafeToClose()) {
|
|
DPF("*** \n");
|
|
DPF("*** Trying to destroy MPLAYER with an ErrorBox up\n");
|
|
DPF("*** \n");
|
|
gfErrorDeath = WM_USER_DESTROY;
|
|
return TRUE;
|
|
}
|
|
|
|
if (!gfRunWithEmbeddingFlag)
|
|
WriteOutPosition();
|
|
|
|
DestroyWindow(hwnd);
|
|
DestroyIcon(hiconApp);
|
|
return TRUE;
|
|
|
|
case WM_USER+500:
|
|
/*
|
|
** This message is sent by the HookProc inside mciole32.dll when
|
|
** it detects that it should stop playing in place of a WOW client
|
|
** application.
|
|
**
|
|
** Because the OleActivate originated in mciole16.dll,
|
|
** mciole32.dll does not know the OLE Object that is being
|
|
** played and therefore dose not know how to close that object.
|
|
** Only mplay32.exe has the necessary information, hence
|
|
** mciole32.dll sends this message to mplay32.exe.
|
|
*/
|
|
if (gfPlayingInPlace) {
|
|
EndPlayInPlace(hwnd);
|
|
}
|
|
PostMessage( hwnd, WM_CLOSE, 0L, 0L );
|
|
break;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, wMsg, wParam, lParam);
|
|
}
|
|
|
|
|
|
|
|
/* InitInstance
|
|
* ------------
|
|
*
|
|
* Create brushes used by the program, the main window, and
|
|
* do any other per-instance initialization.
|
|
*
|
|
* HANDLE hInstance
|
|
*
|
|
* RETURNS: TRUE if successful
|
|
* FALSE otherwise.
|
|
*
|
|
* CUSTOMIZATION: Re-implement
|
|
*
|
|
*/
|
|
BOOL InitInstance (HANDLE hInstance)
|
|
{
|
|
HDC hDC;
|
|
|
|
static SZCODE aszNative[] = TEXT("Native");
|
|
static SZCODE aszEmbedSrc[] = TEXT("Embed Source");
|
|
static SZCODE aszObjDesc[] = TEXT("Object Descriptor");
|
|
static SZCODE aszMplayer[] = TEXT("mplayer");
|
|
static SZCODE aszClientDoc[] = TEXT("Client Document");
|
|
|
|
/* Why doesn't RegisterClipboardFormat return a value of type CLIPFORMAT (WORD)
|
|
* instead of UINT?
|
|
*/
|
|
cfNative = (CLIPFORMAT)RegisterClipboardFormat (aszNative);
|
|
cfEmbedSource = (CLIPFORMAT)RegisterClipboardFormat (aszEmbedSrc);
|
|
cfObjectDescriptor = (CLIPFORMAT)RegisterClipboardFormat (aszObjDesc);
|
|
cfMPlayer = (CLIPFORMAT)RegisterClipboardFormat (aszMplayer);
|
|
|
|
szClient[0] = TEXT('\0');
|
|
|
|
lstrcpy (szClientDoc, aszClientDoc);
|
|
|
|
// Initialize global variables with LOGPIXELSX and LOGPIXELSY
|
|
|
|
hDC = GetDC (NULL); // Get the hDC of the desktop window
|
|
giXppli = GetDeviceCaps (hDC, LOGPIXELSX);
|
|
giYppli = GetDeviceCaps (hDC, LOGPIXELSY);
|
|
ReleaseDC (NULL, hDC);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#define COINIT_APARTMENTTHREADED 2
|
|
|
|
/* InitOLE
|
|
*
|
|
* This should be called only when we're certain that OLE is needed,
|
|
* to avoid loading loads of unnecessary stuff.
|
|
*
|
|
*/
|
|
BOOL InitOLE (PBOOL pfInit, LPMALLOC *ppMalloc)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (*pfInit)
|
|
return TRUE;
|
|
|
|
hr = (HRESULT)OleInitialize(NULL);
|
|
|
|
if (!SUCCEEDED (hr))
|
|
{
|
|
DPF0("OleInitialize failed with error 0x%08x\n", hr);
|
|
Error(NULL, IDS_OLEINIT);
|
|
return FALSE;
|
|
}
|
|
|
|
if (ppMalloc && (CoGetMalloc(MEMCTX_TASK, ppMalloc) != S_OK))
|
|
{
|
|
Error(NULL, IDS_OLENOMEM);
|
|
OleUninitialize();
|
|
return FALSE;
|
|
}
|
|
/*****************************************************************
|
|
** OLE2NOTE: we must remember the fact that OleInitialize has
|
|
** been called successfully. the very last thing an app must
|
|
** do is properly shut down OLE by calling
|
|
** OleUninitialize. This call MUST be guarded! it is only
|
|
** allowable to call OleUninitialize if OleInitialize has
|
|
** been called SUCCESSFULLY.
|
|
*****************************************************************/
|
|
|
|
*pfInit = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// This function cleans up all the OLE2 stuff. It lets the container
|
|
// save the object and informs that it is closing.
|
|
BOOL ExitApplication ()
|
|
{
|
|
|
|
DPFI("\n*******Exitapp\n");
|
|
// if we registered class factory, we must revoke it
|
|
if(gfOle2IPEditing || gfOle2IPPlaying)
|
|
DoInPlaceDeactivate((LPDOC)&docMain);
|
|
|
|
SendDocMsg((LPDOC)&docMain,OLE_CLOSED);
|
|
if (srvrMain.fEmbedding) {
|
|
HRESULT status;
|
|
srvrMain.fEmbedding = FALSE; // HACK--guard against revoking twice
|
|
status = (HRESULT)CoRevokeClassObject (srvrMain.dwRegCF);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* DbgGlobalLock
|
|
*
|
|
* Debug wrapper for GlobalLock
|
|
*
|
|
* Checks that the memory handle to be locked isn't already locked,
|
|
* and checks the return code from GlobalLock.
|
|
*
|
|
* andrewbe, 1 March 1995
|
|
*/
|
|
LPVOID DbgGlobalLock(HGLOBAL hglbMem)
|
|
{
|
|
LPVOID lpReturn;
|
|
|
|
if (GlobalFlags(hglbMem) & GMEM_LOCKCOUNT)
|
|
DPF0("Calling GlobalLock on already locked memory object %08x\n", hglbMem);
|
|
|
|
lpReturn = GlobalLock(hglbMem);
|
|
|
|
if (lpReturn == NULL)
|
|
DPF0("GlobalLock(%08x) failed: Error %d\n", hglbMem, GetLastError());
|
|
|
|
return lpReturn;
|
|
}
|
|
|
|
|
|
/* DbgGlobalUnlock
|
|
*
|
|
* Debug wrapper for GlobalUnlock
|
|
*
|
|
* Checks the return code from GlobalUnlock, and outputs appropriate
|
|
* error messages
|
|
*
|
|
* andrewbe, 1 March 1995
|
|
*/
|
|
BOOL DbgGlobalUnlock(HGLOBAL hglbMem)
|
|
{
|
|
BOOL boolReturn;
|
|
|
|
boolReturn = GlobalUnlock(hglbMem);
|
|
|
|
if ((boolReturn) && (GlobalFlags(hglbMem) & GMEM_LOCKCOUNT))
|
|
{
|
|
DPF0("Locks still outstanding on memory object %08x\n", hglbMem);
|
|
}
|
|
else
|
|
{
|
|
DWORD Error = GetLastError();
|
|
|
|
if (Error == ERROR_NOT_LOCKED)
|
|
{
|
|
DPF0("Attempt to unlock already unlocked memory object %08x\n", hglbMem);
|
|
}
|
|
else if (Error != NO_ERROR)
|
|
{
|
|
DPF0("Error %d attempting to unlock memory object %08x\n", Error, hglbMem);
|
|
}
|
|
}
|
|
|
|
return boolReturn;
|
|
}
|
|
|
|
|
|
/* DbgGlobalFree
|
|
*
|
|
* Debug wrapper for GlobalFree.
|
|
*
|
|
* Checks that the global handle has no locks before freeing,
|
|
* then checks that the call succeeded. Error messages output
|
|
* as appropriate.
|
|
*
|
|
* andrewbe, 1 March 1995
|
|
*
|
|
*/
|
|
HGLOBAL DbgGlobalFree(HGLOBAL hglbMem)
|
|
{
|
|
HGLOBAL hglbReturn;
|
|
|
|
if (GlobalFlags(hglbMem) & GMEM_LOCKCOUNT)
|
|
DPF0("Freeing global memory object %08x still locked\n", hglbMem);
|
|
|
|
hglbReturn = GlobalFree(hglbMem);
|
|
|
|
if (hglbReturn != NULL)
|
|
DPF0("GlobalFree(%08x) failed: Error %d\n", hglbMem, GetLastError());
|
|
|
|
return hglbReturn;
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
/* Note: This function assumes that szFormat strings are NOT unicode.
|
|
* Unicode var params may, however, be passed, as long as %ws is specified
|
|
* in the format string.
|
|
*/
|
|
#endif
|
|
void FAR cdecl dprintf(LPSTR szFormat, ...)
|
|
{
|
|
CHAR ach[_MAX_PATH * 3]; // longest I think we need
|
|
int s,d;
|
|
va_list va;
|
|
|
|
va_start(va, szFormat);
|
|
s = wvsprintfA(ach,szFormat, va);
|
|
va_end(va);
|
|
|
|
#if 0
|
|
strcat(ach,"\n");
|
|
s++;
|
|
#endif
|
|
for (d=sizeof(ach)-1; s>=0; s--)
|
|
{
|
|
if ((ach[d--] = ach[s]) == TEXT('\n'))
|
|
ach[d--] = TEXT('\r');
|
|
}
|
|
|
|
/* Not unicode */
|
|
if (*(ach+d+1) != ' ')
|
|
OutputDebugStringA("MPLAYER: ");
|
|
OutputDebugStringA(ach+d+1);
|
|
}
|
|
|
|
#endif
|