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.

741 lines
19 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. statopts.c
  5. Abstract:
  6. Property sheet handler for "Status Option" page
  7. Environment:
  8. Fax driver user interface
  9. Revision History:
  10. 04/09/00 -taoyuan-
  11. Created it.
  12. Copy part of code from shell\ext\systray\dll\fax.cpp
  13. mm/dd/yy -author-
  14. description
  15. --*/
  16. #include <stdio.h>
  17. #include "faxui.h"
  18. #include "resource.h"
  19. HWND g_hwndTracking = NULL;
  20. INT_PTR
  21. CALLBACK
  22. SoundDlgProc(
  23. HWND hwndDlg, // handle to dialog box
  24. UINT uMsg, // message
  25. WPARAM wParam, // first message parameter
  26. LPARAM lParam // second message parameter
  27. );
  28. BOOL
  29. GetSelectedDeviceId(
  30. HWND hDlg,
  31. DWORD* pdwDeviceId
  32. )
  33. /*++
  34. Routine Description:
  35. Returns selected divice ID from IDC_COMBO_MODEM combo box
  36. Arguments:
  37. hDlg - [in] Handle to the Status Options property sheet page
  38. pdwDeviceId - [out] selected device ID
  39. Return Value:
  40. TRUE for success, FALSE otherwise
  41. --*/
  42. {
  43. DWORD dwCount = 0;
  44. DWORD dwIndex = 0;
  45. HWND hComboModem = NULL;
  46. hComboModem = GetDlgItem(hDlg, IDC_COMBO_MODEM);
  47. if(!hComboModem)
  48. {
  49. Assert(FALSE);
  50. Error(( "GetDlgItem(hDlg, IDC_COMBO_MODEM) failed, ec = %d.\n", GetLastError()));
  51. return FALSE;
  52. }
  53. dwCount = (DWORD)SendMessage(hComboModem, CB_GETCOUNT,0,0);
  54. if(CB_ERR == dwCount || 0 == dwCount)
  55. {
  56. Error(( "SendMessage(hComboModem, CB_GETCOUNT,0,0) failed\n"));
  57. return FALSE;
  58. }
  59. dwIndex = (DWORD)SendMessage(hComboModem, CB_GETCURSEL,0,0);
  60. if(CB_ERR == dwIndex)
  61. {
  62. Error(( "SendMessage(hComboModem, CB_GETCURSEL,0,0) failed\n"));
  63. return FALSE;
  64. }
  65. *pdwDeviceId = (DWORD)SendMessage(hComboModem, CB_GETITEMDATA, dwIndex, 0);
  66. if(CB_ERR == *pdwDeviceId)
  67. {
  68. Error(( "SendMessage(hComboModem, CB_GETITEMDATA, dwIndex, 0) failed\n"));
  69. return FALSE;
  70. }
  71. return TRUE;
  72. }
  73. void
  74. OnDevSelectChanged(
  75. HWND hDlg
  76. )
  77. /*++
  78. Routine Description:
  79. Change IDC_CHECK_MANUAL_ANSWER check box state
  80. according to device selection
  81. Arguments:
  82. hDlg - Handle to the Status Options property sheet page
  83. Return Value:
  84. NONE
  85. --*/
  86. {
  87. BOOL bFaxEnable = FALSE;
  88. DWORD dwSelectedDeviceId = 0;
  89. PFAX_PORT_INFO_EX pPortInfo = NULL;
  90. TCHAR szDeviceNote[MAX_PATH] = {0};
  91. GetSelectedDeviceId(hDlg, &dwSelectedDeviceId);
  92. if(dwSelectedDeviceId)
  93. {
  94. pPortInfo = FindPortInfo(dwSelectedDeviceId);
  95. if(!pPortInfo)
  96. {
  97. Error(("FindPortInfo() failed\n"));
  98. Assert(FALSE);
  99. return;
  100. }
  101. bFaxEnable = pPortInfo->bSend || (FAX_DEVICE_RECEIVE_MODE_OFF != pPortInfo->ReceiveMode);
  102. }
  103. if(!bFaxEnable)
  104. {
  105. if(!LoadString(g_hResource,
  106. 0 == dwSelectedDeviceId ? IDS_NO_DEVICES : IDS_NOT_FAX_DEVICE,
  107. szDeviceNote,
  108. MAX_PATH))
  109. {
  110. Error(( "LoadString() failed with %d.\n", GetLastError()));
  111. Assert(FALSE);
  112. }
  113. }
  114. SetDlgItemText(hDlg, IDC_STATIC_DEVICE_NOTE, szDeviceNote);
  115. ShowWindow(GetDlgItem(hDlg, IDC_STATIC_NOTE_ICON), bFaxEnable ? SW_HIDE : SW_SHOW);
  116. EnableWindow(GetDlgItem(hDlg, IDC_CHECK_MONITOR_ON_SEND), bFaxEnable);
  117. EnableWindow(GetDlgItem(hDlg, IDC_CHECK_MONITOR_ON_RECEIVE), bFaxEnable);
  118. EnableWindow(GetDlgItem(hDlg, IDC_CHECK_NOTIFY_PROGRESS), bFaxEnable);
  119. EnableWindow(GetDlgItem(hDlg, IDC_CHECK_NOTIFY_IN_COMPLETE), bFaxEnable);
  120. EnableWindow(GetDlgItem(hDlg, IDC_CHECK_NOTIFY_OUT_COMPLETE), bFaxEnable);
  121. EnableWindow(GetDlgItem(hDlg, IDC_STATIC_AUTO_OPEN), bFaxEnable);
  122. EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_SOUND), bFaxEnable);
  123. }
  124. VOID
  125. DoInitStatusOptions(
  126. HWND hDlg
  127. )
  128. /*++
  129. Routine Description:
  130. Initializes the Status Options property sheet page with information from the registry
  131. Arguments:
  132. hDlg - Handle to the Status Options property sheet page
  133. Return Value:
  134. NONE
  135. --*/
  136. {
  137. HKEY hRegKey;
  138. DWORD dw;
  139. DWORD dwItem;
  140. DWORD dwSelectedDeviceId=0;
  141. DWORD dwSelectedItem=0;
  142. HWND hComboModem = NULL;
  143. BOOL bDesktopSKU = IsDesktopSKU();
  144. DWORD bNotifyProgress = bDesktopSKU;
  145. DWORD bNotifyInCompletion = bDesktopSKU;
  146. DWORD bNotifyOutCompletion = bDesktopSKU;
  147. DWORD bMonitorOnSend = bDesktopSKU;
  148. DWORD bMonitorOnReceive = bDesktopSKU;
  149. //
  150. // Open the user info registry key for reading
  151. //
  152. if ((hRegKey = OpenRegistryKey(HKEY_CURRENT_USER, REGKEY_FAX_USERINFO, FALSE,KEY_READ)))
  153. {
  154. GetRegistryDwordEx(hRegKey, REGVAL_MONITOR_ON_SEND, &bMonitorOnSend);
  155. GetRegistryDwordEx(hRegKey, REGVAL_MONITOR_ON_RECEIVE, &bMonitorOnReceive);
  156. GetRegistryDwordEx(hRegKey, REGVAL_NOTIFY_PROGRESS, &bNotifyProgress);
  157. GetRegistryDwordEx(hRegKey, REGVAL_NOTIFY_IN_COMPLETE, &bNotifyInCompletion);
  158. GetRegistryDwordEx(hRegKey, REGVAL_NOTIFY_OUT_COMPLETE, &bNotifyOutCompletion);
  159. GetRegistryDwordEx(hRegKey, REGVAL_DEVICE_TO_MONITOR, &dwSelectedDeviceId);
  160. RegCloseKey(hRegKey);
  161. }
  162. CheckDlgButton( hDlg, IDC_CHECK_MONITOR_ON_SEND, bMonitorOnSend ? BST_CHECKED : BST_UNCHECKED);
  163. CheckDlgButton( hDlg, IDC_CHECK_MONITOR_ON_RECEIVE, bMonitorOnReceive ? BST_CHECKED : BST_UNCHECKED);
  164. CheckDlgButton( hDlg, IDC_CHECK_NOTIFY_PROGRESS, bNotifyProgress ? BST_CHECKED : BST_UNCHECKED);
  165. CheckDlgButton( hDlg, IDC_CHECK_NOTIFY_IN_COMPLETE, bNotifyInCompletion ? BST_CHECKED : BST_UNCHECKED);
  166. CheckDlgButton( hDlg, IDC_CHECK_NOTIFY_OUT_COMPLETE, bNotifyOutCompletion ? BST_CHECKED : BST_UNCHECKED);
  167. hComboModem = GetDlgItem(hDlg, IDC_COMBO_MODEM);
  168. if(!hComboModem)
  169. {
  170. Assert(FALSE);
  171. return;
  172. }
  173. for(dw=0; dw < g_dwPortsNum; ++dw)
  174. {
  175. dwItem = (DWORD)SendMessage(hComboModem, CB_ADDSTRING, 0, (LPARAM)g_pFaxPortInfo[dw].lpctstrDeviceName);
  176. if(CB_ERR != dwItem && CB_ERRSPACE != dwItem)
  177. {
  178. SendMessage(hComboModem, CB_SETITEMDATA, dwItem, g_pFaxPortInfo[dw].dwDeviceID);
  179. if(g_pFaxPortInfo[dw].dwDeviceID == dwSelectedDeviceId)
  180. {
  181. dwSelectedItem = dwItem;
  182. }
  183. }
  184. else
  185. {
  186. Error(( "SendMessage(hComboModem, CB_ADDSTRING, 0, pPortsInfo[dw].lpctstrDeviceName) failed\n"));
  187. }
  188. SendMessage(hComboModem, CB_SETCURSEL, dwSelectedItem, 0);
  189. OnDevSelectChanged(hDlg);
  190. }
  191. return;
  192. }
  193. BOOL
  194. DoSaveStatusOptions(
  195. HWND hDlg
  196. )
  197. /*++
  198. Routine Description:
  199. Save the information on the Status Options property sheet page to registry
  200. Arguments:
  201. hDlg - Handle to the Status Options property sheet page
  202. Return Value:
  203. TRUE for success, FALSE otherwise
  204. --*/
  205. #define SaveStatusOptionsCheckBox(id, pValueName) \
  206. SetRegistryDword(hRegKey, pValueName, IsDlgButtonChecked(hDlg, id));
  207. {
  208. HKEY hRegKey;
  209. HWND hWndFaxStat = NULL;
  210. DWORD dwSelectedDeviceId = 0;
  211. DWORD dwRes = 0;
  212. //
  213. // Open the user registry key for writing and create it if necessary
  214. //
  215. if (! (hRegKey = OpenRegistryKey(HKEY_CURRENT_USER, REGKEY_FAX_USERINFO,TRUE, KEY_ALL_ACCESS)))
  216. {
  217. dwRes = GetLastError();
  218. Error(("Can't open registry to save data. Error = %d\n", dwRes));
  219. DisplayErrorMessage(hDlg, 0, dwRes);
  220. return FALSE;
  221. }
  222. SaveStatusOptionsCheckBox(IDC_CHECK_MONITOR_ON_SEND, REGVAL_MONITOR_ON_SEND);
  223. SaveStatusOptionsCheckBox(IDC_CHECK_MONITOR_ON_RECEIVE, REGVAL_MONITOR_ON_RECEIVE);
  224. SaveStatusOptionsCheckBox(IDC_CHECK_NOTIFY_PROGRESS, REGVAL_NOTIFY_PROGRESS);
  225. SaveStatusOptionsCheckBox(IDC_CHECK_NOTIFY_IN_COMPLETE, REGVAL_NOTIFY_IN_COMPLETE);
  226. SaveStatusOptionsCheckBox(IDC_CHECK_NOTIFY_OUT_COMPLETE, REGVAL_NOTIFY_OUT_COMPLETE);
  227. if(GetSelectedDeviceId(hDlg, &dwSelectedDeviceId))
  228. {
  229. SetRegistryDword(hRegKey, REGVAL_DEVICE_TO_MONITOR, dwSelectedDeviceId);
  230. }
  231. //
  232. // Close the registry key before returning to the caller
  233. //
  234. RegCloseKey(hRegKey);
  235. //
  236. // See if faxstat is running
  237. //
  238. hWndFaxStat = FindWindow(FAXSTAT_WINCLASS, NULL);
  239. if (hWndFaxStat)
  240. {
  241. PostMessage(hWndFaxStat, WM_FAXSTAT_CONTROLPANEL, 0, 0);
  242. }
  243. return TRUE;
  244. }
  245. INT_PTR
  246. CALLBACK
  247. StatusOptionDlgProc(
  248. HWND hDlg,
  249. UINT uMsg,
  250. WPARAM wParam,
  251. LPARAM lParam
  252. )
  253. /*++
  254. Routine Description:
  255. Procedure for handling the "status option" tab
  256. Arguments:
  257. hDlg - Identifies the property sheet page
  258. uMsg - Specifies the message
  259. wParam - Specifies additional message-specific information
  260. lParam - Specifies additional message-specific information
  261. Return Value:
  262. Depends on the value of message parameter
  263. --*/
  264. {
  265. switch (uMsg)
  266. {
  267. case WM_INITDIALOG :
  268. {
  269. DoInitStatusOptions(hDlg);
  270. g_hwndTracking = hDlg;
  271. return TRUE;
  272. }
  273. case WM_DESTROY:
  274. g_hwndTracking = NULL;
  275. break;
  276. case WM_COMMAND:
  277. switch(LOWORD(wParam))
  278. {
  279. case IDC_CHECK_MONITOR_ON_SEND:
  280. case IDC_CHECK_MONITOR_ON_RECEIVE:
  281. case IDC_CHECK_NOTIFY_PROGRESS:
  282. case IDC_CHECK_NOTIFY_IN_COMPLETE:
  283. case IDC_CHECK_NOTIFY_OUT_COMPLETE:
  284. if( HIWORD(wParam) == BN_CLICKED ) // notification code
  285. {
  286. Notify_Change(hDlg);
  287. }
  288. break;
  289. case IDC_COMBO_MODEM:
  290. if(HIWORD(wParam) == CBN_SELCHANGE)
  291. {
  292. OnDevSelectChanged(hDlg);
  293. Notify_Change(hDlg);
  294. }
  295. break;
  296. case IDC_BUTTON_SOUND:
  297. //
  298. // open sound dialog
  299. //
  300. DialogBoxParam(g_hResource,
  301. MAKEINTRESOURCE(IDD_SOUNDS),
  302. hDlg,
  303. SoundDlgProc,
  304. (LPARAM)NULL);
  305. break;
  306. default:
  307. break;
  308. }
  309. break;
  310. case WM_NOTIFY:
  311. {
  312. LPNMHDR lpnm = (LPNMHDR) lParam;
  313. switch (lpnm->code)
  314. {
  315. case PSN_SETACTIVE:
  316. OnDevSelectChanged(hDlg);
  317. break;
  318. case PSN_APPLY:
  319. if(!DoSaveStatusOptions(hDlg))
  320. {
  321. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID);
  322. }
  323. else
  324. {
  325. Notify_UnChange(hDlg);
  326. }
  327. return TRUE;
  328. default :
  329. break;
  330. } // switch
  331. break;
  332. }
  333. case WM_HELP:
  334. WinHelpContextPopup(((LPHELPINFO)lParam)->dwContextId, hDlg);
  335. return TRUE;
  336. default:
  337. break;
  338. }
  339. return FALSE;
  340. }
  341. INT_PTR
  342. CALLBACK
  343. SoundDlgProc(
  344. HWND hDlg,
  345. UINT uMsg,
  346. WPARAM wParam,
  347. LPARAM lParam
  348. )
  349. /*++
  350. Routine Description:
  351. Procedure for handling the sound dialog
  352. Arguments:
  353. hDlg - Identifies the property sheet page
  354. uMsg - Specifies the message
  355. wParam - Specifies additional message-specific information
  356. lParam - Specifies additional message-specific information
  357. Return Value:
  358. Depends on the value of message parameter
  359. --*/
  360. {
  361. switch (uMsg)
  362. {
  363. case WM_INITDIALOG :
  364. {
  365. HKEY hRegKey;
  366. DWORD bSoundOnRing = IsDesktopSKU();
  367. DWORD bSoundOnReceive = bSoundOnRing;
  368. DWORD bSoundOnSent = bSoundOnRing;
  369. DWORD bSoundOnError = bSoundOnRing;
  370. if ((hRegKey = OpenRegistryKey(HKEY_CURRENT_USER, REGKEY_FAX_USERINFO, FALSE,KEY_READ)))
  371. {
  372. GetRegistryDwordEx(hRegKey, REGVAL_SOUND_ON_RING, &bSoundOnRing);
  373. GetRegistryDwordEx(hRegKey, REGVAL_SOUND_ON_RECEIVE, &bSoundOnReceive);
  374. GetRegistryDwordEx(hRegKey, REGVAL_SOUND_ON_SENT, &bSoundOnSent);
  375. GetRegistryDwordEx(hRegKey, REGVAL_SOUND_ON_ERROR, &bSoundOnError);
  376. RegCloseKey(hRegKey);
  377. }
  378. CheckDlgButton( hDlg, IDC_CHECK_RING, bSoundOnRing ? BST_CHECKED : BST_UNCHECKED);
  379. CheckDlgButton( hDlg, IDC_CHECK_RECEIVE, bSoundOnReceive ? BST_CHECKED : BST_UNCHECKED);
  380. CheckDlgButton( hDlg, IDC_CHECK_SENT, bSoundOnSent ? BST_CHECKED : BST_UNCHECKED);
  381. CheckDlgButton( hDlg, IDC_CHECK_ERROR, bSoundOnError ? BST_CHECKED : BST_UNCHECKED);
  382. return TRUE;
  383. }
  384. case WM_COMMAND:
  385. switch(LOWORD(wParam))
  386. {
  387. case IDOK:
  388. {
  389. HKEY hRegKey;
  390. DWORD dwRes = 0;
  391. //
  392. // Open the user registry key for writing and create it if necessary
  393. //
  394. if ((hRegKey = OpenRegistryKey(HKEY_CURRENT_USER, REGKEY_FAX_USERINFO,TRUE, KEY_ALL_ACCESS)))
  395. {
  396. SaveStatusOptionsCheckBox(IDC_CHECK_RING, REGVAL_SOUND_ON_RING);
  397. SaveStatusOptionsCheckBox(IDC_CHECK_RECEIVE, REGVAL_SOUND_ON_RECEIVE);
  398. SaveStatusOptionsCheckBox(IDC_CHECK_SENT, REGVAL_SOUND_ON_SENT);
  399. SaveStatusOptionsCheckBox(IDC_CHECK_ERROR, REGVAL_SOUND_ON_ERROR);
  400. RegCloseKey(hRegKey);
  401. EndDialog(hDlg, IDOK);
  402. }
  403. else
  404. {
  405. dwRes = GetLastError();
  406. Error(("Can't open registry to save data. Error = %d\n", dwRes));
  407. DisplayErrorMessage(hDlg, 0, dwRes);
  408. }
  409. }
  410. break;
  411. case IDCANCEL:
  412. EndDialog(hDlg, IDCANCEL);
  413. break;
  414. }
  415. break;
  416. case WM_HELP:
  417. WinHelpContextPopup(((LPHELPINFO)lParam)->dwContextId, hDlg);
  418. return TRUE;
  419. }
  420. return FALSE;
  421. }
  422. DWORD
  423. FindDeviceToMonitor ()
  424. /*++
  425. Routine name : FindDeviceToMonitor
  426. Routine description:
  427. Attempts to find a device which is either send or receive enabled
  428. Author:
  429. Eran Yariv (EranY), Apr, 2001
  430. Arguments:
  431. Return Value:
  432. Device id, zero if none found.
  433. --*/
  434. {
  435. DWORD dwIndex;
  436. for (dwIndex = 0; dwIndex < g_dwPortsNum; dwIndex++)
  437. {
  438. if (g_pFaxPortInfo[dwIndex].bSend || // Device is send enabled or
  439. (FAX_DEVICE_RECEIVE_MODE_OFF != g_pFaxPortInfo[dwIndex].ReceiveMode)) // device is receive enabled
  440. {
  441. //
  442. // We have a match
  443. //
  444. return g_pFaxPortInfo[dwIndex].dwDeviceID;
  445. }
  446. }
  447. return 0;
  448. } // FindDeviceToMonitor
  449. VOID
  450. NotifyDeviceUsageChanged ()
  451. /*++
  452. Routine name : NotifyDeviceUsageChanged
  453. Routine description:
  454. A notification function.
  455. Called whenever the usage of a device has changed.
  456. Author:
  457. Eran Yariv (EranY), Apr, 2001
  458. Arguments:
  459. Return Value:
  460. None.
  461. --*/
  462. {
  463. DWORD dwMonitoredDeviceId;
  464. if (g_hwndTracking)
  465. {
  466. //
  467. // Get data from the combo-box
  468. //
  469. if(!GetSelectedDeviceId(g_hwndTracking, &dwMonitoredDeviceId))
  470. {
  471. //
  472. // Can't read monitored device
  473. //
  474. return;
  475. }
  476. }
  477. else
  478. {
  479. HKEY hRegKey;
  480. //
  481. // Get data from the registry
  482. //
  483. if ((hRegKey = OpenRegistryKey(HKEY_CURRENT_USER, REGKEY_FAX_USERINFO, FALSE, KEY_READ)))
  484. {
  485. if (ERROR_SUCCESS != GetRegistryDwordEx(hRegKey, REGVAL_DEVICE_TO_MONITOR, &dwMonitoredDeviceId))
  486. {
  487. //
  488. // Can't read monitored device
  489. //
  490. RegCloseKey (hRegKey);
  491. return;
  492. }
  493. RegCloseKey (hRegKey);
  494. }
  495. else
  496. {
  497. //
  498. // Can't read monitored device
  499. //
  500. return;
  501. }
  502. }
  503. if (IsDeviceInUse(dwMonitoredDeviceId))
  504. {
  505. //
  506. // Monitored device is in use - no action required
  507. //
  508. return;
  509. }
  510. //
  511. // Now we know that the monitored device is no longer in use.
  512. // Try to find another device to monitor.
  513. //
  514. dwMonitoredDeviceId = FindDeviceToMonitor ();
  515. if (!dwMonitoredDeviceId)
  516. {
  517. //
  518. // Can't find any device to monitor - do nothing.
  519. //
  520. return;
  521. }
  522. //
  523. // Set the new device
  524. //
  525. if (g_hwndTracking)
  526. {
  527. //
  528. // Set data to the combo-box
  529. //
  530. DWORD dwCount = 0;
  531. DWORD dwIndex = 0;
  532. HWND hComboModem = NULL;
  533. hComboModem = GetDlgItem(g_hwndTracking, IDC_COMBO_MODEM);
  534. if(!hComboModem)
  535. {
  536. Assert(FALSE);
  537. return;
  538. }
  539. dwCount = (DWORD)SendMessage(hComboModem, CB_GETCOUNT,0,0);
  540. if(CB_ERR == dwCount || 0 == dwCount)
  541. {
  542. Error(("SendMessage(hComboModem, CB_GETCOUNT,0,0) failed\n"));
  543. return;
  544. }
  545. for (dwIndex = 0; dwIndex < dwCount; dwIndex++)
  546. {
  547. DWORD dwDeviceId;
  548. //
  549. // Look for the device
  550. //
  551. dwDeviceId = (DWORD)SendMessage(hComboModem, CB_GETITEMDATA, dwIndex, 0);
  552. if (dwDeviceId != dwMonitoredDeviceId)
  553. {
  554. continue;
  555. }
  556. //
  557. // Found the new device in the combo-box.
  558. // Select it and mark the page as modified.
  559. //
  560. SendMessage(hComboModem, CB_SETCURSEL, dwIndex, 0);
  561. OnDevSelectChanged(g_hwndTracking);
  562. Notify_Change(g_hwndTracking);
  563. break;
  564. }
  565. }
  566. else
  567. {
  568. HKEY hRegKey;
  569. //
  570. // Set data to the registry
  571. //
  572. if ((hRegKey = OpenRegistryKey(HKEY_CURRENT_USER, REGKEY_FAX_USERINFO, FALSE, KEY_WRITE)))
  573. {
  574. if (!SetRegistryDword(hRegKey, REGVAL_DEVICE_TO_MONITOR, dwMonitoredDeviceId))
  575. {
  576. //
  577. // Can't write monitored device
  578. //
  579. }
  580. RegCloseKey (hRegKey);
  581. }
  582. }
  583. } // NotifyDeviceUsageChanged