/*-----------------------------------------------------------------------------+ | 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 #include #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); }