540 lines
19 KiB
C
540 lines
19 KiB
C
/*-----------------------------------------------------------------------------+
|
|
| TRACKMAP.C |
|
|
| |
|
|
| This file contains the code that implements the "MPlayerTrackMap" control. |
|
|
| The control displays the list of tracks contained in the current medium, or |
|
|
| a time scale appropriate to the length of the medium, in such a way as to |
|
|
| serve as a scale for the scrollbar. |
|
|
| |
|
|
| (C) Copyright Microsoft Corporation 1991. All rights reserved. |
|
|
| |
|
|
| Revision History |
|
|
| Oct-1992 MikeTri Ported to WIN32 / WIN16 common code |
|
|
| |
|
|
+-----------------------------------------------------------------------------*/
|
|
|
|
/* include files */
|
|
|
|
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
#include "mplayer.h"
|
|
#include "toolbar.h"
|
|
|
|
typedef struct tagScale {
|
|
DWORD dwInterval;
|
|
UINT wScale;
|
|
} SCALE;
|
|
|
|
STATICDT SCALE aScale[] =
|
|
{
|
|
{ 1, SCALE_SECONDS },
|
|
{ 2, SCALE_SECONDS },
|
|
{ 5, SCALE_SECONDS },
|
|
{ 10, SCALE_SECONDS },
|
|
{ 25, SCALE_SECONDS },
|
|
{ 50, SCALE_SECONDS },
|
|
{ 100, SCALE_SECONDS },
|
|
{ 250, SCALE_SECONDS },
|
|
{ 500, SCALE_SECONDS },
|
|
{ 1000, SCALE_SECONDS },
|
|
{ 2000, SCALE_SECONDS },
|
|
{ 5000, SCALE_SECONDS },
|
|
{ 10000, SCALE_SECONDS },
|
|
{ 15000, SCALE_MINUTES },
|
|
{ 30000, SCALE_MINUTES },
|
|
{ 60000, SCALE_MINUTES },
|
|
{ 120000, SCALE_MINUTES },
|
|
{ 300000, SCALE_MINUTES },
|
|
{ 600000, SCALE_HOURS },
|
|
{ 1800000, SCALE_HOURS },
|
|
{ 3600000, SCALE_HOURS },
|
|
{ 7200000, SCALE_HOURS },
|
|
{ 18000000, SCALE_HOURS },
|
|
{ 36000000, SCALE_HOURS },
|
|
{ 72000000, SCALE_HOURS }
|
|
};
|
|
|
|
STATICDT SZCODE aszNULL[] = TEXT("");
|
|
STATICDT SZCODE aszOneDigit[] = TEXT("0");
|
|
STATICDT SZCODE aszTwoDigits[] = TEXT("00");
|
|
STATICDT SZCODE aszPositionFormat[] = TEXT("%0d");
|
|
STATICDT SZCODE aszMSecFormat[] = TEXT("%d");
|
|
STATICDT SZCODE aszHourFormat[] = TEXT("%d%c");
|
|
STATICDT SZCODE aszMinuteFormat[] = TEXT("%d%c");
|
|
STATICDT SZCODE aszSecondFormat[] = TEXT("%d%c");
|
|
STATICDT SZCODE aszSecondFormatNoLzero[] = TEXT("%c");
|
|
STATICDT SZCODE aszDecimalFormat[] = TEXT("%02d");
|
|
/*
|
|
* fnMPlayerTrackMap()
|
|
*
|
|
* This is the window procedure for windows of class "MPlayerTrackMap".
|
|
* This window shows the position of the start of each track of the
|
|
* current medium or a time scale, displayed above the scrollbar which shows
|
|
* the current play position within the medium.
|
|
*
|
|
*/
|
|
|
|
void FAR PASCAL CalcTicsOfDoom(void);
|
|
|
|
extern UINT gwCurScale; /* The current scale in which to draw the track map*/
|
|
extern BOOL gfCurrentCDNotAudio;/* TRUE when we have a CD that we can't play */
|
|
|
|
LRESULT FAR PASCAL fnMPlayerTrackMap(
|
|
|
|
HWND hwnd, /*handle to a MPlayerTrackMap window*/
|
|
UINT wMsg, /* message number */
|
|
WPARAM wParam, /* message-dependent parameter */
|
|
LPARAM lParam) /* message-dependent parameter */
|
|
|
|
{
|
|
PAINTSTRUCT ps; /* paint structure for the window */
|
|
RECT rc, rcSB; /* dimensions of the windows */
|
|
POINT ptExtent; /* extent of the track marks */
|
|
TCHAR szLabel[20]; /* string holding the current label */
|
|
TCHAR szLabel2[20]; /* string holding the current label */
|
|
UINT wNumTics,
|
|
wTicNo,
|
|
wTemp,
|
|
wHour,
|
|
wMin,
|
|
wSec,
|
|
wMsec;
|
|
int iOldPosition = -1000;
|
|
int iNewPosition;
|
|
UINT wScale;
|
|
DWORD dwMarkValue;
|
|
int iLargeMarkSize, iFit, iLastPos, iLen;
|
|
BOOL fForceTextDraw = FALSE;
|
|
HBRUSH hbr;
|
|
|
|
switch (wMsg) {
|
|
|
|
case WM_PAINT:
|
|
|
|
BeginPaint(hwnd, &ps);
|
|
|
|
GetClientRect(ghwndTrackbar, &rcSB);
|
|
GetClientRect(hwnd, &rc);
|
|
|
|
/* Set background and text colours */
|
|
|
|
(VOID)SendMessage(ghwndApp, WM_CTLCOLORSTATIC,
|
|
(WPARAM)ps.hdc, (LONG_PTR)hwnd);
|
|
|
|
/* Get the length of the scrollbar we're putting tics under */
|
|
/* Use these numbers for size and position calculations */
|
|
GetClientRect(ghwndMap, &rc);
|
|
|
|
/*
|
|
* Check to see if we actually have a valid device loaded up;
|
|
* if not, don't display anything
|
|
*
|
|
*/
|
|
|
|
if (gwDeviceID == 0
|
|
|| gwStatus == MCI_MODE_OPEN
|
|
|| gwStatus == MCI_MODE_NOT_READY || gdwMediaLength == 0
|
|
|| !gfValidMediaInfo
|
|
|| gfCurrentCDNotAudio) {
|
|
EndPaint(hwnd,&ps);
|
|
//VIJR-SBSetWindowText(ghwndStatic, aszNULL);
|
|
WriteStatusMessage(ghwndStatic, (LPTSTR)aszNULL);
|
|
return 0L;
|
|
}
|
|
|
|
/* Select the font to use */
|
|
|
|
if (ghfontMap != NULL)
|
|
SelectObject(ps.hdc, ghfontMap);
|
|
|
|
/*
|
|
* Because the scrollbar thumb takes up space in the inner part
|
|
* of the scrollbar, compute its width so that we can compensate
|
|
* for it while displaying the trackmap.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Get the child window rectangle and reduce it such that
|
|
* it is the same width as the inner part of the scrollbar.
|
|
*
|
|
*/
|
|
//rc.left; //!!! GetSystemMetrics(SM_CXHSCROLL);
|
|
//rc.right; //!!!(GetSystemMetrics(SM_CXHSCROLL));
|
|
|
|
/* Now, Put text underneath the TICS */
|
|
if (gwCurScale == ID_TRACKS) {
|
|
|
|
SIZE Size;
|
|
|
|
GetTextExtentPoint32( ps.hdc, aszTwoDigits, 2, &Size );
|
|
|
|
ptExtent.x = Size.cx;
|
|
ptExtent.y = Size.cy;
|
|
|
|
/*
|
|
* Based on the width of the child window, compute the positions
|
|
* to place the track markers.
|
|
*
|
|
*/
|
|
|
|
wNumTics = (UINT)SendMessage(ghwndTrackbar, TBM_GETNUMTICS, 0, 0L);
|
|
|
|
/*
|
|
* TBM_GETNUMTICS returns the number of visible tics
|
|
* which includes the first and last tics not created
|
|
* by media player. Subtract 2 to account for the
|
|
* the first and last tics.
|
|
*
|
|
*/
|
|
|
|
if (wNumTics >= 2)
|
|
wNumTics = wNumTics - 2;
|
|
|
|
for(wTicNo = 0; wTicNo < wNumTics; wTicNo++) {
|
|
|
|
/* Get the position of the next tic */
|
|
iNewPosition = (int)SendMessage(ghwndTrackbar, TBM_GETTICPOS,
|
|
(WPARAM)wTicNo, 0L);
|
|
/* Centre it above the marker. */
|
|
iNewPosition -= ptExtent.x / 4;
|
|
|
|
/*
|
|
* Check to make sure that we are not overwriting the
|
|
* text from the previous marker.
|
|
*
|
|
*/
|
|
|
|
if (iNewPosition > iOldPosition) {
|
|
|
|
wsprintf(szLabel, aszPositionFormat, wTicNo + gwFirstTrack);
|
|
TextOut(ps.hdc,
|
|
iNewPosition + rc.left,
|
|
0, szLabel,
|
|
(wTicNo + gwFirstTrack < 10) ? 1 : 2 );
|
|
/* Finish the end of the text string we just printed */
|
|
iOldPosition = iNewPosition +
|
|
((wTicNo + gwFirstTrack < 10)
|
|
? ptExtent.x / 2 : ptExtent.x);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
#define ONE_HOUR (60ul*60ul*1000ul)
|
|
#define ONE_MINUTE (60ul*1000ul)
|
|
#define ONE_SECOND (1000ul)
|
|
|
|
/*
|
|
* The scale is set to display time - find out what units
|
|
* (msec, sec, min, or hour) are most appropriate, for the
|
|
* scale. This requires us to look at both the overall length
|
|
* of the medium and the distance between markers (or
|
|
* granularity).
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Find the maximum number of markers that we can draw without
|
|
* cluttering the display too badly, and find the granularity
|
|
* between these markers.
|
|
*
|
|
*/
|
|
|
|
SIZE Size;
|
|
|
|
GetTextExtentPoint32( ps.hdc, aszOneDigit, 1, &Size );
|
|
|
|
ptExtent.x = Size.cx;
|
|
ptExtent.y = Size.cy;
|
|
|
|
if (gdwMediaLength < 10)
|
|
iLargeMarkSize = 1;
|
|
else if (gdwMediaLength < 100)
|
|
iLargeMarkSize = 2;
|
|
else if (gdwMediaLength < 1000)
|
|
iLargeMarkSize = 3;
|
|
else if (gdwMediaLength < 10000)
|
|
iLargeMarkSize = 4;
|
|
else
|
|
iLargeMarkSize = 5;
|
|
|
|
wNumTics = (UINT)SendMessage(ghwndTrackbar, TBM_GETNUMTICS,
|
|
0, 0L);
|
|
|
|
/*
|
|
* TBM_GETNUMTICS returns the number of visible tics
|
|
* which includes the first and last tics not created
|
|
* by media player. Subtract 2 to account for the
|
|
* the first and last tics.
|
|
*
|
|
*/
|
|
|
|
if (wNumTics >= 2)
|
|
wNumTics = wNumTics - 2;
|
|
|
|
/* Where the text for the last mark will begin */
|
|
if (wNumTics > 1) {
|
|
iLastPos = (int)SendMessage(ghwndTrackbar,
|
|
TBM_GETTICPOS, (WPARAM)wNumTics - 1, 0L);
|
|
iLastPos -= ptExtent.x / 2; // centre 1st numeral
|
|
}
|
|
|
|
/* What scale do we use? Hours, minutes, or seconds? */
|
|
/* NOTE: THIS MUST AGREE WITH WHAT FormatTime() does */
|
|
/* in mplayer.c !!! */
|
|
if (gwCurScale == ID_FRAMES)
|
|
wScale = SCALE_FRAMES;
|
|
else {
|
|
if (gdwMediaLength > ONE_HOUR)
|
|
wScale = SCALE_HOURS;
|
|
else if (gdwMediaLength > ONE_MINUTE)
|
|
wScale = SCALE_MINUTES;
|
|
else
|
|
wScale = SCALE_SECONDS;
|
|
}
|
|
|
|
for (wTicNo = 0; wTicNo < wNumTics; wTicNo++) {
|
|
|
|
/* The text for the last tic is always drawn */
|
|
if (wTicNo == wNumTics - 1)
|
|
fForceTextDraw = TRUE;
|
|
|
|
dwMarkValue = (DWORD)SendMessage(ghwndTrackbar, TBM_GETTIC,
|
|
(WPARAM)wTicNo, 0L);
|
|
iNewPosition = (int)SendMessage(ghwndTrackbar, TBM_GETTICPOS,
|
|
(WPARAM)wTicNo, 0L);
|
|
|
|
|
|
/*
|
|
* Get the text ready for printing and centre it above the
|
|
* marker.
|
|
*
|
|
*/
|
|
|
|
switch ( wScale ) {
|
|
|
|
case SCALE_FRAMES:
|
|
case SCALE_MSEC:
|
|
wsprintf(szLabel, aszMSecFormat, dwMarkValue);
|
|
break;
|
|
|
|
case SCALE_HOURS:
|
|
|
|
wHour = (WORD)(dwMarkValue / 3600000);
|
|
wMin = (WORD)((dwMarkValue % 3600000) / 60000);
|
|
wsprintf(szLabel2,aszDecimalFormat,wMin);
|
|
wsprintf(szLabel,aszHourFormat,wHour, chTime);
|
|
lstrcat(szLabel,szLabel2);
|
|
break;
|
|
|
|
case SCALE_MINUTES :
|
|
|
|
wMin = (WORD)(dwMarkValue / 60000);
|
|
wSec = (WORD)((dwMarkValue % 60000) / 1000);
|
|
wsprintf(szLabel2,aszDecimalFormat,wSec);
|
|
wsprintf(szLabel,aszMinuteFormat,wMin,chTime);
|
|
lstrcat(szLabel,szLabel2);
|
|
break;
|
|
|
|
case SCALE_SECONDS :
|
|
|
|
wSec = (WORD)((dwMarkValue + 5) / 1000);
|
|
wMsec = (WORD)(((dwMarkValue + 5) % 1000) / 10);
|
|
wsprintf(szLabel2,aszDecimalFormat,wMsec);
|
|
if (!wSec && chLzero == TEXT('0'))
|
|
wsprintf(szLabel, aszSecondFormatNoLzero, chDecimal);
|
|
else
|
|
wsprintf(szLabel, aszSecondFormat, wSec, chDecimal);
|
|
lstrcat(szLabel,szLabel2);
|
|
break;
|
|
|
|
}
|
|
|
|
wTemp = STRLEN(szLabel);
|
|
iNewPosition -= ptExtent.x / 2; // centre 1st numeral
|
|
|
|
/* The position after which text will be cut off the */
|
|
/* right edge of the window */
|
|
iFit = rc.right - rc.left - (ptExtent.x * iLargeMarkSize);
|
|
|
|
/* Calculate the length of the text we just printed. */
|
|
/* Leave a little space at the end, too. */
|
|
iLen = (ptExtent.x * wTemp) + ptExtent.x / 2;
|
|
|
|
/* Display the mark if we can without overlapping either
|
|
* the previous mark or the final mark or going off the
|
|
* edge of the window. */
|
|
if (fForceTextDraw ||
|
|
(iNewPosition >= iOldPosition &&
|
|
iNewPosition <= iFit &&
|
|
iNewPosition + iLen <= iLastPos)) {
|
|
TextOut(ps.hdc, iNewPosition + rc.left, 0,
|
|
szLabel, wTemp );
|
|
/* Calculate the end pos of the text we just printed. */
|
|
iOldPosition = iNewPosition + iLen;
|
|
|
|
} else {
|
|
|
|
DPF("Didn't display mark: iNew = %d; iOld = %d; iFit = %d; iLen = %d, iLast = %d\n", iNewPosition, iOldPosition, iFit, iLen, iLastPos);
|
|
}
|
|
}
|
|
}
|
|
EndPaint(hwnd, &ps);
|
|
return 0L;
|
|
|
|
case WM_ERASEBKGND:
|
|
|
|
GetClientRect(hwnd, &rc);
|
|
|
|
hbr = (HBRUSH)SendMessage(ghwndApp, WM_CTLCOLORSTATIC,
|
|
wParam, (LONG_PTR)hwnd);
|
|
|
|
if (hbr != NULL)
|
|
FillRect((HDC)wParam, &rc, hbr);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Let DefWindowProc() process all other window messages */
|
|
|
|
return DefWindowProc(hwnd, wMsg, wParam, lParam);
|
|
|
|
}
|
|
|
|
/* Gee thanks for the helpful spec for this routine! */
|
|
|
|
void FAR PASCAL CalcTicsOfDoom(void)
|
|
{
|
|
UINT wMarkNo;
|
|
int iTableIndex;
|
|
DWORD dwMarkValue,
|
|
dwNewPosition;
|
|
BOOL fDidLastMark = FALSE;
|
|
|
|
if (gfPlayOnly && !gfOle2IPEditing)
|
|
return;
|
|
|
|
DPF2("CalcTicsOfDoom\n");
|
|
SendMessage(ghwndTrackbar, TBM_CLEARTICS, (WPARAM)FALSE, 0L);
|
|
|
|
if (gwCurScale == ID_TRACKS) {
|
|
|
|
/*
|
|
* Based on the width of the child window, compute the positions
|
|
* to place the track marker tics.
|
|
*
|
|
*/
|
|
|
|
for (wMarkNo = 0; wMarkNo < gwNumTracks; wMarkNo++) {
|
|
|
|
/* If zero length, don't mark it, unless it is the end */
|
|
if ((wMarkNo < gwNumTracks - 1) &&
|
|
(gadwTrackStart[wMarkNo] == gadwTrackStart[wMarkNo + 1]))
|
|
continue;
|
|
|
|
/* Compute the centre point and place a marker there */
|
|
|
|
if (gdwMediaLength == 0)
|
|
dwNewPosition = 0;
|
|
else
|
|
dwNewPosition = gadwTrackStart[wMarkNo];
|
|
|
|
SendMessage(ghwndTrackbar,
|
|
TBM_SETTIC,
|
|
(WPARAM)FALSE,
|
|
(LPARAM)dwNewPosition);
|
|
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* The scale is set to display time - find out what units
|
|
* (msec, sec, min, or hour) are most appropriate, for the
|
|
* scale. This requires us to look at both the overall length
|
|
* of the medium and the distance between markers (or
|
|
* granularity).
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Find the maximum number of markers that we can draw without
|
|
* cluttering the display too badly, and find the granularity
|
|
* between these markers.
|
|
*
|
|
*/
|
|
|
|
UINT wNumTicks;
|
|
RECT rc;
|
|
|
|
if(!GetClientRect(ghwndMap, &rc)) {
|
|
DPF0("GetClientRect failed in CalcTicsOfDoom: Error %d\n", GetLastError());
|
|
}
|
|
|
|
wNumTicks = rc.right / 60;
|
|
|
|
if (0 == gdwMediaLength) {
|
|
iTableIndex = 0;
|
|
} else {
|
|
|
|
DPF4("Checking the scale for media length = %d, tick count = %d\n", gdwMediaLength, wNumTicks);
|
|
|
|
for (iTableIndex = (sizeof(aScale) / sizeof(SCALE)) -1;
|
|
(int)iTableIndex >= 0;
|
|
iTableIndex--) {
|
|
|
|
DPF4("Index %02d: %d\n", aScale[iTableIndex].dwInterval * wNumTicks);
|
|
|
|
if ((aScale[iTableIndex].dwInterval * wNumTicks)
|
|
<= gdwMediaLength)
|
|
break;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
if ((int)iTableIndex == -1) {
|
|
DPF("BAD TABLEINDEX\n");
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
// We have enough room to show every tick. Don't let our index wrap
|
|
// around, or we won't see ANY ticks which would look odd.
|
|
if (iTableIndex <0)
|
|
iTableIndex = 0;
|
|
|
|
dwMarkValue = gdwMediaStart;
|
|
|
|
do {
|
|
|
|
/* Compute the centre point and place a marker there */
|
|
|
|
if (gdwMediaLength == 0)
|
|
dwNewPosition = 0;
|
|
else
|
|
dwNewPosition = dwMarkValue; // HACK!! - gdwMediaStart;
|
|
|
|
SendMessage(ghwndTrackbar,
|
|
TBM_SETTIC,
|
|
(WPARAM)FALSE,
|
|
(LPARAM)dwNewPosition);
|
|
|
|
/* If this is the first mark, adjust so it's going
|
|
/* by the right interval. */
|
|
if (dwMarkValue == gdwMediaStart) {
|
|
dwMarkValue += aScale[iTableIndex].dwInterval
|
|
- (dwMarkValue % aScale[iTableIndex].dwInterval);
|
|
} else {
|
|
dwMarkValue += aScale[iTableIndex].dwInterval;
|
|
}
|
|
|
|
/* If we're almost done, do the final mark. */
|
|
if ((dwMarkValue >= (gdwMediaLength + gdwMediaStart))
|
|
&& !(fDidLastMark)) {
|
|
fDidLastMark = TRUE;
|
|
dwMarkValue = gdwMediaLength + gdwMediaStart;
|
|
}
|
|
} while (dwMarkValue <= gdwMediaStart + gdwMediaLength);
|
|
}
|
|
|
|
InvalidateRect(ghwndTrackbar, NULL, FALSE);
|
|
InvalidateRect(ghwndMap, NULL, TRUE);
|
|
}
|