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.

562 lines
12 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. */
  18. #include "_common.h"
  19. #include "_edit.h"
  20. #include "_m_undo.h"
  21. #include "_callmgr.h"
  22. #include "_select.h"
  23. #include "_disp.h"
  24. ASSERTDATA
  25. /*
  26. * CCallMgr::SetChangeEvent
  27. *
  28. * @mfunc informs the callmgr that some data in the document
  29. * changed. The fType parameter describes the actual change
  30. *
  31. * @rdesc void
  32. */
  33. void CCallMgr::SetChangeEvent(
  34. CHANGETYPE fType) //@parm the type of change (e.g. text, etc)
  35. {
  36. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetChangeEvent");
  37. // if another callmgr exists higher up the chain, then
  38. // delegate the call to it
  39. if( _pPrevcallmgr )
  40. {
  41. Assert(_fChange == FALSE);
  42. Assert(_fTextChanged == FALSE);
  43. _pPrevcallmgr->SetChangeEvent(fType);
  44. }
  45. else
  46. {
  47. _fChange = TRUE;
  48. _ped->_fModified = TRUE;
  49. _ped->_fSaved = FALSE;
  50. _fTextChanged = !!(fType & CN_TEXTCHANGED);
  51. }
  52. }
  53. /*
  54. * CCallmgr::ClearChangeEvent
  55. *
  56. * @mfunc If a change happened, then clear the change event bit.
  57. * This allows callers to make changes to the edit control
  58. * _without_ having a notifcation fire. Sometimes, this
  59. * is necessary for backwards compatibility.
  60. *
  61. * @devnote This is a very dangerous method to use. If _fChange
  62. * is set, it may represent more than 1 change; in other words,
  63. * other changes than the one that should be ignored. However,
  64. * for all existing uses of this method, earlier changes are
  65. * irrelevant.
  66. */
  67. void CCallMgr::ClearChangeEvent()
  68. {
  69. if( _pPrevcallmgr )
  70. {
  71. Assert(_fChange == FALSE);
  72. Assert(_fTextChanged == FALSE);
  73. _pPrevcallmgr->ClearChangeEvent();
  74. }
  75. else
  76. {
  77. _fChange = FALSE;
  78. _fTextChanged = FALSE;
  79. // caller is responsible for setting _fModifed
  80. }
  81. }
  82. /*
  83. * CCallMgr::SetNewUndo
  84. *
  85. * @mfunc Informs the notification code that a new undo action has
  86. * been added to the undo stack
  87. *
  88. * @rdesc void
  89. */
  90. void CCallMgr::SetNewUndo()
  91. {
  92. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetNewUndo");
  93. // we should only ever do this once per call
  94. // It's assert during IME composition in Outlook. (see bug #3883)
  95. // Removing the assert does not caused any side effect.
  96. // Assert(_fNewUndo == FALSE);
  97. if( _pPrevcallmgr )
  98. {
  99. _pPrevcallmgr->SetNewUndo();
  100. }
  101. else
  102. {
  103. _fNewUndo = TRUE;
  104. }
  105. }
  106. /*
  107. *
  108. * CCallMgr::SetNewRedo
  109. *
  110. * @mfunc Informs the notification code that a new redo action has
  111. * been added to the redo stack.
  112. *
  113. * @rdesc void
  114. */
  115. void CCallMgr::SetNewRedo()
  116. {
  117. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetNewRedo");
  118. // we should only ever do this once per call.
  119. // The following assert looks bogus as it is forced to occur when an undo is
  120. // called with a count greater than 1. Therefore, for now, I (a-rsail) am
  121. // commenting it out.
  122. // Assert(_fNewRedo == FALSE);
  123. if( _pPrevcallmgr )
  124. {
  125. _pPrevcallmgr->SetNewRedo();
  126. }
  127. else
  128. {
  129. _fNewRedo = TRUE;
  130. }
  131. }
  132. /*
  133. * CCallMgr::SetMaxText
  134. *
  135. * @mfunc Informs the notification code that the max text limit has
  136. * been reached.
  137. *
  138. * @rdesc void
  139. */
  140. void CCallMgr::SetMaxText()
  141. {
  142. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetMaxText");
  143. // if there is a call context higher on the stack, delegate to it.
  144. if( _pPrevcallmgr )
  145. {
  146. Assert(_fMaxText == 0);
  147. _pPrevcallmgr->SetMaxText();
  148. }
  149. else
  150. {
  151. _fMaxText = TRUE;
  152. }
  153. }
  154. /*
  155. * CCallMgr::SetSelectionChanged
  156. *
  157. * @mfunc Informs the notification code that the selection has
  158. * changed
  159. *
  160. * @rdesc void
  161. */
  162. void CCallMgr::SetSelectionChanged()
  163. {
  164. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::SetSelectionChanged");
  165. AssertSz(_ped->DelayChangeNotification() ? _ped->Get10Mode() : 1, "Flag only should be set in 1.0 mode");
  166. if (_ped->DelayChangeNotification())
  167. return;
  168. // if there is a call context higher on the stack, delegate to it.
  169. if( _pPrevcallmgr )
  170. {
  171. Assert(_fSelChanged == 0);
  172. _pPrevcallmgr->SetSelectionChanged();
  173. }
  174. else
  175. {
  176. _fSelChanged = TRUE;
  177. }
  178. }
  179. /*
  180. * CCallMgr::SetOutOfMemory()
  181. *
  182. * @mfunc Informs the notification code that we were unable to allocate
  183. * enough memory.
  184. *
  185. * @rdesc void
  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 void
  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
  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. * @rdesc void
  249. *
  250. */
  251. void CCallMgr::RegisterComponent(
  252. IReEntrantComponent *pcomp, //@parm The component to register
  253. CompName name) //@parm The name for the component
  254. {
  255. pcomp->_idName = name;
  256. pcomp->_pnext = _pcomplist;
  257. _pcomplist = pcomp;
  258. }
  259. /*
  260. * CCallMgr::RevokeComponent
  261. *
  262. * @mfunc Removes a subsystem component from the list of components. The
  263. * component must have been previously registered with _this_
  264. * call context.
  265. *
  266. * @rdesc void
  267. */
  268. void CCallMgr::RevokeComponent(
  269. IReEntrantComponent *pcomp) //@parm The component to remove
  270. {
  271. IReEntrantComponent *plist, **ppprev;
  272. plist = _pcomplist;
  273. ppprev = &_pcomplist;
  274. while( plist != NULL )
  275. {
  276. if( plist == pcomp )
  277. {
  278. *ppprev = plist->_pnext;
  279. break;
  280. }
  281. ppprev = &(plist->_pnext);
  282. plist = plist->_pnext;
  283. }
  284. }
  285. /*
  286. * CCallMgr::GetComponent
  287. *
  288. * @mfunc Retrieves the earliest instance of a registered sub-component.
  289. *
  290. * @rdesc A pointer to the component, if one has been registered. NULL
  291. * otherwise.
  292. */
  293. IReEntrantComponent *CCallMgr::GetComponent(
  294. CompName name) //@parm the subsystem to look for
  295. {
  296. IReEntrantComponent *plist = _pcomplist;
  297. while( plist != NULL )
  298. {
  299. if( plist->_idName == name )
  300. {
  301. return plist;
  302. }
  303. plist = plist->_pnext;
  304. }
  305. // hmm, didn't find anything. Try contexts higher up, if we're
  306. // the top context, then just return NULL.
  307. if( _pPrevcallmgr )
  308. {
  309. return _pPrevcallmgr->GetComponent(name);
  310. }
  311. return NULL;
  312. }
  313. /*
  314. * CCallMgr::CCallMgr
  315. *
  316. * @mfunc Constructor
  317. *
  318. * @rdesc void
  319. */
  320. CCallMgr::CCallMgr(CTxtEdit *ped)
  321. {
  322. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::");
  323. // set everthing to NULL
  324. ZeroMemory(this, sizeof(CCallMgr));
  325. if(ped) // If ped is NULL, a zombie has
  326. { // been entered
  327. _ped = ped;
  328. _pPrevcallmgr = ped->_pcallmgr;
  329. ped->_pcallmgr = this;
  330. NotifyEnterContext();
  331. }
  332. }
  333. /*
  334. * CCallMgr::~CCallMgr
  335. *
  336. * @mfunc Destructor. If appropriate, we will fire any cached
  337. * notifications and cause the edit object to be destroyed.
  338. *
  339. * @rdesc void
  340. */
  341. CCallMgr::~CCallMgr()
  342. {
  343. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::");
  344. if(IsZombie()) // No reentrancy with Zombies
  345. return;
  346. if( _pPrevcallmgr )
  347. {
  348. // we don't allow these flags to be set in re-entrant call
  349. // states
  350. Assert(_fMaxText == FALSE);
  351. Assert(_fSelChanged == FALSE);
  352. Assert(_fTextChanged == FALSE);
  353. Assert(_fChange == FALSE);
  354. Assert(_fNewRedo == FALSE);
  355. Assert(_fNewUndo == FALSE);
  356. Assert(_fOutOfMemory == FALSE);
  357. // set the ped to the next level of the call state
  358. _ped->_pcallmgr = _pPrevcallmgr;
  359. return;
  360. }
  361. // we're the top level. Note that we explicity do not
  362. // have an overall guard for cases where we are re-entered
  363. // while firing these notifications. This is necessary for
  364. // better 1.0 compatibility and for Forms^3, which wants
  365. // to 'guard' their implementation of ITextHost::TxNotify and
  366. // ignore any notifications that happen while they are
  367. // processing our notifications. Make sense?
  368. _ped->_pcallmgr = NULL;
  369. // Process our internal notifications
  370. if(_ped->_fUpdateSelection)
  371. {
  372. CTxtSelection *psel = _ped->GetSel();
  373. _ped->_fUpdateSelection = FALSE;
  374. if(psel && !_ped->_pdp->IsFrozen() && !_fOutOfMemory )
  375. {
  376. // this may cause an out of memory, so set things
  377. // up for that
  378. CCallMgr callmgr(_ped);
  379. psel->Update(FALSE);
  380. }
  381. }
  382. // Now fire any external notifications that may be necessary
  383. if( _fChange || _fSelChanged || _fMaxText || _fOutOfMemory )
  384. {
  385. SendAllNotifications();
  386. }
  387. // finally, we should check to see if we should delete the
  388. // CTxtEdit instance.
  389. if( _ped->_unk._cRefs == 0 && !_ped->_fSelfDestruct)
  390. {
  391. delete _ped;
  392. }
  393. }
  394. //
  395. // PRIVATE methods
  396. //
  397. /*
  398. * CCallMgr::SendAllNotifications
  399. *
  400. * @mfunc sends notifications for any cached notification bits.
  401. *
  402. * @rdesc void
  403. */
  404. void CCallMgr::SendAllNotifications()
  405. {
  406. ITextHost *phost = _ped->GetHost();
  407. CHANGENOTIFY cn;
  408. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CCallMgr::");
  409. //
  410. // COMPATIBILITY ISSUE: The ordering of these events _may_
  411. // be an issue. I've attempted to preserve the ordering
  412. // that the original code would use, but we have ~many~ more
  413. // control paths, so it's difficult.
  414. //
  415. if( _fMaxText )
  416. {
  417. // Beep if we are to emulate the system edit control
  418. if (_ped->_fSystemEditBeep)
  419. _ped->Beep();
  420. phost->TxNotify(EN_MAXTEXT, NULL);
  421. }
  422. if( _fSelChanged )
  423. {
  424. if( (_ped->_dwEventMask & ENM_SELCHANGE) && !(_ped->_fSuppressNotify))
  425. {
  426. SELCHANGE selchg;
  427. ZeroMemory(&selchg, sizeof(SELCHANGE));
  428. _ped->GetSel()->SetSelectionInfo(&selchg);
  429. if (_ped->Get10Mode())
  430. {
  431. selchg.chrg.cpMin = _ped->GetAcpFromCp(selchg.chrg.cpMin);
  432. selchg.chrg.cpMost = _ped->GetAcpFromCp(selchg.chrg.cpMost);
  433. }
  434. phost->TxNotify(EN_SELCHANGE, &selchg);
  435. }
  436. }
  437. if( _fOutOfMemory && !_ped->GetOOMNotified())
  438. {
  439. _fNewUndo = 0;
  440. _fNewRedo = 0;
  441. _ped->ClearUndo(NULL);
  442. _ped->_pdp->InvalidateRecalc();
  443. _ped->SetOOMNotified(TRUE);
  444. phost->TxNotify(EN_ERRSPACE, NULL);
  445. _ped->SetOOMNotified(FALSE);
  446. }
  447. if( _fChange )
  448. {
  449. if( (_ped->_dwEventMask & ENM_CHANGE) && !(_ped->_fSuppressNotify))
  450. {
  451. cn.dwChangeType = 0;
  452. cn.pvCookieData = 0;
  453. if( _fNewUndo )
  454. {
  455. Assert(_ped->_pundo);
  456. cn.dwChangeType |= CN_NEWUNDO;
  457. cn.pvCookieData = _ped->_pundo->GetTopAECookie();
  458. }
  459. else if( _fNewRedo )
  460. {
  461. Assert(_ped->_predo);
  462. cn.dwChangeType |= CN_NEWREDO;
  463. cn.pvCookieData = _ped->_predo->GetTopAECookie();
  464. }
  465. if( _fTextChanged )
  466. {
  467. cn.dwChangeType |= CN_TEXTCHANGED;
  468. }
  469. _ped->_dwEventMask &= ~ENM_CHANGE;
  470. phost->TxNotify(EN_CHANGE, &cn);
  471. _ped->_dwEventMask |= ENM_CHANGE;
  472. }
  473. }
  474. }
  475. /*
  476. * CCallMgr::NotifyEnterContext
  477. *
  478. * @mfunc Notify any registered components that a new context
  479. * has been entered.
  480. *
  481. * @rdesc void
  482. */
  483. void CCallMgr::NotifyEnterContext()
  484. {
  485. IReEntrantComponent *pcomp = _pcomplist;
  486. while( pcomp )
  487. {
  488. pcomp->OnEnterContext();
  489. pcomp = pcomp->_pnext;
  490. }
  491. if( _pPrevcallmgr )
  492. {
  493. _pPrevcallmgr->NotifyEnterContext();
  494. }
  495. }