Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4549 lines
129 KiB

  1. /*----------------------------------------------------------------------------*\
  2. *
  3. * MCIWnd
  4. *
  5. *----------------------------------------------------------------------------*/
  6. // This stuff is not going to work 64-bit
  7. #pragma warning(disable:4312)
  8. #include "mciwndi.h"
  9. #ifdef DAYTONA
  10. // Include if debug string is read from registry
  11. #include <profile.h>
  12. #endif
  13. #ifndef _WIN32
  14. // variable args won't work in a DLL with DS!=SS unless _WINDLL is defined
  15. #define WINDLL
  16. #define _WINDLL
  17. #define __WINDLL
  18. #endif
  19. #include <stdarg.h>
  20. #include <stdlib.h>
  21. #if defined CHICAGO && defined _WIN32
  22. #define CHICAGO32
  23. #endif
  24. #define SQUAWKNUMZ(num) #num
  25. #define SQUAWKNUM(num) SQUAWKNUMZ(num)
  26. #define SQUAWK __FILE__ "(" SQUAWKNUM(__LINE__) ") :squawk: "
  27. #ifdef _WIN32
  28. #define MCIWndOpenA(hwnd, sz, f) (LONG)MCIWndSM(hwnd, MCIWNDM_OPENA, (WPARAM)(UINT)(f),(LPARAM)(LPVOID)(sz))
  29. #endif
  30. #if !defined NUMELMS
  31. #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
  32. #endif
  33. #ifdef UNICODE
  34. #include <wchar.h>
  35. //
  36. // Assist with unicode conversions
  37. //
  38. int Iwcstombs(LPSTR lpstr, LPCWSTR lpwstr, int len)
  39. {
  40. return WideCharToMultiByte(GetACP(), 0, lpwstr, -1, lpstr, len, NULL, NULL);
  41. }
  42. int Imbstowcs(LPWSTR lpwstr, LPCSTR lpstr, int len)
  43. {
  44. return MultiByteToWideChar(GetACP(),
  45. MB_PRECOMPOSED,
  46. lpstr,
  47. -1,
  48. lpwstr,
  49. len);
  50. }
  51. #endif
  52. #ifdef CHICAGO
  53. extern BOOL gfIsRTL;
  54. #else
  55. #define gfIsRTL 0
  56. #endif
  57. LRESULT CALLBACK _LOADDS MCIWndProc(HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam);
  58. LRESULT CALLBACK _LOADDS SubClassedTrackbarWndProc(HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam);
  59. STATICFN HBRUSH NEAR PASCAL CreateDitherBrush(void);
  60. STATICFN LRESULT OwnerDraw(PMCIWND p, UINT msg, WPARAM wParam, LPARAM lParam);
  61. STATICFN BOOL NEAR PASCAL mciDialog(HWND hwnd);
  62. STATICFN void NEAR PASCAL MCIWndCopy(PMCIWND p);
  63. BOOL FAR _cdecl _loadds MCIWndRegisterClass(void)
  64. {
  65. WNDCLASS cls;
  66. // !!! We need to register a global class with the hinstance of the DLL
  67. // !!! because it's the DLL that has the code for the window class.
  68. // !!! Otherwise, the class goes away on us and things start to blow!
  69. // !!! HACK HACK HACK The hInstance is the current DS which is the high
  70. // !!! word of the address of all global variables --- sorry NT
  71. #ifndef _WIN32
  72. HINSTANCE hInstance = (HINSTANCE)HIWORD((LPVOID)&hInst); // random global
  73. #else
  74. extern HINSTANCE ghInst; // in video\init.c
  75. HINSTANCE hInstance = ghInst;
  76. #endif
  77. // If we're already registered, we're OK
  78. if (GetClassInfo(hInstance, aszMCIWndClassName, &cls))
  79. return TRUE;
  80. // !!! Save the instance that created the class in a global for cutils.c
  81. // !!! which may need to know this. I know, it's ugly.
  82. hInst = hInstance;
  83. cls.lpszClassName = aszMCIWndClassName;
  84. cls.lpfnWndProc = (WNDPROC)MCIWndProc;
  85. cls.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC;
  86. cls.hCursor = LoadCursor(NULL,IDC_ARROW);
  87. cls.hIcon = NULL;
  88. cls.lpszMenuName = NULL;
  89. cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  90. cls.hInstance = hInstance;
  91. cls.cbClsExtra = 0;
  92. cls.cbWndExtra = sizeof(LPVOID); // big enough for far pointer
  93. if (RegisterClass(&cls)) {
  94. InitCommonControls();
  95. // !!! Other one-time initialization
  96. return TRUE;
  97. }
  98. return FALSE;
  99. }
  100. //
  101. // Create the window
  102. //
  103. //
  104. #ifdef UNICODE
  105. // HACK ALERT:
  106. // (see comments in MCIWndiCreate
  107. // we set a flag to say that the szFile parameter is unicode. Otherwise if
  108. // the parent window is ansi (which is likely) we will try and interpret the
  109. // string as ascii. This is not safe if MCIWndCreate is called on multiple
  110. // threads, but I do not want (currently... let me change my mind) to add
  111. // a critical section to prevent multiple thread access. Unfortunately
  112. // there is no simple way to pass another parameter to MCIWndiCreate as
  113. // the interface goes through the wndproc WM_CREATE message.
  114. BOOL fFileNameIsUnicode = FALSE;
  115. #endif
  116. HWND FAR _cdecl _loadds MCIWndCreate(HWND hwndParent, HINSTANCE hInstance,
  117. DWORD dwStyle, LPCTSTR szFile)
  118. {
  119. HWND hwnd;
  120. int x,y,dx,dy;
  121. DWORD dwStyleEx;
  122. #ifdef _WIN32
  123. #define GetCurrentInstance() GetModuleHandle(NULL);
  124. #else
  125. #define GetCurrentInstance() SELECTOROF(((LPVOID)&hwndParent))
  126. #endif
  127. if (hInstance == NULL)
  128. hInstance = GetCurrentInstance();
  129. if (!MCIWndRegisterClass())
  130. return NULL;
  131. if (HIWORD(dwStyle) == 0)
  132. {
  133. if (hwndParent)
  134. dwStyle |= WS_CHILD | WS_BORDER | WS_VISIBLE;
  135. else
  136. dwStyle |= WS_OVERLAPPEDWINDOW | WS_VISIBLE;
  137. }
  138. // !!! Do we really want to do this?
  139. dwStyle |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
  140. x = y = dy = 0;
  141. dx = STANDARD_WIDTH;
  142. // If we're making a top level window, pick some reasonable position
  143. if (hwndParent == NULL && !(dwStyle & WS_POPUP)) {
  144. x = CW_USEDEFAULT;
  145. // Visible overlapped windows treat y as a ShowWindow flag
  146. if (dwStyle & WS_VISIBLE)
  147. y = SW_SHOW;
  148. }
  149. // Our preview open dialog rips if we don't provide a non-zero ID for a
  150. // child window.
  151. dwStyleEx = gfIsRTL ? WS_EX_LEFTSCROLLBAR | WS_EX_RIGHT | WS_EX_RTLREADING : 0;
  152. #ifdef UNICODE
  153. fFileNameIsUnicode = TRUE;
  154. // strictly... we should enter a critical section here to lock out
  155. // all other MCIWndCreate calls.
  156. #endif
  157. hwnd = CreateWindowEx(dwStyleEx,
  158. aszMCIWndClassName, szNULL, dwStyle,
  159. x, y, dx, dy,
  160. hwndParent,
  161. (HMENU)((dwStyle & WS_CHILD) ? 0x42 : 0),
  162. hInstance, (LPVOID)szFile);
  163. #ifdef UNICODE
  164. fFileNameIsUnicode = FALSE;
  165. #endif
  166. return hwnd;
  167. }
  168. #ifdef UNICODE
  169. // ansi thunk for above function - stub
  170. HWND FAR _cdecl _loadds MCIWndCreateA(HWND hwndParent, HINSTANCE hInstance,
  171. DWORD dwStyle, LPCSTR szFile)
  172. {
  173. WCHAR * lpW;
  174. int sz;
  175. HWND hwnd;
  176. if (szFile == NULL) {
  177. return MCIWndCreateW(hwndParent, hInstance, dwStyle, NULL);
  178. }
  179. sz = lstrlenA(szFile) + 1;
  180. lpW = (WCHAR *) LocalAlloc(LPTR, sizeof(WCHAR) * sz);
  181. if (!lpW) {
  182. return NULL;
  183. }
  184. Imbstowcs(lpW, szFile, sz);
  185. hwnd = MCIWndCreateW(hwndParent, hInstance, dwStyle, lpW);
  186. LocalFree ((HLOCAL)lpW);
  187. return hwnd;
  188. }
  189. #else
  190. #if defined _WIN32
  191. HWND FAR _loadds MCIWndCreateW(HWND hwndParent, HINSTANCE hInstance,
  192. DWORD dwStyle, LPCWSTR szFile)
  193. {
  194. #pragma message (SQUAWK "maybe later add support here")
  195. return NULL;
  196. }
  197. #endif
  198. #endif // UNICODE
  199. //
  200. // Give a notification of something interesting to the proper authorites.
  201. //
  202. STATICFN LRESULT NotifyOwner(PMCIWND p, unsigned msg, WPARAM wParam, LPARAM lParam
  203. )
  204. {
  205. if (p->hwndOwner)
  206. return SendMessage(p->hwndOwner, msg, wParam, lParam);
  207. else
  208. return 0;
  209. }
  210. //
  211. // If an error occured, set our error code and maybe bring up a dialog
  212. // Clears the error code if command was successful.
  213. //
  214. STATICFN void MCIWndiHandleError(PMCIWND p, MCIERROR dw)
  215. {
  216. TCHAR ach[128];
  217. // Set/Clear our error code
  218. p->dwError = dw;
  219. if (dw) {
  220. // We want to bring up a dialog on errors, so do so.
  221. // Don't bring up a dialog while we're moving the thumb around because
  222. // that'll REALLY confuse the mouse capture
  223. if (!(p->dwStyle & MCIWNDF_NOERRORDLG) && !p->fScrolling &&
  224. !p->fTracking) {
  225. mciGetErrorString(p->dwError, ach, NUMELMS(ach));
  226. MessageBox(p->hwnd, ach, LoadSz(IDS_MCIERROR),
  227. MB_ICONEXCLAMATION | MB_OK);
  228. }
  229. // The "owner" wants to know the error. We tell him after we
  230. // bring up the dialog, because otherwise, our VBX never gets this
  231. // event. (Weird...)
  232. if (p->dwStyle & MCIWNDF_NOTIFYERROR) {
  233. NotifyOwner(p, MCIWNDM_NOTIFYERROR, (WPARAM)p->hwnd, p->dwError);
  234. }
  235. }
  236. }
  237. //
  238. // Send an MCI GetDevCaps command and return whether or not it's supported
  239. // This will not set our error code
  240. //
  241. STATICFN BOOL MCIWndDevCaps(PMCIWND p, DWORD item)
  242. {
  243. MCI_GETDEVCAPS_PARMS mciDevCaps;
  244. DWORD dw;
  245. if (p->wDeviceID == 0)
  246. return FALSE;
  247. mciDevCaps.dwItem = (DWORD)item;
  248. dw = mciSendCommand(p->wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM,
  249. (DWORD_PTR)(LPVOID)&mciDevCaps);
  250. if (dw == 0)
  251. return (BOOL)mciDevCaps.dwReturn;
  252. else
  253. return FALSE;
  254. }
  255. //
  256. // Send an MCI Status command.
  257. // This will not set our error code
  258. //
  259. STATICFN DWORD MCIWndStatus(PMCIWND p, DWORD item, DWORD err)
  260. {
  261. MCI_STATUS_PARMS mciStatus;
  262. DWORD dw;
  263. if (p->wDeviceID == 0)
  264. return err;
  265. mciStatus.dwItem = (DWORD)item;
  266. dw = mciSendCommand(p->wDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
  267. (DWORD_PTR)(LPVOID)&mciStatus);
  268. if (dw == 0)
  269. return (DWORD) mciStatus.dwReturn;
  270. else
  271. return err;
  272. }
  273. //
  274. // Send an MCI String command
  275. // Optionally set our error code. Never clears it.
  276. //
  277. STATICFN DWORD MCIWndString(PMCIWND p, BOOL fSetErr, LPTSTR sz, ...)
  278. {
  279. TCHAR ach[MAX_PATH];
  280. int i;
  281. DWORD dw;
  282. va_list va;
  283. if (p->wDeviceID == 0)
  284. return 0;
  285. for (i=0; *sz && *sz != TEXT(' '); )
  286. ach[i++] = *sz++;
  287. i += wsprintf(&ach[i], TEXT(" %d "), (UINT)p->alias);
  288. va_start(va,sz);
  289. i += wvsprintf(&ach[i], sz, va);
  290. va_end(va);
  291. dw = mciSendString(ach, NULL, 0, NULL);
  292. DPF("MCIWndString('%ls'): %ld",(LPTSTR)ach, dw);
  293. if (fSetErr)
  294. MCIWndiHandleError(p, dw);
  295. return dw;
  296. }
  297. STATICFN long mciwnd_atol(LPTSTR sz)
  298. {
  299. long l;
  300. //!!! check for (-) sign?
  301. for (l=0; *sz >= TEXT('0') && *sz <= TEXT('9'); sz++)
  302. l = l*10 + (*sz - TEXT('0'));
  303. return l;
  304. }
  305. #define SLASH(c) ((c) == TEXT('/') || (c) == TEXT('\\'))
  306. /*--------------------------------------------------------------+
  307. | FileName - return a pointer to the filename part of szPath |
  308. | with no preceding path. |
  309. +--------------------------------------------------------------*/
  310. LPTSTR FAR FileName(LPTSTR szPath)
  311. {
  312. LPCTSTR sz;
  313. sz = &szPath[lstrlen(szPath)];
  314. for (; sz>szPath && !SLASH(*sz) && *sz!=TEXT(':');)
  315. sz = CharPrev(szPath, sz);
  316. return (sz>szPath ? (LPTSTR)++sz : (LPTSTR)sz);
  317. }
  318. //
  319. // Sends an MCI String command and converts the return string to an integer
  320. // Optionally sets our error code. Never clears it.
  321. //
  322. STATICFN DWORD MCIWndGetValue(PMCIWND p, BOOL fSetErr, LPTSTR sz, DWORD err, ...)
  323. {
  324. TCHAR achRet[20];
  325. TCHAR ach[MAX_PATH];
  326. DWORD dw;
  327. int i;
  328. va_list va;
  329. for (i=0; *sz && *sz != TEXT(' '); )
  330. ach[i++] = *sz++;
  331. if (p->wDeviceID)
  332. i += wsprintf(&ach[i], TEXT(" %d "), (UINT)p->alias);
  333. va_start(va, err);
  334. i += wvsprintf(&ach[i], sz, va); //!!!use varargs
  335. va_end(va);
  336. dw = mciSendString(ach, achRet, NUMELMS(achRet), NULL);
  337. DPF("MCIWndGetValue('%ls'): %ld",(LPTSTR)ach, dw);
  338. if (fSetErr)
  339. MCIWndiHandleError(p, dw);
  340. if (dw == 0) {
  341. DPF("GetValue('%ls'): %ld",(LPTSTR)ach, mciwnd_atol(achRet));
  342. return mciwnd_atol(achRet);
  343. } else {
  344. DPF("MCIGetValue('%ls'): error=%ld",(LPTSTR)ach, dw);
  345. return err;
  346. }
  347. }
  348. //
  349. // Send an MCI command and get the return string back
  350. // This never sets our error code.
  351. //
  352. // Note: szRet can be the same string as sz
  353. //
  354. STATICFN DWORD MCIWndGet(PMCIWND p, LPTSTR sz, LPTSTR szRet, int len, ...)
  355. {
  356. TCHAR ach[MAX_PATH];
  357. int i;
  358. DWORD dw;
  359. va_list va;
  360. if (!p->wDeviceID) {
  361. szRet[0] = 0;
  362. return 0L;
  363. }
  364. for (i=0; *sz && *sz != TEXT(' '); )
  365. ach[i++] = *sz++;
  366. i += wsprintf(&ach[i], TEXT(" %d "), (UINT)p->alias);
  367. va_start(va, len);
  368. i += wvsprintf(&ach[i], sz, va); //!!!use varargs
  369. va_end(va);
  370. // initialize to NULL return string
  371. szRet[0] = 0;
  372. dw = mciSendString(ach, szRet, len, p->hwnd);
  373. DPF("MCIWndGet('%ls'): '%ls'",(LPTSTR)ach, (LPTSTR)szRet);
  374. return dw;
  375. }
  376. //
  377. // Gets the source or destination rect from the MCI device
  378. // Does NOT set our error code since this is an internal function
  379. //
  380. STATICFN void MCIWndRect(PMCIWND p, LPRECT prc, BOOL fSource)
  381. {
  382. MCI_DGV_RECT_PARMS mciRect;
  383. DWORD dw=0;
  384. SetRectEmpty(prc);
  385. if (p->wDeviceID)
  386. dw = mciSendCommand(p->wDeviceID, MCI_WHERE,
  387. (DWORD)fSource ? MCI_DGV_WHERE_SOURCE : MCI_DGV_WHERE_DESTINATION,
  388. (DWORD_PTR)(LPVOID)&mciRect);
  389. if (dw == 0)
  390. *prc = mciRect.rc;
  391. prc->right += prc->left;
  392. prc->bottom += prc->top;
  393. }
  394. STATICFN VOID MCIWndiSizePlaybar(PMCIWND p)
  395. {
  396. RECT rc;
  397. UINT w, h;
  398. // No playbar!!
  399. if (p->dwStyle & MCIWNDF_NOPLAYBAR)
  400. return;
  401. #define SLOP 0 // Left outdent of toolbar
  402. // How big a window are we putting a toolbar on?
  403. GetClientRect(p->hwnd, &rc);
  404. w = rc.right;
  405. h = rc.bottom;
  406. SetWindowPos(p->hwndToolbar, NULL,
  407. -SLOP, h - TB_HEIGHT, w + SLOP, TB_HEIGHT,
  408. SWP_NOZORDER);
  409. // Make sure it's visible now
  410. ShowWindow(p->hwndToolbar, SW_SHOW);
  411. // Figure out where the toolbar ends and the trackbar begins
  412. SendMessage(p->hwndToolbar, TB_GETITEMRECT,
  413. (int)SendMessage(p->hwndToolbar, TB_COMMANDTOINDEX,
  414. TOOLBAR_END, 0),
  415. (LPARAM)(LPVOID)&rc);
  416. // Place the trackbar next to the end of the toolbar
  417. SetWindowPos(p->hwndTrackbar, HWND_TOP, rc.right,
  418. h - TB_HEIGHT + 2,
  419. w - rc.right, TB_HEIGHT, // !!!
  420. 0);
  421. //!!! Maybe put menu button on right side of trackbar? So
  422. //!!! make sep the right size (size of the track bar!)
  423. }
  424. // Resize the window by the given percentage
  425. // 0 means use DESTINATION rect and size it automatically
  426. STATICFN VOID MCIWndiSize(PMCIWND p, int iSize)
  427. {
  428. RECT rc, rcT;
  429. int dx, dy;
  430. // If we're given a percentage, we take it from the SOURCE size.
  431. // For default, (zero), we use the destination size
  432. if (iSize)
  433. rc = p->rcNormal; /* get the original "normal size" rect */
  434. else {
  435. if (p->wDeviceID)
  436. MCIWndRect(p, &rc, FALSE);/* get the current (destination) size */
  437. else
  438. SetRect(&rc, 0, 0, 0, 0);
  439. iSize = 100;
  440. }
  441. rc.bottom = MulDiv(rc.bottom, iSize, 100);
  442. rc.right = MulDiv(rc.right, iSize, 100);
  443. // Now set the movie to play in the new rect
  444. if (!IsRectEmpty(&rc))
  445. MCIWndString(p, FALSE, szPutDest,
  446. 0, 0, rc.right - rc.left, rc.bottom - rc.top);
  447. // If we're not supposed to resize our window to this new rect, at least
  448. // we'll fix up the toolbar before we leave (the buttons may have changed)
  449. if (p->dwStyle & MCIWNDF_NOAUTOSIZEWINDOW) {
  450. MCIWndiSizePlaybar(p);
  451. return;
  452. }
  453. // We're not a windowed device, or we're closed - don't touch our width
  454. if (IsRectEmpty(&rc)) {
  455. GetClientRect(p->hwnd, &rcT);
  456. rc.right = rcT.right;
  457. }
  458. // If we will have a playbar, grow the window by its height
  459. if (!(p->dwStyle & MCIWNDF_NOPLAYBAR))
  460. rc.bottom += TB_HEIGHT;
  461. // Now get the size for our window by growing it by its non-client size
  462. AdjustWindowRect(&rc, GetWindowLong(p->hwnd, GWL_STYLE), FALSE);
  463. // Now we have the new size for our MCIWND. If the SetWindowPos didn't
  464. // actually result in changing its size, it will not generate a WM_SIZE
  465. // and it won't fix the toolbar or set the dest rect correctly, so we'll
  466. // have to call a WM_SIZE ourselves. It's not enough to check the size
  467. // we are trying to make it, because it may not give us the size we want
  468. // (WM_GETMINMAXINFO) so we have to compare the original size and the
  469. // ultimate size.
  470. // Sometimes if it only changes by one pixel, it STILL won't generate a
  471. // WM_SIZE.
  472. GetWindowRect(p->hwnd, &rcT);
  473. SetWindowPos(p->hwnd, NULL, 0, 0, rc.right - rc.left,
  474. rc.bottom - rc.top,
  475. SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
  476. GetWindowRect(p->hwnd, &rc);
  477. dx = ABS((rcT.right - rcT.left) - (rc.right - rc.left));
  478. dy = ABS((rcT.bottom - rcT.top) - (rc.bottom - rc.top));
  479. if (dx < 2 && dy < 2) {
  480. PostMessage(p->hwnd, WM_SIZE, 0, 0);
  481. }
  482. }
  483. //
  484. // Figure out the position in ms of the beginning of the track we're on
  485. //
  486. STATICFN DWORD MCIWndiPrevTrack(PMCIWND p)
  487. {
  488. DWORD dw;
  489. int iTrack;
  490. if (!p->fHasTracks)
  491. return 0;
  492. MCIWndString(p, FALSE, szSetFormatTMSF);
  493. dw = MCIWndStatus(p, MCI_STATUS_POSITION, 0); // return value is 0xFFSSMMTT
  494. iTrack = LOWORD(dw) & 0xFF;
  495. // If we're less than 1 second into the track, choose the previous track
  496. if ((iTrack > p->iFirstTrack) && (!(LOWORD(dw) & 0xFF00)) &&
  497. ((HIWORD(dw) & 0xFF) == 0))
  498. iTrack--;
  499. dw = p->pTrackStart[iTrack - p->iFirstTrack];
  500. MCIWndString(p, FALSE, szSetFormatMS);
  501. return dw;
  502. }
  503. //
  504. // Figure out the position in ms of the beginning of the next track
  505. //
  506. STATICFN DWORD MCIWndiNextTrack(PMCIWND p)
  507. {
  508. DWORD dw;
  509. int iTrack;
  510. if (!p->fHasTracks)
  511. return 0;
  512. MCIWndString(p, FALSE, szSetFormatTMSF);
  513. dw = MCIWndStatus(p, MCI_STATUS_POSITION, 0); // return value is 0xTTMMSSFF
  514. iTrack = (LOWORD(dw) & 0xFF) + 1;
  515. if (iTrack >= p->iNumTracks + p->iFirstTrack)
  516. iTrack--;
  517. dw = p->pTrackStart[iTrack - p->iFirstTrack];
  518. MCIWndString(p, FALSE, szSetFormatMS);
  519. return dw;
  520. }
  521. //
  522. // Figure out where the tracks begin for making tics
  523. //
  524. STATICFN void MCIWndiCalcTracks(PMCIWND p)
  525. {
  526. int i;
  527. if (!p->fHasTracks)
  528. return;
  529. p->iNumTracks = (int)MCIWndGetValue(p, FALSE, szStatusNumTracks, 0);
  530. p->iFirstTrack = MCIWndGetValue(p, FALSE, szStatusPosTrack, 0, 0) == 0
  531. ? 1 : 0;
  532. if (p->pTrackStart)
  533. LocalFree((HANDLE)p->pTrackStart);
  534. if (p->iNumTracks) {
  535. p->pTrackStart = (LONG *)LocalAlloc(LPTR,
  536. p->iNumTracks * sizeof(LONG));
  537. if (p->pTrackStart == NULL) {
  538. p->iNumTracks = 0;
  539. p->fHasTracks = FALSE;
  540. }
  541. for (i = 0; i < p->iNumTracks; i++) {
  542. p->pTrackStart[i] =
  543. MCIWndGetValue(p, FALSE, szStatusPosTrack, 0,
  544. p->iFirstTrack + i);
  545. }
  546. }
  547. }
  548. //
  549. // Mark tics on the trackbar for the beginning of tracks
  550. //
  551. STATICFN void MCIWndiMarkTics(PMCIWND p)
  552. {
  553. int i;
  554. if (!p->fHasTracks)
  555. return;
  556. SendMessage(p->hwndTrackbar, TBM_SETTIC, 0, p->dwMediaStart);
  557. for (i = 0; i < p->iNumTracks; i++) {
  558. SendMessage(p->hwndTrackbar, TBM_SETTIC, 0, p->pTrackStart[i]);
  559. }
  560. SendMessage(p->hwndTrackbar, TBM_SETTIC,0, p->dwMediaStart + p->dwMediaLen);
  561. }
  562. STATICFN VOID MCIWndiValidateMedia(PMCIWND p)
  563. {
  564. DWORD dw;
  565. if (!p->wDeviceID) {
  566. p->fMediaValid = FALSE;
  567. return;
  568. }
  569. dw = p->dwMediaLen;
  570. p->fMediaValid = TRUE;
  571. p->dwMediaStart = MCIWndGetStart(p->hwnd);
  572. p->dwMediaLen = MCIWndGetLength(p->hwnd);
  573. // !!! do something special if len=0?
  574. // We have a playbar, so set the ranges of the trackbar if we've changed
  575. if (dw != p->dwMediaLen && !(p->dwStyle & MCIWNDF_NOPLAYBAR)) {
  576. // must set position first or zero length range won't move thumb
  577. SendMessage(p->hwndTrackbar, TBM_CLEARTICS, TRUE, 0);
  578. SendMessage(p->hwndTrackbar, TBM_SETPOS, TRUE, p->dwMediaStart);
  579. SendMessage(p->hwndTrackbar, TBM_SETRANGEMIN, 0, p->dwMediaStart);
  580. SendMessage(p->hwndTrackbar, TBM_SETRANGEMAX, 0,
  581. p->dwMediaStart + p->dwMediaLen);
  582. MCIWndiCalcTracks(p);
  583. MCIWndiMarkTics(p);
  584. }
  585. }
  586. //
  587. // Create the filter for the open dialog. Caution: Don't overflow pchD !!!
  588. //
  589. STATICFN void MCIWndiBuildMeAFilter(LPTSTR pchD)
  590. {
  591. LPTSTR pchS;
  592. TCHAR ach[128];
  593. // Our filter will look like: "MCI Files\0*.avi;*.wav\0All Files\0*.*\0"
  594. // The actual extensions for the MCI files will come from the list in
  595. // the "mci extensions" section of win.ini
  596. lstrcpy(pchD, LoadSz(IDS_MCIFILES));
  597. // Creates a list like: "avi\0wav\0mid\0"
  598. GetProfileString(szMCIExtensions, NULL, szNULL, ach, NUMELMS(ach));
  599. for (pchD += lstrlen(pchD)+1, pchS = ach; *pchS;
  600. pchD += lstrlen(pchS)+3, pchS += lstrlen(pchS)+1) {
  601. lstrcpy(pchD, TEXT("*."));
  602. lstrcpy(pchD + 2, pchS);
  603. lstrcpy(pchD + 2 + lstrlen(pchS), TEXT(";"));
  604. }
  605. if (pchS != ach)
  606. --pchD; // erase the last ;
  607. *pchD = TEXT('\0');
  608. lstrcpy(++pchD, LoadSz(IDS_ALLFILES));
  609. pchD += lstrlen(pchD) + 1;
  610. lstrcpy(pchD, TEXT("*.*\0"));
  611. }
  612. //
  613. // Create the playbar windows we'll need later
  614. //
  615. STATICFN void MCIWndiMakeMeAPlaybar(PMCIWND p)
  616. {
  617. TBBUTTON tb[8];
  618. extern HINSTANCE ghInst; // in video\init.c
  619. DWORD dwStyleEx;
  620. // They don't want a playbar
  621. if (p->dwStyle & MCIWNDF_NOPLAYBAR)
  622. return;
  623. #define MENUSEP 2
  624. tb[0].iBitmap = MENUSEP;
  625. tb[0].idCommand = -1;
  626. tb[0].fsState = 0;
  627. tb[0].fsStyle = TBSTYLE_SEP;
  628. tb[0].iString = -1;
  629. tb[1].iBitmap = 0;
  630. tb[1].idCommand = MCI_PLAY;
  631. tb[1].fsState = TBSTATE_ENABLED | TBSTATE_HIDDEN;
  632. tb[1].fsStyle = TBSTYLE_BUTTON;
  633. tb[1].iString = -1;
  634. tb[2].iBitmap = 2;
  635. tb[2].idCommand = MCI_STOP;
  636. tb[2].fsState = TBSTATE_ENABLED | TBSTATE_HIDDEN;
  637. tb[2].fsStyle = TBSTYLE_BUTTON;
  638. tb[2].iString = -1;
  639. tb[3].iBitmap = 4;
  640. tb[3].idCommand = MCI_RECORD;
  641. tb[3].fsState = TBSTATE_ENABLED | TBSTATE_HIDDEN;
  642. tb[3].fsStyle = TBSTYLE_BUTTON;
  643. tb[3].iString = -1;
  644. tb[4].iBitmap = 5;
  645. tb[4].idCommand = IDM_MCIEJECT;
  646. tb[4].fsState = TBSTATE_ENABLED | TBSTATE_HIDDEN;
  647. tb[4].fsStyle = TBSTYLE_BUTTON;
  648. tb[4].iString = -1;
  649. tb[5].iBitmap = MENUSEP;
  650. tb[5].idCommand = -1;
  651. tb[5].fsState = 0;
  652. tb[5].fsStyle = TBSTYLE_SEP;
  653. tb[5].iString = -1;
  654. tb[6].iBitmap = 3;
  655. tb[6].idCommand = IDM_MENU;
  656. tb[6].fsState = TBSTATE_ENABLED;
  657. tb[6].fsStyle = TBSTYLE_BUTTON;
  658. tb[6].iString = -1;
  659. tb[7].iBitmap = 4;
  660. tb[7].idCommand = TOOLBAR_END;
  661. tb[7].fsState = 0;
  662. tb[7].fsStyle = TBSTYLE_SEP;
  663. tb[7].iString = -1;
  664. // if (p->hbmToolbar)
  665. // DeleteObject(p->hbmToolbar);
  666. // Must use DLL's ghInst to get Bitmap
  667. // p->hbmToolbar = LoadBitmap(ghInst, MAKEINTRESOURCE(IDBMP_TOOLBAR));
  668. // Create invisible for now so it doesn't flash
  669. p->hwndToolbar = CreateToolbarEx(p->hwnd, TBSTYLE_BUTTON | TBSTYLE_TOOLTIPS
  670. | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
  671. CCS_NOPARENTALIGN | CCS_NORESIZE,
  672. ID_TOOLBAR, 8,
  673. // NULL, (UINT)p->hbmToolbar,
  674. ghInst, IDBMP_TOOLBAR,
  675. (LPTBBUTTON)&tb[0], 8,
  676. 13, 13, 13, 13, sizeof(TBBUTTON)); // buttons are 13x13
  677. dwStyleEx = gfIsRTL ? WS_EX_LEFTSCROLLBAR | WS_EX_RIGHT | WS_EX_RTLREADING : 0;
  678. p->hwndTrackbar = CreateWindowEx(dwStyleEx,
  679. TRACKBAR_CLASS, NULL,
  680. WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
  681. 0, 0, 0, 0, p->hwnd, NULL, GetWindowInstance(p->hwnd), NULL);
  682. // The trackbar eats key presses so we need to sub class it to see when
  683. // CTRL-1, CTRL-2, etc. are pressed
  684. fnTrackbarWndProc = (WNDPROC)GetWindowLongPtr(p->hwndTrackbar, GWLP_WNDPROC);
  685. SetWindowLongPtr(p->hwndTrackbar, GWLP_WNDPROC,
  686. (LONG_PTR)SubClassedTrackbarWndProc);
  687. // Force ValidateMedia to actually update
  688. p->dwMediaStart = p->dwMediaLen = 0;
  689. // Set the proper range for the scrollbar
  690. MCIWndiValidateMedia(p);
  691. }
  692. //
  693. // Gray/ungray toolbar buttons as necessary
  694. //
  695. STATICFN void MCIWndiPlaybarGraying(PMCIWND p)
  696. {
  697. DWORD dwMode;
  698. if (!(p->dwStyle & MCIWNDF_NOPLAYBAR)) {
  699. dwMode = MCIWndGetMode(p->hwnd, NULL, 0);
  700. if (dwMode == MCI_MODE_PLAY) {
  701. // Hide PLAY Show STOP
  702. SendMessage(p->hwndToolbar, TB_HIDEBUTTON,
  703. MCI_PLAY, TRUE);
  704. SendMessage(p->hwndToolbar, TB_HIDEBUTTON,
  705. MCI_STOP, FALSE);
  706. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  707. MCI_STOP, TRUE);
  708. if (p->fCanRecord)
  709. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  710. MCI_RECORD, FALSE); // !!! can't record ???
  711. if (p->fCanEject)
  712. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  713. IDM_MCIEJECT, TRUE);
  714. // Treat PAUSE mode like STOP mode
  715. } else if (dwMode == MCI_MODE_PAUSE ||
  716. dwMode == MCI_MODE_STOP) {
  717. // Hide STOP Show PLAY
  718. SendMessage(p->hwndToolbar, TB_HIDEBUTTON,
  719. MCI_STOP, TRUE);
  720. if (p->fCanPlay) {
  721. SendMessage(p->hwndToolbar, TB_HIDEBUTTON,
  722. MCI_PLAY, FALSE);
  723. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  724. MCI_PLAY, TRUE);
  725. }
  726. if (p->fCanRecord)
  727. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  728. MCI_RECORD, TRUE);
  729. if (p->fCanEject)
  730. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  731. IDM_MCIEJECT, TRUE);
  732. } else if (dwMode == MCI_MODE_RECORD) {
  733. // Hide PLAY Show STOP
  734. SendMessage(p->hwndToolbar, TB_HIDEBUTTON,
  735. MCI_PLAY, TRUE);
  736. SendMessage(p->hwndToolbar, TB_HIDEBUTTON,
  737. MCI_STOP, FALSE);
  738. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  739. MCI_STOP, TRUE);
  740. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  741. MCI_RECORD, FALSE);
  742. if (p->fCanEject)
  743. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  744. IDM_MCIEJECT, TRUE); // !!! safe ???
  745. // recording can change the length
  746. p->fMediaValid = FALSE;
  747. } else if (dwMode == MCI_MODE_SEEK) {
  748. // Hide PLAY Show STOP
  749. SendMessage(p->hwndToolbar, TB_HIDEBUTTON,
  750. MCI_PLAY, TRUE);
  751. SendMessage(p->hwndToolbar, TB_HIDEBUTTON,
  752. MCI_STOP, FALSE);
  753. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  754. MCI_STOP, TRUE);
  755. if (p->fCanRecord)
  756. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  757. MCI_RECORD, FALSE);
  758. if (p->fCanEject)
  759. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  760. IDM_MCIEJECT, FALSE);
  761. } else {
  762. // OPEN, NOT_READY, etc. etc.
  763. // Disable everything
  764. if (p->fCanPlay) {
  765. SendMessage(p->hwndToolbar, TB_HIDEBUTTON,
  766. MCI_PLAY, FALSE);
  767. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  768. MCI_PLAY, FALSE);
  769. }
  770. SendMessage(p->hwndToolbar, TB_HIDEBUTTON,
  771. MCI_STOP, TRUE);
  772. if (p->fCanRecord)
  773. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  774. MCI_RECORD, FALSE);
  775. if (p->fCanEject)
  776. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON,
  777. IDM_MCIEJECT, FALSE);
  778. // Clear all tics
  779. SendMessage(p->hwndTrackbar, TBM_CLEARTICS,1,0);
  780. // Clean out the trackbar
  781. // Make a note to re-query start, length later
  782. SendMessage(p->hwndTrackbar, TBM_SETPOS,
  783. TRUE, 0); // set b4 range
  784. SendMessage(p->hwndTrackbar, TBM_SETRANGE,
  785. 0, 0);
  786. p->fMediaValid = FALSE;
  787. }
  788. }
  789. }
  790. //
  791. // Set up the toolbar to have the right buttons
  792. //
  793. STATICFN void MCIWndiFixMyPlaybar(PMCIWND p)
  794. {
  795. if (p->dwStyle & MCIWNDF_NOPLAYBAR)
  796. return;
  797. if (!p->wDeviceID) {
  798. //
  799. // gray the toolbar, go to some default buttons, and set zero len track
  800. //
  801. if (!(p->dwStyle & MCIWNDF_NOPLAYBAR)) {
  802. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, MCI_PLAY, FALSE);
  803. SendMessage(p->hwndToolbar, TB_ENABLEBUTTON, MCI_PLAY, FALSE);
  804. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, MCI_STOP, TRUE );
  805. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, MCI_RECORD, TRUE );
  806. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, IDM_MCIEJECT,TRUE );
  807. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, IDM_MENU,
  808. p->dwStyle & MCIWNDF_NOMENU);
  809. SendMessage(p->hwndTrackbar, TBM_SETPOS, TRUE, 0); // set b4 range
  810. SendMessage(p->hwndTrackbar, TBM_SETRANGE, 0, 0);
  811. }
  812. }
  813. if (p->wDeviceID) {
  814. //
  815. // Use the appropriate buttons
  816. //
  817. if (p->fCanPlay)
  818. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, MCI_PLAY, FALSE);
  819. else
  820. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, MCI_PLAY, TRUE);
  821. if (p->fCanRecord && (p->dwStyle & MCIWNDF_RECORD))
  822. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, MCI_RECORD, FALSE);
  823. else
  824. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, MCI_RECORD, TRUE);
  825. if (p->fCanEject)
  826. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, IDM_MCIEJECT, FALSE);
  827. else
  828. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, IDM_MCIEJECT, TRUE);
  829. SendMessage(p->hwndToolbar, TB_HIDEBUTTON, IDM_MENU,
  830. p->dwStyle & MCIWNDF_NOMENU);
  831. // COMMCTRL toolbar bug ... re-arranging buttons screws up the state
  832. // of the existing buttons, so we better re-gray things
  833. MCIWndiPlaybarGraying(p);
  834. }
  835. }
  836. //
  837. // Make an appropriate menu
  838. //
  839. STATICFN void MCIWndiMakeMeAMenu(PMCIWND p)
  840. {
  841. HMENU hmenu, hmenuWindow = NULL, hmenuVolume = NULL, hmenuSpeed = NULL;
  842. int i;
  843. WORD j;
  844. //
  845. // Create the floating popup menu BY HAND since we have no resource file
  846. //
  847. // Destroy the old menu
  848. if (p->hmenu) {
  849. DestroyMenu(p->hmenu);
  850. if (p->hbrDither) {
  851. DeleteObject(p->hbrDither);
  852. p->hbrDither = NULL;
  853. }
  854. }
  855. p->hmenu = NULL;
  856. p->hmenuVolume = NULL;
  857. p->hmenuSpeed = NULL;
  858. // We don't want a menu!
  859. if (p->dwStyle & MCIWNDF_NOMENU)
  860. return;
  861. //
  862. // If we don't want an open command, and nothing's open, don't make
  863. // a menu.
  864. //
  865. if (!p->wDeviceID && (p->dwStyle & MCIWNDF_NOOPEN))
  866. return;
  867. //
  868. // Create the WINDOW sub-popup
  869. // !!! Do we want to have this menu if an AUTOSIZE flag is off?
  870. //
  871. if (p->wDeviceID && p->fCanWindow) {
  872. hmenuWindow = CreatePopupMenu();
  873. if (hmenuWindow) {
  874. AppendMenu(hmenuWindow, MF_ENABLED, IDM_MCIZOOM+50,
  875. LoadSz(IDS_HALFSIZE));
  876. AppendMenu(hmenuWindow, MF_ENABLED, IDM_MCIZOOM+100,
  877. LoadSz(IDS_NORMALSIZE));
  878. AppendMenu(hmenuWindow, MF_ENABLED, IDM_MCIZOOM+200,
  879. LoadSz(IDS_DOUBLESIZE));
  880. }
  881. }
  882. //
  883. // Create the VOLUME sub-popup
  884. //
  885. if (p->wDeviceID && p->fVolume) {
  886. hmenuVolume = CreatePopupMenu();
  887. if (hmenuVolume) {
  888. // !!! Hack
  889. // Put a bad menu item at the top. When WINDOWS tries to select
  890. // it after we bring up the menu, we won't let it. We want the
  891. // thumb to stay on the current value.
  892. AppendMenu(hmenuVolume, MF_ENABLED | MF_OWNERDRAW,
  893. IDM_MCIVOLUME + VOLUME_MAX + 1, NULL);
  894. // Create all the Real menu items. Make the menu VOLUME_MAX items
  895. // tall even though the number of unique entries may be less
  896. for (i=IDM_MCIVOLUME + p->wMaxVol; i>=IDM_MCIVOLUME; i-=5)
  897. for (j=0; j < VOLUME_MAX / p->wMaxVol; j++)
  898. AppendMenu(hmenuVolume, MF_ENABLED | MF_OWNERDRAW, i, NULL);
  899. // Now put a filler item at the bottom so every REAL item falls
  900. // inside the channel and there's a unique thumb position for each
  901. // item.
  902. AppendMenu(hmenuVolume, MF_ENABLED | MF_OWNERDRAW,
  903. IDM_MCIVOLUME + VOLUME_MAX + 2, NULL);
  904. // Now CHECK the current volume so the thumb can draw there
  905. // round to nearest 5 so it matches a menu item identifier
  906. i = ((int)MCIWndGetValue(p, FALSE, szStatusVolume, 1000) / 50) * 5;
  907. CheckMenuItem(hmenuVolume, IDM_MCIVOLUME + i, MF_CHECKED);
  908. }
  909. }
  910. //
  911. // Create the SPEED sub-popup
  912. //
  913. if (p->wDeviceID && p->fSpeed) {
  914. hmenuSpeed = CreatePopupMenu();
  915. if (hmenuSpeed) {
  916. // !!! Hack from Hell
  917. // Put a bogus menu item at the top. When WINDOWS tries to select
  918. // it after we bring up the menu, we won't let it. We want the
  919. // thumb to stay on the current value.
  920. AppendMenu(hmenuSpeed, MF_ENABLED | MF_OWNERDRAW,
  921. IDM_MCISPEED + SPEED_MAX + 1, NULL);
  922. // Create all the Real menu items
  923. for (i=IDM_MCISPEED + SPEED_MAX; i>=IDM_MCISPEED; i-=5)
  924. AppendMenu(hmenuSpeed, MF_ENABLED | MF_OWNERDRAW, i, NULL);
  925. // Now put a filler item at the bottom so every REAL item falls
  926. // inside the channel and there's a unique thumb position for each
  927. // item.
  928. AppendMenu(hmenuSpeed, MF_ENABLED | MF_OWNERDRAW,
  929. IDM_MCISPEED + SPEED_MAX + 2, NULL);
  930. // Now CHECK the current speed so the thumb can draw there
  931. // round to nearest 5 so it matches a menu item identifier
  932. i = ((int)MCIWndGetValue(p, FALSE, szStatusSpeed, 1000) / 50) * 5;
  933. CheckMenuItem(hmenuSpeed, IDM_MCISPEED + i, MF_CHECKED);
  934. }
  935. }
  936. hmenu = CreatePopupMenu();
  937. if (hmenu) {
  938. if (p->wDeviceID && p->dwStyle & MCIWNDF_NOPLAYBAR) {
  939. if (p->fCanPlay) {
  940. AppendMenu(hmenu, MF_ENABLED, MCI_PLAY, LoadSz(IDS_PLAY));
  941. AppendMenu(hmenu, MF_ENABLED, MCI_STOP, LoadSz(IDS_STOP));
  942. }
  943. if (p->fCanRecord && (p->dwStyle & MCIWNDF_RECORD))
  944. AppendMenu(hmenu, MF_ENABLED, MCI_RECORD, LoadSz(IDS_RECORD));
  945. if (p->fCanEject)
  946. AppendMenu(hmenu, MF_ENABLED, IDM_MCIEJECT, LoadSz(IDS_EJECT));
  947. if (p->fCanPlay ||
  948. (p->fCanRecord && (p->dwStyle & MCIWNDF_RECORD)) ||
  949. p->fCanEject)
  950. AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
  951. }
  952. if (hmenuWindow)
  953. AppendMenu(hmenu, MF_ENABLED|MF_POPUP, (UINT_PTR)hmenuWindow,
  954. LoadSz(IDS_VIEW));
  955. if (hmenuVolume)
  956. AppendMenu(hmenu, MF_ENABLED|MF_POPUP, (UINT_PTR)hmenuVolume,
  957. LoadSz(IDS_VOLUME));
  958. if (hmenuSpeed)
  959. AppendMenu(hmenu, MF_ENABLED|MF_POPUP, (UINT_PTR)hmenuSpeed,
  960. LoadSz(IDS_SPEED));
  961. if (hmenuWindow || hmenuVolume || hmenuSpeed)
  962. AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
  963. if (p->wDeviceID && p->fCanRecord && (p->dwStyle & MCIWNDF_RECORD))
  964. AppendMenu(hmenu, MF_ENABLED, IDM_MCINEW, LoadSz(IDS_NEW));
  965. if (!(p->dwStyle & MCIWNDF_NOOPEN))
  966. AppendMenu(hmenu, MF_ENABLED, IDM_MCIOPEN, LoadSz(IDS_OPEN));
  967. if (p->wDeviceID && p->fCanSave && (p->dwStyle & MCIWNDF_RECORD))
  968. AppendMenu(hmenu, MF_ENABLED, MCI_SAVE, LoadSz(IDS_SAVE));
  969. if (p->wDeviceID) {
  970. if (!(p->dwStyle & MCIWNDF_NOOPEN)) {
  971. AppendMenu(hmenu, MF_ENABLED, IDM_MCICLOSE, LoadSz(IDS_CLOSE));
  972. AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
  973. }
  974. AppendMenu(hmenu, p->fAllowCopy ? MF_ENABLED : MF_GRAYED,
  975. IDM_COPY, LoadSz(IDS_COPY));
  976. if (p->fCanConfig)
  977. AppendMenu(hmenu, MF_ENABLED, IDM_MCICONFIG,
  978. LoadSz(IDS_CONFIGURE));
  979. // !!! Should we only show this in debug, or if a flag is set?
  980. AppendMenu(hmenu, MF_ENABLED, IDM_MCICOMMAND, LoadSz(IDS_COMMAND));
  981. }
  982. p->hmenu = hmenu;
  983. p->hmenuVolume = hmenuVolume;
  984. p->hmenuSpeed = hmenuSpeed;
  985. p->hbrDither = CreateDitherBrush(); // we'll need this for OwnerDraw
  986. }
  987. }
  988. //
  989. // Set up everything for an empty window
  990. //
  991. STATICFN LONG MCIWndiClose(PMCIWND p, BOOL fRedraw)
  992. {
  993. MCI_GENERIC_PARMS mciGeneric;
  994. // Oh no! The MCI device (probably MMP) has hooked our window proc and if
  995. // we close the device, it will go away, and the hook will DIE! We need to
  996. // do everything BUT the closing of the device. We'll delay that.
  997. if (GetWindowLongPtr(p->hwnd, GWLP_WNDPROC) != (LONG_PTR)MCIWndProc &&
  998. p->wDeviceID && p->fCanWindow) {
  999. MCIWndString(p, FALSE, szWindowHandle, NULL); // GO AWAY, DEVICE!
  1000. PostMessage(p->hwnd, MCI_CLOSE, 0, p->wDeviceID);
  1001. } else if (p->wDeviceID)
  1002. // buggy drivers crash if we pass a null parms address
  1003. mciSendCommand(p->wDeviceID, MCI_CLOSE, 0, (DWORD_PTR)(LPVOID)&mciGeneric);
  1004. //
  1005. // if the device had a palette, we need to send palette changes to
  1006. // every window because we just deleted the palette that was realized.
  1007. //
  1008. if (p->fHasPalette) {
  1009. // If we're dying this won't go through unless we SEND
  1010. SendMessage(p->hwnd, MCIWNDM_PALETTEKICK, 0, 0);
  1011. }
  1012. // execute this function even if there's no deviceID since we may want
  1013. // to gray things
  1014. // The next timer will kill itself since wDeviceID is NULL
  1015. p->wDeviceID = 0;
  1016. p->achFileName[0] = 0; // kill the filename
  1017. p->dwMediaLen = 0; // so next open will invalidate media
  1018. // We don't want to redraw cuz we're opening a new file right away
  1019. if (!fRedraw)
  1020. return 0;
  1021. // One of the show bits is on... clear the caption
  1022. if (p->dwStyle & MCIWNDF_SHOWALL)
  1023. SetWindowText(p->hwnd, LoadSz(IDS_NODEVICE));
  1024. // Gray all the stuff on the playbar
  1025. MCIWndiFixMyPlaybar(p);
  1026. // Make an appropriate menu for our null device
  1027. MCIWndiMakeMeAMenu(p);
  1028. // Possibly snap ourselves to a small size since there's no device loaded
  1029. // Also reposition the toolbar after it's been fixed up
  1030. MCIWndiSize(p, 0);
  1031. // We need to notify our "owner" that we've closed
  1032. // note that a unicode szNull is also a valid ansi szNull.
  1033. // so we dont have to thunk this for MCIWNDF_NOTIFYANSI
  1034. // !!! This flag can have more than one bit set so the test is different
  1035. if (p->dwStyle & MCIWNDF_NOTIFYMEDIA & ~MCIWNDF_NOTIFYANSI)
  1036. NotifyOwner(p, MCIWNDM_NOTIFYMEDIA, (WPARAM)p->hwnd,
  1037. (LPARAM)(LPVOID)szNULL);
  1038. InvalidateRect(p->hwnd, NULL, TRUE);
  1039. return 0;
  1040. }
  1041. #ifdef UNICODE
  1042. //
  1043. // Check to see if our parent is unicode. We need this test to determine
  1044. // if the passed filename is unicode or ascii
  1045. //
  1046. BOOL TestForUnicode(HWND hwnd)
  1047. {
  1048. HWND hwndSave;
  1049. /*
  1050. ** Find the top level window associated with hwnd
  1051. */
  1052. hwndSave = hwnd;
  1053. while ( hwndSave != (HWND)NULL ) {
  1054. hwnd = hwndSave;
  1055. hwndSave = GetParent( hwndSave );
  1056. }
  1057. return(IsWindowUnicode(hwnd));
  1058. }
  1059. #endif
  1060. //
  1061. // This is the WM_CREATE msg of our WndProc
  1062. //
  1063. STATICFN BOOL MCIWndiCreate(HWND hwnd, LPARAM lParam)
  1064. {
  1065. PMCIWND p;
  1066. DWORD_PTR dw;
  1067. TCHAR ach[20];
  1068. HWND hwndP;
  1069. p = (PMCIWND)LocalAlloc(LPTR, sizeof(MCIWND));
  1070. if (!p)
  1071. return FALSE;
  1072. SetWindowLongPtr(hwnd, 0, (UINT_PTR)p);
  1073. p->hwnd = hwnd;
  1074. p->hwndOwner = GetParent(hwnd); // we'll send notifications here
  1075. // Otherwise see if there's an owner
  1076. if (p->hwndOwner == NULL)
  1077. p->hwndOwner = GetWindowOwner(hwnd);
  1078. p->alias = (UINT)(UINT_PTR)hwnd;
  1079. p->dwStyle = GetWindowLong(hwnd, GWL_STYLE);
  1080. DragAcceptFiles(p->hwnd, (p->dwStyle & (MCIWNDF_NOMENU | MCIWNDF_NOOPEN)) == 0);
  1081. if (!(p->dwStyle & WS_CAPTION))
  1082. p->dwStyle &= ~MCIWNDF_SHOWALL;
  1083. // !!! Don't remove NOTIFY bits if there's no owner, because someone might
  1084. // set one later.
  1085. dw = (DWORD_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams;
  1086. //
  1087. // see if we are in a MDIClient
  1088. //
  1089. if ((p->dwStyle & WS_CHILD) && (hwndP = GetParent(hwnd))) {
  1090. GetClassName(hwndP, ach, NUMELMS(ach));
  1091. p->fMdiWindow = lstrcmpi(ach, szMDIClient) == 0;
  1092. if (p->fMdiWindow)
  1093. dw = ((LPMDICREATESTRUCT)dw)->lParam;
  1094. }
  1095. MCIWndiMakeMeAPlaybar(p);
  1096. // if (szOpenFilter[0] == 0)
  1097. // MCIWndiBuildMeAFilter(szOpenFilter);
  1098. // Set the default timer frequencies
  1099. p->iActiveTimerRate = ACTIVE_TIMER;
  1100. p->iInactiveTimerRate = INACTIVE_TIMER;
  1101. // initialize the OFN structure we'll use to open files
  1102. p->achFileName[0] = TEXT('\0');
  1103. p->ofn.lStructSize = sizeof(OPENFILENAME);
  1104. p->ofn.hwndOwner = hwnd;
  1105. p->ofn.hInstance = NULL;
  1106. // p->ofn.lpstrFilter = szOpenFilter;
  1107. p->ofn.lpstrCustomFilter = NULL;
  1108. p->ofn.nMaxCustFilter = 0;
  1109. p->ofn.nFilterIndex = 0;
  1110. ; p->ofn.lpstrFile = p->achFileName;
  1111. ; p->ofn.nMaxFile = NUMELMS(p->achFileName);
  1112. p->ofn.lpstrFileTitle = NULL;
  1113. p->ofn.nMaxFileTitle = 0;
  1114. p->ofn.lpstrInitialDir = NULL;
  1115. p->ofn.lpstrTitle = NULL; // "Open Device";
  1116. p->ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
  1117. p->ofn.nFileOffset = 0;
  1118. p->ofn.nFileExtension = 0;
  1119. p->ofn.lpstrDefExt = NULL;
  1120. p->ofn.lCustData = 0;
  1121. p->ofn.lpfnHook = NULL;
  1122. p->ofn.lpTemplateName = NULL;
  1123. p->hicon = LoadIcon(hInst, MAKEINTRESOURCE(MPLAYERICON));
  1124. // Gray stuff; disable things that aren't applicable with no device loaded
  1125. MCIWndClose(hwnd);
  1126. #ifndef UNICODE
  1127. if (dw && *(LPSTR)dw) // treat extra parm as a filename
  1128. MCIWndOpen(hwnd, (LPSTR)dw, 0);
  1129. #else //UNICODE
  1130. /*
  1131. ** Check that owner window is also Unicode.
  1132. ** First check that there is a parameter to pass
  1133. */
  1134. if (dw) {
  1135. // We now have to work out whether the extra parameter is
  1136. // a unicode or ascii string pointer. If we have gone through
  1137. // our internal ascii open routines then the parameter will have
  1138. // been converted to unicode. If an application has registered the
  1139. // mciwnd class, and then creates a window directly the conversion
  1140. // will not have happened.
  1141. if (fFileNameIsUnicode
  1142. || TestForUnicode(p->hwndOwner)) {
  1143. if (dw && *(LPWSTR)dw) // treat extra parm as a filename
  1144. MCIWndOpen(hwnd, (LPWSTR)dw, 0);
  1145. }
  1146. else {
  1147. if (dw && *(LPSTR)dw) { // treat extra parm as a filename
  1148. MCIWndOpenA(hwnd, (LPSTR)dw, 0);
  1149. }
  1150. }
  1151. }
  1152. #endif
  1153. return TRUE;
  1154. }
  1155. //
  1156. // Brings up an OpenDialog or a SaveDialog for the application and returns the
  1157. // filename. Returns TRUE if a file name was chosen, FALSE on error or CANCEL.
  1158. //
  1159. STATICFN BOOL MCIWndOpenDlg(PMCIWND p, BOOL fSave, LPTSTR szFile, int len)
  1160. {
  1161. BOOL f;
  1162. // !!! Maybe this is a device name and our GetOpenFileName will fail.
  1163. // !!! Find someway of bringing up an initial filename anyway?
  1164. szFile[0] = 0;
  1165. p->ofn.lpstrFile = szFile;
  1166. p->ofn.nMaxFile = len;
  1167. if (fSave)
  1168. p->ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
  1169. else
  1170. p->ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
  1171. //
  1172. // use achReturn to hold the MCI Filter.
  1173. //
  1174. MCIWndiBuildMeAFilter(p->achReturn);
  1175. p->ofn.lpstrFilter = p->achReturn;
  1176. /* prompt user for file to open or save */
  1177. if (fSave)
  1178. f = GetSaveFileNamePreview(&(p->ofn));
  1179. else
  1180. f = GetOpenFileNamePreview(&(p->ofn));
  1181. return f;
  1182. }
  1183. // Set our timer, if it's needed
  1184. STATICFN void MCIWndiSetTimer(PMCIWND p)
  1185. {
  1186. // We need a TIMER to notify the "owner" when things change
  1187. if (!(p->dwStyle & MCIWNDF_NOPLAYBAR) ||
  1188. (p->dwStyle & MCIWNDF_NOTIFYMODE) ||
  1189. (p->dwStyle & MCIWNDF_SHOWMODE) ||
  1190. (p->dwStyle & MCIWNDF_SHOWPOS) ||
  1191. (p->dwStyle & MCIWNDF_NOTIFYPOS)) {
  1192. p->wTimer = SetTimer(p->hwnd, TIMER1,
  1193. p->fActive? p->iActiveTimerRate : p->iInactiveTimerRate,
  1194. NULL);
  1195. }
  1196. }
  1197. //
  1198. // Set the caption based on what they want to see... Name? Pos? Mode?
  1199. //
  1200. STATICFN VOID MCIWndiSetCaption(PMCIWND p)
  1201. {
  1202. TCHAR ach[200], achMode[40], achT[40], achPos[40];
  1203. // Don't touch their window text if they don't want us to
  1204. if (!(p->dwStyle & MCIWNDF_SHOWALL))
  1205. return;
  1206. ach[0] = TEXT('\0');
  1207. if (p->wDeviceID == 0)
  1208. return;
  1209. if (p->dwStyle & MCIWNDF_SHOWNAME)
  1210. wsprintf(ach, TEXT("%s"), FileName(p->achFileName));
  1211. if (p->dwStyle & (MCIWNDF_SHOWPOS | MCIWNDF_SHOWMODE))
  1212. lstrcat(ach, TEXT(" ("));
  1213. if (p->dwStyle & MCIWNDF_SHOWPOS) {
  1214. // Get the pretty version of the position as a string
  1215. MCIWndGetPositionString(p->hwnd, achPos, NUMELMS(achPos));
  1216. if (p->dwStyle & MCIWNDF_SHOWMODE)
  1217. wsprintf(achT, TEXT("%s - "), (LPTSTR)achPos);
  1218. else
  1219. wsprintf(achT, TEXT("%s"), (LPTSTR)achPos);
  1220. lstrcat(ach, achT);
  1221. }
  1222. if (p->dwStyle & MCIWNDF_SHOWMODE) {
  1223. MCIWndGet(p, szStatusMode, achMode, NUMELMS(achMode));
  1224. lstrcat(ach, achMode);
  1225. }
  1226. if (p->dwStyle & (MCIWNDF_SHOWPOS | MCIWNDF_SHOWMODE))
  1227. lstrcat(ach, TEXT(")"));
  1228. SetWindowText(p->hwnd, ach);
  1229. }
  1230. //
  1231. // Save a file. Returns 0 for success
  1232. //
  1233. STATICFN LRESULT MCIWndiSave(PMCIWND p, WPARAM wFlags, LPTSTR szFile)
  1234. {
  1235. TCHAR ach[128];
  1236. LRESULT dw;
  1237. //
  1238. // If we don't have a filename to save, then get one from a dialog
  1239. //
  1240. if (szFile == (LPVOID)-1L) {
  1241. lstrcpy(ach, p->achFileName);
  1242. if (!MCIWndOpenDlg(p, TRUE, ach, NUMELMS(ach)))
  1243. return -1;
  1244. szFile = ach;
  1245. }
  1246. //
  1247. // HACK HACK: This is a temporary method of stopping a very long file name
  1248. // from overflowing the internal members of the MCIWND struct, which uses
  1249. // only 128 chars for achFileName as well as in achReturn array.
  1250. // Since this was designed in the days of Windows 3.1, 128 chars for handling
  1251. // filename was more than sufficient. So we'll fail the call for any filename
  1252. // longer than that. That will keep it from overflowing the internal buffers.
  1253. //
  1254. #define MAX_FILENAME_LENGTH 128
  1255. if (lstrlen(szFile) >= MAX_FILENAME_LENGTH)
  1256. {
  1257. DPF("WARNING: MCIWndiSave() got very long (%d chars) filename.\n", lstrlen(szFile)) ;
  1258. p->dwError = MCIERR_INVALID_FILE ; // error out
  1259. return p->dwError ;
  1260. }
  1261. // !!! All good little boys should be saving to background... don't wait
  1262. dw = MCIWndString(p, TRUE, szSave, szFile);
  1263. if (dw == 0) {
  1264. // Fix the filename of the current file
  1265. lstrcpy(p->achFileName, szFile);
  1266. // Fix the window caption
  1267. MCIWndiSetCaption(p);
  1268. // It's now OK to copy again. (For MCIWAVE, something freshly
  1269. // recorded isn't persisted to the file and wouldn't have been
  1270. // copied with our copy command, so it was disabled).
  1271. p->fAllowCopy = TRUE;
  1272. MCIWndiMakeMeAMenu(p);
  1273. }
  1274. return dw;
  1275. }
  1276. //
  1277. // Actually open a file and set up the window. Returns 0 for success
  1278. //
  1279. STATICFN LRESULT MCIWndiOpen(PMCIWND p, WPARAM wFlags, LPTSTR szFile)
  1280. {
  1281. DWORD dw = 0;
  1282. HCURSOR hcurPrev;
  1283. TCHAR ach[128];
  1284. UINT wDeviceID;
  1285. BOOL fNew = (wFlags & MCIWNDOPENF_NEW) != 0;
  1286. //
  1287. // We're opening an existing file, szFile is that filename
  1288. // If we don't have a filename to open, then get one from a dialog
  1289. //
  1290. if (!fNew && szFile == (LPVOID)-1L) {
  1291. lstrcpy(ach, p->achFileName);
  1292. if (!MCIWndOpenDlg(p, FALSE, ach, NUMELMS(ach)))
  1293. return -1;
  1294. szFile = ach;
  1295. }
  1296. //
  1297. // We want to open a new file, szFile is the device to open
  1298. // If it's NULL, we use the current device
  1299. //
  1300. if (fNew && (szFile == NULL || *szFile == 0)) {
  1301. // There is no device, so we can't do anything
  1302. if (!p->wDeviceID)
  1303. return 42; // !!! failure
  1304. MCIWndGetDevice(p->hwnd, ach, NUMELMS(ach));
  1305. szFile = ach;
  1306. }
  1307. // save the current device ID so we can put it back in case open fails.
  1308. wDeviceID = p->wDeviceID;
  1309. KillTimer(p->hwnd, TIMER1); // setting the deviceID to 0 will mess up timer
  1310. p->wDeviceID = 0; // and if open fails, we don't want that
  1311. p->alias++; // use a new alias
  1312. //
  1313. // HACK HACK: Same too long file name problem (as in MCIWndiSave) is plugged
  1314. // by erroring out on a file name longer than 128 chars.
  1315. //
  1316. // #define MAX_FILENAME_LENGTH 128
  1317. if (lstrlen(szFile) >= MAX_FILENAME_LENGTH)
  1318. {
  1319. DPF("WARNING: MCIWndiOpen() got very long (%d chars) filename.\n", lstrlen(szFile)) ;
  1320. p->dwError = MCIERR_INVALID_FILE ; // error out
  1321. return p->dwError ;
  1322. }
  1323. /*
  1324. * Show the hourglass cursor -- who knows how long this stuff
  1325. * will take
  1326. */
  1327. hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1328. // Open a NEW file
  1329. if (fNew) {
  1330. dw = MCIWndGetValue(p, TRUE, szNew, 0,
  1331. szFile, (UINT)p->alias);
  1332. // open an existing file
  1333. } else {
  1334. // first, try to open it shareable
  1335. // don't show or update errors since we try to open it twice
  1336. //
  1337. // dont try shareable for "file" devices
  1338. // hack to check for extension.
  1339. //
  1340. #ifdef LATER
  1341. !!! HACK ALERT !!!
  1342. For NT (and probably Chicago) this test is VERY error prone. If
  1343. we have a long file name then we are in big trouble....
  1344. #endif
  1345. #if 0 // IF the file exists, then do not open shareable.
  1346. // This section, or something like it, should replace the gross hack
  1347. // below. Checking for a '.' as the fourth last character... yuk!
  1348. {
  1349. DWORD attr = GetFileAttributes(szFile);
  1350. if (attr != (DWORD)(~0))
  1351. // The error value is -1, i.e. bitwise inverse of 0
  1352. dw = 0; // Force non shareable
  1353. else
  1354. dw = MCIWndGetValue(p, FALSE, szOpenShareable, 0,
  1355. (LPTSTR)szFile, (UINT)p->alias);
  1356. }
  1357. #else
  1358. {
  1359. UINT n = lstrlen(szFile);
  1360. if (n > 4 && szFile[n-4] == TEXT('.'))
  1361. dw = 0;
  1362. else
  1363. dw = MCIWndGetValue(p, FALSE, szOpenShareable, 0,
  1364. (LPTSTR)szFile, (UINT)p->alias);
  1365. }
  1366. #endif
  1367. // Error! Try again, not shareable.
  1368. if (dw == 0) {
  1369. dw = MCIWndGetValue(p, FALSE, szOpen, 0,
  1370. (LPTSTR)szFile, (UINT)p->alias);
  1371. // Last ditch attempt! Try AVI. It'll open anything. This time,
  1372. // show, set errors.
  1373. if (dw == 0) {
  1374. dw = MCIWndGetValue(p, TRUE, szOpenAVI, 0,
  1375. (LPTSTR)szFile, (UINT)p->alias);
  1376. }
  1377. }
  1378. }
  1379. if (hcurPrev)
  1380. SetCursor(hcurPrev);
  1381. //
  1382. // Ack! No deviceID... we failed to open
  1383. //
  1384. if (dw == 0)
  1385. {
  1386. p->wDeviceID = wDeviceID;
  1387. MCIWndiSetTimer(p); // Put the timer back now that DeviceID is back
  1388. // p->achFileName[0] = 0; // don't hurt the old filename!
  1389. p->alias--; // back to old alias
  1390. // in case error box or open box wiped us out and we didn't paint
  1391. // because our p->wDeviceID was null because of our open hack
  1392. InvalidateRect(p->hwnd, NULL, TRUE);
  1393. return p->dwError;
  1394. }
  1395. //
  1396. // it worked, now close the old device and open the new.
  1397. //
  1398. if (wDeviceID)
  1399. {
  1400. p->wDeviceID = wDeviceID;
  1401. p->alias--; // back to old alias so the close might actually work
  1402. MCIWndiClose(p, FALSE); // don't redraw
  1403. p->alias++; // new alias again (ACK!)
  1404. }
  1405. p->wDeviceID = (UINT)dw;
  1406. p->dwMode = (DWORD)~0L; // first mode set will be detected as a change
  1407. p->dwPos = (DWORD)~0L; // first pos set will be detected as a change
  1408. // Copy the file or device name into our filename spot. Set the window
  1409. // caption (if desired).
  1410. lstrcpy(p->achFileName, szFile);
  1411. MCIWndiSetCaption(p); // wDeviceID must be set before calling this
  1412. // !!! p->wDeviceType = QueryDeviceTypeMCI(p->wDeviceID);
  1413. p->fAllowCopy = TRUE; // until recorded into
  1414. // Now set the playback window to be our MCI window
  1415. p->fCanWindow = MCIWndString(p, FALSE, szWindowHandle, (UINT_PTR)p->hwnd) == 0;
  1416. if (p->fCanWindow)
  1417. MCIWndGetDest(p->hwnd, &p->rcNormal);
  1418. else
  1419. SetRect(&p->rcNormal, 0, 0, 0, 0);
  1420. // Find out if the device supports palettes.
  1421. p->fHasPalette = MCIWndString(p, FALSE, szStatusPalette) == 0;
  1422. //
  1423. // Now find out the capabilities of this device
  1424. //
  1425. // !!! What about these ???
  1426. // MCI_GETDEVCAPS_DEVICE_TYPE 0x00000004L
  1427. // MCI_GETDEVCAPS_COMPOUND_DEVICE 0x00000006L
  1428. // Find out if the device can record
  1429. p->fCanRecord = MCIWndDevCaps(p, MCI_GETDEVCAPS_CAN_RECORD);
  1430. // Find out if the device can play
  1431. p->fCanPlay = MCIWndDevCaps(p, MCI_GETDEVCAPS_CAN_PLAY);
  1432. // Find out if the device can save
  1433. p->fCanSave = MCIWndDevCaps(p, MCI_GETDEVCAPS_CAN_SAVE);
  1434. // Find out if the device can eject
  1435. p->fCanEject = MCIWndDevCaps(p, MCI_GETDEVCAPS_CAN_EJECT);
  1436. // Find out if the device is file based
  1437. p->fUsesFiles = MCIWndDevCaps(p, MCI_GETDEVCAPS_USES_FILES);
  1438. // Find out if the device has video
  1439. p->fVideo = MCIWndDevCaps(p, MCI_GETDEVCAPS_HAS_VIDEO);
  1440. // Find out if the device has video
  1441. p->fAudio = MCIWndDevCaps(p, MCI_GETDEVCAPS_HAS_AUDIO);
  1442. // Find out if the device can configure
  1443. p->fCanConfig = (MCIWndString(p, FALSE, szConfigureTest) == 0);
  1444. //
  1445. //
  1446. //
  1447. // Now see if we support speed - try normal, half, and max
  1448. p->fSpeed = MCIWndString(p, FALSE, szSetSpeed1000Test) == 0 &&
  1449. MCIWndString(p, FALSE, szSetSpeed500Test) == 0 &&
  1450. MCIWndString(p, FALSE, szSetSpeedTest, SPEED_MAX * 10) == 0;
  1451. // Now see if we support volume - try normal, mute, and max
  1452. p->fVolume = MCIWndString(p, FALSE, szSetVolumeTest, VOLUME_MAX * 5) ==0 &&
  1453. MCIWndString(p, FALSE, szSetVolume0Test) == 0;
  1454. p->wMaxVol = 100;
  1455. // If someone happens to support double volume, let's give it to them.
  1456. if (MCIWndString(p, FALSE, szSetVolumeTest, VOLUME_MAX * 10) == 0)
  1457. p->wMaxVol = 200;
  1458. // See if the device would support tmsf mode. If so, use milliseconds mode
  1459. // and later on we'll fake knowing where tracks begin and end
  1460. p->fHasTracks = (MCIWndString(p, FALSE, szSetFormatTMSF) == 0);
  1461. if (p->fHasTracks) {
  1462. dw = MCIWndString(p, FALSE, szSetFormatMS);
  1463. if (dw != 0)
  1464. p->fHasTracks = FALSE;
  1465. }
  1466. if (!p->fHasTracks) {
  1467. // Force us into a reasonable time format
  1468. dw = MCIWndString(p, FALSE, szSetFormatFrames);
  1469. if (dw != 0)
  1470. dw = MCIWndString(p, FALSE, szSetFormatMS);
  1471. if (dw != 0)
  1472. ; // !!! What to do? Don't turn playbar off without
  1473. } // !!! destroying it...
  1474. // Set the media length and trackbar range
  1475. MCIWndiValidateMedia(p);
  1476. // Fix the toolbar buttons for the new device
  1477. MCIWndiFixMyPlaybar(p);
  1478. // Make an appropriate menu for this device
  1479. MCIWndiMakeMeAMenu(p);
  1480. // We need a TIMER to notify the "owner" when things change
  1481. MCIWndiSetTimer(p);
  1482. // Set the size of the movie (and maybe the window) and re-draw new toolbar
  1483. MCIWndiSize(p, p->iZoom);
  1484. #if 0 // We need the focus on our main window to get key accelerators
  1485. // Bring focus to the thumb so caret will flash
  1486. // I know the WM_SETFOCUS msg does this, but it seems to need to happen here
  1487. // too.
  1488. if (p->hwndTrackbar && GetFocus() == p->hwnd)
  1489. SetFocus(p->hwndTrackbar);
  1490. #endif
  1491. // We need to notify our "owner" that we've opened a new file
  1492. // !!! This flag can have more than one bit set so the test is different
  1493. if (p->dwStyle & MCIWNDF_NOTIFYMEDIA & ~MCIWNDF_NOTIFYANSI) {
  1494. #ifdef UNICODE
  1495. if (p->dwStyle & MCIWNDF_NOTIFYANSI) {
  1496. char * lpA;
  1497. int sz;
  1498. sz = lstrlen(szFile) + 1;
  1499. lpA = (char *)LocalAlloc(LPTR, sz * sizeof(char));
  1500. if (lpA) {
  1501. Iwcstombs(lpA, szFile, sz);
  1502. NotifyOwner(p, MCIWNDM_NOTIFYMEDIA, (WPARAM)p->hwnd,(LPARAM)lpA);
  1503. LocalFree ((HLOCAL)lpA);
  1504. }
  1505. }
  1506. else
  1507. #endif
  1508. {
  1509. NotifyOwner(p, MCIWNDM_NOTIFYMEDIA, (WPARAM)p->hwnd,
  1510. (LPARAM)szFile);
  1511. }
  1512. }
  1513. // Make sure the newly opened movie paints in the window now
  1514. InvalidateRect(p->hwnd, NULL, TRUE);
  1515. return 0; // success
  1516. }
  1517. STATICFN LONG MCIWndiChangeStyles(PMCIWND p, UINT mask, UINT value)
  1518. {
  1519. DWORD dwOldStyle = p->dwStyle;
  1520. DWORD dwMaskOff, dwValue, dwChanged;
  1521. //
  1522. // Using the mask, change the appropriate bits in the style
  1523. //
  1524. dwMaskOff = dwOldStyle & (~(DWORD)mask);
  1525. dwValue = (DWORD)mask & (DWORD)value;
  1526. p->dwStyle = dwMaskOff | dwValue;
  1527. //
  1528. // Which bits changed?
  1529. //
  1530. dwChanged = (dwOldStyle & (DWORD)mask) ^ (dwValue & (DWORD)mask);
  1531. //
  1532. // We changed whether or not we want a menu button or a record button
  1533. // on the playbar
  1534. //
  1535. if (dwChanged & (MCIWNDF_NOMENU | MCIWNDF_NOOPEN | MCIWNDF_RECORD)) {
  1536. MCIWndiMakeMeAMenu(p); // add/remove record from the menu
  1537. // We have a playbar, so fix it
  1538. if (!(p->dwStyle & MCIWNDF_NOPLAYBAR)) {
  1539. MCIWndiFixMyPlaybar(p);
  1540. MCIWndiSize(p, 0);
  1541. }
  1542. }
  1543. //
  1544. // We changed the show/don't show playbar flag!
  1545. //
  1546. if (dwChanged & MCIWNDF_NOPLAYBAR) {
  1547. // Remove the playbar
  1548. if (p->dwStyle & MCIWNDF_NOPLAYBAR) {
  1549. DestroyWindow(p->hwndToolbar);
  1550. DestroyWindow(p->hwndTrackbar);
  1551. p->hwndToolbar = NULL;
  1552. p->hwndTrackbar = NULL;
  1553. MCIWndiMakeMeAMenu(p); // since toolbar's gone, menus change
  1554. if (!(p->dwStyle & MCIWNDF_NOAUTOSIZEWINDOW)) {
  1555. // Now resize the window smaller to account for the missing
  1556. // playbar. Don't touch the movie size.
  1557. MCIWndiSize(p, 0);
  1558. // If the window isn't being resized, we may still need to grow
  1559. // the movie size a bit to take up the extra space where the toolbar
  1560. // vanished. (happens automatically in the previous case)
  1561. } else if (!(p->dwStyle & MCIWNDF_NOAUTOSIZEMOVIE)) {
  1562. PostMessage(p->hwnd, WM_SIZE, 0, 0L);
  1563. }
  1564. // Add a playbar
  1565. } else {
  1566. MCIWndiMakeMeAPlaybar(p);
  1567. MCIWndiFixMyPlaybar(p);
  1568. MCIWndiMakeMeAMenu(p); // since toolbar's used, menus change
  1569. if (!(p->dwStyle & MCIWNDF_NOAUTOSIZEWINDOW)) {
  1570. // Now resize the window a little bigger to account for the new
  1571. // playbar. Don't touch the movie size.
  1572. MCIWndiSize(p, 0);
  1573. // If the window isn't being resized, we may still need to shrink
  1574. // the movie size because the toolbar covers up some extra space.
  1575. // (happens automatically in the previous case)
  1576. } else if (!(p->dwStyle & MCIWNDF_NOAUTOSIZEMOVIE)) {
  1577. PostMessage(p->hwnd, WM_SIZE, 0, 0L);
  1578. // Irregardless, we need to fix the toolbar
  1579. } else
  1580. // Put the toolbar in a reasonable spot
  1581. MCIWndiSizePlaybar(p);
  1582. }
  1583. }
  1584. //
  1585. // We changed a SHOW flag and need to reset the caption
  1586. //
  1587. if (dwChanged & MCIWNDF_SHOWALL)
  1588. MCIWndiSetCaption(p);
  1589. //
  1590. // We turned the AUTOSIZEMOVIE flag on and need to resize the device.
  1591. // This happens before AUTOSIZEWINDOW so if both flags are turned on
  1592. // the movie will snap to the window not vice versa.
  1593. // !!! Should we even snap it right now?
  1594. //
  1595. if (dwChanged & MCIWNDF_NOAUTOSIZEMOVIE &&
  1596. !(p->dwStyle & MCIWNDF_NOAUTOSIZEMOVIE))
  1597. PostMessage(p->hwnd, WM_SIZE, 0, 0);
  1598. //
  1599. // We turned the AUTOSIZEWINDOW flag on
  1600. // Snap our window to the current movie size.
  1601. //
  1602. if (dwChanged & MCIWNDF_NOAUTOSIZEWINDOW &&
  1603. !(p->dwStyle & MCIWNDF_NOAUTOSIZEWINDOW))
  1604. MCIWndiSize(p, 0);
  1605. DragAcceptFiles(p->hwnd, (p->dwStyle & MCIWNDF_NOMENU | MCIWNDF_NOOPEN) == 0);
  1606. return 0; // !!! success ?
  1607. }
  1608. //
  1609. // We're about to play. We might want to seek to the beginning first if we're
  1610. // at the end, or seek to the end first if we're at the beginning and playing
  1611. // backwards.
  1612. //
  1613. STATICFN void MCIWndiPlaySeek(PMCIWND p, BOOL fBackwards)
  1614. {
  1615. // Playing backwards? If we're at the beginning, seek to the end
  1616. if (fBackwards) {
  1617. if (MCIWndGetPosition(p->hwnd) <= MCIWndGetStart(p->hwnd))
  1618. MCIWndSeek(p->hwnd, MCIWND_END);
  1619. return;
  1620. }
  1621. // Playing forwards.
  1622. // If we're near the end, rewind before playing
  1623. // Some devices are broken so we can't just test being at the end
  1624. // Frames mode ... last or second to last frame
  1625. if (MCIWndGetTimeFormat(p->hwnd, NULL, 0) == MCI_FORMAT_FRAMES) {
  1626. if (MCIWndGetPosition(p->hwnd) >= MCIWndGetEnd(p->hwnd) - 1)
  1627. MCIWndSeek(p->hwnd, MCIWND_START);
  1628. // Millisecond mode ... within last 1/4 second
  1629. } else if (MCIWndGetTimeFormat(p->hwnd, NULL, 0) ==
  1630. MCI_FORMAT_MILLISECONDS) {
  1631. if (MCIWndGetEnd(p->hwnd) - MCIWndGetPosition(p->hwnd) < 250)
  1632. MCIWndSeek(p->hwnd, MCIWND_START);
  1633. // something else ... no hack
  1634. } else {
  1635. if (MCIWndGetPosition(p->hwnd) == MCIWndGetEnd(p->hwnd))
  1636. MCIWndSeek(p->hwnd, MCIWND_START);
  1637. }
  1638. }
  1639. //
  1640. // Handle our WM_TIMER
  1641. //
  1642. STATICFN void MCIWndiTimerStuff(PMCIWND p)
  1643. {
  1644. DWORD dwMode;
  1645. DWORD dwPos;
  1646. //
  1647. // Someone's interested in knowing the mode of the device
  1648. //
  1649. if ((p->dwStyle & MCIWNDF_NOTIFYMODE) ||
  1650. !(p->dwStyle & MCIWNDF_NOPLAYBAR) ||
  1651. (p->dwStyle & MCIWNDF_SHOWMODE)) {
  1652. dwMode = MCIWndGetMode(p->hwnd, NULL, 0);
  1653. //
  1654. // If we haven't set the trackbar range or media length yet
  1655. // because we weren't ready, maybe we can do it now!
  1656. // Also, don't update media until you're done recording.
  1657. //
  1658. if (dwMode != MCI_MODE_NOT_READY && dwMode != MCI_MODE_OPEN &&
  1659. dwMode != MCI_MODE_RECORD && p->fMediaValid == FALSE)
  1660. MCIWndiValidateMedia(p);
  1661. //
  1662. // No device loaded? Time to kill our timer
  1663. //
  1664. if (p->wDeviceID == 0)
  1665. KillTimer(p->hwnd, TIMER1);
  1666. //
  1667. // The mode has changed!
  1668. //
  1669. if (dwMode != p->dwMode) {
  1670. p->dwMode = dwMode;
  1671. //
  1672. // Notify the "owner" of the mode change
  1673. //
  1674. if ((p->dwStyle & MCIWNDF_NOTIFYMODE))
  1675. NotifyOwner(p, MCIWNDM_NOTIFYMODE, (WPARAM)p->hwnd, dwMode);
  1676. //
  1677. // Set the Window Caption to include the new mode
  1678. //
  1679. if ((p->dwStyle & MCIWNDF_SHOWMODE))
  1680. MCIWndiSetCaption(p);
  1681. //
  1682. // Fix up the toolbar bitmaps if the mode has changed
  1683. //
  1684. MCIWndiPlaybarGraying(p);
  1685. }
  1686. }
  1687. //
  1688. // Someone's interested in knowing the new position
  1689. //
  1690. if (!(p->dwStyle & MCIWNDF_NOPLAYBAR) ||
  1691. (p->dwStyle & MCIWNDF_NOTIFYPOS) ||
  1692. (p->dwStyle & MCIWNDF_SHOWPOS)) {
  1693. dwPos = MCIWndGetPosition(p->hwnd);
  1694. //
  1695. // The position has changed!
  1696. //
  1697. if (dwPos != p->dwPos) {
  1698. //
  1699. // Make sure start and length haven't changed too (format change) ?
  1700. //
  1701. MCIWndiValidateMedia(p);
  1702. p->dwPos = dwPos;
  1703. //
  1704. // Notify the "owner" of the position change
  1705. //
  1706. if ((p->dwStyle & MCIWNDF_NOTIFYPOS))
  1707. NotifyOwner(p, MCIWNDM_NOTIFYPOS, (WPARAM)p->hwnd, dwPos);
  1708. //
  1709. // Set the Window Caption to include the new position
  1710. //
  1711. if ((p->dwStyle & MCIWNDF_SHOWPOS))
  1712. MCIWndiSetCaption(p);
  1713. //
  1714. // Update the trackbar to the new position but not while
  1715. // we're dragging the thumb
  1716. //
  1717. if (!(p->dwStyle & MCIWNDF_NOPLAYBAR) && !p->fScrolling && p->dwMode != MCI_MODE_SEEK)
  1718. SendMessage(p->hwndTrackbar, TBM_SETPOS, TRUE, dwPos);
  1719. }
  1720. }
  1721. }
  1722. STATICFN void MCIWndiDrop(HWND hwnd, WPARAM wParam)
  1723. {
  1724. TCHAR szPath[MAX_PATH];
  1725. UINT nDropped;
  1726. // Get number of files dropped
  1727. nDropped = DragQueryFile((HANDLE)wParam, (UINT)-1, NULL, 0);
  1728. if (nDropped) {
  1729. SetActiveWindow(hwnd);
  1730. // Get the file that was dropped....
  1731. DragQueryFile((HANDLE)wParam, 0, szPath, NUMELMS(szPath));
  1732. MCIWndOpen(hwnd, szPath, 0);
  1733. }
  1734. DragFinish((HANDLE)wParam); /* Delete structure alocated */
  1735. }
  1736. //--------ansi thunk functions ---------------------------------
  1737. #ifdef UNICODE
  1738. STATICFN LRESULT MCIWndiOpenA(PMCIWND p, WPARAM wFlags, LPCSTR szFile)
  1739. {
  1740. WCHAR * lpW;
  1741. int sz;
  1742. LRESULT l;
  1743. if ((szFile == NULL) || (szFile == (LPSTR)-1)) {
  1744. return MCIWndiOpen(p, wFlags, (LPTSTR)szFile);
  1745. }
  1746. sz = lstrlenA(szFile) + 1;
  1747. lpW = (WCHAR *)LocalAlloc(LPTR, sz * sizeof(WCHAR));
  1748. if (!lpW) {
  1749. return -1;
  1750. }
  1751. Imbstowcs(lpW, szFile, sz);
  1752. l = MCIWndiOpen(p, wFlags, lpW);
  1753. LocalFree((HLOCAL)lpW);
  1754. return l;
  1755. }
  1756. // ansi version of MCIWndGet
  1757. STATICFN DWORD MCIWndGetA(PMCIWND p, LPSTR sz, LPSTR szRet, int len, ...)
  1758. {
  1759. char ach[MAX_PATH];
  1760. int i;
  1761. DWORD dw;
  1762. va_list va;
  1763. if (!p->wDeviceID) {
  1764. szRet[0] = 0;
  1765. return 0L;
  1766. }
  1767. for (i=0; *sz && *sz != ' '; )
  1768. ach[i++] = *sz++;
  1769. i += wsprintfA(&ach[i], " %d ", (UINT)p->alias);
  1770. va_start(va, len);
  1771. i += wvsprintfA(&ach[i], sz, va); //!!!use varargs
  1772. va_end(va);
  1773. // initialize to NULL return string
  1774. szRet[0] = 0;
  1775. dw = mciSendStringA(ach, szRet, len, p->hwnd);
  1776. DPF("MCIWndGetA('%s'): '%s'", (LPSTR)ach, (LPSTR)szRet);
  1777. return dw;
  1778. }
  1779. #endif
  1780. /*--------------------------------------------------------------+
  1781. | MCIWndCommand - WM_COMMAND processing for MCIWnd class | |
  1782. | |
  1783. +--------------------------------------------------------------*/
  1784. static LRESULT MCIWndCommands (
  1785. PMCIWND p, // IN:
  1786. HWND hwnd, // IN:
  1787. WPARAM wParam, // IN:
  1788. LPARAM lParam) // IN:
  1789. {
  1790. WORD wID = GET_WM_COMMAND_ID(wParam, lParam);
  1791. HWND hwndCtl = GET_WM_COMMAND_HWND(wParam, lParam);
  1792. WORD wNotify = GET_WM_COMMAND_CMD(wParam, lParam);
  1793. // Check for ZOOM commands
  1794. if (wID >= IDM_MCIZOOM && wID < IDM_MCIZOOM + 1000)
  1795. MCIWndSetZoom(hwnd, wID - IDM_MCIZOOM);
  1796. // !!! Hack from Hell
  1797. // If our bogus top menu item is selected, turn it into the REAL
  1798. // menu item closest to it.
  1799. //
  1800. if (wID == IDM_MCIVOLUME + VOLUME_MAX + 1)
  1801. wID = IDM_MCIVOLUME + p->wMaxVol;
  1802. if (wID == IDM_MCIVOLUME + VOLUME_MAX + 2)
  1803. wID = IDM_MCIVOLUME;
  1804. // VOLUME command? Uncheck old one, reset volume, and check new one
  1805. // Round to the nearest 5 to match a menu identifier
  1806. if (wID >=IDM_MCIVOLUME && wID <=IDM_MCIVOLUME + p->wMaxVol) {
  1807. // 42 means we're auditioning changes as we drag the menu bar, but
  1808. // we don't actually want to remember this value permanently (see
  1809. // OwnerDraw).
  1810. if (MCIWndSetVolume(hwnd, (wID - IDM_MCIVOLUME) * 10) == 0
  1811. && lParam != 42) {
  1812. CheckMenuItem(p->hmenuVolume, p->uiHack, MF_UNCHECKED);
  1813. CheckMenuItem(p->hmenuVolume, wID, MF_CHECKED);
  1814. }
  1815. }
  1816. // !!! Hack from Hell
  1817. // If our bogus top menu item is selected, turn it into the REAL
  1818. // menu item closest to it.
  1819. if (wID == IDM_MCISPEED + SPEED_MAX + 1)
  1820. wID = IDM_MCISPEED + SPEED_MAX;
  1821. if (wID == IDM_MCISPEED + SPEED_MAX + 2)
  1822. wID = IDM_MCISPEED;
  1823. // SPEED command? Uncheck old one, reset speed, and check new one
  1824. // Round to the nearest 5 to match a menu identifier
  1825. if (wID >=IDM_MCISPEED && wID <= IDM_MCISPEED + SPEED_MAX) {
  1826. // 42 means we're auditioning changes as we drag the menu bar, but
  1827. // we don't actually want to remember this value permanently (see
  1828. // OwnerDraw).
  1829. if (MCIWndSetSpeed(hwnd, (wID - IDM_MCISPEED) * 10) == 0
  1830. && lParam != 42) {
  1831. CheckMenuItem(p->hmenuSpeed, p->uiHack, MF_UNCHECKED);
  1832. CheckMenuItem(p->hmenuSpeed, wID, MF_CHECKED);
  1833. }
  1834. }
  1835. switch(wID)
  1836. {
  1837. case MCI_RECORD:
  1838. if (GetKeyState(VK_SHIFT) < 0)
  1839. {
  1840. //!!! toggle?
  1841. //MCIWndRecordPreview(hwnd);
  1842. }
  1843. else
  1844. {
  1845. MCIWndRecord(hwnd);
  1846. // would not copy newly recorded stuff anyway
  1847. p->fAllowCopy = FALSE;
  1848. MCIWndiMakeMeAMenu(p);
  1849. }
  1850. break;
  1851. // PLAY = normal play
  1852. // SHIFT+PLAY = play backward
  1853. // CTRL+PLAY = play fullscreen
  1854. // SHIFT+CTRL+PLAY = play fullscreen backward
  1855. //
  1856. case MCI_PLAY:
  1857. #define MaybeRepeat (p->fRepeat ? (LPTSTR)szRepeat : (LPTSTR)szNULL)
  1858. // NOTE: We never set errors for the repeat play, because
  1859. // lots of device don't support it and would fail.
  1860. if (GetKeyState(VK_SHIFT) < 0)
  1861. // If we're at the beginning, seek to the end.
  1862. MCIWndiPlaySeek(p, TRUE);
  1863. else
  1864. // If we're at the end, seek to the beginning.
  1865. MCIWndiPlaySeek(p, FALSE);
  1866. if (GetKeyState(VK_CONTROL) < 0)
  1867. {
  1868. if (GetKeyState(VK_SHIFT) < 0) {
  1869. if (MCIWndString(p, FALSE, szPlayFullscreenReverse,
  1870. MaybeRepeat))
  1871. MCIWndString(p, TRUE, szPlayFullscreenReverse,
  1872. (LPTSTR)szNULL);
  1873. } else {
  1874. if (MCIWndString(p, FALSE, szPlayFullscreen,
  1875. MaybeRepeat))
  1876. MCIWndString(p, TRUE, szPlayFullscreen,
  1877. (LPTSTR)szNULL);
  1878. }
  1879. } else if (GetKeyState(VK_SHIFT) < 0) {
  1880. if (MCIWndString(p, FALSE, szPlayReverse, MaybeRepeat))
  1881. MCIWndString(p, TRUE, szPlayReverse, (LPTSTR)szNULL);
  1882. } else {
  1883. if (MCIWndString(p, FALSE, szPlay, MaybeRepeat))
  1884. MCIWndString(p, TRUE, szPlay, (LPTSTR)szNULL);
  1885. }
  1886. // Kick ourselves to fix up toolbar since mode changed
  1887. MCIWndiTimerStuff(p);
  1888. break;
  1889. case MCI_STOP:
  1890. return MCIWndStop(hwnd);
  1891. case MCI_PAUSE:
  1892. return MCIWndPause(hwnd);
  1893. case IDM_MCINEW:
  1894. return MCIWndNew(hwnd, NULL);
  1895. case IDM_MCIOPEN:
  1896. return MCIWndOpenDialog(hwnd);
  1897. case MCI_SAVE:
  1898. return MCIWndSaveDialog(hwnd);
  1899. case IDM_MCICLOSE:
  1900. return MCIWndClose(hwnd);
  1901. case IDM_MCICONFIG:
  1902. MCIWndString(p, TRUE, szConfigure);
  1903. // AVI's configure box might change the size (zoom by 2)
  1904. // so we better call our size routine.
  1905. MCIWndiSize(p, 0);
  1906. // Taking ZOOM X 2 off might leave the outside not painted
  1907. InvalidateRect(hwnd, NULL, TRUE);
  1908. break;
  1909. case IDM_MCICOMMAND:
  1910. mciDialog(hwnd);
  1911. // call mciwndisize?
  1912. break;
  1913. case IDM_COPY:
  1914. MCIWndCopy(p);
  1915. break;
  1916. case IDM_MCIREWIND:
  1917. return MCIWndSeek(hwnd, MCIWND_START);
  1918. case IDM_MCIEJECT:
  1919. return MCIWndEject(hwnd);
  1920. // When somebody presses a toolbar button in 16 bit Chicago, we are told
  1921. // about it via a WM_COMMAND. 32 bit Chicago and NT uses WM_NOTIFY, handled
  1922. // elsewhere.
  1923. #ifndef _WIN32
  1924. case ID_TOOLBAR: {
  1925. RECT rc;
  1926. RECT rcT;
  1927. MSG msgT;
  1928. // We're only interested in a pressing of the Menu button
  1929. if (wNotify != TBN_BEGINDRAG ||
  1930. (UINT)hwndCtl != IDM_MENU ||
  1931. !SendMessage(p->hwndToolbar, TB_ISBUTTONENABLED,
  1932. IDM_MENU, 0) ||
  1933. !p->hmenu)
  1934. break;
  1935. SendMessage(p->hwndToolbar, TB_GETITEMRECT,
  1936. (int)SendMessage(p->hwndToolbar, TB_COMMANDTOINDEX,
  1937. IDM_MENU, 0),
  1938. (LPARAM)(LPVOID)&rc);
  1939. rcT = rc;
  1940. ClientToScreen(p->hwndToolbar, (LPPOINT)&rc);
  1941. ClientToScreen(p->hwndToolbar, (LPPOINT)&rc + 1);
  1942. // Push the button down (accelerator won't have done this)
  1943. SendMessage(p->hwndToolbar, TB_PRESSBUTTON, IDM_MENU,
  1944. TRUE);
  1945. // Don't allow error dlgs to come up while we're tracking.
  1946. // That would cause windows to shatter and send shrapnel
  1947. // flying.
  1948. p->fTracking = TRUE;
  1949. TrackPopupMenu(p->hmenu, 0, rc.left, rc.bottom - 1, 0,
  1950. hwnd, &rc); // don't dismiss menu inside button
  1951. p->fTracking = FALSE;
  1952. // Bring the button back up.
  1953. SendMessage(p->hwndToolbar, TB_PRESSBUTTON, IDM_MENU,
  1954. FALSE);
  1955. // What if we press the menu button to make the menu go
  1956. // away? It's just going to bring the menu back up again!
  1957. // So we need to pull the click out of the queue.
  1958. // There are bugs in the toolbar code to prevent me from
  1959. // doing this any other way (like disabling the button)
  1960. if (PeekMessage(&msgT, p->hwndToolbar, WM_LBUTTONDOWN,
  1961. WM_LBUTTONDOWN, PM_NOREMOVE)) {
  1962. #ifdef _WIN32
  1963. POINT pt = {MAKEPOINTS(msgT.lParam).x,
  1964. MAKEPOINTS(msgT.lParam).y };
  1965. #else
  1966. POINT pt = MAKEPOINT(msgT.lParam);
  1967. #endif
  1968. if (PtInRect(&rcT, pt))
  1969. PeekMessage(&msgT, p->hwndToolbar, WM_LBUTTONDOWN,
  1970. WM_LBUTTONDOWN, PM_REMOVE);
  1971. }
  1972. break;
  1973. }
  1974. #endif
  1975. default:
  1976. break;
  1977. }
  1978. return 0;
  1979. }
  1980. /*--------------------------------------------------------------+
  1981. | SubClassedTrackbarWndProc - eat key presses that do something |
  1982. | special so trackbar never sees them. |
  1983. +--------------------------------------------------------------*/
  1984. LRESULT CALLBACK _LOADDS SubClassedTrackbarWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1985. {
  1986. switch(msg) {
  1987. case WM_KEYDOWN:
  1988. case WM_KEYUP:
  1989. case WM_SYSCHAR:
  1990. return SendMessage(GetParent(hwnd), msg, wParam, lParam);
  1991. }
  1992. return CallWindowProc(fnTrackbarWndProc, hwnd, msg, wParam, lParam);
  1993. }
  1994. /*--------------------------------------------------------------+
  1995. | MCIWndProc - MCI window's window proc |
  1996. | |
  1997. +--------------------------------------------------------------*/
  1998. LRESULT CALLBACK _LOADDS MCIWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1999. {
  2000. PMCIWND p;
  2001. DWORD dw;
  2002. HDC hdc;
  2003. PAINTSTRUCT ps;
  2004. DWORD dwPos;
  2005. POINT pt;
  2006. MINMAXINFO FAR *lpmmi;
  2007. RECT rc;
  2008. BOOL f;
  2009. TCHAR ach[80];
  2010. MCI_GENERIC_PARMS mciGeneric;
  2011. LPRECT prc;
  2012. int i;
  2013. HWND hwndD;
  2014. p = (PMCIWND)(UINT)GetWindowLong(hwnd, 0);
  2015. switch(msg){
  2016. case WM_CREATE:
  2017. if (!MCIWndiCreate(hwnd, lParam))
  2018. return -1;
  2019. break;
  2020. // Make the trackbar background BTNFACE to match the colour scheme
  2021. #ifdef _WIN32
  2022. case WM_CTLCOLORBTN:
  2023. case WM_CTLCOLORDLG:
  2024. case WM_CTLCOLOREDIT:
  2025. case WM_CTLCOLORLISTBOX:
  2026. case WM_CTLCOLORMSGBOX:
  2027. case WM_CTLCOLORSCROLLBAR:
  2028. case WM_CTLCOLORSTATIC:
  2029. if ((HWND)lParam == p->hwndTrackbar) {
  2030. #else
  2031. case WM_CTLCOLOR:
  2032. if ((HWND)LOWORD(lParam) == p->hwndTrackbar) {
  2033. #endif
  2034. // return (LRESULT)(UINT)GetStockObject(LTGRAY_BRUSH);
  2035. SetBkColor((HDC)wParam, GetSysColor(COLOR_BTNFACE));
  2036. SetTextColor((HDC)wParam, GetSysColor(COLOR_BTNTEXT));
  2037. return (LRESULT)(UINT_PTR)GetSysColorBrush(COLOR_BTNFACE);
  2038. }
  2039. break;
  2040. case MCI_SAVE:
  2041. // wParam presently unused and not given by the macro
  2042. return MCIWndiSave(p, wParam, (LPTSTR)lParam);
  2043. #ifdef _WIN32
  2044. case MCIWNDM_OPEN:
  2045. #else
  2046. case MCI_OPEN:
  2047. #endif
  2048. return MCIWndiOpen(p, wParam, (LPTSTR)lParam);
  2049. #ifdef UNICODE
  2050. // ansi thunk for above
  2051. case MCIWNDM_OPENA:
  2052. return MCIWndiOpenA(p, wParam, (LPSTR)lParam);
  2053. #endif
  2054. case MCIWNDM_NEW:
  2055. return MCIWndiOpen(p, MCIWNDOPENF_NEW, (LPTSTR)lParam);
  2056. #ifdef UNICODE
  2057. //ansi thunk for above
  2058. case MCIWNDM_NEWA:
  2059. return MCIWndiOpenA(p, MCIWNDOPENF_NEW, (LPSTR) lParam);
  2060. #endif
  2061. case MCI_PLAY:
  2062. if (!p->wDeviceID)
  2063. return 0;
  2064. // Seek to the beginning if we're near the end
  2065. MCIWndiPlaySeek(p, FALSE);
  2066. case MCI_STOP:
  2067. case MCI_PAUSE:
  2068. case MCI_RESUME:
  2069. case MCI_RECORD:
  2070. dw = 0;
  2071. // Report/Show errors for this
  2072. if (p->wDeviceID) {
  2073. // buggy drivers crash if we pass a null parms address
  2074. dw = mciSendCommand(p->wDeviceID, msg, 0,
  2075. (DWORD_PTR)(LPVOID)&mciGeneric);
  2076. if (dw == 0 && msg == MCI_RECORD) {
  2077. // RECORDING dirties MCIWAVE and what we would copy would
  2078. // still be the old file, so just disable it to save grief.
  2079. p->fAllowCopy = FALSE;
  2080. MCIWndiMakeMeAMenu(p);
  2081. }
  2082. MCIWndiHandleError(p, dw);
  2083. // kick ourselves to show new Mode
  2084. MCIWndiTimerStuff(p);
  2085. }
  2086. return dw;
  2087. case MCIWNDM_PLAYREVERSE:
  2088. if (!p->wDeviceID)
  2089. return 0;
  2090. // Seek to the end if we're near the beginning
  2091. MCIWndiPlaySeek(p, TRUE);
  2092. dw = MCIWndString(p, TRUE, szPlayReverse, (LPTSTR)szNULL);
  2093. // kick ourselves to show new Mode
  2094. MCIWndiTimerStuff(p);
  2095. return dw;
  2096. case MCI_CLOSE:
  2097. if (lParam)
  2098. // We delayed the closing of the MCI device because the MCI
  2099. // device may have hooked our window proc and be on our stack
  2100. // and killing it before would have destroyed the universe.
  2101. // buggy drivers crash if we pass a null parms address
  2102. return mciSendCommand((UINT)lParam, MCI_CLOSE, 0,
  2103. (DWORD_PTR)(LPVOID)&mciGeneric);
  2104. else
  2105. // Do all the stuff for closing
  2106. return MCIWndiClose(p, TRUE);
  2107. case MCIWNDM_EJECT:
  2108. return MCIWndString(p, TRUE, szSetDoorOpen);
  2109. case MCIWNDM_PLAYFROM:
  2110. if (lParam == MCIWND_START)
  2111. dw = MCIWndString(p, TRUE, szPlayFrom, MCIWndGetStart(hwnd));
  2112. else
  2113. dw = MCIWndString(p, TRUE, szPlayFrom, (LONG)lParam);
  2114. MCIWndiTimerStuff(p); // kick ourselves to see mode change
  2115. return dw;
  2116. case MCIWNDM_PLAYTO:
  2117. if (lParam == MCIWND_END)
  2118. dw = MCIWndString(p, TRUE, szPlayTo, MCIWndGetEnd(hwnd));
  2119. else if (lParam == MCIWND_START)
  2120. dw = MCIWndString(p, TRUE, szPlayTo, MCIWndGetStart(hwnd));
  2121. else
  2122. dw = MCIWndString(p, TRUE, szPlayTo, (LONG)lParam);
  2123. MCIWndiTimerStuff(p); // kick ourselves to see mode change
  2124. return dw;
  2125. case MCI_STEP:
  2126. return MCIWndString(p, TRUE, szStep, (LONG)lParam);
  2127. case MCI_SEEK:
  2128. if (lParam == MCIWND_START)
  2129. return MCIWndString(p, TRUE, szSeek, MCIWndGetStart(hwnd));
  2130. else if (lParam == MCIWND_END)
  2131. return MCIWndString(p, TRUE, szSeek, MCIWndGetEnd(hwnd));
  2132. else
  2133. return MCIWndString(p, TRUE, szSeek, (LONG)lParam);
  2134. case MCIWNDM_SETREPEAT:
  2135. p->fRepeat = (BOOL)lParam;
  2136. return 0;
  2137. case MCIWNDM_GETREPEAT:
  2138. return p->fRepeat;
  2139. case MCIWNDM_GETDEVICEID:
  2140. return p->wDeviceID;
  2141. case MCIWNDM_GETALIAS:
  2142. return p->alias;
  2143. case MCIWNDM_GETMODE:
  2144. if (lParam)
  2145. MCIWndGet(p, szStatusMode, (LPTSTR)lParam, (UINT)wParam);
  2146. return MCIWndStatus(p, MCI_STATUS_MODE, MCI_MODE_NOT_READY);
  2147. #ifdef UNICODE
  2148. // ansi thunk for above
  2149. case MCIWNDM_GETMODEA:
  2150. if (lParam) {
  2151. MCIWndGetA(p, szStatusModeA, (LPSTR)lParam, (UINT)wParam);
  2152. }
  2153. return MCIWndStatus(p, MCI_STATUS_MODE, MCI_MODE_NOT_READY);
  2154. #endif
  2155. // Return the position as a string if they give us a buffer
  2156. case MCIWNDM_GETPOSITION:
  2157. #ifdef UNICODE
  2158. case MCIWNDM_GETPOSITIONA:
  2159. #endif
  2160. if (lParam) {
  2161. // If we can do tracks, let's give them a pretty string
  2162. if (p->fHasTracks)
  2163. MCIWndString(p, FALSE, szSetFormatTMSF);
  2164. #ifdef UNICODE
  2165. if (msg == MCIWNDM_GETPOSITIONA) {
  2166. MCIWndGetA(p, szStatusPositionA,
  2167. (LPSTR)lParam,(UINT)wParam);
  2168. }
  2169. else
  2170. #endif
  2171. {
  2172. MCIWndGet(p, szStatusPosition,
  2173. (LPTSTR)lParam,(UINT)wParam);
  2174. }
  2175. if (p->fHasTracks)
  2176. MCIWndString(p, FALSE, szSetFormatMS);
  2177. }
  2178. return MCIWndStatus(p, MCI_STATUS_POSITION, 0);
  2179. case MCIWNDM_GETSTART:
  2180. // Start is a command that works differently
  2181. return MCIWndGetValue(p, FALSE, szStatusStart, 0);
  2182. case MCIWNDM_GETLENGTH:
  2183. return MCIWndStatus(p, MCI_STATUS_LENGTH, 0);
  2184. case MCIWNDM_GETEND:
  2185. return MCIWndGetStart(hwnd) + MCIWndGetLength(hwnd);
  2186. case MCIWNDM_SETZOOM:
  2187. p->iZoom = (int)lParam;
  2188. MCIWndiSize(p, (int)lParam);
  2189. return 0;
  2190. case MCIWNDM_GETZOOM:
  2191. return p->iZoom ? p->iZoom : 100;
  2192. case MCIWNDM_GETPALETTE:
  2193. return MCIWndGetValue(p, FALSE, szStatusPalette, 0);
  2194. case MCIWNDM_SETPALETTE:
  2195. return MCIWndString(p, TRUE, szSetPalette, (HPALETTE)wParam);
  2196. //
  2197. // Returns our error code
  2198. //
  2199. case MCIWNDM_GETERROR:
  2200. if (lParam) {
  2201. mciGetErrorString(p->dwError, (LPTSTR)lParam, (UINT)wParam);
  2202. }
  2203. dw = p->dwError;
  2204. // p->dwError = 0L; // we never clear the error
  2205. return dw;
  2206. #ifdef UNICODE
  2207. // ansi thunk for above
  2208. case MCIWNDM_GETERRORA:
  2209. if (lParam) {
  2210. mciGetErrorStringA(p->dwError, (LPSTR)lParam, (UINT)wParam);
  2211. }
  2212. return p->dwError;
  2213. #endif
  2214. case MCIWNDM_GETFILENAME:
  2215. if (lParam)
  2216. lstrcpyn((LPTSTR)lParam, p->achFileName, (UINT)wParam);
  2217. return (lParam == 0); // !!!
  2218. #ifdef UNICODE
  2219. // ansi thunk for above
  2220. case MCIWNDM_GETFILENAMEA:
  2221. if (lParam) {
  2222. Iwcstombs((LPSTR)lParam, p->achFileName, (UINT)wParam);
  2223. }
  2224. return (lParam == 0);
  2225. #endif
  2226. case MCIWNDM_GETDEVICE:
  2227. if (lParam)
  2228. return MCIWndGet(p, szSysInfo, (LPTSTR)lParam,
  2229. (UINT)wParam);
  2230. return 42; // !!!
  2231. #ifdef UNICODE
  2232. // ansi thunk for above
  2233. case MCIWNDM_GETDEVICEA:
  2234. if (lParam) {
  2235. return MCIWndGetA(p, szSysInfoA, (LPSTR)lParam, (UINT)wParam);
  2236. } else {
  2237. return 42; // why do they do this??
  2238. }
  2239. #endif
  2240. case MCIWNDM_SETVOLUME:
  2241. // Uncheck the current volume, and check the new one.
  2242. // Round to nearest 5 so it matches a menu item identifier
  2243. i = ((int)MCIWndGetValue(p, FALSE, szStatusVolume, 1000) / 50) * 5;
  2244. if (p->hmenuVolume)
  2245. CheckMenuItem(p->hmenuVolume, IDM_MCIVOLUME + i, MF_UNCHECKED);
  2246. dw = MCIWndString(p, TRUE, szSetVolume, (int)lParam);
  2247. i = ((int)lParam / 50) * 5;
  2248. if (p->hmenuVolume)
  2249. CheckMenuItem(p->hmenuVolume, IDM_MCIVOLUME + i, MF_CHECKED);
  2250. return dw;
  2251. case MCIWNDM_GETVOLUME:
  2252. return MCIWndGetValue(p, FALSE, szStatusVolume, 1000);
  2253. case MCIWNDM_SETSPEED:
  2254. // Uncheck the current speed, and check the new one.
  2255. // Round to nearest 5 so it matches a menu item identifier
  2256. i = ((int)MCIWndGetValue(p, FALSE, szStatusSpeed, 1000) / 50) * 5;
  2257. if (p->hmenuSpeed)
  2258. CheckMenuItem(p->hmenuSpeed, IDM_MCISPEED + i, MF_UNCHECKED);
  2259. dw = MCIWndString(p, TRUE, szSetSpeed, (int)lParam);
  2260. i = ((int)lParam / 50) * 5;
  2261. if (p->hmenuSpeed)
  2262. CheckMenuItem(p->hmenuSpeed, IDM_MCISPEED + i, MF_CHECKED);
  2263. return dw;
  2264. case MCIWNDM_GETSPEED:
  2265. return MCIWndGetValue(p, FALSE, szStatusSpeed, 1000);
  2266. case MCIWNDM_SETTIMEFORMAT:
  2267. dw = MCIWndString(p, TRUE, szSetFormat, (LPTSTR)lParam);
  2268. MCIWndiValidateMedia(p);
  2269. return dw;
  2270. #ifdef UNICODE
  2271. // ansi thunk for above
  2272. case MCIWNDM_SETTIMEFORMATA:
  2273. {
  2274. WCHAR * lpW;
  2275. int sz = lstrlenA( (LPSTR) lParam) + 1;
  2276. lpW = (WCHAR *) LocalAlloc(LPTR, sz * sizeof(WCHAR));
  2277. if (lpW) {
  2278. Imbstowcs(lpW, (LPSTR) lParam, sz);
  2279. dw = MCIWndString(p, TRUE, szSetFormat, (LPTSTR)lpW);
  2280. MCIWndiValidateMedia(p);
  2281. LocalFree((HLOCAL)lpW);
  2282. } else {
  2283. dw = MCIERR_OUT_OF_MEMORY;
  2284. }
  2285. return dw;
  2286. }
  2287. #endif
  2288. case MCIWNDM_GETTIMEFORMAT:
  2289. if (lParam)
  2290. MCIWndGet(p, szStatusFormat, (LPTSTR)lParam, (UINT)wParam);
  2291. return MCIWndStatus(p, MCI_STATUS_TIME_FORMAT, 0); // !!!
  2292. #ifdef UNICODE
  2293. // ansi thunk for above
  2294. case MCIWNDM_GETTIMEFORMATA:
  2295. if (lParam) {
  2296. MCIWndGetA(p, szStatusFormatA, (LPSTR) lParam, (UINT)wParam);
  2297. }
  2298. return MCIWndStatus(p, MCI_STATUS_TIME_FORMAT, 0);
  2299. #endif
  2300. case MCIWNDM_VALIDATEMEDIA:
  2301. MCIWndiValidateMedia(p);
  2302. break;
  2303. case MCIWNDM_GETSTYLES:
  2304. return (UINT)(p->dwStyle & 0xFFFF);
  2305. case MCIWNDM_CHANGESTYLES:
  2306. return MCIWndiChangeStyles(p, (UINT)wParam, (UINT)lParam);
  2307. case MCIWNDM_SETACTIVETIMER:
  2308. if (wParam)
  2309. p->iActiveTimerRate = (unsigned)wParam;
  2310. if (p->fActive) {
  2311. KillTimer(hwnd, TIMER1);
  2312. MCIWndiSetTimer(p);
  2313. }
  2314. break;
  2315. case MCIWNDM_SETINACTIVETIMER:
  2316. if (wParam)
  2317. p->iInactiveTimerRate = (unsigned)wParam;
  2318. if (!p->fActive) {
  2319. KillTimer(hwnd, TIMER1);
  2320. MCIWndiSetTimer(p);
  2321. }
  2322. break;
  2323. case MCIWNDM_SETTIMERS:
  2324. if (wParam)
  2325. p->iActiveTimerRate = (unsigned)wParam;
  2326. if (lParam)
  2327. p->iInactiveTimerRate = (unsigned)lParam;
  2328. KillTimer(hwnd, TIMER1);
  2329. MCIWndiSetTimer(p);
  2330. break;
  2331. case MCIWNDM_GETACTIVETIMER:
  2332. return p->iActiveTimerRate;
  2333. case MCIWNDM_GETINACTIVETIMER:
  2334. return p->iInactiveTimerRate;
  2335. case MCIWNDM_SENDSTRING:
  2336. //
  2337. // App wants to send a string command.
  2338. // special case the CLOSE command to do our clean up
  2339. lstrcpyn(ach, (LPTSTR)lParam, lstrlen(szClose) + 1);
  2340. if (lstrcmpi((LPTSTR)ach, szClose) == 0)
  2341. return MCIWndClose(hwnd);
  2342. // Always sets/clears our error code
  2343. dw = MCIWndGet(p, (LPTSTR)lParam, p->achReturn,NUMELMS(p->achReturn));
  2344. MCIWndiHandleError(p, dw);
  2345. // kick ourselves in case mode changed from this command
  2346. MCIWndiTimerStuff(p);
  2347. return dw;
  2348. #ifdef UNICODE
  2349. // ansi thunk for above
  2350. // convert string and then re-send message rather than using
  2351. // MCIWndGetA since we need to store the error string in UNICODE.
  2352. case MCIWNDM_SENDSTRINGA:
  2353. {
  2354. WCHAR * lpW;
  2355. int sz = lstrlenA( (LPSTR) lParam) + 1;
  2356. lpW = (WCHAR *)LocalAlloc(LPTR, sz * sizeof(WCHAR));
  2357. if (lpW) {
  2358. Imbstowcs(lpW, (LPSTR) lParam, sz);
  2359. dw = (DWORD) MCIWndProc(hwnd, MCIWNDM_SENDSTRING, 0,
  2360. (LPARAM) (LPTSTR)lpW);
  2361. LocalFree((HLOCAL)lpW);
  2362. } else {
  2363. dw = MCIERR_OUT_OF_MEMORY;
  2364. }
  2365. return dw;
  2366. }
  2367. #endif
  2368. // Gets the return string from the most recent MCIWndSendString()
  2369. case MCIWNDM_RETURNSTRING:
  2370. if (lParam)
  2371. lstrcpyn((LPTSTR)lParam, p->achReturn, (DWORD) wParam);
  2372. return (lParam == 0); // !!!
  2373. #ifdef UNICODE
  2374. // ansi thunk for above
  2375. case MCIWNDM_RETURNSTRINGA:
  2376. if (lParam) {
  2377. Iwcstombs((LPSTR) lParam, p->achReturn, (DWORD) wParam);
  2378. }
  2379. return (lParam == 0);
  2380. #endif
  2381. case MCIWNDM_REALIZE:
  2382. // buggy drivers crash if we pass a null parms address
  2383. dw = mciSendCommand(p->wDeviceID, MCI_REALIZE,
  2384. (BOOL)wParam ? MCI_ANIM_REALIZE_BKGD : MCI_ANIM_REALIZE_NORM,
  2385. (DWORD_PTR)(LPVOID)&mciGeneric);
  2386. break;
  2387. case MCIWNDM_GET_SOURCE:
  2388. MCIWndRect(p, (LPRECT)lParam, TRUE);
  2389. return 0L;
  2390. case MCIWNDM_GET_DEST:
  2391. MCIWndRect(p, (LPRECT)lParam, FALSE);
  2392. return 0L;
  2393. case MCIWNDM_PUT_SOURCE:
  2394. prc = (LPRECT)lParam;
  2395. return MCIWndString(p, FALSE, szPutSource,
  2396. prc->left, prc->top,
  2397. prc->right - prc->left,
  2398. prc->bottom - prc->top);
  2399. case MCIWNDM_PUT_DEST:
  2400. prc = (LPRECT)lParam;
  2401. return MCIWndString(p, FALSE, szPutDest,
  2402. prc->left, prc->top,
  2403. prc->right - prc->left,
  2404. prc->bottom - prc->top);
  2405. case MCIWNDM_CAN_PLAY: return p->fCanPlay;
  2406. case MCIWNDM_CAN_WINDOW: return p->fCanWindow;
  2407. case MCIWNDM_CAN_RECORD: return p->fCanRecord;
  2408. case MCIWNDM_CAN_SAVE: return p->fCanSave;
  2409. case MCIWNDM_CAN_EJECT: return p->fCanEject;
  2410. case MCIWNDM_CAN_CONFIG: return p->fCanConfig;
  2411. case WM_TIMER:
  2412. // This timer means we've moved the mouse off of the menu and need
  2413. // to snap the thumb back to the original value
  2414. if (wParam == TIMER2) {
  2415. KillTimer(hwnd, TIMER2);
  2416. // If only this would cause OwnerDraw to execute, we could see
  2417. // the thumb bounce back to it's default place. Alas, no can do
  2418. //CheckMenuItem(p->hmenuHack, p->uiHack, MF_UNCHECKED);
  2419. //CheckMenuItem(p->hmenuHack, p->uiHack, MF_CHECKED);
  2420. // This code will at least set the parameter back even though
  2421. // the thumb won't physically move.
  2422. if (p->hmenuHack == p->hmenuVolume)
  2423. MCIWndSetVolume(hwnd, (p->uiHack - IDM_MCIVOLUME) * 10);
  2424. else
  2425. MCIWndSetSpeed(hwnd, (p->uiHack - IDM_MCISPEED) * 10);
  2426. }
  2427. //
  2428. // This is not our timer. Bail.
  2429. //
  2430. if (wParam != TIMER1)
  2431. break;
  2432. MCIWndiTimerStuff(p);
  2433. break;
  2434. case WM_GETMINMAXINFO:
  2435. // bug fix?
  2436. //
  2437. if (!p)
  2438. break;
  2439. // We don't want anybody messing with the window size
  2440. if (p->dwStyle & MCIWNDF_NOAUTOSIZEWINDOW)
  2441. break;
  2442. // do we have a playbar?
  2443. f = !(p->dwStyle & MCIWNDF_NOPLAYBAR);
  2444. lpmmi = (MINMAXINFO FAR *)(lParam);
  2445. SetRect(&rc, 0, 0, SMALLEST_WIDTH, f ? TB_HEIGHT : 0);
  2446. AdjustWindowRect(&rc, GetWindowLong(hwnd, GWL_STYLE), FALSE);
  2447. lpmmi->ptMinTrackSize.y = rc.bottom - rc.top;
  2448. lpmmi->ptMinTrackSize.x = rc.right - rc.left;
  2449. if (!(p->wDeviceID) || !(p->fCanWindow))
  2450. lpmmi->ptMaxTrackSize.y = lpmmi->ptMinTrackSize.y;
  2451. break;
  2452. case WM_SIZE:
  2453. GetClientRect(hwnd, &rc);
  2454. if (!IsIconic(hwnd)) {
  2455. // if we have a playbar, fix it up to the new size
  2456. f = !(p->dwStyle & MCIWNDF_NOPLAYBAR);
  2457. if (f) {
  2458. MCIWndiSizePlaybar(p);
  2459. rc.bottom -= TB_HEIGHT;
  2460. }
  2461. if (!(p->dwStyle & MCIWNDF_NOAUTOSIZEMOVIE))
  2462. MCIWndString(p, FALSE, szPutDest, 0,0, rc.right, rc.bottom);
  2463. } else {
  2464. if (!(p->dwStyle & MCIWNDF_NOAUTOSIZEMOVIE))
  2465. MCIWndString(p, FALSE, szPutDest, 0,0, rc.right, rc.bottom);
  2466. }
  2467. // We need to notify the "owner" that our size changed. Watch for
  2468. // excessive recursion, which happens with the VfW 1.1d MCIPUZZL
  2469. // sample app. That sample's NOTIFYSIZE handler resizes the owner
  2470. // window as a function of the MCI window size using
  2471. // AdjustWindowRect(). AdjustWindowRect() used to have a bug which
  2472. // MCIPUZZL adjusted for by adding 1 to the window height.
  2473. // Since the AdjustWindowRect() bug has been fixed, that sample
  2474. // would recurse infinitely because it will size the owner window
  2475. // one larger which will then resize the MCIWnd one larger which
  2476. // will then notify the owner which will resize the owner wnd one
  2477. // larger, etc...
  2478. if (p->cOnSizeReentered < 3) {
  2479. p->cOnSizeReentered++;
  2480. if (p->dwStyle & MCIWNDF_NOTIFYSIZE) {
  2481. NotifyOwner(p, MCIWNDM_NOTIFYSIZE, (WPARAM)p->hwnd, (LPARAM)NULL);
  2482. }
  2483. p->cOnSizeReentered--;
  2484. }
  2485. break;
  2486. case WM_RBUTTONDOWN:
  2487. case WM_NCRBUTTONDOWN:
  2488. case WM_PARENTNOTIFY:
  2489. // If we haven't got a menu, or we don't want it, bail
  2490. if (!p->hmenu || p->dwStyle & MCIWNDF_NOMENU)
  2491. break;
  2492. // If this is not a right button down, bail
  2493. if (msg == WM_PARENTNOTIFY && wParam != WM_RBUTTONDOWN)
  2494. break;
  2495. GetCursorPos(&pt);
  2496. // Don't allow error dlgs to come up while we're tracking. That
  2497. // would cause windows to enter the twilight zone.
  2498. p->fTracking = TRUE;
  2499. TrackPopupMenu(p->hmenu,
  2500. TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL);
  2501. p->fTracking = FALSE;
  2502. return 0;
  2503. case WM_PALETTECHANGED:
  2504. if ((HWND)wParam != hwnd && p->fHasPalette)
  2505. InvalidateRect(hwnd, NULL, FALSE);
  2506. break;
  2507. case WM_QUERYNEWPALETTE:
  2508. if (p->fHasPalette)
  2509. MCIWndRealize(hwnd, FALSE);
  2510. break;
  2511. // Send a WM_PALETTECHANGED to everyone in the system. We need to do
  2512. // this manually sometimes because of GDI.
  2513. case MCIWNDM_PALETTEKICK:
  2514. hwndD = GetDesktopWindow(); // tell everyone DESKTOP changed it
  2515. PostMessage((HWND)-1, WM_PALETTECHANGED, (WPARAM)hwndD, 0);
  2516. // DESKTOP won't repaint if we give it it's own HWND, so pick a
  2517. // random window and PRAY it'll stay valid.
  2518. hwndD = GetActiveWindow();
  2519. hwndD = GetWindow(hwndD, GW_HWNDLAST);
  2520. PostMessage(GetDesktopWindow(), WM_PALETTECHANGED, (WPARAM)hwndD,0);
  2521. return 0;
  2522. case MCIWNDM_OPENINTERFACE:
  2523. wsprintf(ach, szInterface, lParam);
  2524. return MCIWndiOpen(p, 0, (LPTSTR)ach);
  2525. case MCIWNDM_SETOWNER:
  2526. p->hwndOwner = (HWND)wParam;
  2527. return 0;
  2528. case WM_ERASEBKGND:
  2529. if (p->fCanWindow) {
  2530. MCIWndRect(p, &rc, FALSE);
  2531. SaveDC((HDC)wParam);
  2532. ExcludeClipRect((HDC)wParam, rc.left, rc.top, rc.right,
  2533. rc.bottom);
  2534. DefWindowProc(hwnd, msg, wParam, lParam);
  2535. RestoreDC((HDC)wParam, -1);
  2536. return 0;
  2537. }
  2538. break;
  2539. case WM_PAINT:
  2540. hdc = BeginPaint(hwnd, &ps);
  2541. if (p->wDeviceID && p->fCanWindow)
  2542. {
  2543. MCI_ANIM_UPDATE_PARMS mciUpdate;
  2544. mciUpdate.hDC = hdc;
  2545. dw = mciSendCommand(p->wDeviceID, MCI_UPDATE,
  2546. MCI_ANIM_UPDATE_HDC | MCI_WAIT |
  2547. MCI_DGV_UPDATE_PAINT,
  2548. (DWORD_PTR)(LPVOID)&mciUpdate);
  2549. if (dw != 0) /* if the update fails then erase */
  2550. DefWindowProc(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
  2551. } else if (IsIconic(hwnd)) {
  2552. DefWindowProc(hwnd, WM_ICONERASEBKGND, (WPARAM)hdc, 0);
  2553. DrawIcon(ps.hdc, 0, 0, p->hicon);
  2554. }
  2555. EndPaint(hwnd, &ps);
  2556. break;
  2557. case WM_KEYDOWN:
  2558. switch(wParam) {
  2559. case VK_LEFT:
  2560. SendMessage(hwnd, WM_HSCROLL, TB_LINEUP, 0); return 0;
  2561. case VK_RIGHT:
  2562. SendMessage(hwnd, WM_HSCROLL, TB_LINEDOWN, 0); return 0;
  2563. case VK_PRIOR:
  2564. SendMessage(hwnd, WM_HSCROLL, TB_PAGEUP, 0); return 0;
  2565. case VK_NEXT:
  2566. SendMessage(hwnd, WM_HSCROLL, TB_PAGEDOWN, 0); return 0;
  2567. case VK_HOME:
  2568. SendMessage(hwnd, WM_HSCROLL, TB_TOP, 0); return 0;
  2569. case VK_END:
  2570. SendMessage(hwnd, WM_HSCROLL, TB_BOTTOM, 0); return 0;
  2571. case VK_UP:
  2572. case VK_DOWN:
  2573. dw = MCIWndGetValue(p, FALSE, szStatusVolume, 1000);
  2574. if (wParam == VK_UP)
  2575. i = min((int)p->wMaxVol * 10, (int) dw + 100);
  2576. else
  2577. i = max(0, (int) dw - 100);
  2578. MCIWndSetVolume(p->hwnd, i);
  2579. return 0; // break will ding
  2580. default:
  2581. break;
  2582. }
  2583. break;
  2584. case WM_KEYUP:
  2585. switch(wParam) {
  2586. case VK_LEFT:
  2587. case VK_RIGHT:
  2588. case VK_PRIOR:
  2589. case VK_NEXT:
  2590. case VK_HOME:
  2591. case VK_END:
  2592. if (p->fScrolling)
  2593. SendMessage(hwnd, WM_HSCROLL, TB_ENDTRACK, 0);
  2594. return 0; // break will ding
  2595. case VK_ESCAPE:
  2596. MCIWndStop(hwnd);
  2597. return 0;
  2598. default:
  2599. break;
  2600. }
  2601. if (GetKeyState(VK_CONTROL) & 0x8000) {
  2602. switch(wParam) {
  2603. case '1':
  2604. case '2':
  2605. case '3':
  2606. case '4':
  2607. // Don't let somebody resize us if we're not normally
  2608. // resizable.
  2609. if (!(p->dwStyle & MCIWNDF_NOAUTOSIZEWINDOW) &&
  2610. (p->dwStyle & WS_THICKFRAME))
  2611. MCIWndSetZoom(hwnd, 100 * (wParam - '0'));
  2612. return 0; // break will ding
  2613. case 'P':
  2614. MCIWndPlay(hwnd); return 0;
  2615. case 'S':
  2616. MCIWndStop(hwnd); return 0;
  2617. case 'D':
  2618. // The key accelerator should only work if we gave
  2619. // them a menu for this command
  2620. if (!(p->dwStyle & MCIWNDF_NOMENU))
  2621. PostMessage(hwnd, WM_COMMAND, IDM_MCICONFIG, 0);
  2622. return 0;
  2623. case 'C':
  2624. PostMessage(hwnd, WM_COMMAND, IDM_COPY, 0); return 0;
  2625. case VK_F5:
  2626. // The key accelerator should only work if we gave
  2627. // them a menu for this command
  2628. if (!(p->dwStyle & MCIWNDF_NOMENU))
  2629. PostMessage(hwnd, WM_COMMAND, IDM_MCICOMMAND, 0);
  2630. return 0;
  2631. case 'F':
  2632. case 'O':
  2633. if (!(p->dwStyle & MCIWNDF_NOOPEN))
  2634. MCIWndOpenDialog(hwnd);
  2635. return 0;
  2636. case 'M':
  2637. PostMessage(hwnd, WM_COMMAND,
  2638. GET_WM_COMMAND_MPS(ID_TOOLBAR, IDM_MENU,
  2639. TBN_BEGINDRAG));
  2640. return 0;
  2641. default:
  2642. break;
  2643. }
  2644. }
  2645. break;
  2646. case WM_SYSCHAR:
  2647. switch(wParam) {
  2648. case '1':
  2649. case '2':
  2650. case '3':
  2651. case '4':
  2652. // Don't let somebody resize us if we're not normally
  2653. // resizable.
  2654. if (!(p->dwStyle & MCIWNDF_NOAUTOSIZEWINDOW) &&
  2655. (p->dwStyle & WS_THICKFRAME))
  2656. MCIWndSetZoom(hwnd, 100 / ((UINT) wParam - '0'));
  2657. return 0; // break will ding
  2658. default:
  2659. break;
  2660. }
  2661. break;
  2662. case WM_HSCROLL:
  2663. #define FORWARD 1
  2664. #define BACKWARD 2
  2665. dwPos = (DWORD) SendMessage(p->hwndTrackbar, TBM_GETPOS, 0, 0);
  2666. // nothing to do - spurious END without BEGIN
  2667. if (!p->fScrolling && wParam == TB_ENDTRACK)
  2668. break;
  2669. // Turn seek exactly off while scrolling and remember what it was
  2670. // Also, remember if we were playing just before we seeked so we
  2671. // can continue playing after the seek (so moving the thumb doesn't
  2672. // stop the play).
  2673. if (!p->fScrolling) {
  2674. p->fScrolling = TRUE;
  2675. // Wierd artifacts happen if you turn seek exactly off while
  2676. // seeking. You see the key frame and then the actual frame.
  2677. // Nobody can remember why this was ever a good idea.
  2678. //p->fSeekExact = MCIWndSeekExact(p, FALSE);
  2679. // if we're still seeking from last time, don't change this
  2680. if (p->dwMode != MCI_MODE_SEEK)
  2681. p->fPlayAfterSeek = (p->dwMode == MCI_MODE_PLAY);
  2682. // Now which direction was it playing in?
  2683. if (p->fPlayAfterSeek) {
  2684. MCIWndGet(p, szStatusForward, ach, NUMELMS(ach));
  2685. if (ach[0] == TEXT('F') || ach[0] == TEXT('f'))
  2686. p->fPlayAfterSeek = BACKWARD;
  2687. else // by default, choose forward. Some devices
  2688. // don't understand this command and fail.
  2689. p->fPlayAfterSeek = FORWARD;
  2690. }
  2691. }
  2692. switch(wParam)
  2693. {
  2694. case TB_LINEUP:
  2695. dwPos--; break;
  2696. case TB_LINEDOWN:
  2697. dwPos++; break;
  2698. case TB_PAGEUP:
  2699. if (p->fHasTracks) {
  2700. dwPos = MCIWndiPrevTrack(p); break;
  2701. } else {
  2702. dwPos -= p->dwMediaLen / 16; break;
  2703. }
  2704. case TB_PAGEDOWN:
  2705. if (p->fHasTracks) {
  2706. dwPos = MCIWndiNextTrack(p); break;
  2707. } else {
  2708. dwPos += p->dwMediaLen / 16; break;
  2709. }
  2710. case TB_TOP:
  2711. dwPos = p->dwMediaStart; break;
  2712. case TB_BOTTOM:
  2713. dwPos = p->dwMediaStart + p->dwMediaLen; break;
  2714. case TB_THUMBTRACK:
  2715. case TB_THUMBPOSITION:
  2716. break;
  2717. case TB_ENDTRACK:
  2718. // All done. Put seek exact back to what it used to be
  2719. p->fScrolling = FALSE;
  2720. // Don't do this anymore (see above)
  2721. //MCIWndSeekExact(p, p->fSeekExact);
  2722. break;
  2723. default:
  2724. break;
  2725. }
  2726. // If we're windowed, update the position as we scroll. That would
  2727. // be annoying for CD or wave, though. Also, update as soon as we
  2728. // let go of the thumb. Also, never seek around while we're open
  2729. // or not ready.
  2730. if ((p->fCanWindow || !p->fScrolling) && p->dwMode != MCI_MODE_OPEN
  2731. && p->dwMode != MCI_MODE_NOT_READY) {
  2732. MCIWndSeek(hwnd, dwPos);
  2733. MCIWndiTimerStuff(p); // kick ourselves to update mode
  2734. }
  2735. // After we're done, if we were playing before, go back to playing
  2736. if (!p->fScrolling && p->fPlayAfterSeek) {
  2737. if (p->fPlayAfterSeek == FORWARD)
  2738. MCIWndPlay(hwnd);
  2739. else
  2740. MCIWndPlayReverse(hwnd);
  2741. MCIWndiTimerStuff(p); // kick ourselves to update mode
  2742. }
  2743. // Set the trackbar to the (possibly) new position
  2744. SendMessage(p->hwndTrackbar, TBM_SETPOS, TRUE, dwPos);
  2745. break;
  2746. case WM_MENUSELECT:
  2747. break;
  2748. // Sent from a toolbar button being pressed
  2749. case WM_COMMAND:
  2750. return MCIWndCommands (p, hwnd, wParam, lParam);
  2751. break;
  2752. // When somebody presses a toolbar button in 32 bit Chicago-land, we are told
  2753. // about it via a WM_NOTIFY. Else, we are sent a WM_COMMAND, handled elsewhere.
  2754. #ifdef _WIN32
  2755. case WM_NOTIFY: {
  2756. #define lpHdr ((TBNOTIFY *)lParam)
  2757. if (lpHdr->hdr.code == TBN_BEGINDRAG) {
  2758. RECT rc;
  2759. RECT rcT;
  2760. MSG msgT;
  2761. // We're only interested in a pressing of the Menu button
  2762. if (lpHdr->hdr.idFrom != ID_TOOLBAR ||
  2763. lpHdr->iItem != IDM_MENU ||
  2764. !SendMessage(p->hwndToolbar, TB_ISBUTTONENABLED,
  2765. IDM_MENU, 0) ||
  2766. !p->hmenu)
  2767. break;
  2768. SendMessage(p->hwndToolbar, TB_GETITEMRECT,
  2769. (int)SendMessage(p->hwndToolbar, TB_COMMANDTOINDEX,
  2770. IDM_MENU, 0),
  2771. (LPARAM)(LPVOID)&rc);
  2772. rcT = rc;
  2773. ClientToScreen(p->hwndToolbar, (LPPOINT)&rc);
  2774. ClientToScreen(p->hwndToolbar, (LPPOINT)&rc + 1);
  2775. // Push the button down (accelerator won't have done this)
  2776. SendMessage(p->hwndToolbar, TB_PRESSBUTTON, IDM_MENU,
  2777. TRUE);
  2778. // Don't allow error dlgs to come up while we're tracking.
  2779. // That would cause windows to shatter and send shrapnel
  2780. // flying.
  2781. p->fTracking = TRUE;
  2782. TrackPopupMenu(p->hmenu, 0, rc.left, rc.bottom - 1, 0,
  2783. hwnd, &rc); // don't dismiss menu inside button
  2784. p->fTracking = FALSE;
  2785. // Bring the button back up.
  2786. SendMessage(p->hwndToolbar, TB_PRESSBUTTON, IDM_MENU,
  2787. FALSE);
  2788. // What if we press the menu button to make the menu go
  2789. // away? It's just going to bring the menu back up again!
  2790. // So we need to pull the click out of the queue.
  2791. // There are bugs in the toolbar code to prevent me from
  2792. // doing this any other way (like disabling the button)
  2793. if (PeekMessage(&msgT, p->hwndToolbar, WM_LBUTTONDOWN,
  2794. WM_LBUTTONDOWN, PM_NOREMOVE)) {
  2795. POINT pt = {MAKEPOINTS(msgT.lParam).x,
  2796. MAKEPOINTS(msgT.lParam).y };
  2797. if (PtInRect(&rcT, pt))
  2798. PeekMessage(&msgT, p->hwndToolbar, WM_LBUTTONDOWN,
  2799. WM_LBUTTONDOWN, PM_REMOVE);
  2800. }
  2801. }
  2802. else if (lpHdr->hdr.code == TTN_NEEDTEXT) {
  2803. LPTOOLTIPTEXT lpTt;
  2804. extern HINSTANCE ghInst; // in video\init.c
  2805. lpTt = (LPTOOLTIPTEXT)lParam;
  2806. LoadString( ghInst, (UINT) lpTt->hdr.idFrom,
  2807. lpTt->szText, NUMELMS(lpTt->szText) );
  2808. return 0;
  2809. }
  2810. break;
  2811. }
  2812. #endif
  2813. case WM_DESTROY:
  2814. // !!! MMP CLOSE will be deferred till AFTER the DESTROY
  2815. // Don't palette kick when we're going down.
  2816. //
  2817. p->fHasPalette = FALSE;
  2818. MCIWndiClose(p, FALSE); //don't leave us playing into a random DC
  2819. break;
  2820. // We can't free our pointer until now, because WM_NCDESTROY is the
  2821. // guaranteed last message we'll get. If we do it sooner, we could
  2822. // fault trying to use it.
  2823. case WM_NCDESTROY:
  2824. if (p->hmenu) {
  2825. DestroyMenu(p->hmenu);
  2826. if (p->hbrDither) {
  2827. DeleteObject(p->hbrDither);
  2828. p->hbrDither = NULL;
  2829. }
  2830. }
  2831. if (p->pTrackStart)
  2832. LocalFree((HANDLE)p->pTrackStart);
  2833. if (p->hfont) {
  2834. // !!! Someone else may have to go and create it again, but oh
  2835. // !!! well.
  2836. DeleteObject(p->hfont);
  2837. p->hfont = NULL;
  2838. }
  2839. if (p->hicon) {
  2840. DestroyIcon(p->hicon);
  2841. p->hicon = NULL;
  2842. }
  2843. // if (p->hbmToolbar) {
  2844. // DeleteObject(p->hbmToolbar);
  2845. // p->hbmToolbar = NULL;
  2846. // }
  2847. // We can't destroy our pointer and then fall through and use it
  2848. f = p->fMdiWindow;
  2849. LocalFree((HLOCAL) p);
  2850. SetWindowLong(hwnd, 0, 0); // our p
  2851. if (f)
  2852. return DefMDIChildProc(hwnd, msg, wParam, lParam);
  2853. else
  2854. return DefWindowProc(hwnd, msg, wParam, lParam);
  2855. // Use a different rate for the timer depending on if we're active
  2856. // or not.
  2857. case WM_NCACTIVATE:
  2858. // MDI windows need to realize their palette here
  2859. if (p->wDeviceID && p->fMdiWindow && p->fHasPalette)
  2860. MCIWndRealize(hwnd, wParam == FALSE);
  2861. #if 0
  2862. case WM_ACTIVATE:
  2863. p->fActive = wParam;
  2864. KillTimer(hwnd, TIMER1);
  2865. MCIWndiSetTimer(p);
  2866. #endif
  2867. break;
  2868. case WM_SETFOCUS:
  2869. p->fActive = TRUE;
  2870. KillTimer(hwnd, TIMER1);
  2871. MCIWndiSetTimer(p);
  2872. break;
  2873. case WM_KILLFOCUS:
  2874. p->fActive = FALSE;
  2875. KillTimer(hwnd, TIMER1);
  2876. MCIWndiSetTimer(p);
  2877. break;
  2878. // If the user uses MCINOTIFY we pass the notify on to the "owner"
  2879. case MM_MCINOTIFY:
  2880. // Kick ourselves to update toolbar/titles since getting a notify
  2881. // means that stuff might have changed.
  2882. MCIWndiTimerStuff(p);
  2883. return NotifyOwner(p, msg, wParam, lParam);
  2884. case WM_DRAWITEM:
  2885. case WM_MEASUREITEM:
  2886. case WM_DELETEITEM:
  2887. OwnerDraw(p, msg, wParam, lParam);
  2888. return TRUE; // !!!
  2889. case WM_SYSCOMMAND:
  2890. switch (wParam & ~0xF) {
  2891. case SC_MINIMIZE:
  2892. // Minimizing from MAXIMIZED state better do the same thing
  2893. // as restore or windows will always think it's maximized
  2894. // and start wierding out on us (Chico bug 19541).
  2895. if (IsZoomed(hwnd)) {
  2896. wParam = SC_RESTORE | (wParam & 0xF);
  2897. break; // MUST let DefWndProc run
  2898. }
  2899. if (p->wDeviceID && p->fCanWindow) {
  2900. RECT rc;
  2901. MCIWndGetDest(hwnd, &rc);
  2902. if (rc.right > p->rcNormal.right &&
  2903. rc.bottom > p->rcNormal.bottom) {
  2904. // We pressed buttons on the title bar... we really
  2905. // better autosize window.
  2906. DWORD dw = p->dwStyle;
  2907. p->dwStyle &= ~MCIWNDF_NOAUTOSIZEWINDOW;
  2908. MCIWndSetZoom(hwnd, 100);
  2909. p->dwStyle = dw;
  2910. return 0;
  2911. }
  2912. }
  2913. break;
  2914. case SC_MAXIMIZE:
  2915. if (p->fCanWindow && !IsIconic(hwnd)) {
  2916. RECT rc;
  2917. MCIWndGetDest(hwnd, &rc);
  2918. if (rc.right < p->rcNormal.right &&
  2919. rc.bottom < p->rcNormal.bottom) {
  2920. // We pressed buttons on the title bar... we really
  2921. // better autosize window.
  2922. DWORD dw = p->dwStyle;
  2923. p->dwStyle &= ~MCIWNDF_NOAUTOSIZEWINDOW;
  2924. MCIWndSetZoom(hwnd, 100);
  2925. p->dwStyle = dw;
  2926. return 0;
  2927. }
  2928. if (rc.right >= p->rcNormal.right &&
  2929. rc.right < p->rcNormal.right*2 &&
  2930. rc.bottom >= p->rcNormal.bottom &&
  2931. rc.bottom < p->rcNormal.bottom*2) {
  2932. // We pressed buttons on the title bar... we really
  2933. // better autosize window.
  2934. DWORD dw = p->dwStyle;
  2935. p->dwStyle &= ~MCIWNDF_NOAUTOSIZEWINDOW;
  2936. MCIWndSetZoom(hwnd, 200);
  2937. p->dwStyle = dw;
  2938. return 0;
  2939. }
  2940. }
  2941. break;
  2942. }
  2943. break;
  2944. case WM_DROPFILES:
  2945. MCIWndiDrop(hwnd, wParam);
  2946. break;
  2947. case WM_QUERYDRAGICON:
  2948. return (LONG_PTR)(UINT_PTR)p->hicon;
  2949. }
  2950. if (p && p->fMdiWindow)
  2951. return DefMDIChildProc(hwnd, msg, wParam, lParam);
  2952. else
  2953. return DefWindowProc(hwnd, msg, wParam, lParam);
  2954. }
  2955. STATICFN void NEAR PASCAL PatRect(HDC hdc,int x,int y,int dx,int dy)
  2956. {
  2957. RECT rc;
  2958. rc.left = x;
  2959. rc.top = y;
  2960. rc.right = x + dx;
  2961. rc.bottom = y + dy;
  2962. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  2963. }
  2964. #define FillRC(hdc, prc) PatRect(hdc, (prc)->left, (prc)->top, (prc)->right - (prc)->left, (prc)->bottom-(prc)->top)
  2965. STATICFN HBITMAP NEAR PASCAL CreateDitherBitmap(void)
  2966. {
  2967. PBITMAPINFO pbmi;
  2968. HBITMAP hbm;
  2969. HDC hdc;
  2970. int i;
  2971. long patGray[8];
  2972. DWORD rgb;
  2973. pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) +
  2974. (sizeof(RGBQUAD) * 16));
  2975. if (!pbmi)
  2976. return NULL;
  2977. pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  2978. pbmi->bmiHeader.biWidth = 8;
  2979. pbmi->bmiHeader.biHeight = 8;
  2980. pbmi->bmiHeader.biPlanes = 1;
  2981. pbmi->bmiHeader.biBitCount = 1;
  2982. pbmi->bmiHeader.biCompression = BI_RGB;
  2983. rgb = GetSysColor(COLOR_BTNFACE);
  2984. pbmi->bmiColors[0].rgbBlue = GetBValue(rgb);
  2985. pbmi->bmiColors[0].rgbGreen = GetGValue(rgb);
  2986. pbmi->bmiColors[0].rgbRed = GetRValue(rgb);
  2987. pbmi->bmiColors[0].rgbReserved = 0;
  2988. rgb = GetSysColor(COLOR_BTNHIGHLIGHT);
  2989. pbmi->bmiColors[1].rgbBlue = GetBValue(rgb);
  2990. pbmi->bmiColors[1].rgbGreen = GetGValue(rgb);
  2991. pbmi->bmiColors[1].rgbRed = GetRValue(rgb);
  2992. pbmi->bmiColors[1].rgbReserved = 0;
  2993. /* initialize the brushes */
  2994. for (i = 0; i < 8; i++)
  2995. if (i & 1)
  2996. patGray[i] = 0xAAAA5555L; // 0x11114444L; // lighter gray
  2997. else
  2998. patGray[i] = 0x5555AAAAL; // 0x11114444L; // lighter gray
  2999. hdc = GetDC(NULL);
  3000. hbm = CreateDIBitmap(hdc, &pbmi->bmiHeader, CBM_INIT, patGray, pbmi,
  3001. DIB_RGB_COLORS);
  3002. ReleaseDC(NULL, hdc);
  3003. LocalFree((HANDLE)pbmi);
  3004. return hbm;
  3005. }
  3006. STATICFN HBRUSH NEAR PASCAL CreateDitherBrush(void)
  3007. {
  3008. HBITMAP hbmGray;
  3009. HBRUSH hbrDither;
  3010. hbmGray = CreateDitherBitmap();
  3011. if (hbmGray) {
  3012. hbrDither = CreatePatternBrush(hbmGray);
  3013. DeleteObject(hbmGray);
  3014. return hbrDither;
  3015. }
  3016. return NULL;
  3017. }
  3018. //
  3019. // Draw the channel for the volume and speed menu controls
  3020. //
  3021. STATICFN void NEAR PASCAL DrawChannel(HDC hdc, LPRECT prc, HBRUSH hbrDither)
  3022. {
  3023. HBRUSH hbrTemp;
  3024. int iWidth = prc->right - prc->left;
  3025. // draw the frame around the window
  3026. SetBkColor(hdc, GetSysColor(COLOR_WINDOWFRAME));
  3027. PatRect(hdc, prc->left, prc->top, iWidth, 1);
  3028. PatRect(hdc, prc->left, prc->bottom-2, iWidth, 1);
  3029. PatRect(hdc, prc->left, prc->top, 1, prc->bottom-prc->top-1);
  3030. PatRect(hdc, prc->right-1, prc->top, 1, prc->bottom-prc->top-1);
  3031. SetBkColor(hdc, GetSysColor(COLOR_BTNHIGHLIGHT));
  3032. PatRect(hdc, prc->left, prc->bottom-1, iWidth, 1);
  3033. SetBkColor(hdc, GetSysColor(COLOR_BTNSHADOW));
  3034. PatRect(hdc, prc->left+1, prc->top + 1, iWidth-2,1);
  3035. // draw the background in dither gray
  3036. hbrTemp = SelectObject(hdc, hbrDither);
  3037. if (hbrTemp) {
  3038. PatBlt(hdc, prc->left+1, prc->top + 2,
  3039. iWidth-2, prc->bottom-prc->top-4, PATCOPY);
  3040. SelectObject(hdc, hbrTemp);
  3041. }
  3042. }
  3043. STATICFN LRESULT OwnerDraw(PMCIWND p, UINT msg, WPARAM wParam, LPARAM lParam)
  3044. {
  3045. RECT rc, rcMenu, rcChannel, rcThumb;
  3046. HDC hdc;
  3047. int i,dx,dy,len;
  3048. SIZE size;
  3049. TCHAR ach[10];
  3050. HWND hwnd = p->hwnd;
  3051. #define lpMIS ((LPMEASUREITEMSTRUCT)lParam)
  3052. #define lpDIS ((LPDRAWITEMSTRUCT)lParam)
  3053. #define WIDTH_FROM_THIN_AIR 14
  3054. #define CHANNEL_INDENT 6 // for VOLUME and SPEED menu trackbar
  3055. #define MENU_WIDTH 10
  3056. #define THUMB 5
  3057. #define MENU_ITEM_HEIGHT 2
  3058. switch (msg)
  3059. {
  3060. case WM_MEASUREITEM:
  3061. if (p->hfont == NULL)
  3062. p->hfont = CreateFont (8, 0, 0, 0,
  3063. FW_NORMAL,FALSE,FALSE,FALSE,
  3064. ANSI_CHARSET,OUT_DEFAULT_PRECIS,
  3065. CLIP_DEFAULT_PRECIS,PROOF_QUALITY,
  3066. VARIABLE_PITCH | FF_DONTCARE,
  3067. szSmallFonts);
  3068. //
  3069. // The first and last menu items are the spaces above and below
  3070. // the channel, so they need to be taller.
  3071. //
  3072. if (lpMIS->itemID == IDM_MCIVOLUME + VOLUME_MAX + 1
  3073. || lpMIS->itemID == IDM_MCISPEED + SPEED_MAX + 1
  3074. || lpMIS->itemID == IDM_MCIVOLUME + VOLUME_MAX + 2
  3075. || lpMIS->itemID == IDM_MCISPEED + SPEED_MAX + 2) {
  3076. lpMIS->itemHeight = CHANNEL_INDENT;
  3077. lpMIS->itemWidth = MENU_WIDTH;
  3078. } else {
  3079. lpMIS->itemHeight = MENU_ITEM_HEIGHT;
  3080. lpMIS->itemWidth = MENU_WIDTH;
  3081. }
  3082. return TRUE;
  3083. case WM_DRAWITEM:
  3084. rc = lpDIS->rcItem;
  3085. hdc = lpDIS->hDC;
  3086. //
  3087. // Something has been deselected. If we don't see a new selection
  3088. // soon, it means we've dragged the cursor off the menu, and we
  3089. // should pop the thumb back to its original spot.
  3090. //
  3091. if ((lpDIS->itemAction & ODA_SELECT) &&
  3092. !(lpDIS->itemState & ODS_SELECTED))
  3093. SetTimer(p->hwnd, TIMER2, 500, NULL);
  3094. //
  3095. // When asked to draw the selected or checked menu item, we will
  3096. // draw the entire menu. Otherwise, we don't do a thing
  3097. //
  3098. if (lpDIS->itemState & (ODS_SELECTED | ODS_CHECKED)) {
  3099. // This is the item that is checked, or the original spot for
  3100. // the thumb. Remember it so when we drag off the menu, we
  3101. // can bounce the thumb back here.
  3102. if (lpDIS->itemState & ODS_CHECKED) {
  3103. p->uiHack = lpDIS->itemID;
  3104. if (p->uiHack >= IDM_MCISPEED &&
  3105. p->uiHack <= IDM_MCISPEED + SPEED_MAX)
  3106. p->hmenuHack = p->hmenuSpeed;
  3107. else
  3108. p->hmenuHack = p->hmenuVolume;
  3109. }
  3110. // Something is being selected. Obviously the mouse is still
  3111. // on the menu. Scrap our timer that was waiting to see if
  3112. // we've dragged off the menu.
  3113. if (lpDIS->itemState & ODS_SELECTED)
  3114. KillTimer(p->hwnd, TIMER2);
  3115. // !!! Hack from Hell !!!
  3116. // If we try to highlight the bogus menu items, bail!
  3117. if (lpDIS->itemID == IDM_MCIVOLUME + VOLUME_MAX + 1)
  3118. break;
  3119. if (lpDIS->itemID == IDM_MCIVOLUME + VOLUME_MAX + 2)
  3120. break;
  3121. if (lpDIS->itemID == IDM_MCISPEED + SPEED_MAX + 1)
  3122. break;
  3123. if (lpDIS->itemID == IDM_MCISPEED + SPEED_MAX + 2)
  3124. break;
  3125. // Actually set the parameter to the value we're dragging so
  3126. // we can hear it change as we move the slider.
  3127. // 42 means DON'T CHECK it (remember which item was originally
  3128. // checked).
  3129. SendMessage(hwnd, WM_COMMAND, lpDIS->itemID, 42);
  3130. //
  3131. // Get the rect of our menu window. GetClipBox is
  3132. // not quite right, so we'll adjust for the border. Our lpDIS
  3133. // contains the proper width of the client area, so we'll use
  3134. // that.
  3135. //
  3136. GetClipBox(hdc, &rc);
  3137. rc.top++; //!!! top border width
  3138. rc.bottom -= 2; //!!! bottom border width
  3139. rc.left = lpDIS->rcItem.left;
  3140. rc.right = lpDIS->rcItem.right;
  3141. rcMenu = rc; // This is the rect of the whole menu
  3142. // !!!
  3143. // Deflate the rect to the area we want the channel to be
  3144. // drawn in. Use HACKY constants.
  3145. // !!!
  3146. i = (rc.right - rc.left - WIDTH_FROM_THIN_AIR) / 2;
  3147. rc.top += CHANNEL_INDENT;
  3148. rc.bottom -= CHANNEL_INDENT;
  3149. rc.left += i;
  3150. rc.right -= i;
  3151. rcChannel = rc; // This is the rect of the channel
  3152. //
  3153. // See where the thumb belongs
  3154. //
  3155. rc = lpDIS->rcItem;
  3156. rc.bottom = rc.top + 2; // Ouch! Make sure size is 2
  3157. //
  3158. // Don't draw the thumb higher than the top of the channel
  3159. //
  3160. if (rc.top < rcChannel.top) {
  3161. rc.top = rcChannel.top;
  3162. rc.bottom = rc.top + 2; // itemHeight
  3163. }
  3164. //
  3165. // Don't draw the thumb below the bottom of the channel
  3166. //
  3167. if (rc.top > rcChannel.bottom - 2) { // where border is
  3168. rc.top = rcChannel.bottom - 2;
  3169. rc.bottom = rc.top + 2;
  3170. }
  3171. //
  3172. // Munge the rect in a bit and draw the thumb there
  3173. //
  3174. rc.left += 2;
  3175. rc.right -= 2;
  3176. rc.bottom+= THUMB;
  3177. rc.top -= THUMB;
  3178. #if 0
  3179. // Make the thumb a little bigger on the checked value
  3180. if (lpDIS->itemState & ODS_CHECKED) {
  3181. rc.top -= 1;
  3182. rc.bottom += 1;
  3183. }
  3184. #endif
  3185. rcThumb = rc; // This is the rect of the thumb
  3186. dx = rc.right - rc.left;
  3187. dy = rc.bottom - rc.top;
  3188. SetBkColor(hdc, GetSysColor(COLOR_WINDOWFRAME));
  3189. PatRect(hdc, rc.left+1, rc.top, dx-2,1 );
  3190. PatRect(hdc, rc.left+1, rc.bottom-1,dx-2,1 );
  3191. PatRect(hdc, rc.left, rc.top+1, 1,dy-2 );
  3192. PatRect(hdc, rc.right-1, rc.top+1, 1,dy-2 );
  3193. InflateRect(&rc,-1,-1);
  3194. dx = rc.right - rc.left;
  3195. dy = rc.bottom - rc.top;
  3196. SetBkColor(hdc, GetSysColor(COLOR_BTNHIGHLIGHT));
  3197. PatRect(hdc, rc.left, rc.top, 1,dy);
  3198. PatRect(hdc, rc.left, rc.top, dx,1);
  3199. SetBkColor(hdc, GetSysColor(COLOR_BTNSHADOW));
  3200. PatRect(hdc, rc.right-1,rc.top+1, 1,dy-1);
  3201. PatRect(hdc, rc.left+1, rc.bottom-1, dx-1,1);
  3202. InflateRect(&rc,-1,-1);
  3203. SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
  3204. SelectObject(hdc, p->hfont);
  3205. len = wsprintf(ach, TEXT("%d"), lpMIS->itemID % 1000);
  3206. GetTextExtentPoint(hdc, ach, len, &size);
  3207. ExtTextOut(hdc,
  3208. (rc.right + rc.left - size.cx)/2,
  3209. (rc.bottom + rc.top - size.cy)/2,
  3210. ETO_OPAQUE,&rc,ach,len,NULL);
  3211. //
  3212. // Exclude the ClipRect that all that garbage drew into
  3213. //
  3214. ExcludeClipRect(hdc, rcThumb.left, rcThumb.top,
  3215. rcThumb.right, rcThumb.bottom);
  3216. //
  3217. // Next, draw the channel
  3218. //
  3219. DrawChannel(hdc, &rcChannel, p->hbrDither);
  3220. ExcludeClipRect(hdc, rcChannel.left, rcChannel.top,
  3221. rcChannel.right, rcChannel.bottom);
  3222. //
  3223. // Lastly, fill the entire menu rect with the menu colour
  3224. //
  3225. SetBkColor(hdc, GetSysColor(COLOR_MENU));
  3226. FillRC(hdc, &rcMenu);
  3227. }
  3228. return TRUE;
  3229. case WM_DELETEITEM:
  3230. return TRUE;
  3231. }
  3232. return TRUE;
  3233. }
  3234. //
  3235. // Code to implement the MCI command dialog box
  3236. //
  3237. void PositionWindowNearParent(HWND hwnd)
  3238. {
  3239. RECT rc;
  3240. RECT rcParent;
  3241. GetWindowRect(hwnd, &rc);
  3242. rc.bottom -= rc.top;
  3243. rc.right -= rc.left;
  3244. GetWindowRect(GetParent(hwnd), &rcParent);
  3245. if (rcParent.bottom + rc.bottom <
  3246. GetSystemMetrics(SM_CYSCREEN)) {
  3247. SetWindowPos(hwnd, NULL,
  3248. min(rc.left, GetSystemMetrics(SM_CXSCREEN) - rc.right),
  3249. rcParent.bottom,
  3250. 0, 0,
  3251. SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
  3252. } else if (rc.bottom < rcParent.top) {
  3253. SetWindowPos(hwnd, NULL,
  3254. min(rc.left, GetSystemMetrics(SM_CXSCREEN) - rc.right),
  3255. rcParent.top - rc.bottom,
  3256. 0, 0,
  3257. SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
  3258. }
  3259. }
  3260. /*--------------------------------------------------------------+
  3261. | mciDialog - bring up the dialog for MCI Send Command |
  3262. | |
  3263. +--------------------------------------------------------------*/
  3264. INT_PTR FAR PASCAL _loadds mciDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  3265. {
  3266. TCHAR ach[255];
  3267. TCHAR achT[40];
  3268. UINT w;
  3269. DWORD dw;
  3270. PMCIWND p;
  3271. HWND hwndP;
  3272. switch (msg)
  3273. {
  3274. case WM_INITDIALOG:
  3275. // Remember our actually true parent
  3276. SetWindowLongPtr(hwnd, DWLP_USER, lParam);
  3277. PositionWindowNearParent(hwnd);
  3278. return TRUE;
  3279. case WM_COMMAND:
  3280. switch (GET_WM_COMMAND_ID(wParam,lParam))
  3281. {
  3282. case IDOK:
  3283. Edit_SetSel (GetDlgItem (hwnd, IDC_MCICOMMAND), 0, -1);
  3284. w = GetDlgItemText(hwnd, IDC_MCICOMMAND, ach, NUMELMS(ach));
  3285. hwndP = (HWND)GetWindowLongPtr(hwnd, DWLP_USER);
  3286. p = (PMCIWND)(UINT)GetWindowLong(hwndP, 0);
  3287. // special case the CLOSE command to do our clean up
  3288. lstrcpyn(achT, (LPTSTR)ach, lstrlen(szClose) + 1);
  3289. if (lstrcmpi((LPTSTR)achT, szClose) == 0) {
  3290. MCIWndClose(hwndP);
  3291. break;
  3292. }
  3293. dw = MCIWndGet(p, ach, ach, NUMELMS(ach));
  3294. if (dw != 0)
  3295. mciGetErrorString(dw, ach, NUMELMS(ach));
  3296. SetDlgItemText(hwnd, IDC_RESULT, ach);
  3297. // kick ourselves in case mode changed from this command
  3298. MCIWndiTimerStuff(p);
  3299. break;
  3300. case IDCANCEL:
  3301. EndDialog(hwnd, FALSE);
  3302. break;
  3303. }
  3304. break;
  3305. }
  3306. return FALSE;
  3307. }
  3308. STATICFN BOOL NEAR PASCAL mciDialog(HWND hwnd)
  3309. {
  3310. DialogBoxParam(hInst, MAKEINTATOM(DLG_MCICOMMAND), hwnd,
  3311. mciDlgProc, (LPARAM)hwnd);
  3312. return TRUE;
  3313. }
  3314. //
  3315. // Code to implement the Copy command:
  3316. //
  3317. //
  3318. // MCIWnd tries to copy the same things to the clipboard that VfW MPlayer
  3319. // would have.
  3320. //
  3321. #define SLASH(c) ((c) == TEXT('/') || (c) == TEXT('\\'))
  3322. /**************************************************************************
  3323. convert a file name to a fully qualifed path name, if the file
  3324. exists on a net drive the UNC name is returned.
  3325. ***************************************************************************/
  3326. STATICFN BOOL NetParseFile(LPTSTR szFile, LPTSTR szPath)
  3327. {
  3328. TCHAR achDrive[4];
  3329. TCHAR achRemote[128];
  3330. int cbRemote = NUMELMS(achRemote);
  3331. // Dynamically link to WNetGetConnection. That way MPR.DLL will only
  3332. // get pulled in if it is needed.
  3333. #ifdef UNICODE
  3334. typedef DWORD (APIENTRY *LPWNETGETCONNECTION)(
  3335. LPCWSTR lpLocalName,
  3336. LPWSTR lpRemoteName,
  3337. LPDWORD lpnLength
  3338. );
  3339. char szWNetGetConnection[] = "WNetGetConnectionW";
  3340. #else
  3341. typedef DWORD (APIENTRY *LPWNETGETCONNECTION)(
  3342. LPCSTR lpLocalName,
  3343. LPSTR lpRemoteName,
  3344. LPDWORD lpnLength
  3345. );
  3346. char szWNetGetConnection[] = "WNetGetConnectionA";
  3347. #endif
  3348. TCHAR szMpr[] = TEXT("MPR.DLL");
  3349. LPWNETGETCONNECTION lpFn;
  3350. HINSTANCE hInst;
  3351. if (szPath == NULL)
  3352. szPath = szFile;
  3353. else
  3354. szPath[0] = 0;
  3355. //
  3356. // Fully qualify the file name
  3357. //
  3358. #ifdef _WIN32
  3359. {
  3360. LPTSTR pfile;
  3361. TCHAR achPath[MAX_PATH];
  3362. achPath[0] = 0;
  3363. if (GetFullPathName(szFile, MAX_PATH, achPath, &pfile) == 0) {
  3364. return FALSE;
  3365. }
  3366. lstrcpy( szPath, achPath );
  3367. }
  3368. #else
  3369. {
  3370. OFSTRUCT of;
  3371. if (OpenFile(szFile, &of, OF_PARSE) == -1)
  3372. return FALSE;
  3373. lstrcpy(szPath, of.szPathName);
  3374. }
  3375. #endif
  3376. //
  3377. // if the file is not drive based (probably UNC)
  3378. //
  3379. if (szPath[1] != TEXT(':'))
  3380. return TRUE;
  3381. achDrive[0] = szPath[0];
  3382. achDrive[1] = TEXT(':');
  3383. achDrive[2] = TEXT('\0');
  3384. hInst = LoadLibrary(szMpr);
  3385. if (hInst == NULL) {
  3386. return FALSE;
  3387. }
  3388. *(FARPROC *)&lpFn = GetProcAddress( hInst, szWNetGetConnection);
  3389. if ( (*lpFn)(achDrive, achRemote, &cbRemote) != WN_SUCCESS) {
  3390. FreeLibrary(hInst);
  3391. return FALSE;
  3392. }
  3393. FreeLibrary(hInst);
  3394. if (!SLASH(achRemote[0]) || !SLASH(achRemote[1]))
  3395. return TRUE;
  3396. lstrcat(achRemote, szPath+2);
  3397. lstrcpy(szPath, achRemote);
  3398. return TRUE;
  3399. }
  3400. SZCODE aszMPlayerName[] = TEXT("MPlayer");
  3401. HANDLE GetMPlayerData(PMCIWND p)
  3402. {
  3403. TCHAR szFileName[MAX_PATH];
  3404. TCHAR ach[40];
  3405. TCHAR szDevice[40];
  3406. HANDLE h;
  3407. LPSTR psz;
  3408. int len;
  3409. LPTSTR lpszCaption = szFileName;
  3410. UINT wOptions;
  3411. RECT rc;
  3412. BOOL fCompound, fFile;
  3413. DWORD dw;
  3414. MCI_GETDEVCAPS_PARMS mciDevCaps; /* for the MCI_GETDEVCAPS command */
  3415. //
  3416. // Get the Device Name
  3417. //
  3418. MCIWndGet(p, TEXT("sysinfo installname"), szDevice, NUMELMS(szDevice));
  3419. //
  3420. // determine if the device is simple or compound
  3421. //
  3422. mciDevCaps.dwItem = MCI_GETDEVCAPS_COMPOUND_DEVICE;
  3423. dw = mciSendCommand(p->wDeviceID, MCI_GETDEVCAPS,
  3424. MCI_GETDEVCAPS_ITEM, (DWORD_PTR)(LPSTR)&mciDevCaps);
  3425. fCompound = (dw == 0 && mciDevCaps.dwReturn != 0);
  3426. //
  3427. // determine if the device handles files
  3428. //
  3429. if (fCompound) {
  3430. mciDevCaps.dwItem = MCI_GETDEVCAPS_USES_FILES;
  3431. dw = mciSendCommand(p->wDeviceID, MCI_GETDEVCAPS,
  3432. MCI_GETDEVCAPS_ITEM, (DWORD_PTR)(LPSTR)&mciDevCaps);
  3433. fFile = (dw == 0 && mciDevCaps.dwReturn != 0);
  3434. }
  3435. //
  3436. // Compound devices that support files have an associated filename
  3437. //
  3438. if (fCompound && fFile) {
  3439. lstrcpy(szFileName, p->achFileName);
  3440. //
  3441. // Sometimes the filename is really "device!filename" so we have to peel
  3442. // the real filename out of it
  3443. //
  3444. lstrcpyn(ach, szFileName, lstrlen(szDevice) + 1);
  3445. if ((lstrcmpi(szDevice, ach) == 0) &&
  3446. (szFileName[lstrlen(szDevice)] == TEXT('!'))) {
  3447. lstrcpy(szFileName, &(p->achFileName[lstrlen(szDevice) + 1]));
  3448. }
  3449. NetParseFile(szFileName, (LPTSTR)NULL);
  3450. #ifndef _WIN32
  3451. OemToAnsi(szFileName,szFileName); // Map extended chars.
  3452. #endif
  3453. } else {
  3454. szFileName[0] = TEXT('\0');
  3455. }
  3456. #ifdef DEBUG
  3457. DPF(" GetLink: %ls|%ls!%ls\n",
  3458. (LPTSTR)aszMPlayerName,
  3459. (LPTSTR)szFileName,
  3460. (LPTSTR)szDevice);
  3461. #endif
  3462. /* How much data will we be writing? */
  3463. len = 9 + // all the delimeters
  3464. lstrlen(aszMPlayerName) +
  3465. lstrlen(szFileName) +
  3466. lstrlen(szDevice) +
  3467. 5 + 10 + 10 + 10 + // max length of int and long strings
  3468. lstrlen(lpszCaption);
  3469. h = GlobalAlloc(GMEM_DDESHARE|GMEM_ZEROINIT, len);
  3470. if (!h)
  3471. return NULL;
  3472. psz = GlobalLock(h);
  3473. wOptions = 0x0030; // !!!! OPT_PLAY|OPT_BAR
  3474. switch (MCIWndStatus(p, MCI_STATUS_TIME_FORMAT, 0)) {
  3475. case MCI_FORMAT_FRAMES:
  3476. wOptions |= 1; // frame mode
  3477. break;
  3478. case MCI_FORMAT_MILLISECONDS:
  3479. wOptions |= 2; // time mode
  3480. break;
  3481. }
  3482. MCIWndRect(p, &rc, FALSE);
  3483. wsprintfA(psz,
  3484. #ifdef UNICODE
  3485. "%ls%c%ls%c%ls%c%d%c%d%c%d%c%d%c%d%c%ls%c",
  3486. #else
  3487. "%s%c%s%c%s%c%d%c%ld%c%ld%c%ld%c%d%c%s%c",
  3488. #endif
  3489. (LPTSTR)aszMPlayerName, '\0',
  3490. (LPTSTR)szFileName, '\0',
  3491. (LPTSTR)szDevice, ',',
  3492. wOptions, ',',
  3493. 0L, ',', // !!! sel start
  3494. 0L, ',', // !!! sel length
  3495. p->dwPos, ';',
  3496. rc.bottom - rc.top, ',',
  3497. lpszCaption, '\0');
  3498. return h;
  3499. }
  3500. HBITMAP FAR PASCAL BitmapMCI(PMCIWND p)
  3501. {
  3502. HDC hdc, hdcMem;
  3503. HBITMAP hbm, hbmT;
  3504. HBRUSH hbrOld;
  3505. DWORD dw;
  3506. RECT rc;
  3507. HBRUSH hbrWindowColour;
  3508. /* Minimum size of bitmap is icon size */
  3509. int ICON_MINX = GetSystemMetrics(SM_CXICON);
  3510. int ICON_MINY = GetSystemMetrics(SM_CYICON);
  3511. /* Get size of a frame or an icon that we'll be drawing */
  3512. MCIWndRect(p, &rc, FALSE);
  3513. SetRect(&rc, 0, 0,
  3514. max(ICON_MINX, rc.right - rc.left),
  3515. max(ICON_MINX, rc.bottom - rc.top));
  3516. hdc = GetDC(NULL);
  3517. if (hdc == NULL)
  3518. return NULL;
  3519. hdcMem = CreateCompatibleDC(NULL);
  3520. if (hdcMem == NULL) {
  3521. ReleaseDC(NULL, hdc);
  3522. return NULL;
  3523. }
  3524. /* Big enough to hold text caption too, if necessary */
  3525. hbm = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
  3526. ReleaseDC(NULL, hdc);
  3527. if (hbm == NULL) {
  3528. DeleteDC(hdcMem);
  3529. return NULL;
  3530. }
  3531. hbmT = SelectObject(hdcMem, hbm);
  3532. hbrWindowColour = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  3533. hbrOld = SelectObject(hdcMem, hbrWindowColour);
  3534. PatBlt(hdcMem, 0,0, rc.right, rc.bottom, PATCOPY);
  3535. SelectObject(hdcMem, hbrOld);
  3536. DeleteObject(hbrWindowColour);
  3537. if (p->wDeviceID && p->fCanWindow)
  3538. {
  3539. MCI_ANIM_UPDATE_PARMS mciUpdate;
  3540. mciUpdate.hDC = hdcMem;
  3541. dw = mciSendCommand(p->wDeviceID, MCI_UPDATE,
  3542. MCI_ANIM_UPDATE_HDC | MCI_WAIT,
  3543. (DWORD_PTR)(LPVOID)&mciUpdate);
  3544. } else {
  3545. DrawIcon(hdcMem, rc.left, rc.top, p->hicon);
  3546. }
  3547. if (hbmT)
  3548. SelectObject(hdcMem, hbmT);
  3549. DeleteDC(hdcMem);
  3550. return hbm;
  3551. }
  3552. HPALETTE CopyPalette(HPALETTE hpal)
  3553. {
  3554. PLOGPALETTE ppal;
  3555. int nNumEntries = 0;
  3556. int i;
  3557. if (!hpal)
  3558. return NULL;
  3559. GetObject(hpal,sizeof(int),(LPVOID)&nNumEntries);
  3560. if (nNumEntries == 0)
  3561. return NULL;
  3562. ppal = (PLOGPALETTE)LocalAlloc(LPTR,sizeof(LOGPALETTE) +
  3563. nNumEntries * sizeof(PALETTEENTRY));
  3564. if (!ppal)
  3565. return NULL;
  3566. ppal->palVersion = 0x300;
  3567. ppal->palNumEntries = (WORD) nNumEntries;
  3568. GetPaletteEntries(hpal,0,nNumEntries,ppal->palPalEntry);
  3569. for (i=0; i<nNumEntries; i++)
  3570. ppal->palPalEntry[i].peFlags = 0;
  3571. hpal = CreatePalette(ppal);
  3572. LocalFree((HANDLE)ppal);
  3573. return hpal;
  3574. }
  3575. HANDLE FAR PASCAL PictureFromDib(HANDLE hdib, HPALETTE hpal)
  3576. {
  3577. LPMETAFILEPICT pmfp;
  3578. HANDLE hmfp;
  3579. HANDLE hmf;
  3580. HANDLE hdc;
  3581. LPBITMAPINFOHEADER lpbi;
  3582. if (!hdib)
  3583. return NULL;
  3584. lpbi = (LPVOID)GlobalLock(hdib);
  3585. if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
  3586. lpbi->biClrUsed = 1 << lpbi->biBitCount;
  3587. hdc = CreateMetaFile(NULL);
  3588. if (!hdc)
  3589. return NULL;
  3590. SetWindowOrgEx(hdc, 0, 0, NULL);
  3591. SetWindowExtEx(hdc, (int)lpbi->biWidth, (int)lpbi->biHeight, NULL);
  3592. if (hpal)
  3593. {
  3594. SelectPalette(hdc,hpal,FALSE);
  3595. RealizePalette(hdc);
  3596. }
  3597. SetStretchBltMode(hdc, COLORONCOLOR);
  3598. StretchDIBits(hdc,
  3599. 0,0,(int)lpbi->biWidth, (int)lpbi->biHeight,
  3600. 0,0,(int)lpbi->biWidth, (int)lpbi->biHeight,
  3601. (LPBYTE)lpbi + (int)lpbi->biSize + (int)lpbi->biClrUsed * sizeof(RGBQUAD),
  3602. (LPBITMAPINFO)lpbi,
  3603. DIB_RGB_COLORS,
  3604. SRCCOPY);
  3605. if (hpal)
  3606. SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
  3607. hmf = CloseMetaFile(hdc);
  3608. if (hmfp = GlobalAlloc(GMEM_DDESHARE|GMEM_MOVEABLE, sizeof(METAFILEPICT)))
  3609. {
  3610. pmfp = (LPMETAFILEPICT)GlobalLock(hmfp);
  3611. hdc = GetDC(NULL);
  3612. #if 1
  3613. pmfp->mm = MM_ANISOTROPIC;
  3614. pmfp->hMF = hmf;
  3615. pmfp->xExt = MulDiv((int)lpbi->biWidth ,2540,GetDeviceCaps(hdc, LOGPIXELSX));
  3616. pmfp->yExt = MulDiv((int)lpbi->biHeight,2540,GetDeviceCaps(hdc, LOGPIXELSX));
  3617. #else
  3618. pmfp->mm = MM_TEXT;
  3619. pmfp->hMF = hmf;
  3620. pmfp->xExt = (int)lpbi->biWidth;
  3621. pmfp->yExt = (int)lpbi->biHeight;
  3622. #endif
  3623. ReleaseDC(NULL, hdc);
  3624. }
  3625. else
  3626. {
  3627. DeleteMetaFile(hmf);
  3628. }
  3629. return hmfp;
  3630. }
  3631. #define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
  3632. /*
  3633. * DibFromBitmap()
  3634. *
  3635. * Will create a global memory block in DIB format that represents the DDB
  3636. * passed in
  3637. *
  3638. */
  3639. HANDLE FAR PASCAL DibFromBitmap(HBITMAP hbm, HPALETTE hpal)
  3640. {
  3641. BITMAP bm;
  3642. BITMAPINFOHEADER bi;
  3643. BITMAPINFOHEADER FAR *lpbi;
  3644. DWORD dw;
  3645. HANDLE hdib;
  3646. HDC hdc;
  3647. HPALETTE hpalT;
  3648. if (!hbm)
  3649. return NULL;
  3650. GetObject(hbm,sizeof(bm),(LPVOID)&bm);
  3651. bi.biSize = sizeof(BITMAPINFOHEADER);
  3652. bi.biWidth = bm.bmWidth;
  3653. bi.biHeight = bm.bmHeight;
  3654. bi.biPlanes = 1;
  3655. bi.biBitCount = (bm.bmPlanes * bm.bmBitsPixel) > 8 ? 24 : 8;
  3656. bi.biCompression = BI_RGB;
  3657. bi.biSizeImage = (DWORD)WIDTHBYTES(bi.biWidth * bi.biBitCount) * bi.biHeight;
  3658. bi.biXPelsPerMeter = 0;
  3659. bi.biYPelsPerMeter = 0;
  3660. bi.biClrUsed = bi.biBitCount == 8 ? 256 : 0;
  3661. bi.biClrImportant = 0;
  3662. dw = bi.biSize + bi.biClrUsed * sizeof(RGBQUAD) + bi.biSizeImage;
  3663. hdib = GlobalAlloc(GHND | GMEM_DDESHARE, dw);
  3664. if (!hdib)
  3665. return NULL;
  3666. lpbi = (LPBITMAPINFOHEADER)GlobalLock(hdib);
  3667. *lpbi = bi;
  3668. hdc = CreateCompatibleDC(NULL);
  3669. if (hpal)
  3670. {
  3671. hpalT = SelectPalette(hdc,hpal,FALSE);
  3672. RealizePalette(hdc);
  3673. }
  3674. GetDIBits(hdc, hbm, 0, (UINT)bi.biHeight,
  3675. (LPBYTE)lpbi + (int)lpbi->biSize + (int)lpbi->biClrUsed * sizeof(RGBQUAD),
  3676. (LPBITMAPINFO)lpbi, DIB_RGB_COLORS);
  3677. if (hpal)
  3678. SelectPalette(hdc,hpalT,FALSE);
  3679. DeleteDC(hdc);
  3680. return hdib;
  3681. }
  3682. SZCODE aszNative[] = TEXT("Native");
  3683. SZCODE aszOwnerLink[] = TEXT("OwnerLink");
  3684. // Pretend to be MPlayer copying to the clipboard
  3685. STATICFN void NEAR PASCAL MCIWndCopy(PMCIWND p)
  3686. {
  3687. UINT cfNative;
  3688. UINT cfOwnerLink;
  3689. HBITMAP hbm;
  3690. HPALETTE hpal;
  3691. HANDLE hdib;
  3692. HANDLE hmfp;
  3693. cfNative = RegisterClipboardFormat(aszNative);
  3694. cfOwnerLink = RegisterClipboardFormat(aszOwnerLink);
  3695. if (p->wDeviceID) {
  3696. OpenClipboard(p->hwnd);
  3697. EmptyClipboard();
  3698. SetClipboardData(cfNative, GetMPlayerData(p));
  3699. SetClipboardData(cfOwnerLink, GetMPlayerData(p));
  3700. hbm = BitmapMCI(p);
  3701. hpal = MCIWndGetPalette(p->hwnd);
  3702. hpal = CopyPalette(hpal);
  3703. if (hbm) {
  3704. hdib = DibFromBitmap(hbm, hpal);
  3705. hmfp = PictureFromDib(hdib, hpal);
  3706. if (hmfp)
  3707. SetClipboardData(CF_METAFILEPICT, hmfp);
  3708. if (hdib)
  3709. SetClipboardData(CF_DIB, hdib);
  3710. DeleteObject(hbm);
  3711. }
  3712. if (hpal)
  3713. SetClipboardData(CF_PALETTE, hpal);
  3714. CloseClipboard();
  3715. }
  3716. }
  3717. /*****************************************************************************
  3718. ****************************************************************************/
  3719. #ifdef DEBUG
  3720. STATICFN void cdecl dprintf(PSTR szFormat, ...)
  3721. {
  3722. char ach[128];
  3723. va_list va;
  3724. static BOOL fDebug = -1;
  3725. if (fDebug == -1) {
  3726. fDebug = mmGetProfileIntA(szDebug, MODNAME, FALSE);
  3727. }
  3728. if (!fDebug)
  3729. return;
  3730. lstrcpyA(ach, MODNAME ": ");
  3731. va_start(va,szFormat);
  3732. wvsprintfA(ach+lstrlenA(ach),szFormat, va);
  3733. va_end(va);
  3734. lstrcatA(ach, "\r\n");
  3735. OutputDebugStringA(ach);
  3736. }
  3737. #endif