winsplit.cpp 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366
  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10. #include "stdafx.h"
  11. #ifdef AFX_CORE3_SEG
  12. #pragma code_seg(AFX_CORE3_SEG)
  13. #endif
  14. #ifdef _DEBUG
  15. #undef THIS_FILE
  16. static char THIS_FILE[] = __FILE__;
  17. #endif
  18. /////////////////////////////////////////////////////////////////////////////
  19. // Visual attributes and other constants
  20. // HitTest return values (values and spacing between values is important)
  21. enum HitTestValue
  22. {
  23. noHit = 0,
  24. vSplitterBox = 1,
  25. hSplitterBox = 2,
  26. bothSplitterBox = 3, // just for keyboard
  27. vSplitterBar1 = 101,
  28. vSplitterBar15 = 115,
  29. hSplitterBar1 = 201,
  30. hSplitterBar15 = 215,
  31. splitterIntersection1 = 301,
  32. splitterIntersection225 = 525
  33. };
  34. /////////////////////////////////////////////////////////////////////////////
  35. // CSplitterWnd
  36. BEGIN_MESSAGE_MAP(CSplitterWnd, CWnd)
  37. //{{AFX_MSG_MAP(CSplitterWnd)
  38. ON_WM_SETCURSOR()
  39. ON_WM_MOUSEMOVE()
  40. ON_WM_PAINT()
  41. ON_WM_LBUTTONDOWN()
  42. ON_WM_LBUTTONDBLCLK()
  43. ON_WM_LBUTTONUP()
  44. ON_WM_KEYDOWN()
  45. ON_WM_SIZE()
  46. ON_WM_HSCROLL()
  47. ON_WM_VSCROLL()
  48. ON_WM_NCCREATE()
  49. ON_WM_SYSCOMMAND()
  50. ON_WM_CANCELMODE()
  51. ON_MESSAGE_VOID(WM_DISPLAYCHANGE, OnDisplayChange)
  52. ON_MESSAGE_VOID(WM_WININICHANGE, OnDisplayChange)
  53. ON_MESSAGE_VOID(WM_SETTINGCHANGE, OnDisplayChange)
  54. ON_WM_MOUSEWHEEL()
  55. //}}AFX_MSG_MAP
  56. END_MESSAGE_MAP()
  57. /////////////////////////////////////////////////////////////////////////////
  58. // CSplitterWnd construction/destruction
  59. CSplitterWnd::CSplitterWnd()
  60. {
  61. AFX_ZERO_INIT_OBJECT(CWnd);
  62. // default splitter box/bar sizes (includes borders)
  63. if (!afxData.bWin4)
  64. {
  65. m_cxSplitter = m_cySplitter = 4;
  66. m_cxBorderShare = m_cyBorderShare = 1;
  67. m_cxSplitterGap = m_cySplitterGap = 4 + 1 + 1;
  68. ASSERT(m_cxBorder == 0 && m_cyBorder == 0);
  69. }
  70. else
  71. {
  72. m_cxSplitter = m_cySplitter = 3 + 2 + 2;
  73. m_cxBorderShare = m_cyBorderShare = 0;
  74. m_cxSplitterGap = m_cySplitterGap = 3 + 2 + 2;
  75. m_cxBorder = m_cyBorder = 2;
  76. }
  77. #ifdef _DEBUG
  78. if (GetSystemMetrics(SM_CXBORDER) != 1 ||
  79. GetSystemMetrics(SM_CYBORDER) != 1)
  80. {
  81. TRACE0("Warning: CSplitterWnd assumes 1 pixel border.\n");
  82. // will look ugly if borders are not 1 pixel wide and 1 pixel high
  83. }
  84. #endif
  85. }
  86. CSplitterWnd::~CSplitterWnd()
  87. {
  88. delete[] m_pRowInfo;
  89. delete[] m_pColInfo;
  90. }
  91. BOOL CSplitterWnd::Create(CWnd* pParentWnd,
  92. int nMaxRows, int nMaxCols, SIZE sizeMin,
  93. CCreateContext* pContext, DWORD dwStyle, UINT nID)
  94. {
  95. ASSERT(pParentWnd != NULL);
  96. ASSERT(sizeMin.cx > 0 && sizeMin.cy > 0); // minimum must be non-zero
  97. ASSERT(pContext != NULL);
  98. ASSERT(pContext->m_pNewViewClass != NULL);
  99. ASSERT(dwStyle & WS_CHILD);
  100. ASSERT(dwStyle & SPLS_DYNAMIC_SPLIT); // must have dynamic split behavior
  101. // Dynamic splitters are limited to 2x2
  102. ASSERT(nMaxRows >= 1 && nMaxRows <= 2);
  103. ASSERT(nMaxCols >= 1 && nMaxCols <= 2);
  104. ASSERT(nMaxCols > 1 || nMaxRows > 1); // 1x1 is not permitted
  105. m_nMaxRows = nMaxRows;
  106. m_nMaxCols = nMaxCols;
  107. ASSERT(m_nRows == 0 && m_nCols == 0); // none yet
  108. m_nRows = m_nCols = 1; // start off as 1x1
  109. if (!CreateCommon(pParentWnd, sizeMin, dwStyle, nID))
  110. return FALSE;
  111. ASSERT(m_nRows == 1 && m_nCols == 1); // still 1x1
  112. ASSERT(pContext->m_pNewViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
  113. m_pDynamicViewClass = pContext->m_pNewViewClass;
  114. // save for later dynamic creations
  115. // add the first initial pane
  116. if (!CreateView(0, 0, m_pDynamicViewClass, sizeMin, pContext))
  117. {
  118. DestroyWindow(); // will clean up child windows
  119. return FALSE;
  120. }
  121. m_pColInfo[0].nIdealSize = sizeMin.cx;
  122. m_pRowInfo[0].nIdealSize = sizeMin.cy;
  123. return TRUE;
  124. }
  125. // simple "wiper" splitter
  126. BOOL CSplitterWnd::CreateStatic(CWnd* pParentWnd,
  127. int nRows, int nCols, DWORD dwStyle, UINT nID)
  128. {
  129. ASSERT(pParentWnd != NULL);
  130. ASSERT(nRows >= 1 && nRows <= 16);
  131. ASSERT(nCols >= 1 && nCols <= 16);
  132. ASSERT(nCols > 1 || nRows > 1); // 1x1 is not permitted
  133. ASSERT(dwStyle & WS_CHILD);
  134. ASSERT(!(dwStyle & SPLS_DYNAMIC_SPLIT)); // can't have dynamic split
  135. ASSERT(m_nRows == 0 && m_nCols == 0); // none yet
  136. m_nRows = m_nMaxRows = nRows;
  137. m_nCols = m_nMaxCols = nCols;
  138. // create with zero minimum pane size
  139. if (!CreateCommon(pParentWnd, CSize(0, 0), dwStyle, nID))
  140. return FALSE;
  141. // all panes must be created with explicit calls to CreateView
  142. return TRUE;
  143. }
  144. BOOL CSplitterWnd::CreateCommon(CWnd* pParentWnd,
  145. SIZE sizeMin, DWORD dwStyle, UINT nID)
  146. {
  147. ASSERT(pParentWnd != NULL);
  148. ASSERT(sizeMin.cx >= 0 && sizeMin.cy >= 0);
  149. ASSERT(dwStyle & WS_CHILD);
  150. ASSERT(nID != 0);
  151. ASSERT(m_pColInfo == NULL && m_pRowInfo == NULL); // only do once
  152. ASSERT(m_nMaxCols > 0 && m_nMaxRows > 0);
  153. // the Windows scroll bar styles bits turn on the smart scrollbars
  154. DWORD dwCreateStyle = dwStyle & ~(WS_HSCROLL|WS_VSCROLL);
  155. if (afxData.bWin4)
  156. dwCreateStyle &= ~WS_BORDER;
  157. VERIFY(AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG));
  158. // create with the same wnd-class as MDI-Frame (no erase bkgnd)
  159. if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle, 0, 0, 0, 0,
  160. pParentWnd->m_hWnd, (HMENU)nID, NULL))
  161. return FALSE; // create invisible
  162. // attach the initial splitter parts
  163. TRY
  164. {
  165. m_pColInfo = new CRowColInfo[m_nMaxCols];
  166. for (int col = 0; col < m_nMaxCols; col++)
  167. {
  168. m_pColInfo[col].nMinSize = m_pColInfo[col].nIdealSize = sizeMin.cx;
  169. m_pColInfo[col].nCurSize = -1; // will be set in RecalcLayout
  170. }
  171. m_pRowInfo = new CRowColInfo[m_nMaxRows];
  172. for (int row = 0; row < m_nMaxRows; row++)
  173. {
  174. m_pRowInfo[row].nMinSize = m_pRowInfo[row].nIdealSize = sizeMin.cy;
  175. m_pRowInfo[row].nCurSize = -1; // will be set in RecalcLayout
  176. }
  177. // create scroll bars by setting the style
  178. SetScrollStyle(dwStyle);
  179. }
  180. CATCH_ALL(e)
  181. {
  182. DestroyWindow(); // will clean up child windows
  183. // Note: DELETE_EXCEPTION(e) not required
  184. return FALSE;
  185. }
  186. END_CATCH_ALL
  187. return TRUE;
  188. }
  189. BOOL CSplitterWnd::OnNcCreate(LPCREATESTRUCT lpcs)
  190. {
  191. if (!CWnd::OnNcCreate(lpcs))
  192. return FALSE;
  193. // remove WS_EX_CLIENTEDGE style from parent window
  194. // (the splitter itself will provide the 3d look)
  195. CWnd* pParent = GetParent();
  196. ASSERT_VALID(pParent);
  197. pParent->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
  198. return TRUE;
  199. }
  200. /////////////////////////////////////////////////////////////////////////////
  201. // CSplitterWnd default creation of parts
  202. // You must create ALL panes unless DYNAMIC_SPLIT is defined!
  203. // Usually the splitter window is invisible when creating a pane
  204. BOOL CSplitterWnd::CreateView(int row, int col,
  205. CRuntimeClass* pViewClass, SIZE sizeInit, CCreateContext* pContext)
  206. {
  207. #ifdef _DEBUG
  208. ASSERT_VALID(this);
  209. ASSERT(row >= 0 && row < m_nRows);
  210. ASSERT(col >= 0 && col < m_nCols);
  211. ASSERT(pViewClass != NULL);
  212. ASSERT(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
  213. ASSERT(AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE));
  214. if (GetDlgItem(IdFromRowCol(row, col)) != NULL)
  215. {
  216. TRACE2("Error: CreateView - pane already exists for row %d, col %d.\n",
  217. row, col);
  218. ASSERT(FALSE);
  219. return FALSE;
  220. }
  221. #endif
  222. // set the initial size for that pane
  223. m_pColInfo[col].nIdealSize = sizeInit.cx;
  224. m_pRowInfo[row].nIdealSize = sizeInit.cy;
  225. BOOL bSendInitialUpdate = FALSE;
  226. CCreateContext contextT;
  227. if (pContext == NULL)
  228. {
  229. // if no context specified, generate one from the currently selected
  230. // client if possible
  231. CView* pOldView = (CView*)GetActivePane();
  232. if (pOldView != NULL && pOldView->IsKindOf(RUNTIME_CLASS(CView)))
  233. {
  234. // set info about last pane
  235. ASSERT(contextT.m_pCurrentFrame == NULL);
  236. contextT.m_pLastView = pOldView;
  237. contextT.m_pCurrentDoc = pOldView->GetDocument();
  238. if (contextT.m_pCurrentDoc != NULL)
  239. contextT.m_pNewDocTemplate =
  240. contextT.m_pCurrentDoc->GetDocTemplate();
  241. }
  242. pContext = &contextT;
  243. bSendInitialUpdate = TRUE;
  244. }
  245. CWnd* pWnd;
  246. TRY
  247. {
  248. pWnd = (CWnd*)pViewClass->CreateObject();
  249. if (pWnd == NULL)
  250. AfxThrowMemoryException();
  251. }
  252. CATCH_ALL(e)
  253. {
  254. TRACE0("Out of memory creating a splitter pane.\n");
  255. // Note: DELETE_EXCEPTION(e) not required
  256. return FALSE;
  257. }
  258. END_CATCH_ALL
  259. ASSERT_KINDOF(CWnd, pWnd);
  260. ASSERT(pWnd->m_hWnd == NULL); // not yet created
  261. DWORD dwStyle = AFX_WS_DEFAULT_VIEW;
  262. if (afxData.bWin4)
  263. dwStyle &= ~WS_BORDER;
  264. // Create with the right size (wrong position)
  265. CRect rect(CPoint(0,0), sizeInit);
  266. if (!pWnd->Create(NULL, NULL, dwStyle,
  267. rect, this, IdFromRowCol(row, col), pContext))
  268. {
  269. TRACE0("Warning: couldn't create client pane for splitter.\n");
  270. // pWnd will be cleaned up by PostNcDestroy
  271. return FALSE;
  272. }
  273. ASSERT((int)_AfxGetDlgCtrlID(pWnd->m_hWnd) == IdFromRowCol(row, col));
  274. // send initial notification message
  275. if (bSendInitialUpdate)
  276. pWnd->SendMessage(WM_INITIALUPDATE);
  277. return TRUE;
  278. }
  279. BOOL CSplitterWnd::CreateScrollBarCtrl(DWORD dwStyle, UINT nID)
  280. {
  281. ASSERT_VALID(this);
  282. ASSERT(m_hWnd != NULL);
  283. HWND hWnd = ::CreateWindow(_T("SCROLLBAR"), NULL,
  284. dwStyle | WS_VISIBLE | WS_CHILD,
  285. 0, 0, 1, 1, m_hWnd, (HMENU)nID,
  286. AfxGetInstanceHandle(), NULL);
  287. #ifdef _DEBUG
  288. if (hWnd == NULL)
  289. TRACE1("Warning: Window creation failed: GetLastError returns 0x%8.8X\n",
  290. GetLastError());
  291. #endif
  292. return hWnd != NULL;
  293. }
  294. int CSplitterWnd::IdFromRowCol(int row, int col) const
  295. {
  296. ASSERT_VALID(this);
  297. ASSERT(row >= 0);
  298. ASSERT(row < m_nRows);
  299. ASSERT(col >= 0);
  300. ASSERT(col < m_nCols);
  301. return AFX_IDW_PANE_FIRST + row * 16 + col;
  302. }
  303. /////////////////////////////////////////////////////////////////////////////
  304. // CSplitterWnd attributes
  305. CWnd* CSplitterWnd::GetPane(int row, int col) const
  306. {
  307. ASSERT_VALID(this);
  308. CWnd* pView = GetDlgItem(IdFromRowCol(row, col));
  309. ASSERT(pView != NULL); // panes can be a CWnd, but are usually CViews
  310. return pView;
  311. }
  312. BOOL CSplitterWnd::IsChildPane(CWnd* pWnd, int* pRow, int* pCol)
  313. {
  314. ASSERT_VALID(this);
  315. ASSERT_VALID(pWnd);
  316. UINT nID = _AfxGetDlgCtrlID(pWnd->m_hWnd);
  317. if (IsChild(pWnd) && nID >= AFX_IDW_PANE_FIRST && nID <= AFX_IDW_PANE_LAST)
  318. {
  319. if (pRow != NULL)
  320. *pRow = (nID - AFX_IDW_PANE_FIRST) / 16;
  321. if (pCol != NULL)
  322. *pCol = (nID - AFX_IDW_PANE_FIRST) % 16;
  323. ASSERT(pRow == NULL || *pRow < m_nRows);
  324. ASSERT(pCol == NULL || *pCol < m_nCols);
  325. return TRUE;
  326. }
  327. else
  328. {
  329. if (pRow != NULL)
  330. *pRow = -1;
  331. if (pCol != NULL)
  332. *pCol = -1;
  333. return FALSE;
  334. }
  335. }
  336. /////////////////////////////////////////////////////////////////////////////
  337. // CSplitterWnd information access
  338. // The get routines return the current size
  339. // The set routines set the ideal size
  340. // RecalcLayout must be called to update current size
  341. void CSplitterWnd::GetRowInfo(int row, int& cyCur, int& cyMin) const
  342. {
  343. ASSERT_VALID(this);
  344. ASSERT(row >= 0 && row < m_nMaxRows);
  345. cyCur = m_pRowInfo[row].nCurSize;
  346. cyMin = m_pRowInfo[row].nMinSize;
  347. }
  348. void CSplitterWnd::SetRowInfo(int row, int cyIdeal, int cyMin)
  349. {
  350. ASSERT_VALID(this);
  351. ASSERT(row >= 0 && row < m_nMaxRows);
  352. ASSERT(cyIdeal >= 0);
  353. ASSERT(cyMin >= 0);
  354. m_pRowInfo[row].nIdealSize = cyIdeal;
  355. m_pRowInfo[row].nMinSize = cyMin;
  356. }
  357. void CSplitterWnd::GetColumnInfo(int col, int& cxCur, int& cxMin) const
  358. {
  359. ASSERT_VALID(this);
  360. ASSERT(col >= 0 && col < m_nMaxCols);
  361. cxCur = m_pColInfo[col].nCurSize;
  362. cxMin = m_pColInfo[col].nMinSize;
  363. }
  364. void CSplitterWnd::SetColumnInfo(int col, int cxIdeal, int cxMin)
  365. {
  366. ASSERT_VALID(this);
  367. ASSERT(col >= 0 && col < m_nMaxCols);
  368. ASSERT(cxIdeal >= 0);
  369. ASSERT(cxMin >= 0);
  370. m_pColInfo[col].nIdealSize = cxIdeal;
  371. m_pColInfo[col].nMinSize = cxMin;
  372. }
  373. DWORD CSplitterWnd::GetScrollStyle() const
  374. {
  375. DWORD dwStyle = 0;
  376. if (m_bHasHScroll)
  377. dwStyle |= WS_HSCROLL;
  378. if (m_bHasVScroll)
  379. dwStyle |= WS_VSCROLL;
  380. return dwStyle;
  381. }
  382. void CSplitterWnd::SetScrollStyle(DWORD dwStyle)
  383. {
  384. // optimize for scroll info already set correctly
  385. dwStyle &= (WS_HSCROLL|WS_VSCROLL);
  386. if (GetScrollStyle() == dwStyle)
  387. return;
  388. // update to new state
  389. m_bHasHScroll = (dwStyle & WS_HSCROLL) != 0;
  390. m_bHasVScroll = (dwStyle & WS_VSCROLL) != 0;
  391. CWnd* pScrollBar;
  392. // show/hide all the shared horz scroll bars
  393. for (int col = 0; col < m_nCols; col++)
  394. {
  395. pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
  396. if (pScrollBar == NULL)
  397. {
  398. // create the scroll bar when necessary
  399. if (!CreateScrollBarCtrl(SBS_HORZ, AFX_IDW_HSCROLL_FIRST + col))
  400. AfxThrowResourceException();
  401. pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
  402. }
  403. pScrollBar->ShowWindow(m_bHasHScroll ? SW_SHOW : SW_HIDE);
  404. }
  405. // show/hide all the shared vert scroll bars
  406. for (int row = 0; row < m_nRows; row++)
  407. {
  408. pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
  409. if (pScrollBar == NULL)
  410. {
  411. // create the scroll bar when necessary
  412. if (!CreateScrollBarCtrl(SBS_VERT, AFX_IDW_VSCROLL_FIRST + row))
  413. AfxThrowResourceException();
  414. pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
  415. }
  416. pScrollBar->ShowWindow(m_bHasVScroll ? SW_SHOW : SW_HIDE);
  417. }
  418. // show/destroy size box if necessary
  419. if (m_bHasVScroll && m_bHasHScroll)
  420. {
  421. pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
  422. if (pScrollBar == NULL)
  423. {
  424. // create size box when necessary
  425. if (!CreateScrollBarCtrl(SBS_SIZEBOX|WS_DISABLED, AFX_IDW_SIZE_BOX))
  426. AfxThrowResourceException();
  427. pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
  428. }
  429. pScrollBar->ShowWindow(SW_SHOW);
  430. }
  431. else
  432. {
  433. // the size box can be destroyed instead of hidden
  434. pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
  435. if (pScrollBar != NULL)
  436. pScrollBar->DestroyWindow();
  437. }
  438. // Note: call RecalcLayout for the new layout to take effect
  439. }
  440. /////////////////////////////////////////////////////////////////////////////
  441. // CSplitterWnd client operations/overridables
  442. void CSplitterWnd::DeleteView(int row, int col)
  443. {
  444. ASSERT_VALID(this);
  445. // if active child is being deleted - activate next
  446. CWnd* pPane = GetPane(row, col);
  447. ASSERT_KINDOF(CView, pPane);
  448. if (GetActivePane() == pPane)
  449. ActivateNext(FALSE);
  450. // default implementation assumes view will auto delete in PostNcDestroy
  451. pPane->DestroyWindow();
  452. }
  453. void CSplitterWnd::OnDrawSplitter(CDC* pDC, ESplitType nType,
  454. const CRect& rectArg)
  455. {
  456. // if pDC == NULL, then just invalidate
  457. if (pDC == NULL)
  458. {
  459. RedrawWindow(rectArg, NULL, RDW_INVALIDATE|RDW_NOCHILDREN);
  460. return;
  461. }
  462. ASSERT_VALID(pDC);
  463. // otherwise, actually draw
  464. CRect rect = rectArg;
  465. switch (nType)
  466. {
  467. case splitBorder:
  468. ASSERT(afxData.bWin4);
  469. pDC->Draw3dRect(rect, afxData.clrBtnShadow, afxData.clrBtnHilite);
  470. rect.InflateRect(-CX_BORDER, -CY_BORDER);
  471. pDC->Draw3dRect(rect, afxData.clrWindowFrame, afxData.clrBtnFace);
  472. return;
  473. case splitIntersection:
  474. ASSERT(!afxData.bWin4);
  475. break;
  476. case splitBox:
  477. if (afxData.bWin4)
  478. {
  479. pDC->Draw3dRect(rect, afxData.clrBtnFace, afxData.clrWindowFrame);
  480. rect.InflateRect(-CX_BORDER, -CY_BORDER);
  481. pDC->Draw3dRect(rect, afxData.clrBtnHilite, afxData.clrBtnShadow);
  482. rect.InflateRect(-CX_BORDER, -CY_BORDER);
  483. break;
  484. }
  485. // fall through...
  486. case splitBar:
  487. if (!afxData.bWin4)
  488. {
  489. pDC->Draw3dRect(rect, afxData.clrBtnHilite, afxData.clrBtnShadow);
  490. rect.InflateRect(-CX_BORDER, -CY_BORDER);
  491. }
  492. break;
  493. default:
  494. ASSERT(FALSE); // unknown splitter type
  495. }
  496. // fill the middle
  497. COLORREF clr = afxData.clrBtnFace;
  498. pDC->FillSolidRect(rect, clr);
  499. }
  500. /////////////////////////////////////////////////////////////////////////////
  501. // Dynamic row/col split etc
  502. AFX_STATIC int AFXAPI _AfxCanSplitRowCol(CSplitterWnd::CRowColInfo* pInfoBefore,
  503. int nBeforeSize, int nSizeSplitter)
  504. // request to split Before row at point nBeforeSize
  505. // returns size of new pane (nBeforeSize will be new size of Before pane)
  506. // return -1 if not big enough
  507. {
  508. ASSERT(pInfoBefore->nCurSize > 0);
  509. ASSERT(pInfoBefore->nMinSize > 0);
  510. ASSERT(nBeforeSize <= pInfoBefore->nCurSize);
  511. // space gets take from before pane (weird UI for > 2 splits)
  512. if (nBeforeSize < pInfoBefore->nMinSize)
  513. {
  514. TRACE0("Warning: split too small to fit in a new pane.\n");
  515. return -1;
  516. }
  517. int nNewSize = pInfoBefore->nCurSize - nBeforeSize - nSizeSplitter;
  518. if (nBeforeSize < pInfoBefore->nMinSize)
  519. {
  520. TRACE0("Warning: split too small to shrink old pane.\n");
  521. return -1;
  522. }
  523. if (nNewSize < (pInfoBefore+1)->nMinSize)
  524. {
  525. TRACE0("Warning: split too small to create new pane.\n");
  526. return -1;
  527. }
  528. return nNewSize;
  529. }
  530. BOOL CSplitterWnd::SplitRow(int cyBefore)
  531. {
  532. ASSERT_VALID(this);
  533. ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  534. ASSERT(m_pDynamicViewClass != NULL);
  535. ASSERT(m_nRows < m_nMaxRows);
  536. cyBefore -= m_cyBorder;
  537. int rowNew = m_nRows;
  538. int cyNew = _AfxCanSplitRowCol(&m_pRowInfo[rowNew-1], cyBefore, m_cySplitter);
  539. if (cyNew == -1)
  540. return FALSE; // too small to split
  541. // create the scroll bar first (so new views can see that it is there)
  542. if (m_bHasVScroll &&
  543. !CreateScrollBarCtrl(SBS_VERT, AFX_IDW_VSCROLL_FIRST + rowNew))
  544. {
  545. TRACE0("Warning: SplitRow failed to create scroll bar.\n");
  546. return FALSE;
  547. }
  548. m_nRows++; // bump count during view creation
  549. // create new views to fill the new row (RecalcLayout will position)
  550. for (int col = 0; col < m_nCols; col++)
  551. {
  552. CSize size(m_pColInfo[col].nCurSize, cyNew);
  553. if (!CreateView(rowNew, col, m_pDynamicViewClass, size, NULL))
  554. {
  555. TRACE0("Warning: SplitRow failed to create new row.\n");
  556. // delete anything we partially created 'col' = # columns created
  557. while (col > 0)
  558. DeleteView(rowNew, --col);
  559. if (m_bHasVScroll)
  560. GetDlgItem(AFX_IDW_VSCROLL_FIRST + rowNew)->DestroyWindow();
  561. m_nRows--; // it didn't work out
  562. return FALSE;
  563. }
  564. }
  565. // new parts created - resize and re-layout
  566. m_pRowInfo[rowNew-1].nIdealSize = cyBefore;
  567. m_pRowInfo[rowNew].nIdealSize = cyNew;
  568. ASSERT(m_nRows == rowNew+1);
  569. RecalcLayout();
  570. return TRUE;
  571. }
  572. BOOL CSplitterWnd::SplitColumn(int cxBefore)
  573. {
  574. ASSERT_VALID(this);
  575. ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  576. ASSERT(m_pDynamicViewClass != NULL);
  577. ASSERT(m_nCols < m_nMaxCols);
  578. cxBefore -= m_cxBorder;
  579. int colNew = m_nCols;
  580. int cxNew = _AfxCanSplitRowCol(&m_pColInfo[colNew-1], cxBefore, m_cxSplitter);
  581. if (cxNew == -1)
  582. return FALSE; // too small to split
  583. // create the scroll bar first (so new views can see that it is there)
  584. if (m_bHasHScroll &&
  585. !CreateScrollBarCtrl(SBS_HORZ, AFX_IDW_HSCROLL_FIRST + colNew))
  586. {
  587. TRACE0("Warning: SplitRow failed to create scroll bar.\n");
  588. return FALSE;
  589. }
  590. m_nCols++; // bump count during view creation
  591. // create new views to fill the new column (RecalcLayout will position)
  592. for (int row = 0; row < m_nRows; row++)
  593. {
  594. CSize size(cxNew, m_pRowInfo[row].nCurSize);
  595. if (!CreateView(row, colNew, m_pDynamicViewClass, size, NULL))
  596. {
  597. TRACE0("Warning: SplitColumn failed to create new column.\n");
  598. // delete anything we partially created 'col' = # columns created
  599. while (row > 0)
  600. DeleteView(--row, colNew);
  601. if (m_bHasHScroll)
  602. GetDlgItem(AFX_IDW_HSCROLL_FIRST + colNew)->DestroyWindow();
  603. m_nCols--; // it didn't work out
  604. return FALSE;
  605. }
  606. }
  607. // new parts created - resize and re-layout
  608. m_pColInfo[colNew-1].nIdealSize = cxBefore;
  609. m_pColInfo[colNew].nIdealSize = cxNew;
  610. ASSERT(m_nCols == colNew+1);
  611. RecalcLayout();
  612. return TRUE;
  613. }
  614. void CSplitterWnd::DeleteRow(int rowDelete)
  615. {
  616. ASSERT_VALID(this);
  617. ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  618. ASSERT(m_nRows > 1);
  619. ASSERT(rowDelete < m_nRows);
  620. int rowActive, colActive;
  621. if (GetActivePane(&rowActive, &colActive) != NULL && rowActive == rowDelete)
  622. {
  623. if (++rowActive >= m_nRows)
  624. rowActive = 0;
  625. SetActivePane(rowActive, colActive);
  626. }
  627. CWnd* pScrollDel = m_bHasVScroll ?
  628. GetDlgItem(AFX_IDW_VSCROLL_FIRST+rowDelete) : NULL;
  629. for (int col = 0; col < m_nCols; col++)
  630. {
  631. DeleteView(rowDelete, col);
  632. for (int row = rowDelete+1; row < m_nRows; row++)
  633. {
  634. CWnd* pPane = GetPane(row, col);
  635. ASSERT(pPane != NULL);
  636. pPane->SetDlgCtrlID(IdFromRowCol(row-1, col));
  637. if (m_bHasVScroll && col == m_nCols-1)
  638. {
  639. CWnd* pScroll = GetDlgItem(AFX_IDW_VSCROLL_FIRST+row);
  640. if (pScroll != NULL)
  641. pScroll->SetDlgCtrlID(AFX_IDW_VSCROLL_FIRST+row-1);
  642. }
  643. }
  644. }
  645. m_nRows--;
  646. if (pScrollDel != NULL)
  647. pScrollDel->DestroyWindow();
  648. RecalcLayout(); // re-assign the space
  649. }
  650. void CSplitterWnd::DeleteColumn(int colDelete)
  651. {
  652. ASSERT_VALID(this);
  653. ASSERT(GetStyle() & SPLS_DYNAMIC_SPLIT);
  654. ASSERT(m_nCols > 1);
  655. ASSERT(colDelete < m_nCols);
  656. int rowActive, colActive;
  657. if (GetActivePane(&rowActive, &colActive) != NULL && colActive == colDelete)
  658. {
  659. if (++colActive >= m_nCols)
  660. colActive = 0;
  661. SetActivePane(rowActive, colActive);
  662. }
  663. CWnd* pScrollDel = m_bHasHScroll ?
  664. GetDlgItem(AFX_IDW_HSCROLL_FIRST+colDelete) : NULL;
  665. for (int row = 0; row < m_nRows; row++)
  666. {
  667. DeleteView(row, colDelete);
  668. for (int col = colDelete+1; col < m_nCols; col++)
  669. {
  670. CWnd* pPane = GetPane(row, col);
  671. ASSERT(pPane != NULL);
  672. pPane->SetDlgCtrlID(IdFromRowCol(row, col-1));
  673. if (m_bHasHScroll && row == m_nRows-1)
  674. {
  675. CWnd* pScroll = GetDlgItem(AFX_IDW_HSCROLL_FIRST+col);
  676. if (pScroll != NULL)
  677. pScroll->SetDlgCtrlID(AFX_IDW_HSCROLL_FIRST+col-1);
  678. }
  679. }
  680. }
  681. m_nCols--;
  682. if (pScrollDel != NULL)
  683. pScrollDel->DestroyWindow();
  684. RecalcLayout(); // re-assign the space
  685. }
  686. /////////////////////////////////////////////////////////////////////////////
  687. // CSplitterWnd tracking support
  688. // like GetClientRect but inset by shared scrollbars
  689. void CSplitterWnd::GetInsideRect(CRect& rect) const
  690. {
  691. ASSERT_VALID(this);
  692. GetClientRect(rect);
  693. ASSERT(rect.left == 0 && rect.top == 0);
  694. // subtract space for 3d borders
  695. rect.InflateRect(-m_cxBorder, -m_cyBorder);
  696. // subtract scrollbar clearance
  697. if (m_bHasVScroll)
  698. rect.right -= afxData.cxVScroll - CX_BORDER;
  699. if (m_bHasHScroll)
  700. rect.bottom -= afxData.cyHScroll - CY_BORDER;
  701. }
  702. void CSplitterWnd::StartTracking(int ht)
  703. {
  704. ASSERT_VALID(this);
  705. if (ht == noHit)
  706. return;
  707. // GetHitRect will restrict 'm_rectLimit' as appropriate
  708. GetInsideRect(m_rectLimit);
  709. if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
  710. {
  711. // split two directions (two tracking rectangles)
  712. int row = (ht - splitterIntersection1) / 15;
  713. int col = (ht - splitterIntersection1) % 15;
  714. GetHitRect(row + vSplitterBar1, m_rectTracker);
  715. int yTrackOffset = m_ptTrackOffset.y;
  716. m_bTracking2 = TRUE;
  717. GetHitRect(col + hSplitterBar1, m_rectTracker2);
  718. m_ptTrackOffset.y = yTrackOffset;
  719. }
  720. else if (ht == bothSplitterBox)
  721. {
  722. // hit on splitter boxes (for keyboard)
  723. GetHitRect(vSplitterBox, m_rectTracker);
  724. int yTrackOffset = m_ptTrackOffset.y;
  725. m_bTracking2 = TRUE;
  726. GetHitRect(hSplitterBox, m_rectTracker2);
  727. m_ptTrackOffset.y = yTrackOffset;
  728. // center it
  729. m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2);
  730. m_rectTracker2.OffsetRect(m_rectLimit.Width()/2, 0);
  731. }
  732. else
  733. {
  734. // only hit one bar
  735. GetHitRect(ht, m_rectTracker);
  736. }
  737. // allow active view to preserve focus before taking it away
  738. CView* pView = (CView*)GetActivePane();
  739. if (pView != NULL && pView->IsKindOf(RUNTIME_CLASS(CView)))
  740. {
  741. ASSERT_VALID(pView);
  742. CFrameWnd* pFrameWnd = GetParentFrame();
  743. ASSERT_VALID(pFrameWnd);
  744. pView->OnActivateFrame(WA_INACTIVE, pFrameWnd);
  745. }
  746. // steal focus and capture
  747. SetCapture();
  748. SetFocus();
  749. // make sure no updates are pending
  750. RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW);
  751. // set tracking state and appropriate cursor
  752. m_bTracking = TRUE;
  753. OnInvertTracker(m_rectTracker);
  754. if (m_bTracking2)
  755. OnInvertTracker(m_rectTracker2);
  756. m_htTrack = ht;
  757. SetSplitCursor(ht);
  758. }
  759. void CSplitterWnd::TrackRowSize(int y, int row)
  760. {
  761. ASSERT_VALID(this);
  762. ASSERT(m_nRows > 1);
  763. CPoint pt(0, y);
  764. ClientToScreen(&pt);
  765. GetPane(row, 0)->ScreenToClient(&pt);
  766. m_pRowInfo[row].nIdealSize = pt.y; // new size
  767. if (pt.y < m_pRowInfo[row].nMinSize)
  768. {
  769. // resized too small
  770. m_pRowInfo[row].nIdealSize = 0; // make it go away
  771. if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  772. DeleteRow(row);
  773. }
  774. else if (m_pRowInfo[row].nCurSize + m_pRowInfo[row+1].nCurSize
  775. < pt.y + m_pRowInfo[row+1].nMinSize)
  776. {
  777. // not enough room for other pane
  778. if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  779. DeleteRow(row + 1);
  780. }
  781. }
  782. void CSplitterWnd::TrackColumnSize(int x, int col)
  783. {
  784. ASSERT_VALID(this);
  785. ASSERT(m_nCols > 1);
  786. CPoint pt(x, 0);
  787. ClientToScreen(&pt);
  788. GetPane(0, col)->ScreenToClient(&pt);
  789. m_pColInfo[col].nIdealSize = pt.x; // new size
  790. if (pt.x < m_pColInfo[col].nMinSize)
  791. {
  792. // resized too small
  793. m_pColInfo[col].nIdealSize = 0; // make it go away
  794. if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  795. DeleteColumn(col);
  796. }
  797. else if (m_pColInfo[col].nCurSize + m_pColInfo[col+1].nCurSize
  798. < pt.x + m_pColInfo[col+1].nMinSize)
  799. {
  800. // not enough room for other pane
  801. if (GetStyle() & SPLS_DYNAMIC_SPLIT)
  802. DeleteColumn(col + 1);
  803. }
  804. }
  805. void CSplitterWnd::StopTracking(BOOL bAccept)
  806. {
  807. ASSERT_VALID(this);
  808. if (!m_bTracking)
  809. return;
  810. ReleaseCapture();
  811. // erase tracker rectangle
  812. OnInvertTracker(m_rectTracker);
  813. if (m_bTracking2)
  814. OnInvertTracker(m_rectTracker2);
  815. m_bTracking = m_bTracking2 = FALSE;
  816. // save old active view
  817. CWnd* pOldActiveView = GetActivePane();
  818. // m_rectTracker is set to the new splitter position (without border)
  819. // (so, adjust relative to where the border will be)
  820. m_rectTracker.OffsetRect(-CX_BORDER , -CY_BORDER);
  821. m_rectTracker2.OffsetRect(-CX_BORDER, -CY_BORDER);
  822. if (bAccept)
  823. {
  824. if (m_htTrack == vSplitterBox)
  825. {
  826. SplitRow(m_rectTracker.top);
  827. }
  828. else if (m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
  829. {
  830. // set row height
  831. TrackRowSize(m_rectTracker.top, m_htTrack - vSplitterBar1);
  832. RecalcLayout();
  833. }
  834. else if (m_htTrack == hSplitterBox)
  835. {
  836. SplitColumn(m_rectTracker.left);
  837. }
  838. else if (m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
  839. {
  840. // set column width
  841. TrackColumnSize(m_rectTracker.left, m_htTrack - hSplitterBar1);
  842. RecalcLayout();
  843. }
  844. else if (m_htTrack >= splitterIntersection1 &&
  845. m_htTrack <= splitterIntersection225)
  846. {
  847. // set row height and column width
  848. int row = (m_htTrack - splitterIntersection1) / 15;
  849. int col = (m_htTrack - splitterIntersection1) % 15;
  850. TrackRowSize(m_rectTracker.top, row);
  851. TrackColumnSize(m_rectTracker2.left, col);
  852. RecalcLayout();
  853. }
  854. else if (m_htTrack == bothSplitterBox)
  855. {
  856. // rectTracker is vSplitter (splits rows)
  857. // rectTracker2 is hSplitter (splits cols)
  858. SplitRow(m_rectTracker.top);
  859. SplitColumn(m_rectTracker2.left);
  860. }
  861. }
  862. if (pOldActiveView == GetActivePane())
  863. {
  864. if (pOldActiveView != NULL)
  865. {
  866. SetActivePane(-1, -1, pOldActiveView); // re-activate
  867. pOldActiveView->SetFocus(); // make sure focus is restored
  868. }
  869. }
  870. }
  871. void CSplitterWnd::GetHitRect(int ht, CRect& rectHit)
  872. {
  873. ASSERT_VALID(this);
  874. CRect rectClient;
  875. GetClientRect(&rectClient);
  876. rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
  877. int cx = rectClient.Width();
  878. int cy = rectClient.Height();
  879. int x = rectClient.top;
  880. int y = rectClient.left;
  881. // hit rectangle does not include border
  882. // m_rectLimit will be limited to valid tracking rect
  883. // m_ptTrackOffset will be set to appropriate tracking offset
  884. m_ptTrackOffset.x = 0;
  885. m_ptTrackOffset.y = 0;
  886. if (ht == vSplitterBox)
  887. {
  888. cy = m_cySplitter - (2*m_cyBorder - afxData.bWin4);
  889. m_ptTrackOffset.y = -(cy / 2);
  890. ASSERT(m_pRowInfo[0].nCurSize > 0);
  891. m_rectLimit.bottom -= cy;
  892. }
  893. else if (ht == hSplitterBox)
  894. {
  895. cx = m_cxSplitter - (2*m_cxBorder - afxData.bWin4);
  896. m_ptTrackOffset.x = -(cx / 2);
  897. ASSERT(m_pColInfo[0].nCurSize > 0);
  898. m_rectLimit.right -= cx;
  899. }
  900. else if (ht >= vSplitterBar1 && ht <= vSplitterBar15)
  901. {
  902. cy = m_cySplitter - (2*m_cyBorder - afxData.bWin4);
  903. m_ptTrackOffset.y = -(cy / 2);
  904. for (int row = 0; row < ht - vSplitterBar1; row++)
  905. y += m_pRowInfo[row].nCurSize + m_cySplitterGap;
  906. m_rectLimit.top = y;
  907. y += m_pRowInfo[row].nCurSize + m_cyBorderShare + afxData.bWin4;
  908. m_rectLimit.bottom -= cy;
  909. }
  910. else if (ht >= hSplitterBar1 && ht <= hSplitterBar15)
  911. {
  912. cx = m_cxSplitter - (2*m_cxBorder - afxData.bWin4);
  913. m_ptTrackOffset.x = -(cx / 2);
  914. for (int col = 0; col < ht - hSplitterBar1; col++)
  915. x += m_pColInfo[col].nCurSize + m_cxSplitterGap;
  916. m_rectLimit.left = x;
  917. x += m_pColInfo[col].nCurSize + m_cxBorderShare + afxData.bWin4;
  918. m_rectLimit.right -= cx;
  919. }
  920. else
  921. {
  922. TRACE1("Error: GetHitRect(%d): Not Found!\n", ht);
  923. ASSERT(FALSE);
  924. }
  925. rectHit.right = (rectHit.left = x) + cx;
  926. rectHit.bottom = (rectHit.top = y) + cy;
  927. }
  928. int CSplitterWnd::HitTest(CPoint pt) const
  929. {
  930. ASSERT_VALID(this);
  931. CRect rectClient;
  932. GetClientRect(&rectClient);
  933. rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
  934. CRect rectInside;
  935. GetInsideRect(rectInside);
  936. if (m_bHasVScroll && m_nRows < m_nMaxRows &&
  937. CRect(rectInside.right, rectClient.top, rectClient.right,
  938. rectClient.top + m_cySplitter - afxData.bWin4).PtInRect(pt))
  939. {
  940. return vSplitterBox;
  941. }
  942. if (m_bHasHScroll && m_nCols < m_nMaxCols &&
  943. CRect(rectClient.left, rectInside.bottom,
  944. rectClient.left + m_cxSplitter - afxData.bWin4,
  945. rectClient.bottom).PtInRect(pt))
  946. {
  947. return hSplitterBox;
  948. }
  949. // for hit detect, include the border of splitters
  950. CRect rect;
  951. rect = rectClient;
  952. for (int col = 0; col < m_nCols - 1; col++)
  953. {
  954. rect.left += m_pColInfo[col].nCurSize;
  955. rect.right = rect.left + m_cxSplitterGap;
  956. if (rect.PtInRect(pt))
  957. break;
  958. rect.left = rect.right;
  959. }
  960. rect = rectClient;
  961. for (int row = 0; row < m_nRows - 1; row++)
  962. {
  963. rect.top += m_pRowInfo[row].nCurSize;
  964. rect.bottom = rect.top + m_cySplitterGap;
  965. if (rect.PtInRect(pt))
  966. break;
  967. rect.top = rect.bottom;
  968. }
  969. // row and col set for hit splitter (if not hit will be past end)
  970. if (col != m_nCols - 1)
  971. {
  972. if (row != m_nRows - 1)
  973. return splitterIntersection1 + row * 15 + col;
  974. return hSplitterBar1 + col;
  975. }
  976. if (row != m_nRows - 1)
  977. return vSplitterBar1 + row;
  978. return noHit;
  979. }
  980. /////////////////////////////////////////////////////////////////////////////
  981. // CSplitterWnd tracking visuals
  982. void CSplitterWnd::OnInvertTracker(const CRect& rect)
  983. {
  984. ASSERT_VALID(this);
  985. ASSERT(!rect.IsRectEmpty());
  986. ASSERT((GetStyle() & WS_CLIPCHILDREN) == 0);
  987. // pat-blt without clip children on
  988. CDC* pDC = GetDC();
  989. // invert the brush pattern (looks just like frame window sizing)
  990. CBrush* pBrush = CDC::GetHalftoneBrush();
  991. HBRUSH hOldBrush = NULL;
  992. if (pBrush != NULL)
  993. hOldBrush = (HBRUSH)SelectObject(pDC->m_hDC, pBrush->m_hObject);
  994. pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT);
  995. if (hOldBrush != NULL)
  996. SelectObject(pDC->m_hDC, hOldBrush);
  997. ReleaseDC(pDC);
  998. }
  999. /////////////////////////////////////////////////////////////////////////////
  1000. // CSplitterWnd commands
  1001. // Keyboard interface
  1002. BOOL CSplitterWnd::DoKeyboardSplit()
  1003. {
  1004. ASSERT_VALID(this);
  1005. int ht;
  1006. if (m_nRows > 1 && m_nCols > 1)
  1007. ht = splitterIntersection1; // split existing row+col
  1008. else if (m_nRows > 1)
  1009. ht = vSplitterBar1; // split existing row
  1010. else if (m_nCols > 1)
  1011. ht = hSplitterBar1; // split existing col
  1012. else if (m_nMaxRows > 1 && m_nMaxCols > 1)
  1013. ht = bothSplitterBox; // we can split both
  1014. else if (m_nMaxRows > 1)
  1015. ht = vSplitterBox; // we can split rows
  1016. else if (m_nMaxCols > 1)
  1017. ht = hSplitterBox; // we can split columns
  1018. else
  1019. return FALSE; // can't split
  1020. // start tracking
  1021. StartTracking(ht);
  1022. CRect rect;
  1023. rect.left = m_rectTracker.Width() / 2;
  1024. rect.top = m_rectTracker.Height() / 2;
  1025. if (m_ptTrackOffset.y != 0)
  1026. rect.top = m_rectTracker.top;
  1027. if (m_ptTrackOffset.x != 0)
  1028. rect.left = m_bTracking2 ? m_rectTracker2.left :m_rectTracker.left;
  1029. rect.OffsetRect(-m_ptTrackOffset.x, -m_ptTrackOffset.y);
  1030. ClientToScreen(&rect);
  1031. SetCursorPos(rect.left, rect.top);
  1032. return TRUE;
  1033. }
  1034. /////////////////////////////////////////////////////////////////////////////
  1035. // Main drawing and layout
  1036. void CSplitterWnd::OnDisplayChange()
  1037. {
  1038. if (!IsIconic() && IsWindowVisible())
  1039. RecalcLayout();
  1040. }
  1041. void CSplitterWnd::OnSize(UINT nType, int cx, int cy)
  1042. {
  1043. if (nType != SIZE_MINIMIZED && cx > 0 && cy > 0)
  1044. RecalcLayout();
  1045. CWnd::OnSize(nType, cx, cy);
  1046. }
  1047. // Generic routine:
  1048. // for X direction: pInfo = m_pColInfo, nMax = m_nMaxCols, nSize = cx
  1049. // for Y direction: pInfo = m_pRowInfo, nMax = m_nMaxRows, nSize = cy
  1050. AFX_STATIC void AFXAPI _AfxLayoutRowCol(CSplitterWnd::CRowColInfo* pInfoArray,
  1051. int nMax, int nSize, int nSizeSplitter)
  1052. {
  1053. ASSERT(pInfoArray != NULL);
  1054. ASSERT(nMax > 0);
  1055. ASSERT(nSizeSplitter > 0);
  1056. CSplitterWnd::CRowColInfo* pInfo;
  1057. int i;
  1058. if (nSize < 0)
  1059. nSize = 0; // if really too small, layout as zero size
  1060. // start with ideal sizes
  1061. for (i = 0, pInfo = pInfoArray; i < nMax-1; i++, pInfo++)
  1062. {
  1063. if (pInfo->nIdealSize < pInfo->nMinSize)
  1064. pInfo->nIdealSize = 0; // too small to see
  1065. pInfo->nCurSize = pInfo->nIdealSize;
  1066. }
  1067. pInfo->nCurSize = INT_MAX; // last row/column takes the rest
  1068. for (i = 0, pInfo = pInfoArray; i < nMax; i++, pInfo++)
  1069. {
  1070. ASSERT(nSize >= 0);
  1071. if (nSize == 0)
  1072. {
  1073. // no more room (set pane to be invisible)
  1074. pInfo->nCurSize = 0;
  1075. continue; // don't worry about splitters
  1076. }
  1077. else if (nSize < pInfo->nMinSize && i != 0)
  1078. {
  1079. // additional panes below the recommended minimum size
  1080. // aren't shown and the size goes to the previous pane
  1081. pInfo->nCurSize = 0;
  1082. // previous pane already has room for splitter + border
  1083. // add remaining size and remove the extra border
  1084. ASSERT(afxData.cxBorder2 == afxData.cyBorder2);
  1085. (pInfo-1)->nCurSize += nSize + afxData.cxBorder2;
  1086. nSize = 0;
  1087. }
  1088. else
  1089. {
  1090. // otherwise we can add the second pane
  1091. ASSERT(nSize > 0);
  1092. if (pInfo->nCurSize == 0)
  1093. {
  1094. // too small to see
  1095. if (i != 0)
  1096. pInfo->nCurSize = 0;
  1097. }
  1098. else if (nSize < pInfo->nCurSize)
  1099. {
  1100. // this row/col won't fit completely - make as small as possible
  1101. pInfo->nCurSize = nSize;
  1102. nSize = 0;
  1103. }
  1104. else
  1105. {
  1106. // can fit everything
  1107. nSize -= pInfo->nCurSize;
  1108. }
  1109. }
  1110. // see if we should add a splitter
  1111. ASSERT(nSize >= 0);
  1112. if (i != nMax - 1)
  1113. {
  1114. // should have a splitter
  1115. if (nSize > nSizeSplitter)
  1116. {
  1117. nSize -= nSizeSplitter; // leave room for splitter + border
  1118. ASSERT(nSize > 0);
  1119. }
  1120. else
  1121. {
  1122. // not enough room - add left over less splitter size
  1123. ASSERT(afxData.cxBorder2 == afxData.cyBorder2);
  1124. pInfo->nCurSize += nSize;
  1125. if (pInfo->nCurSize > (nSizeSplitter - afxData.cxBorder2))
  1126. pInfo->nCurSize -= (nSizeSplitter - afxData.cyBorder2);
  1127. nSize = 0;
  1128. }
  1129. }
  1130. }
  1131. ASSERT(nSize == 0); // all space should be allocated
  1132. }
  1133. // repositions client area of specified window
  1134. // assumes everything has WS_BORDER or is inset like it does
  1135. // (includes scroll bars)
  1136. AFX_STATIC void AFXAPI _AfxDeferClientPos(AFX_SIZEPARENTPARAMS* lpLayout,
  1137. CWnd* pWnd, int x, int y, int cx, int cy, BOOL bScrollBar)
  1138. {
  1139. ASSERT(pWnd != NULL);
  1140. ASSERT(pWnd->m_hWnd != NULL);
  1141. if (bScrollBar)
  1142. {
  1143. // if there is enough room, draw scroll bar without border
  1144. // if there is not enough room, set the WS_BORDER bit so that
  1145. // we will at least get a proper border drawn
  1146. BOOL bNeedBorder = (cx <= CX_BORDER || cy <= CY_BORDER);
  1147. pWnd->ModifyStyle(bNeedBorder ? 0 : WS_BORDER,
  1148. bNeedBorder ? WS_BORDER : 0);
  1149. }
  1150. CRect rect(x, y, x+cx, y+cy);
  1151. // adjust for border size (even if zero client size)
  1152. if (!afxData.bWin4)
  1153. {
  1154. if (bScrollBar)
  1155. rect.InflateRect(CX_BORDER, CY_BORDER);
  1156. else
  1157. pWnd->CalcWindowRect(&rect);
  1158. }
  1159. // adjust for 3d border (splitter windows have implied border)
  1160. if ((pWnd->GetExStyle() & WS_EX_CLIENTEDGE) ||
  1161. pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
  1162. rect.InflateRect(afxData.cxBorder2, afxData.cyBorder2);
  1163. // first check if the new rectangle is the same as the current
  1164. CRect rectOld;
  1165. pWnd->GetWindowRect(rectOld);
  1166. pWnd->GetParent()->ScreenToClient(&rectOld);
  1167. if (rect != rectOld)
  1168. AfxRepositionWindow(lpLayout, pWnd->m_hWnd, rect);
  1169. }
  1170. CWnd* CSplitterWnd::GetSizingParent()
  1171. {
  1172. ASSERT_VALID(this);
  1173. if (!afxData.bWin4)
  1174. return NULL;
  1175. // size box is in lower right corner of this window
  1176. CRect rectClient;
  1177. GetClientRect(rectClient);
  1178. // find sizeable parent window
  1179. CWnd* pParent = this;
  1180. if (!(pParent->GetStyle() & WS_THICKFRAME))
  1181. pParent = GetParent();
  1182. // only allow if not maximized and has thick frame
  1183. ASSERT_VALID(pParent);
  1184. if ((pParent->GetStyle() & (WS_THICKFRAME|WS_MAXIMIZE)) == WS_THICKFRAME)
  1185. {
  1186. // convert client area of frame window relative to splitter
  1187. CRect rect;
  1188. pParent->GetClientRect(rect);
  1189. pParent->ClientToScreen(rect);
  1190. ScreenToClient(rect);
  1191. // must match exactly to get the size box
  1192. if (rectClient.BottomRight() == rect.BottomRight())
  1193. return pParent;
  1194. }
  1195. return NULL; // no sizeable parent found
  1196. }
  1197. void CSplitterWnd::RecalcLayout()
  1198. {
  1199. ASSERT_VALID(this);
  1200. ASSERT(m_nRows > 0 && m_nCols > 0); // must have at least one pane
  1201. CRect rectClient;
  1202. GetClientRect(rectClient);
  1203. rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
  1204. CRect rectInside;
  1205. GetInsideRect(rectInside);
  1206. // layout columns (restrict to possible sizes)
  1207. _AfxLayoutRowCol(m_pColInfo, m_nCols, rectInside.Width(), m_cxSplitterGap);
  1208. _AfxLayoutRowCol(m_pRowInfo, m_nRows, rectInside.Height(), m_cySplitterGap);
  1209. // adjust the panes (and optionally scroll bars)
  1210. // give the hint for the maximum number of HWNDs
  1211. AFX_SIZEPARENTPARAMS layout;
  1212. layout.hDWP = ::BeginDeferWindowPos((m_nCols + 1) * (m_nRows + 1) + 1);
  1213. // size of scrollbars
  1214. int cx = (rectClient.right - rectInside.right) - afxData.bNotWin4;
  1215. int cy = (rectClient.bottom - rectInside.bottom) - afxData.bNotWin4;
  1216. // reposition size box
  1217. if (m_bHasHScroll && m_bHasVScroll)
  1218. {
  1219. CWnd* pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
  1220. ASSERT(pScrollBar != NULL);
  1221. // fix style if necessary
  1222. BOOL bSizingParent = (GetSizingParent() != NULL);
  1223. // modifyStyle returns TRUE if style changes
  1224. if (pScrollBar->ModifyStyle(SBS_SIZEGRIP|SBS_SIZEBOX,
  1225. bSizingParent ? SBS_SIZEGRIP : SBS_SIZEBOX))
  1226. pScrollBar->Invalidate();
  1227. pScrollBar->EnableWindow(bSizingParent);
  1228. // reposition the size box
  1229. _AfxDeferClientPos(&layout, pScrollBar,
  1230. rectInside.right + afxData.bNotWin4,
  1231. rectInside.bottom + afxData.bNotWin4, cx, cy, TRUE);
  1232. }
  1233. // reposition scroll bars
  1234. if (m_bHasHScroll)
  1235. {
  1236. int cxSplitterBox = m_cxSplitter + afxData.bNotWin4;// split box bigger
  1237. int x = rectClient.left;
  1238. int y = rectInside.bottom + afxData.bNotWin4;
  1239. for (int col = 0; col < m_nCols; col++)
  1240. {
  1241. CWnd* pScrollBar = GetDlgItem(AFX_IDW_HSCROLL_FIRST + col);
  1242. ASSERT(pScrollBar != NULL);
  1243. int cx = m_pColInfo[col].nCurSize;
  1244. if (col == 0 && m_nCols < m_nMaxCols)
  1245. x += cxSplitterBox, cx -= cxSplitterBox;
  1246. _AfxDeferClientPos(&layout, pScrollBar, x, y, cx, cy, TRUE);
  1247. x += cx + m_cxSplitterGap;
  1248. }
  1249. }
  1250. if (m_bHasVScroll)
  1251. {
  1252. int cySplitterBox = m_cySplitter + afxData.bNotWin4;// split box bigger
  1253. int x = rectInside.right + afxData.bNotWin4;
  1254. int y = rectClient.top;
  1255. for (int row = 0; row < m_nRows; row++)
  1256. {
  1257. CWnd* pScrollBar = GetDlgItem(AFX_IDW_VSCROLL_FIRST + row);
  1258. ASSERT(pScrollBar != NULL);
  1259. int cy = m_pRowInfo[row].nCurSize;
  1260. if (row == 0 && m_nRows < m_nMaxRows)
  1261. y += cySplitterBox, cy -= cySplitterBox;
  1262. _AfxDeferClientPos(&layout, pScrollBar, x, y, cx, cy, TRUE);
  1263. y += cy + m_cySplitterGap;
  1264. }
  1265. }
  1266. //BLOCK: Reposition all the panes
  1267. {
  1268. int x = rectClient.left;
  1269. for (int col = 0; col < m_nCols; col++)
  1270. {
  1271. int cx = m_pColInfo[col].nCurSize;
  1272. int y = rectClient.top;
  1273. for (int row = 0; row < m_nRows; row++)
  1274. {
  1275. int cy = m_pRowInfo[row].nCurSize;
  1276. CWnd* pWnd = GetPane(row, col);
  1277. _AfxDeferClientPos(&layout, pWnd, x, y, cx, cy, FALSE);
  1278. y += cy + m_cySplitterGap;
  1279. }
  1280. x += cx + m_cxSplitterGap;
  1281. }
  1282. }
  1283. // move and resize all the windows at once!
  1284. if (layout.hDWP == NULL || !::EndDeferWindowPos(layout.hDWP))
  1285. TRACE0("Warning: DeferWindowPos failed - low system resources.\n");
  1286. // invalidate all the splitter bars (with NULL pDC)
  1287. DrawAllSplitBars(NULL, rectInside.right, rectInside.bottom);
  1288. }
  1289. void CSplitterWnd::DrawAllSplitBars(CDC* pDC, int cxInside, int cyInside)
  1290. {
  1291. ASSERT_VALID(this);
  1292. // draw column split bars
  1293. CRect rect;
  1294. GetClientRect(rect);
  1295. rect.left += m_cxBorder;
  1296. for (int col = 0; col < m_nCols - 1; col++)
  1297. {
  1298. rect.left += m_pColInfo[col].nCurSize + m_cxBorderShare;
  1299. rect.right = rect.left + m_cxSplitter;
  1300. if (rect.left > cxInside)
  1301. break; // stop if not fully visible
  1302. OnDrawSplitter(pDC, splitBar, rect);
  1303. rect.left = rect.right + m_cxBorderShare;
  1304. }
  1305. // draw row split bars
  1306. GetClientRect(rect);
  1307. rect.top += m_cyBorder;
  1308. for (int row = 0; row < m_nRows - 1; row++)
  1309. {
  1310. rect.top += m_pRowInfo[row].nCurSize + m_cyBorderShare;
  1311. rect.bottom = rect.top + m_cySplitter;
  1312. if (rect.top > cyInside)
  1313. break; // stop if not fully visible
  1314. OnDrawSplitter(pDC, splitBar, rect);
  1315. rect.top = rect.bottom + m_cyBorderShare;
  1316. }
  1317. // draw pane borders
  1318. if (afxData.bWin4)
  1319. {
  1320. GetClientRect(rect);
  1321. int x = rect.left;
  1322. for (col = 0; col < m_nCols; col++)
  1323. {
  1324. int cx = m_pColInfo[col].nCurSize + 2*m_cxBorder;
  1325. if (col == m_nCols-1 && m_bHasVScroll)
  1326. cx += afxData.cxVScroll - CX_BORDER;
  1327. int y = rect.top;
  1328. for (int row = 0; row < m_nRows; row++)
  1329. {
  1330. int cy = m_pRowInfo[row].nCurSize + 2*m_cyBorder;
  1331. if (row == m_nRows-1 && m_bHasHScroll)
  1332. cy += afxData.cyHScroll - CX_BORDER;
  1333. OnDrawSplitter(pDC, splitBorder, CRect(x, y, x+cx, y+cy));
  1334. y += cy + m_cySplitterGap - 2*m_cyBorder;
  1335. }
  1336. x += cx + m_cxSplitterGap - 2*m_cxBorder;
  1337. }
  1338. }
  1339. }
  1340. void CSplitterWnd::OnPaint()
  1341. {
  1342. ASSERT_VALID(this);
  1343. CPaintDC dc(this);
  1344. CRect rectClient;
  1345. GetClientRect(&rectClient);
  1346. rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
  1347. CRect rectInside;
  1348. GetInsideRect(rectInside);
  1349. // draw the splitter boxes
  1350. if (m_bHasVScroll && m_nRows < m_nMaxRows)
  1351. {
  1352. OnDrawSplitter(&dc, splitBox,
  1353. CRect(rectInside.right + afxData.bNotWin4, rectClient.top,
  1354. rectClient.right, rectClient.top + m_cySplitter));
  1355. }
  1356. if (m_bHasHScroll && m_nCols < m_nMaxCols)
  1357. {
  1358. OnDrawSplitter(&dc, splitBox,
  1359. CRect(rectClient.left, rectInside.bottom + afxData.bNotWin4,
  1360. rectClient.left + m_cxSplitter, rectClient.bottom));
  1361. }
  1362. // extend split bars to window border (past margins)
  1363. DrawAllSplitBars(&dc, rectInside.right, rectInside.bottom);
  1364. if (!afxData.bWin4)
  1365. {
  1366. // draw splitter intersections (inside only)
  1367. GetInsideRect(rectInside);
  1368. dc.IntersectClipRect(rectInside);
  1369. CRect rect;
  1370. rect.top = rectInside.top;
  1371. for (int row = 0; row < m_nRows - 1; row++)
  1372. {
  1373. rect.top += m_pRowInfo[row].nCurSize + m_cyBorderShare;
  1374. rect.bottom = rect.top + m_cySplitter;
  1375. rect.left = rectInside.left;
  1376. for (int col = 0; col < m_nCols - 1; col++)
  1377. {
  1378. rect.left += m_pColInfo[col].nCurSize + m_cxBorderShare;
  1379. rect.right = rect.left + m_cxSplitter;
  1380. OnDrawSplitter(&dc, splitIntersection, rect);
  1381. rect.left = rect.right + m_cxBorderShare;
  1382. }
  1383. rect.top = rect.bottom + m_cxBorderShare;
  1384. }
  1385. }
  1386. }
  1387. BOOL CSplitterWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
  1388. {
  1389. if (nHitTest == HTCLIENT && pWnd == this && !m_bTracking)
  1390. return TRUE; // we will handle it in the mouse move
  1391. return CWnd::OnSetCursor(pWnd, nHitTest, message);
  1392. }
  1393. // cache of last needed cursor
  1394. AFX_STATIC_DATA HCURSOR _afx_hcurLast = NULL;
  1395. AFX_STATIC_DATA HCURSOR _afx_hcurDestroy = NULL;
  1396. AFX_STATIC_DATA UINT _afx_idcPrimaryLast = 0; // store the primary IDC
  1397. void CSplitterWnd::SetSplitCursor(int ht)
  1398. {
  1399. UINT idcPrimary; // app supplied cursor
  1400. LPCTSTR idcSecondary; // system supplied cursor (MAKEINTRESOURCE)
  1401. AfxLockGlobals(CRIT_SPLITTERWND);
  1402. if (ht == vSplitterBox ||
  1403. ht >= vSplitterBar1 && ht <= vSplitterBar15)
  1404. {
  1405. idcPrimary = AFX_IDC_VSPLITBAR;
  1406. idcSecondary = IDC_SIZENS;
  1407. }
  1408. else if (ht == hSplitterBox ||
  1409. ht >= hSplitterBar1 && ht <= hSplitterBar15)
  1410. {
  1411. idcPrimary = AFX_IDC_HSPLITBAR;
  1412. idcSecondary = IDC_SIZEWE;
  1413. }
  1414. else if (ht == bothSplitterBox ||
  1415. (ht >= splitterIntersection1 && ht <= splitterIntersection225))
  1416. {
  1417. idcPrimary = AFX_IDC_SMALLARROWS;
  1418. idcSecondary = IDC_SIZEALL;
  1419. }
  1420. else
  1421. {
  1422. SetCursor(afxData.hcurArrow);
  1423. idcPrimary = 0; // don't use it
  1424. idcSecondary = 0; // don't use it
  1425. }
  1426. if (idcPrimary != 0)
  1427. {
  1428. HCURSOR hcurToDestroy = NULL;
  1429. if (idcPrimary != _afx_idcPrimaryLast)
  1430. {
  1431. HINSTANCE hInst = AfxFindResourceHandle(
  1432. MAKEINTRESOURCE(idcPrimary), RT_GROUP_CURSOR);
  1433. // load in another cursor
  1434. hcurToDestroy = _afx_hcurDestroy;
  1435. // Note: If this LoadCursor call fails, it is likely that
  1436. // _AFX_NO_SPLITTER_RESOURCES is defined in your .RC file.
  1437. // To correct the situation, remove the following line from your
  1438. // resource script:
  1439. // #define _AFX_NO_SPLITTER_RESOURCES
  1440. // This should be done using the Resource.Set Includes... command.
  1441. if ((_afx_hcurDestroy = _afx_hcurLast =
  1442. ::LoadCursor(hInst, MAKEINTRESOURCE(idcPrimary))) == NULL)
  1443. {
  1444. // will not look as good
  1445. TRACE0("Warning: Could not find splitter cursor - using system provided alternative.\n");
  1446. ASSERT(_afx_hcurDestroy == NULL); // will not get destroyed
  1447. _afx_hcurLast = ::LoadCursor(NULL, idcSecondary);
  1448. ASSERT(_afx_hcurLast != NULL);
  1449. }
  1450. _afx_idcPrimaryLast = idcPrimary;
  1451. }
  1452. ASSERT(_afx_hcurLast != NULL);
  1453. ::SetCursor(_afx_hcurLast);
  1454. ASSERT(_afx_hcurLast != hcurToDestroy);
  1455. if (hcurToDestroy != NULL)
  1456. ::DestroyCursor(hcurToDestroy); // destroy after being set
  1457. }
  1458. AfxUnlockGlobals(CRIT_SPLITTERWND);
  1459. }
  1460. void CSplitterWnd::OnMouseMove(UINT /*nFlags*/, CPoint pt)
  1461. {
  1462. if (GetCapture() != this)
  1463. StopTracking(FALSE);
  1464. if (m_bTracking)
  1465. {
  1466. // move tracker to current cursor position
  1467. pt.Offset(m_ptTrackOffset); // pt is the upper right of hit detect
  1468. // limit the point to the valid split range
  1469. if (pt.y < m_rectLimit.top)
  1470. pt.y = m_rectLimit.top;
  1471. else if (pt.y > m_rectLimit.bottom)
  1472. pt.y = m_rectLimit.bottom;
  1473. if (pt.x < m_rectLimit.left)
  1474. pt.x = m_rectLimit.left;
  1475. else if (pt.x > m_rectLimit.right)
  1476. pt.x = m_rectLimit.right;
  1477. if (m_htTrack == vSplitterBox ||
  1478. m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
  1479. {
  1480. if (m_rectTracker.top != pt.y)
  1481. {
  1482. OnInvertTracker(m_rectTracker);
  1483. m_rectTracker.OffsetRect(0, pt.y - m_rectTracker.top);
  1484. OnInvertTracker(m_rectTracker);
  1485. }
  1486. }
  1487. else if (m_htTrack == hSplitterBox ||
  1488. m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
  1489. {
  1490. if (m_rectTracker.left != pt.x)
  1491. {
  1492. OnInvertTracker(m_rectTracker);
  1493. m_rectTracker.OffsetRect(pt.x - m_rectTracker.left, 0);
  1494. OnInvertTracker(m_rectTracker);
  1495. }
  1496. }
  1497. else if (m_htTrack == bothSplitterBox ||
  1498. (m_htTrack >= splitterIntersection1 &&
  1499. m_htTrack <= splitterIntersection225))
  1500. {
  1501. if (m_rectTracker.top != pt.y)
  1502. {
  1503. OnInvertTracker(m_rectTracker);
  1504. m_rectTracker.OffsetRect(0, pt.y - m_rectTracker.top);
  1505. OnInvertTracker(m_rectTracker);
  1506. }
  1507. if (m_rectTracker2.left != pt.x)
  1508. {
  1509. OnInvertTracker(m_rectTracker2);
  1510. m_rectTracker2.OffsetRect(pt.x - m_rectTracker2.left, 0);
  1511. OnInvertTracker(m_rectTracker2);
  1512. }
  1513. }
  1514. }
  1515. else
  1516. {
  1517. // simply hit-test and set appropriate cursor
  1518. int ht = HitTest(pt);
  1519. SetSplitCursor(ht);
  1520. }
  1521. }
  1522. void CSplitterWnd::OnLButtonDown(UINT /*nFlags*/, CPoint pt)
  1523. {
  1524. if (m_bTracking)
  1525. return;
  1526. StartTracking(HitTest(pt));
  1527. }
  1528. void CSplitterWnd::OnLButtonDblClk(UINT /*nFlags*/, CPoint pt)
  1529. {
  1530. int ht = HitTest(pt);
  1531. CRect rect;
  1532. StopTracking(FALSE);
  1533. if ((GetStyle() & SPLS_DYNAMIC_SPLIT) == 0)
  1534. return; // do nothing if layout is static
  1535. if (ht == vSplitterBox)
  1536. {
  1537. // half split
  1538. SplitRow(m_pRowInfo[0].nCurSize / 2);
  1539. }
  1540. else if (ht == hSplitterBox)
  1541. {
  1542. // half split
  1543. SplitColumn(m_pColInfo[0].nCurSize / 2);
  1544. }
  1545. else if (ht >= vSplitterBar1 && ht <= vSplitterBar15)
  1546. {
  1547. int rowDelete = ht - vSplitterBar1;
  1548. // don't delete the active row
  1549. int row;
  1550. if (GetActivePane(&row, NULL) != NULL && rowDelete == row)
  1551. ++rowDelete;
  1552. DeleteRow(rowDelete);
  1553. }
  1554. else if (ht >= hSplitterBar1 && ht <= hSplitterBar15)
  1555. {
  1556. int colDelete = ht - hSplitterBar1;
  1557. // don't delete the active column
  1558. int col;
  1559. if (GetActivePane(NULL, &col) != NULL && colDelete == col)
  1560. ++colDelete;
  1561. DeleteColumn(colDelete);
  1562. }
  1563. else if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
  1564. {
  1565. int rowDelete = (ht - splitterIntersection1) / 15;
  1566. int colDelete = (ht - splitterIntersection1) % 15;
  1567. int row, col;
  1568. if (GetActivePane(&row, &col) != NULL)
  1569. {
  1570. // don't delete the active row or column
  1571. if (col == colDelete)
  1572. ++colDelete;
  1573. if (row == rowDelete)
  1574. ++rowDelete;
  1575. }
  1576. DeleteRow(rowDelete);
  1577. DeleteColumn(colDelete);
  1578. }
  1579. }
  1580. void CSplitterWnd::OnLButtonUp(UINT /*nFlags*/, CPoint /*pt*/)
  1581. {
  1582. StopTracking(TRUE);
  1583. }
  1584. void CSplitterWnd::OnCancelMode()
  1585. {
  1586. StopTracking(FALSE);
  1587. }
  1588. void CSplitterWnd::OnKeyDown(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/)
  1589. {
  1590. CPoint pt;
  1591. GetCursorPos(&pt);
  1592. int cz = GetKeyState(VK_CONTROL) < 0 ? 1 : 16;
  1593. int dx = 0;
  1594. int dy = 0;
  1595. switch (nChar)
  1596. {
  1597. case VK_ESCAPE:
  1598. StopTracking(FALSE);
  1599. return;
  1600. case VK_RETURN:
  1601. StopTracking(TRUE);
  1602. return;
  1603. case VK_LEFT:
  1604. dx = -1;
  1605. break;
  1606. case VK_RIGHT:
  1607. dx = +1;
  1608. break;
  1609. case VK_UP:
  1610. dy = -1;
  1611. break;
  1612. case VK_DOWN:
  1613. dy = +1;
  1614. break;
  1615. default:
  1616. Default(); // pass other keys through
  1617. return;
  1618. }
  1619. if (m_htTrack == vSplitterBox ||
  1620. m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
  1621. {
  1622. // no movement along X axis
  1623. dx = 0;
  1624. }
  1625. if (m_htTrack == hSplitterBox ||
  1626. m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
  1627. {
  1628. // no movement along Y axis
  1629. dy = 0;
  1630. }
  1631. // adjust to new position
  1632. pt.x += dx * cz;
  1633. pt.y += dy * cz;
  1634. // make sure within valid limits
  1635. ScreenToClient(&pt);
  1636. if (pt.y < m_rectLimit.top)
  1637. pt.y = m_rectLimit.top;
  1638. else if (pt.y > m_rectLimit.bottom)
  1639. pt.y = m_rectLimit.bottom;
  1640. if (pt.x < m_rectLimit.left)
  1641. pt.x = m_rectLimit.left;
  1642. else if (pt.x > m_rectLimit.right)
  1643. pt.x = m_rectLimit.right;
  1644. ClientToScreen(&pt);
  1645. // cause WM_MOUSEMOVE to filter through
  1646. SetCursorPos(pt.x, pt.y);
  1647. }
  1648. void CSplitterWnd::OnSysCommand(UINT nID, LPARAM lParam)
  1649. {
  1650. if ((nID & 0xFFF0) == SC_SIZE)
  1651. {
  1652. CWnd* pParent = GetSizingParent();
  1653. if (pParent != NULL)
  1654. {
  1655. pParent->SendMessage(WM_SYSCOMMAND, (WPARAM)nID, lParam);
  1656. return;
  1657. }
  1658. }
  1659. CWnd::OnSysCommand(nID, lParam);
  1660. }
  1661. /////////////////////////////////////////////////////////////////////////////
  1662. // CSplitterWnd command routing
  1663. BOOL CSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam)
  1664. {
  1665. if (CWnd::OnCommand(wParam, lParam))
  1666. return TRUE;
  1667. // route commands to the splitter to the parent frame window
  1668. return GetParentFrame()->SendMessage(WM_COMMAND, wParam, lParam);
  1669. }
  1670. BOOL CSplitterWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  1671. {
  1672. if (CWnd::OnNotify(wParam, lParam, pResult))
  1673. return TRUE;
  1674. // route commands to the splitter to the parent frame window
  1675. *pResult = GetParentFrame()->SendMessage(WM_NOTIFY, wParam, lParam);
  1676. return TRUE;
  1677. }
  1678. /////////////////////////////////////////////////////////////////////////////
  1679. // Scroll messages
  1680. BOOL CSplitterWnd::OnMouseWheel(UINT fFlags, short zDelta, CPoint point)
  1681. {
  1682. BOOL bRetVal = FALSE;
  1683. int row;
  1684. int col;
  1685. // find panes in the splitter that has scroll bars
  1686. // and have them do their scrolling
  1687. for (row = 0; row < m_nRows; row++)
  1688. {
  1689. for (col = 0; col < m_nCols; col++)
  1690. {
  1691. // only do the scrolling if the window is-a CScrollView
  1692. CWnd* pPane = GetPane(row, col);
  1693. CScrollView* pView = DYNAMIC_DOWNCAST(CScrollView, pPane);
  1694. if (pView != NULL)
  1695. {
  1696. // prefer to scroll vertically if available
  1697. CScrollBar* pBar = pView->GetScrollBarCtrl(SB_VERT);
  1698. if (pBar == NULL)
  1699. {
  1700. pBar = pView->GetScrollBarCtrl(SB_HORZ);
  1701. if (pBar == NULL)
  1702. continue;
  1703. }
  1704. // get the old position, do the scrolling, and
  1705. // then trigger the repaint
  1706. int nOldPos = pBar->GetScrollPos();
  1707. if (pView->DoMouseWheel(fFlags, zDelta, point))
  1708. bRetVal = TRUE;
  1709. if (col < m_nCols -1)
  1710. pBar->SetScrollPos(nOldPos, FALSE);
  1711. }
  1712. }
  1713. }
  1714. return TRUE;
  1715. }
  1716. void CSplitterWnd::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  1717. {
  1718. ASSERT(pScrollBar != NULL);
  1719. int col = _AfxGetDlgCtrlID(pScrollBar->m_hWnd) - AFX_IDW_HSCROLL_FIRST;
  1720. ASSERT(col >= 0 && col < m_nMaxCols);
  1721. ASSERT(m_nRows > 0);
  1722. int nOldPos = pScrollBar->GetScrollPos();
  1723. #ifdef _DEBUG
  1724. int nNewPos;
  1725. #endif
  1726. for (int row = 0; row < m_nRows; row++)
  1727. {
  1728. GetPane(row, col)->SendMessage(WM_HSCROLL,
  1729. MAKELONG(nSBCode, nPos), (LPARAM)pScrollBar->m_hWnd);
  1730. #ifdef _DEBUG
  1731. if (row == 0)
  1732. {
  1733. nNewPos = pScrollBar->GetScrollPos();
  1734. if (pScrollBar->GetScrollPos() != nNewPos)
  1735. {
  1736. TRACE0("Warning: scroll panes setting different scroll positions.\n");
  1737. // stick with the last one set
  1738. }
  1739. }
  1740. #endif //_DEBUG
  1741. // set the scroll pos to the value it was originally for the next pane
  1742. if (row < m_nRows - 1)
  1743. pScrollBar->SetScrollPos(nOldPos, FALSE);
  1744. }
  1745. }
  1746. void CSplitterWnd::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  1747. {
  1748. ASSERT(pScrollBar != NULL);
  1749. int row = _AfxGetDlgCtrlID(pScrollBar->m_hWnd) - AFX_IDW_VSCROLL_FIRST;
  1750. ASSERT(row >= 0 && row < m_nMaxRows);
  1751. ASSERT(m_nCols > 0);
  1752. int nOldPos = pScrollBar->GetScrollPos();
  1753. #ifdef _DEBUG
  1754. int nNewPos;
  1755. #endif
  1756. for (int col = 0; col < m_nCols; col++)
  1757. {
  1758. GetPane(row, col)->SendMessage(WM_VSCROLL,
  1759. MAKELONG(nSBCode, nPos), (LPARAM)pScrollBar->m_hWnd);
  1760. #ifdef _DEBUG
  1761. if (col == 0)
  1762. {
  1763. nNewPos = pScrollBar->GetScrollPos();
  1764. if (pScrollBar->GetScrollPos() != nNewPos)
  1765. {
  1766. TRACE0("Warning: scroll panes setting different scroll positions.\n");
  1767. // stick with the last one set
  1768. }
  1769. }
  1770. #endif //_DEBUG
  1771. // set the scroll pos to the value it was originally for the next pane
  1772. if (col < m_nCols - 1)
  1773. pScrollBar->SetScrollPos(nOldPos, FALSE);
  1774. }
  1775. }
  1776. // synchronized scrolling
  1777. BOOL CSplitterWnd::DoScroll(CView* pViewFrom, UINT nScrollCode, BOOL bDoScroll)
  1778. {
  1779. ASSERT_VALID(pViewFrom);
  1780. int rowFrom, colFrom;
  1781. if (!IsChildPane(pViewFrom, &rowFrom, &colFrom))
  1782. return FALSE;
  1783. BOOL bResult = FALSE;
  1784. // save original positions
  1785. int nOldVert = 0;
  1786. CScrollBar* pScrollVert = pViewFrom->GetScrollBarCtrl(SB_VERT);
  1787. if (pScrollVert != NULL)
  1788. nOldVert = pScrollVert->GetScrollPos();
  1789. int nOldHorz = 0;
  1790. CScrollBar* pScrollHorz = pViewFrom->GetScrollBarCtrl(SB_HORZ);
  1791. if (pScrollHorz != NULL)
  1792. nOldHorz = pScrollHorz->GetScrollPos();
  1793. // scroll the view from which the message is from
  1794. if (pViewFrom->OnScroll(nScrollCode, 0, bDoScroll))
  1795. bResult = TRUE;
  1796. if (pScrollVert != NULL)
  1797. {
  1798. #ifdef _DEBUG
  1799. int nNewVert = pScrollVert->GetScrollPos();
  1800. #endif
  1801. // scroll related columns
  1802. for (int col = 0; col < m_nCols; col++)
  1803. {
  1804. if (col == colFrom)
  1805. continue;
  1806. // set the scroll pos to the value it was originally
  1807. pScrollVert->SetScrollPos(nOldVert, FALSE);
  1808. // scroll the pane
  1809. CView* pView = (CView*)GetPane(rowFrom, col);
  1810. ASSERT_KINDOF(CView, pView);
  1811. ASSERT(pView != pViewFrom);
  1812. if (pView->OnScroll(MAKEWORD(-1, HIBYTE(nScrollCode)), 0,
  1813. bDoScroll))
  1814. {
  1815. bResult = TRUE;
  1816. }
  1817. #ifdef _DEBUG
  1818. if (pScrollVert->GetScrollPos() != nNewVert)
  1819. {
  1820. TRACE0("Warning: scroll panes setting different scroll positions.\n");
  1821. // stick with the last one set
  1822. }
  1823. #endif
  1824. }
  1825. }
  1826. if (pScrollHorz != NULL)
  1827. {
  1828. #ifdef _DEBUG
  1829. int nNewHorz = pScrollHorz->GetScrollPos();
  1830. #endif
  1831. // scroll related rows
  1832. for (int row = 0; row < m_nRows; row++)
  1833. {
  1834. if (row == rowFrom)
  1835. continue;
  1836. // set the scroll pos to the value it was originally
  1837. pScrollHorz->SetScrollPos(nOldHorz, FALSE);
  1838. // scroll the pane
  1839. CView* pView = (CView*)GetPane(row, colFrom);
  1840. ASSERT_KINDOF(CView, pView);
  1841. ASSERT(pView != pViewFrom);
  1842. if (pView->OnScroll(MAKEWORD(LOBYTE(nScrollCode), -1), 0,
  1843. bDoScroll))
  1844. {
  1845. bResult = TRUE;
  1846. }
  1847. #ifdef _DEBUG
  1848. if (pScrollHorz->GetScrollPos() != nNewHorz)
  1849. {
  1850. TRACE0("Warning: scroll panes setting different scroll positions.\n");
  1851. // stick with the last one set
  1852. }
  1853. #endif
  1854. }
  1855. }
  1856. return bResult;
  1857. }
  1858. BOOL CSplitterWnd::DoScrollBy(CView* pViewFrom, CSize sizeScroll, BOOL bDoScroll)
  1859. {
  1860. int rowFrom, colFrom;
  1861. if (!IsChildPane(pViewFrom, &rowFrom, &colFrom))
  1862. return FALSE;
  1863. BOOL bResult = FALSE;
  1864. // save original positions
  1865. int nOldVert = 0;
  1866. CScrollBar* pScrollVert = pViewFrom->GetScrollBarCtrl(SB_VERT);
  1867. if (pScrollVert != NULL)
  1868. nOldVert = pScrollVert->GetScrollPos();
  1869. int nOldHorz = 0;
  1870. CScrollBar* pScrollHorz = pViewFrom->GetScrollBarCtrl(SB_HORZ);
  1871. if (pScrollHorz != NULL)
  1872. nOldHorz = pScrollHorz->GetScrollPos();
  1873. // scroll the view from which the message is from
  1874. if (pViewFrom->OnScrollBy(sizeScroll, bDoScroll))
  1875. bResult = TRUE;
  1876. if (pScrollVert != NULL)
  1877. {
  1878. #ifdef _DEBUG
  1879. int nNewVert = pScrollVert->GetScrollPos();
  1880. #endif
  1881. // scroll related columns
  1882. for (int col = 0; col < m_nCols; col++)
  1883. {
  1884. if (col == colFrom)
  1885. continue;
  1886. // set the scroll pos to the value it was originally for the next pane
  1887. pScrollVert->SetScrollPos(nOldVert, FALSE);
  1888. // scroll the pane
  1889. CView* pView = (CView*)GetPane(rowFrom, col);
  1890. ASSERT_KINDOF(CView, pView);
  1891. ASSERT(pView != pViewFrom);
  1892. if (pView->OnScrollBy(CSize(0, sizeScroll.cy), bDoScroll))
  1893. bResult = TRUE;
  1894. #ifdef _DEBUG
  1895. if (pScrollVert->GetScrollPos() != nNewVert)
  1896. {
  1897. TRACE0("Warning: scroll panes setting different scroll positions.\n");
  1898. // stick with the last one set
  1899. }
  1900. #endif
  1901. }
  1902. }
  1903. if (pScrollHorz != NULL)
  1904. {
  1905. #ifdef _DEBUG
  1906. int nNewHorz = pScrollHorz->GetScrollPos();
  1907. #endif
  1908. // scroll related rows
  1909. for (int row = 0; row < m_nRows; row++)
  1910. {
  1911. if (row == rowFrom)
  1912. continue;
  1913. // set the scroll pos to the value it was originally for the next pane
  1914. pScrollHorz->SetScrollPos(nOldHorz, FALSE);
  1915. // scroll the pane
  1916. CView* pView = (CView*)GetPane(row, colFrom);
  1917. ASSERT_KINDOF(CView, pView);
  1918. ASSERT(pView != pViewFrom);
  1919. if (pView->OnScrollBy(CSize(sizeScroll.cx, 0), bDoScroll))
  1920. bResult = TRUE;
  1921. #ifdef _DEBUG
  1922. if (pScrollHorz->GetScrollPos() != nNewHorz)
  1923. {
  1924. TRACE0("Warning: scroll panes setting different scroll positions.\n");
  1925. // stick with the last one set
  1926. }
  1927. #endif
  1928. }
  1929. }
  1930. return bResult;
  1931. }
  1932. /////////////////////////////////////////////////////////////////////////////
  1933. // Focus control and control over the current pane/child
  1934. BOOL CSplitterWnd::CanActivateNext(BOOL)
  1935. {
  1936. ASSERT_VALID(this);
  1937. if (GetActivePane() == NULL)
  1938. {
  1939. TRACE0("Warning: Can't go to next pane - there is no current pane.\n");
  1940. return FALSE;
  1941. }
  1942. ASSERT(m_nRows != 0);
  1943. ASSERT(m_nCols != 0);
  1944. // if more than 1x1 we can go to the next or prev pane
  1945. return (m_nRows > 1) || (m_nCols > 1);
  1946. }
  1947. void CSplitterWnd::ActivateNext(BOOL bPrev)
  1948. {
  1949. ASSERT_VALID(this);
  1950. // find the coordinate of the current pane
  1951. int row, col;
  1952. if (GetActivePane(&row, &col) == NULL)
  1953. {
  1954. TRACE0("Warning: Cannot go to next pane - there is no current view.\n");
  1955. return;
  1956. }
  1957. ASSERT(row >= 0 && row < m_nRows);
  1958. ASSERT(col >= 0 && col < m_nCols);
  1959. // determine next pane
  1960. if (bPrev)
  1961. {
  1962. // prev
  1963. if (--col < 0)
  1964. {
  1965. col = m_nCols - 1;
  1966. if (--row < 0)
  1967. row = m_nRows - 1;
  1968. }
  1969. }
  1970. else
  1971. {
  1972. // next
  1973. if (++col >= m_nCols)
  1974. {
  1975. col = 0;
  1976. if (++row >= m_nRows)
  1977. row = 0;
  1978. }
  1979. }
  1980. // set newly active pane
  1981. SetActivePane(row, col);
  1982. }
  1983. void CSplitterWnd::SetActivePane(int row, int col, CWnd* pWnd)
  1984. {
  1985. // set the focus to the pane
  1986. CWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd;
  1987. if (pPane->IsKindOf(RUNTIME_CLASS(CView)))
  1988. {
  1989. CFrameWnd* pFrameWnd = GetParentFrame();
  1990. ASSERT_VALID(pFrameWnd);
  1991. pFrameWnd->SetActiveView((CView*)pPane);
  1992. }
  1993. else
  1994. {
  1995. TRACE0("Warning: Next pane is not a view - calling SetFocus.\n");
  1996. pPane->SetFocus();
  1997. }
  1998. }
  1999. CWnd* CSplitterWnd::GetActivePane(int* pRow, int* pCol)
  2000. // return active view, NULL when no active view
  2001. {
  2002. ASSERT_VALID(this);
  2003. // attempt to use active view of frame window
  2004. CWnd* pView = NULL;
  2005. CFrameWnd* pFrameWnd = GetParentFrame();
  2006. ASSERT_VALID(pFrameWnd);
  2007. pView = pFrameWnd->GetActiveView();
  2008. // failing that, use the current focus
  2009. if (pView == NULL)
  2010. pView = GetFocus();
  2011. // make sure the pane is a child pane of the splitter
  2012. if (pView != NULL && !IsChildPane(pView, pRow, pCol))
  2013. pView = NULL;
  2014. return pView;
  2015. }
  2016. /////////////////////////////////////////////////////////////////////////////
  2017. // CSplitterWnd diagnostics
  2018. #ifdef _DEBUG
  2019. void CSplitterWnd::AssertValid() const
  2020. {
  2021. CWnd::AssertValid();
  2022. ASSERT(m_nMaxRows >= 1);
  2023. ASSERT(m_nMaxCols >= 1);
  2024. ASSERT(m_nMaxCols > 1 || m_nMaxRows > 1); // 1x1 is not permitted
  2025. ASSERT(m_nRows >= 1);
  2026. ASSERT(m_nCols >= 1);
  2027. ASSERT(m_nRows <= m_nMaxRows);
  2028. ASSERT(m_nCols <= m_nMaxCols);
  2029. }
  2030. void CSplitterWnd::Dump(CDumpContext& dc) const
  2031. {
  2032. CWnd::Dump(dc);
  2033. if (m_pDynamicViewClass != NULL)
  2034. dc << "m_pDynamicViewClass = " << m_pDynamicViewClass->m_lpszClassName;
  2035. dc << "\nm_nMaxRows = " << m_nMaxRows;
  2036. dc << "\nm_nMaxCols = " << m_nMaxCols;
  2037. dc << "\nm_nRows = " << m_nRows;
  2038. dc << "\nm_nCols = " << m_nCols;
  2039. dc << "\nm_bHasHScroll = " << m_bHasHScroll;
  2040. dc << "\nm_bHasVScroll = " << m_bHasVScroll;
  2041. dc << "\nm_cxSplitter = " << m_cxSplitter;
  2042. dc << "\nm_cySplitter = " << m_cySplitter;
  2043. if (m_bTracking)
  2044. {
  2045. dc << "\nTRACKING: m_htTrack = " << m_htTrack;
  2046. dc << "\nm_rectLimit = " << m_rectLimit;
  2047. dc << "\nm_ptTrackOffset = " << m_ptTrackOffset;
  2048. dc << "\nm_rectTracker = " << m_rectTracker;
  2049. if (m_bTracking2)
  2050. dc << "\nm_rectTracker2 = " << m_rectTracker2;
  2051. }
  2052. dc << "\n";
  2053. }
  2054. #endif
  2055. /////////////////////////////////////////////////////////////////////////////