Leaked source code of windows server 2003
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.

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