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

611 lines
13 KiB

  1. /*
  2. *
  3. * @doc INTERNAL
  4. *
  5. * @module CALLMGR.CPP CCallMgr implementation |
  6. *
  7. * Purpose: The call manager controls various aspects of
  8. * a client call chain, including re-entrancy management,
  9. * undo contexts, and change notifications.
  10. *
  11. * Author: <nl>
  12. * alexgo 2/8/96
  13. *
  14. * See the documentation in reimplem.doc for a detailed explanation
  15. * of how all this stuff works.
  16. *
  17. * Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved.
  18. */
  19. #include "_common.h"
  20. #include "_edit.h"
  21. #include "_m_undo.h"
  22. #include "_callmgr.h"
  23. #include "_select.h"
  24. #include "_disp.h"
  25. #include "_dxfrobj.h"
  26. #ifndef NOPRIVATEMESSAGE
  27. #include "_MSREMSG.H"
  28. #endif
  29. #define EN_CLIPFORMAT 0x0712
  30. #define ENM_CLIPFORMAT 0x00000080
  31. typedef struct _clipboardformat
  32. {
  33. NMHDR nmhdr;
  34. CLIPFORMAT cf;
  35. } CLIPBOARDFORMAT;
  36. ASSERTDATA
  37. /*
  38. * CCallMgr::SetChangeEvent(fType)
  39. *
  40. * @mfunc informs the callmgr that some data in the document
  41. * changed. The fType parameter describes the actual change
  42. */
  43. void CCallMgr::SetChangeEvent(
  44. CHANGETYPE fType) //@parm the type of change (e.g. text, etc)
  45. {
  46. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetChangeEvent");
  47. // if another callmgr exists higher up the chain, then
  48. // delegate the call to it
  49. if( _pPrevcallmgr )
  50. {
  51. Assert(_fChange == FALSE);
  52. Assert(_fTextChanged == FALSE);
  53. _pPrevcallmgr->SetChangeEvent(fType);
  54. }
  55. else
  56. {
  57. _fChange = TRUE;
  58. _ped->_fModified = TRUE;
  59. _ped->_fSaved = FALSE;
  60. _fTextChanged = !!(fType & CN_TEXTCHANGED);
  61. }
  62. }
  63. /*
  64. * CCallmgr::ClearChangeEvent()
  65. *
  66. * @mfunc If a change happened, then clear the change event bit.
  67. * This allows callers to make changes to the edit control
  68. * _without_ having a notifcation fire. Sometimes, this
  69. * is necessary for backwards compatibility.
  70. *
  71. * @devnote This is a very dangerous method to use. If _fChange
  72. * is set, it may represent more than 1 change; in other words,
  73. * other changes than the one that should be ignored. However,
  74. * for all existing uses of this method, earlier changes are
  75. * irrelevant.
  76. */
  77. void CCallMgr::ClearChangeEvent()
  78. {
  79. if( _pPrevcallmgr )
  80. {
  81. Assert(_fChange == FALSE);
  82. Assert(_fTextChanged == FALSE);
  83. _pPrevcallmgr->ClearChangeEvent();
  84. }
  85. else
  86. {
  87. _fChange = FALSE;
  88. _fTextChanged = FALSE;
  89. // caller is responsible for setting _fModifed
  90. }
  91. }
  92. /*
  93. * CCallMgr::SetNewUndo()
  94. *
  95. * @mfunc Informs the notification code that a new undo action has
  96. * been added to the undo stack
  97. */
  98. void CCallMgr::SetNewUndo()
  99. {
  100. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetNewUndo");
  101. // we should only ever do this once per call
  102. // It's assert during IME composition in Outlook. (see bug #3883)
  103. // Removing the assert does not caused any side effect.
  104. // Assert(_fNewUndo == FALSE);
  105. if( _pPrevcallmgr )
  106. {
  107. _pPrevcallmgr->SetNewUndo();
  108. }
  109. else
  110. {
  111. _fNewUndo = TRUE;
  112. }
  113. }
  114. /*
  115. *
  116. * CCallMgr::SetNewRedo ()
  117. *
  118. * @mfunc Informs the notification code that a new redo action has
  119. * been added to the redo stack.
  120. */
  121. void CCallMgr::SetNewRedo()
  122. {
  123. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetNewRedo");
  124. // we should only ever do this once per call.
  125. // The following assert looks bogus as it is forced to occur when an undo is
  126. // called with a count greater than 1. Therefore, for now, I (a-rsail) am
  127. // commenting it out.
  128. // Assert(_fNewRedo == FALSE);
  129. if( _pPrevcallmgr )
  130. {
  131. _pPrevcallmgr->SetNewRedo();
  132. }
  133. else
  134. {
  135. _fNewRedo = TRUE;
  136. }
  137. }
  138. /*
  139. * CCallMgr::SetMaxText()
  140. *
  141. * @mfunc Informs the notification code that the max text limit has
  142. * been reached.
  143. */
  144. void CCallMgr::SetMaxText()
  145. {
  146. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetMaxText");
  147. // if there is a call context higher on the stack, delegate to it.
  148. if( _pPrevcallmgr )
  149. {
  150. Assert(_fMaxText == 0);
  151. _pPrevcallmgr->SetMaxText();
  152. }
  153. else
  154. {
  155. _fMaxText = TRUE;
  156. }
  157. }
  158. /*
  159. * CCallMgr::SetSelectionChanged()
  160. *
  161. * @mfunc Informs the notification code that the selection has
  162. * changed
  163. */
  164. void CCallMgr::SetSelectionChanged()
  165. {
  166. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetSelectionChanged");
  167. AssertSz(_ped->DelayChangeNotification() ? _ped->Get10Mode() : 1, "Flag only should be set in 1.0 mode");
  168. if (_ped->DelayChangeNotification())
  169. return;
  170. // if there is a call context higher on the stack, delegate to it.
  171. if( _pPrevcallmgr )
  172. {
  173. Assert(_fSelChanged == 0);
  174. _pPrevcallmgr->SetSelectionChanged();
  175. }
  176. else
  177. {
  178. _fSelChanged = TRUE;
  179. }
  180. }
  181. /*
  182. * CCallMgr::SetOutOfMemory()
  183. *
  184. * @mfunc Informs the notification code that we were unable to allocate
  185. * enough memory.
  186. */
  187. void CCallMgr::SetOutOfMemory()
  188. {
  189. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetOutOfMemory");
  190. // if there is a call context higher on the stack, delegate to it.
  191. if( _pPrevcallmgr )
  192. {
  193. Assert(_fOutOfMemory == 0);
  194. _pPrevcallmgr->SetOutOfMemory();
  195. }
  196. else
  197. {
  198. _fOutOfMemory = TRUE;
  199. }
  200. }
  201. /*
  202. * CCallMgr::SetInProtected
  203. *
  204. * @mfunc Indicates that we are currently processing an EN_PROTECTED
  205. * notification
  206. *
  207. * @rdesc void
  208. */
  209. void CCallMgr::SetInProtected(BOOL flag)
  210. {
  211. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetInProtected");
  212. if( _pPrevcallmgr )
  213. {
  214. _pPrevcallmgr->SetInProtected(flag);
  215. }
  216. else
  217. {
  218. _fInProtected = flag;
  219. }
  220. }
  221. /*
  222. * CCallMgr:GetInProtected()
  223. *
  224. * @mfunc retrieves the InProtected flag, whether or not we are currently
  225. * processing an EN_PROTECTED notification
  226. *
  227. * @rdesc TRUE if we're processing an EN_PROTECTED notification
  228. */
  229. BOOL CCallMgr::GetInProtected()
  230. {
  231. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::GetInProtected");
  232. if( _pPrevcallmgr )
  233. {
  234. return _pPrevcallmgr->GetInProtected();
  235. }
  236. else
  237. {
  238. return _fInProtected;
  239. }
  240. }
  241. /*
  242. * CCallMgr::RegisterComponent(pcomp, name)
  243. *
  244. * @mfunc Registers a subsystem component implementing IReEntrantComponent.
  245. * This enables this call manager to inform those objects about
  246. * relevant changes in our re-entrancy status.
  247. */
  248. void CCallMgr::RegisterComponent(
  249. IReEntrantComponent *pcomp, //@parm The component to register
  250. CompName name) //@parm The name for the component
  251. {
  252. pcomp->_idName = name;
  253. pcomp->_pnext = _pcomplist;
  254. _pcomplist = pcomp;
  255. }
  256. /*
  257. * CCallMgr::RevokeComponent(pcomp)
  258. *
  259. * @mfunc Removes a subsystem component from the list of components. The
  260. * component must have been previously registered with _this_
  261. * call context.
  262. */
  263. void CCallMgr::RevokeComponent(
  264. IReEntrantComponent *pcomp) //@parm The component to remove
  265. {
  266. IReEntrantComponent *plist, **ppprev;
  267. plist = _pcomplist;
  268. ppprev = &_pcomplist;
  269. while( plist != NULL )
  270. {
  271. if( plist == pcomp )
  272. {
  273. *ppprev = plist->_pnext;
  274. break;
  275. }
  276. ppprev = &(plist->_pnext);
  277. plist = plist->_pnext;
  278. }
  279. }
  280. /*
  281. * CCallMgr::GetComponent(name)
  282. *
  283. * @mfunc Retrieves the earliest instance of a registered sub-component.
  284. *
  285. * @rdesc A pointer to the component, if one has been registered. NULL
  286. * otherwise.
  287. */
  288. IReEntrantComponent *CCallMgr::GetComponent(
  289. CompName name) //@parm the subsystem to look for
  290. {
  291. IReEntrantComponent *plist = _pcomplist;
  292. while( plist != NULL )
  293. {
  294. if( plist->_idName == name )
  295. {
  296. return plist;
  297. }
  298. plist = plist->_pnext;
  299. }
  300. // hmm, didn't find anything. Try contexts higher up, if we're
  301. // the top context, then just return NULL.
  302. if( _pPrevcallmgr )
  303. {
  304. return _pPrevcallmgr->GetComponent(name);
  305. }
  306. return NULL;
  307. }
  308. /*
  309. * CCallMgr::CCallMgr(ped)
  310. *
  311. * @mfunc Constructor
  312. *
  313. * @rdesc void
  314. */
  315. CCallMgr::CCallMgr(CTxtEdit *ped)
  316. {
  317. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::");
  318. // set everthing to NULL
  319. ZeroMemory(this, sizeof(CCallMgr));
  320. if(ped) // If ped is NULL, a zombie has
  321. { // been entered
  322. _ped = ped;
  323. _pPrevcallmgr = ped->_pcallmgr;
  324. ped->_pcallmgr = this;
  325. NotifyEnterContext();
  326. #ifndef NOPRIVATEMESSAGE
  327. if (!_pPrevcallmgr && _ped->_pMsgFilter && _ped->_pMsgCallBack)
  328. _ped->_pMsgCallBack->NotifyEvents(NE_ENTERTOPLEVELCALLMGR);
  329. #endif
  330. }
  331. }
  332. /*
  333. * CCallMgr::~CCallMgr()
  334. *
  335. * @mfunc Destructor. If appropriate, we will fire any cached
  336. * notifications and cause the edit object to be destroyed.
  337. *
  338. * @rdesc void
  339. */
  340. CCallMgr::~CCallMgr()
  341. {
  342. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::");
  343. if(IsZombie()) // No reentrancy with Zombies
  344. return;
  345. if( _pPrevcallmgr )
  346. {
  347. // we don't allow these flags to be set in re-entrant call
  348. // states
  349. Assert(_fMaxText == FALSE);
  350. Assert(_fSelChanged == FALSE);
  351. Assert(_fTextChanged == FALSE);
  352. Assert(_fChange == FALSE);
  353. Assert(_fNewRedo == FALSE);
  354. Assert(_fNewUndo == FALSE);
  355. Assert(_fOutOfMemory == FALSE);
  356. // set the ped to the next level of the call state
  357. _ped->_pcallmgr = _pPrevcallmgr;
  358. return;
  359. }
  360. // we're the top level. Note that we explicity do not
  361. // have an overall guard for cases where we are re-entered
  362. // while firing these notifications. This is necessary for
  363. // better 1.0 compatibility and for Forms^3, which wants
  364. // to 'guard' their implementation of ITextHost::TxNotify and
  365. // ignore any notifications that happen while they are
  366. // processing our notifications. Make sense?
  367. _ped->_pcallmgr = NULL;
  368. // Process our internal notifications
  369. if(_ped->_fUpdateSelection)
  370. {
  371. CTxtSelection *psel = _ped->GetSel();
  372. _ped->_fUpdateSelection = FALSE;
  373. if(psel && !_ped->_pdp->IsFrozen() && !_fOutOfMemory )
  374. {
  375. // this may cause an out of memory, so set things
  376. // up for that
  377. CCallMgr callmgr(_ped);
  378. psel->Update(FALSE);
  379. }
  380. }
  381. // Now fire any external notifications that may be necessary
  382. if( _fChange || _fSelChanged || _fMaxText || _fOutOfMemory )
  383. {
  384. SendAllNotifications();
  385. }
  386. // finally, we should check to see if we should delete the
  387. // CTxtEdit instance.
  388. if(!_ped->_fSelfDestruct)
  389. {
  390. if( _ped->_unk._cRefs == 0)
  391. {
  392. delete _ped;
  393. }
  394. #ifndef NOPRIVATEMESSAGE
  395. else
  396. {
  397. if (_ped->_pMsgFilter && _ped->_pMsgCallBack)
  398. {
  399. DWORD dwEvents = NE_EXITTOPLEVELCALLMGR;
  400. if (_fSelChanged)
  401. dwEvents |= NE_CALLMGRSELCHANGE;
  402. if (_fChange)
  403. dwEvents |= NE_CALLMGRCHANGE;
  404. _ped->_pMsgCallBack->NotifyEvents(dwEvents);
  405. }
  406. }
  407. #endif
  408. }
  409. }
  410. //
  411. // PRIVATE methods
  412. //
  413. /*
  414. * CCallMgr::SendAllNotifications()
  415. *
  416. * @mfunc sends notifications for any cached notification bits.
  417. */
  418. void CCallMgr::SendAllNotifications()
  419. {
  420. //if the ped has already destructed, we cant make calls though it
  421. if (_ped->_fSelfDestruct)
  422. return;
  423. ITextHost *phost = _ped->GetHost();
  424. CHANGENOTIFY cn;
  425. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::");
  426. //
  427. // COMPATIBILITY ISSUE: The ordering of these events _may_
  428. // be an issue. I've attempted to preserve the ordering
  429. // that the original code would use, but we have ~many~ more
  430. // control paths, so it's difficult.
  431. //
  432. if( _fMaxText )
  433. {
  434. // Beep if we are to emulate the system edit control
  435. if (_ped->_fSystemEditBeep)
  436. _ped->Beep();
  437. phost->TxNotify(EN_MAXTEXT, NULL);
  438. }
  439. if( _fSelChanged )
  440. {
  441. if( (_ped->_dwEventMask & ENM_SELCHANGE) && !(_ped->_fSuppressNotify))
  442. {
  443. CTxtSelection * const psel = _ped->GetSel();
  444. if(psel)
  445. {
  446. SELCHANGE selchg;
  447. ZeroMemory(&selchg, sizeof(SELCHANGE));
  448. psel->SetSelectionInfo(&selchg);
  449. if (_ped->Get10Mode())
  450. {
  451. selchg.chrg.cpMin = _ped->GetAcpFromCp(selchg.chrg.cpMin);
  452. selchg.chrg.cpMost = _ped->GetAcpFromCp(selchg.chrg.cpMost);
  453. }
  454. phost->TxNotify(EN_SELCHANGE, &selchg);
  455. }
  456. }
  457. }
  458. if( _fOutOfMemory && !_ped->GetOOMNotified())
  459. {
  460. _fNewUndo = 0;
  461. _fNewRedo = 0;
  462. _ped->ClearUndo(NULL);
  463. _ped->_pdp->InvalidateRecalc();
  464. _ped->SetOOMNotified(TRUE);
  465. phost->TxNotify(EN_ERRSPACE, NULL);
  466. _ped->SetOOMNotified(FALSE);
  467. }
  468. if( _fChange )
  469. {
  470. if( (_ped->_dwEventMask & ENM_CHANGE) && !(_ped->_fSuppressNotify))
  471. {
  472. cn.dwChangeType = 0;
  473. cn.pvCookieData = 0;
  474. if( _fNewUndo )
  475. {
  476. Assert(_ped->_pundo);
  477. cn.dwChangeType |= CN_NEWUNDO;
  478. cn.pvCookieData = _ped->_pundo->GetTopAECookie();
  479. }
  480. else if( _fNewRedo )
  481. {
  482. Assert(_ped->_predo);
  483. cn.dwChangeType |= CN_NEWREDO;
  484. cn.pvCookieData = _ped->_predo->GetTopAECookie();
  485. }
  486. if( _fTextChanged )
  487. {
  488. cn.dwChangeType |= CN_TEXTCHANGED;
  489. }
  490. _ped->_dwEventMask &= ~ENM_CHANGE;
  491. phost->TxNotify(EN_CHANGE, &cn);
  492. _ped->_dwEventMask |= ENM_CHANGE;
  493. }
  494. }
  495. if((_ped->_dwEventMask & ENM_CLIPFORMAT) && _ped->_ClipboardFormat)
  496. {
  497. CLIPBOARDFORMAT cf;
  498. ZeroMemory(&cf, sizeof(CLIPBOARDFORMAT));
  499. cf.cf = g_rgFETC[_ped->_ClipboardFormat - 1].cfFormat;
  500. _ped->_ClipboardFormat = 0;
  501. phost->TxNotify(EN_CLIPFORMAT, &cf);
  502. }
  503. #if !defined(NOLINESERVICES) && !defined(NOCOMPLEXSCRIPTS)
  504. extern char *g_szMsgBox;
  505. if(g_szMsgBox)
  506. {
  507. CLock lock;
  508. if(g_szMsgBox)
  509. {
  510. MessageBoxA(NULL, g_szMsgBox, NULL, MB_ICONEXCLAMATION | MB_TASKMODAL | MB_SETFOREGROUND);
  511. FreePv((void *)g_szMsgBox);
  512. g_szMsgBox = NULL;
  513. }
  514. }
  515. #endif
  516. }
  517. /*
  518. * CCallMgr::NotifyEnterContext()
  519. *
  520. * @mfunc Notify any registered components that a new context
  521. * has been entered.
  522. *
  523. */
  524. void CCallMgr::NotifyEnterContext()
  525. {
  526. IReEntrantComponent *pcomp = _pcomplist;
  527. while( pcomp )
  528. {
  529. pcomp->OnEnterContext();
  530. pcomp = pcomp->_pnext;
  531. }
  532. if( _pPrevcallmgr )
  533. {
  534. _pPrevcallmgr->NotifyEnterContext();
  535. }
  536. }