DebuggerWindow.cpp 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "../../sys/win32/rc/debugger_resource.h"
  23. #include "DebuggerApp.h"
  24. #include "../Common/OpenFileDialog.h"
  25. #include "DebuggerQuickWatchDlg.h"
  26. #include "DebuggerFindDlg.h"
  27. #define DEBUGGERWINDOWCLASS "QUAKE4_DEBUGGER_WINDOW"
  28. #define ID_DBG_WINDOWMIN 18900
  29. #define ID_DBG_WINDOWMAX 19900
  30. #define IDC_DBG_SCRIPT 31000
  31. #define IDC_DBG_OUTPUT 31001
  32. #define IDC_DBG_SPLITTER 31002
  33. #define IDC_DBG_TABS 31003
  34. #define IDC_DBG_BORDER 31004
  35. #define IDC_DBG_CONSOLE 31005
  36. #define IDC_DBG_CALLSTACK 31006
  37. #define IDC_DBG_WATCH 31007
  38. #define IDC_DBG_THREADS 31008
  39. #define IDC_DBG_TOOLBAR 31009
  40. #define ID_DBG_FILE_MRU1 10000
  41. /*
  42. ================
  43. rvDebuggerWindow::rvDebuggerWindow
  44. Constructor
  45. ================
  46. */
  47. rvDebuggerWindow::rvDebuggerWindow ( )
  48. {
  49. mWnd = NULL;
  50. mWndScript = NULL;
  51. mInstance = NULL;
  52. mZoomScaleNum = 0;
  53. mZoomScaleDem = 0;
  54. mWindowMenuPos = 0;
  55. mActiveScript = 0;
  56. mSplitterDrag = false;
  57. mLastActiveScript = -1;
  58. mCurrentStackDepth = 0;
  59. mRecentFileInsertPos = 0;
  60. mRecentFileMenu = NULL;
  61. mClient = NULL;
  62. }
  63. /*
  64. ================
  65. rvDebuggerWindow::~rvDebuggerWindow
  66. Destructor
  67. ================
  68. */
  69. rvDebuggerWindow::~rvDebuggerWindow ( )
  70. {
  71. int i;
  72. if ( mWnd )
  73. {
  74. DestroyWindow ( mWnd );
  75. }
  76. if ( mImageList )
  77. {
  78. ImageList_Destroy ( mImageList );
  79. }
  80. for ( i = 0; i < mScripts.Num (); i ++ )
  81. {
  82. delete mScripts[i];
  83. }
  84. }
  85. /*
  86. ================
  87. rvDebuggerWindow::RegisterClass
  88. Registers the window class used by the debugger window. This is called when
  89. the window is created.
  90. ================
  91. */
  92. bool rvDebuggerWindow::RegisterClass ( void )
  93. {
  94. WNDCLASSEX wcex;
  95. wcex.cbSize = sizeof(WNDCLASSEX);
  96. wcex.style = CS_HREDRAW | CS_VREDRAW;
  97. wcex.lpfnWndProc = (WNDPROC)WndProc;
  98. wcex.cbClsExtra = 0;
  99. wcex.cbWndExtra = 0;
  100. wcex.hInstance = mInstance;
  101. wcex.hIcon = NULL;
  102. wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
  103. wcex.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE+1);
  104. wcex.lpszMenuName = MAKEINTRESOURCE(IDR_DBG_MAIN);
  105. wcex.lpszClassName = DEBUGGERWINDOWCLASS;
  106. wcex.hIconSm = NULL;
  107. return RegisterClassEx(&wcex) ? true : false;
  108. }
  109. /*
  110. ================
  111. rvDebuggerWindow::Create
  112. Creates the debugger window
  113. ================
  114. */
  115. bool rvDebuggerWindow::Create ( HINSTANCE instance )
  116. {
  117. mInstance = instance;
  118. if ( !RegisterClass ( ) )
  119. {
  120. return false;
  121. }
  122. // Cache the client pointer for ease of use
  123. mClient = &gDebuggerApp.GetClient();
  124. // Create the debugger window
  125. mWnd = CreateWindow( DEBUGGERWINDOWCLASS, "",
  126. WS_OVERLAPPEDWINDOW,
  127. CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, mInstance, this);
  128. if ( !mWnd )
  129. {
  130. return false;
  131. }
  132. // Determine where the window names will be added in the menus
  133. mWindowMenu = GetSubMenu ( GetMenu ( mWnd ), 2 );
  134. mWindowMenuPos = GetMenuItemCount ( mWindowMenu );
  135. UpdateTitle ( );
  136. Printf ( "Quake 4 Script Debugger v0.1\n\n" );
  137. ShowWindow ( mWnd, SW_SHOW );
  138. UpdateWindow ( mWnd );
  139. return true;
  140. }
  141. /*
  142. ================
  143. rvDebuggerWindow::ScriptWordBreakProc
  144. Determines where word breaks are in the script window. This is used for determining
  145. the word that someone is over with their mouse cursor. Since the default windows one
  146. doesnt understand the delimiters of the scripting language it had to be overridden.
  147. ================
  148. */
  149. int CALLBACK rvDebuggerWindow::ScriptWordBreakProc (LPTSTR text, int current, int max, int action )
  150. {
  151. static TCHAR delimiters[]=TEXT("!@#$%^&*()-+=[]{}|\\;:'\"/,.<>? \t\r\n") ;
  152. switch ( action )
  153. {
  154. default:
  155. break;
  156. case WB_ISDELIMITER:
  157. return _tcschr ( delimiters, *(text + current * 2) ) ? TRUE : FALSE;
  158. case WB_MOVEWORDLEFT:
  159. case WB_LEFT:
  160. current--;
  161. // Run as long as the current index is valid.
  162. while ( current > 0 )
  163. {
  164. // If we hit a delimiter then return the index + 1 since
  165. // it was the last character we were on that was valid
  166. if ( _tcschr ( delimiters, *(text + current * 2) ) )
  167. {
  168. return current + 1;
  169. }
  170. // Going backwards
  171. current--;
  172. }
  173. return current;
  174. case WB_MOVEWORDRIGHT:
  175. case WB_RIGHT:
  176. // If we are already on a delimiter then just return the current index
  177. if ( _tcschr ( delimiters, *(text + current * 2) ) )
  178. {
  179. return current;
  180. }
  181. // Run until we hit the end of the control
  182. while ( current < max )
  183. {
  184. // If we found a delimiter then return the index we are on
  185. if ( _tcschr ( delimiters, *(text + current * 2) ) )
  186. {
  187. return current;
  188. }
  189. current++;
  190. }
  191. return current;
  192. }
  193. return 0;
  194. }
  195. LRESULT CALLBACK rvDebuggerWindow::ScriptWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam )
  196. {
  197. static int lastStart = -1;
  198. static int lastEnd = -1;
  199. rvDebuggerWindow* window = (rvDebuggerWindow*)GetWindowLong ( wnd, GWL_USERDATA );
  200. WNDPROC wndproc = window->mOldScriptProc;
  201. switch ( msg )
  202. {
  203. case WM_RBUTTONUP:
  204. return SendMessage ( wnd, WM_LBUTTONUP, wparam, lparam );
  205. case WM_RBUTTONDOWN:
  206. {
  207. POINT point = { LOWORD(lparam), HIWORD(lparam) };
  208. HMENU menu;
  209. SendMessage ( wnd, WM_LBUTTONDOWN, wparam, lparam );
  210. menu = LoadMenu ( window->mInstance, MAKEINTRESOURCE(IDR_DBG_SCRIPT_POPUP) );
  211. ClientToScreen ( wnd, &point );
  212. TrackPopupMenu ( GetSubMenu( menu, 0 ), TPM_RIGHTBUTTON|TPM_LEFTALIGN, point.x, point.y, 0, window->mWnd, NULL );
  213. DestroyMenu ( menu );
  214. return 0;
  215. }
  216. case WM_MOUSEMOVE:
  217. {
  218. // Figure out the start and end of the mouse is over
  219. POINTL pos = {LOWORD(lparam),HIWORD(lparam)};
  220. int c = SendMessage ( wnd, EM_CHARFROMPOS, 0, (WPARAM)&pos );
  221. int start = SendMessage ( wnd, EM_FINDWORDBREAK, WB_LEFT, c );
  222. int end = SendMessage ( wnd, EM_FINDWORDBREAK, WB_RIGHT, c );
  223. // If the start and the end of the word we are over havent changed
  224. // then the word hasnt changed so no need to re-setup the tool tip
  225. if ( lastStart == start && lastEnd == end )
  226. {
  227. break;
  228. }
  229. // Save the current start and end for the next mouse move
  230. lastStart = start;
  231. lastEnd = end;
  232. // Get rid of the last tool tip if there is one
  233. if ( window->mTooltipVar.Length() )
  234. {
  235. TOOLINFO ti;
  236. ti.cbSize = sizeof(TOOLINFO);
  237. ti.hwnd = wnd;
  238. ti.uId = 0;
  239. SendMessage ( window->mWndToolTips, TTM_DELTOOL, 0, (LPARAM) (LPTOOLINFO) &ti );
  240. window->mTooltipVar.Empty ( );
  241. }
  242. // If there is no word then ignore it
  243. if ( start == end )
  244. {
  245. break;
  246. }
  247. TEXTRANGE range;
  248. TOOLINFO ti;
  249. // grab the actual word from the edit control
  250. char* temp = new char[end-start+10];
  251. range.chrg.cpMin = start;
  252. range.chrg.cpMax = end;
  253. range.lpstrText = temp;
  254. SendMessage ( wnd, EM_GETTEXTRANGE, 0, (LPARAM) &range );
  255. window->mTooltipVar = temp;
  256. delete[] temp;
  257. // Request the variable's value from the debugger server
  258. window->mClient->InspectVariable ( window->mTooltipVar.c_str(), window->mCurrentStackDepth );
  259. // Prepare to add the new tooltip
  260. ti.cbSize = sizeof(TOOLINFO);
  261. ti.uFlags = TTF_SUBCLASS;
  262. ti.hwnd = wnd;
  263. ti.hinst = (HINSTANCE)GetModuleHandle(NULL);
  264. ti.uId = 0;
  265. ti.lpszText = (LPSTR)window->mTooltipVar.c_str();
  266. // Calculate the bounding box around the word we are over. We do this
  267. // by getting to the top left from the start character and the right side
  268. // from the end character. The bottom is the top from the start character
  269. // plus the height of one line
  270. SendMessage ( wnd, EM_POSFROMCHAR, (WPARAM)&pos, start );
  271. ti.rect.left = pos.x;
  272. ti.rect.top = pos.y;
  273. SendMessage ( wnd, EM_POSFROMCHAR, (WPARAM)&pos, end );
  274. ti.rect.right = pos.x;
  275. SendMessage ( wnd, EM_POSFROMCHAR, (WPARAM)&pos, SendMessage ( wnd, EM_LINEINDEX, 0, 0 ) );
  276. ti.rect.bottom = ti.rect.top - pos.y;
  277. SendMessage ( wnd, EM_POSFROMCHAR, (WPARAM)&pos, SendMessage ( wnd, EM_LINEINDEX, 1, 0 ) );
  278. ti.rect.bottom = ti.rect.bottom + pos.y;
  279. // Add the new tool tip to the control
  280. SendMessage ( window->mWndToolTips, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti );
  281. SendMessage ( window->mWndToolTips, TTM_UPDATE, 0, 0 );
  282. break;
  283. }
  284. }
  285. return CallWindowProc ( wndproc, wnd, msg, wparam, lparam );
  286. }
  287. LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam )
  288. {
  289. rvDebuggerWindow* window = (rvDebuggerWindow*) GetWindowLong ( wnd, GWL_USERDATA );
  290. switch ( msg )
  291. {
  292. case WM_RBUTTONDOWN:
  293. return SendMessage ( window->mWndScript, WM_RBUTTONDOWN, wparam, lparam );
  294. case WM_RBUTTONUP:
  295. return SendMessage ( window->mWndScript, WM_RBUTTONUP, wparam, lparam );
  296. case WM_LBUTTONDBLCLK:
  297. {
  298. int result = SendMessage ( window->mWndScript, WM_LBUTTONDBLCLK, wparam, lparam );
  299. window->ToggleBreakpoint ( );
  300. return result;
  301. }
  302. case WM_LBUTTONDOWN:
  303. {
  304. int result = SendMessage ( window->mWndScript, WM_LBUTTONDOWN, wparam, lparam );
  305. window->ToggleBreakpoint ( );
  306. return result;
  307. }
  308. case WM_LBUTTONUP:
  309. return SendMessage ( window->mWndScript, WM_LBUTTONUP, wparam, lparam );
  310. case WM_PAINT:
  311. {
  312. HDC dc;
  313. int size = window->mMarginSize - 2;
  314. PAINTSTRUCT ps;
  315. RECT rect;
  316. GetClientRect ( wnd, &rect );
  317. dc = BeginPaint ( wnd, &ps );
  318. FillRect ( dc, &rect, GetSysColorBrush ( COLOR_3DFACE ) );
  319. if ( window->mScripts.Num ( ) )
  320. {
  321. for ( int i = 0; i < window->mClient->GetBreakpointCount(); i ++ )
  322. {
  323. rvDebuggerBreakpoint* bp = window->mClient->GetBreakpoint ( i );
  324. assert( bp );
  325. if ( !idStr::Icmp ( window->mScripts[window->mActiveScript]->GetFilename ( ), bp->GetFilename ( ) ) )
  326. {
  327. int c;
  328. POINTL pos;
  329. c = SendMessage ( window->mWndScript, EM_LINEINDEX, bp->GetLineNumber ( ) - 1, 0 );
  330. SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c );
  331. ImageList_DrawEx ( window->mImageList, 2, dc, rect.left, pos.y, size, size, CLR_NONE, CLR_NONE, ILD_NORMAL );
  332. }
  333. }
  334. if ( window->mClient->IsStopped ( ) )
  335. {
  336. if ( !idStr::Icmp ( window->mClient->GetBreakFilename(),
  337. window->mScripts[window->mActiveScript]->GetFilename ( ) ) )
  338. {
  339. int c;
  340. POINTL pos;
  341. c = SendMessage ( window->mWndScript, EM_LINEINDEX, window->mClient->GetBreakLineNumber() - 1, 0 );
  342. SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c );
  343. ImageList_DrawEx ( window->mImageList, 3, dc, rect.left, pos.y, size, size, CLR_NONE, CLR_NONE, ILD_NORMAL );
  344. }
  345. }
  346. if ( window->mCurrentStackDepth != 0 )
  347. {
  348. if ( !window->mClient->GetCallstack()[window->mCurrentStackDepth]->mFilename.Icmp ( window->mScripts[window->mActiveScript]->GetFilename ( ) ) )
  349. {
  350. int c;
  351. POINTL pos;
  352. c = SendMessage ( window->mWndScript, EM_LINEINDEX, window->mClient->GetCallstack()[window->mCurrentStackDepth]->mLineNumber - 1, 0 );
  353. SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c );
  354. ImageList_DrawEx ( window->mImageList, 1, dc, rect.left, pos.y, size, size, CLR_NONE, CLR_NONE, ILD_NORMAL );
  355. }
  356. }
  357. }
  358. rect.right-=2;
  359. rect.left = rect.right + 1;
  360. HPEN pen = CreatePen ( PS_SOLID, 1, GetSysColor ( COLOR_3DSHADOW ) );
  361. HPEN old = (HPEN)SelectObject ( dc, pen );
  362. MoveToEx ( dc, rect.right, rect.top, NULL );
  363. LineTo ( dc, rect.right, rect.bottom );
  364. SelectObject ( dc, old );
  365. DeleteObject ( pen );
  366. EndPaint ( wnd, &ps );
  367. break;
  368. }
  369. }
  370. return DefWindowProc ( wnd, msg, wparam, lparam );
  371. }
  372. /*
  373. ================
  374. rvDebuggerWindow::UpdateTitle
  375. Updates the window title of the script debugger to show a few states
  376. ================
  377. */
  378. void rvDebuggerWindow::UpdateTitle ( void )
  379. {
  380. idStr title;
  381. title = "Quake 4 Script Debugger - ";
  382. if ( mClient->IsConnected ( ) )
  383. {
  384. if ( mClient->IsStopped ( ) )
  385. {
  386. title += "[break]";
  387. }
  388. else
  389. {
  390. title += "[run]";
  391. }
  392. }
  393. else
  394. {
  395. title += "[disconnected]";
  396. }
  397. if ( mScripts.Num ( ) )
  398. {
  399. title += " - [";
  400. title += idStr( mScripts[mActiveScript]->GetFilename() ).StripPath ( );
  401. title += "]";
  402. }
  403. SetWindowText ( mWnd, title );
  404. }
  405. /*
  406. ================
  407. rvDebuggerWindow::UpdateScript
  408. Updates the edit window to contain the current script
  409. ================
  410. */
  411. void rvDebuggerWindow::UpdateScript ( void )
  412. {
  413. UpdateTitle ( );
  414. // Dont reupdate if the given active script is the one being displayed.
  415. if ( mActiveScript == mLastActiveScript )
  416. {
  417. return;
  418. }
  419. mLastActiveScript = mActiveScript;
  420. // Show and hide the script window depending on whether or not
  421. // there are loaded scripts
  422. if ( mScripts.Num ( ) < 1 )
  423. {
  424. ShowWindow ( mWndScript, SW_HIDE );
  425. return;
  426. }
  427. else
  428. {
  429. ShowWindow ( mWndScript, SW_SHOW );
  430. }
  431. // Update the script
  432. SendMessage ( mWndScript, EM_SETSEL, 0, -1 );
  433. SendMessage ( mWndScript, EM_REPLACESEL, 0, (LPARAM)"" );
  434. SendMessage ( mWndScript, EM_SETSEL, 0, -1 );
  435. SendMessage ( mWndScript, EM_REPLACESEL, 0, (LPARAM)mScripts[mActiveScript]->GetContents ( ) );
  436. }
  437. /*
  438. ================
  439. rvDebuggerWindow::UpdateWindowMenu
  440. Updates the windows displayed in the window menu
  441. ================
  442. */
  443. void rvDebuggerWindow::UpdateWindowMenu ( void )
  444. {
  445. while ( GetMenuItemCount ( mWindowMenu ) > mWindowMenuPos )
  446. {
  447. DeleteMenu ( mWindowMenu, mWindowMenuPos, MF_BYPOSITION );
  448. }
  449. if ( mScripts.Num() )
  450. {
  451. AppendMenu ( mWindowMenu, MF_SEPARATOR, 0, "" );
  452. }
  453. int i;
  454. for ( i = 0; i < mScripts.Num(); i ++ )
  455. {
  456. idStr name;
  457. name = mScripts[i]->GetFilename ( );
  458. name.StripPath ( );
  459. name = idStr(va("&%d ", i + 1 )) + name;
  460. AppendMenu ( mWindowMenu, MF_STRING, ID_DBG_WINDOWMIN + i, name );
  461. }
  462. }
  463. /*
  464. ================
  465. rvDebuggerWindow::UpdateCallstack
  466. Updates the contents of teh callastack
  467. ================
  468. */
  469. void rvDebuggerWindow::UpdateCallstack ( void )
  470. {
  471. LVITEM item;
  472. ListView_DeleteAllItems ( mWndCallstack );
  473. ZeroMemory ( &item, sizeof(item) );
  474. item.mask = LVIF_TEXT|LVIF_IMAGE;
  475. for ( int i = 0; i < mClient->GetCallstack().Num(); i ++ )
  476. {
  477. rvDebuggerCallstack* entry = mClient->GetCallstack()[i];
  478. item.iItem = ListView_GetItemCount ( mWndCallstack );
  479. item.pszText = "";
  480. item.iImage = (i == mCurrentStackDepth) ? 1 : 0;
  481. ListView_InsertItem ( mWndCallstack, &item );
  482. ListView_SetItemText ( mWndCallstack, item.iItem, 1, (LPSTR)entry->mFunction.c_str() );
  483. ListView_SetItemText ( mWndCallstack, item.iItem, 2, va("%d", entry->mLineNumber ) );
  484. ListView_SetItemText ( mWndCallstack, item.iItem, 3, (LPSTR)entry->mFilename.c_str() );
  485. }
  486. }
  487. /*
  488. ================
  489. rvDebuggerWindow::UpdateWatch
  490. Updates the contents of the watch window
  491. ================
  492. */
  493. void rvDebuggerWindow::UpdateWatch ( void )
  494. {
  495. int i;
  496. // Inspect all the variables we are watching
  497. for ( i = 0; i < mWatches.Num(); i ++ )
  498. {
  499. mWatches[i]->mModified = false;
  500. mClient->InspectVariable ( mWatches[i]->mVariable, mCurrentStackDepth );
  501. }
  502. InvalidateRect ( mWndWatch, NULL, FALSE );
  503. UpdateWindow ( mWndWatch );
  504. }
  505. /*
  506. ================
  507. rvDebuggerWindow::HandleInitMenu
  508. Handles the initialization of the main menu
  509. ================
  510. */
  511. int rvDebuggerWindow::HandleInitMenu ( WPARAM wParam, LPARAM lParam )
  512. {
  513. int cMenuItems = GetMenuItemCount((HMENU)wParam);
  514. int nPos;
  515. int id;
  516. UINT flags;
  517. HMENU hmenu;
  518. hmenu = (HMENU) wParam;
  519. // Run through all the menu items in the menu and see if any of them need
  520. // modification in any way
  521. for (nPos = 0; nPos < cMenuItems; nPos++)
  522. {
  523. id = GetMenuItemID(hmenu, nPos);
  524. flags = 0;
  525. // Handle popup menus too
  526. if ( id < 0 )
  527. {
  528. HMENU sub = GetSubMenu ( hmenu, nPos );
  529. if ( sub )
  530. {
  531. HandleInitMenu ( (WPARAM) sub, 0 );
  532. continue;
  533. }
  534. }
  535. // Handle the dynamic menu items specially
  536. if ( id >= ID_DBG_WINDOWMIN && id <= ID_DBG_WINDOWMAX )
  537. {
  538. if ( id - ID_DBG_WINDOWMIN == mActiveScript )
  539. {
  540. CheckMenuItem ( hmenu, nPos, MF_BYPOSITION|MF_CHECKED );
  541. }
  542. else
  543. {
  544. CheckMenuItem ( hmenu, nPos, MF_BYPOSITION|MF_UNCHECKED );
  545. }
  546. continue;
  547. }
  548. // Menu items that are completely unrelated to the workspace
  549. switch ( id )
  550. {
  551. case ID_DBG_DEBUG_RUN:
  552. {
  553. MENUITEMINFO info;
  554. idStr run;
  555. info.cbSize = sizeof(info);
  556. info.fMask = MIIM_TYPE|MIIM_STATE;
  557. info.fType = MFT_STRING;
  558. if ( !mClient->IsConnected() )
  559. {
  560. run = "Run";
  561. info.fState = MFS_ENABLED;
  562. }
  563. else
  564. {
  565. run = "Continue";
  566. info.fState = mClient->IsStopped()?MFS_ENABLED:MFS_GRAYED;
  567. }
  568. info.dwTypeData = (LPSTR)run.c_str();
  569. info.cch = run.Length ( );
  570. SendMessage ( mWndToolbar, TB_ENABLEBUTTON, id, MAKELONG(((info.fState==MFS_ENABLED) ? TRUE : FALSE), 0) );
  571. SetMenuItemInfo ( hmenu, id, FALSE, &info );
  572. break;
  573. }
  574. case ID_DBG_DEBUG_BREAK:
  575. if ( !mClient->IsConnected() || mClient->IsStopped() )
  576. {
  577. EnableMenuItem ( hmenu, nPos, MF_GRAYED|MF_BYPOSITION);
  578. SendMessage ( mWndToolbar, TB_ENABLEBUTTON, id, MAKELONG(FALSE,0) );
  579. }
  580. else
  581. {
  582. EnableMenuItem ( hmenu, nPos, MF_ENABLED|MF_BYPOSITION);
  583. SendMessage ( mWndToolbar, TB_ENABLEBUTTON, id, MAKELONG(TRUE,0) );
  584. }
  585. break;
  586. case ID_DBG_DEBUG_RUNTOCURSOR:
  587. case ID_DBG_DEBUG_STEPOUT:
  588. case ID_DBG_DEBUG_STEPOVER:
  589. case ID_DBG_DEBUG_STEPINTO:
  590. case ID_DBG_DEBUG_SHOWNEXTSTATEMENT:
  591. // case ID_DBG_DEBUG_QUICKWATCH:
  592. if ( !mClient->IsConnected() || !mClient->IsStopped() )
  593. {
  594. EnableMenuItem ( hmenu, nPos, MF_GRAYED|MF_BYPOSITION );
  595. SendMessage ( mWndToolbar, TB_ENABLEBUTTON, id, MAKELONG(FALSE,0) );
  596. }
  597. else
  598. {
  599. EnableMenuItem ( hmenu, nPos, MF_ENABLED|MF_BYPOSITION );
  600. SendMessage ( mWndToolbar, TB_ENABLEBUTTON, id, MAKELONG(TRUE,0) );
  601. }
  602. break;
  603. case ID_DBG_WINDOW_CLOSEALL:
  604. case ID_DBG_FILE_CLOSE:
  605. case ID_DBG_DEBUG_TOGGLEBREAKPOINT:
  606. case ID_DBG_EDIT_FIND:
  607. EnableMenuItem ( hmenu, nPos, (mScripts.Num()?MF_ENABLED:MF_GRAYED)|MF_BYPOSITION);
  608. break;
  609. }
  610. }
  611. return 0;
  612. }
  613. /*
  614. ================
  615. rvDebuggerWindow::HandleCreate
  616. Handles the WM_CREATE command
  617. ================
  618. */
  619. int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam )
  620. {
  621. UINT tabsize = 16;
  622. TEXTMETRIC tm;
  623. HDC dc;
  624. LOGFONT lf;
  625. int i;
  626. gDebuggerApp.GetOptions().GetWindowPlacement ( "wp_main", mWnd );
  627. // Create the main toolbar
  628. CreateToolbar ( );
  629. // Create the script window
  630. LoadLibrary ( "Riched20.dll" );
  631. mWndScript = CreateWindow ( "RichEdit20A", "", WS_CHILD|WS_BORDER|ES_NOHIDESEL|ES_READONLY|ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|ES_AUTOHSCROLL|WS_VSCROLL|WS_HSCROLL, 0, 0, 100, 100, mWnd, (HMENU) IDC_DBG_SCRIPT, mInstance, 0 );
  632. SendMessage ( mWndScript, EM_SETEVENTMASK, 0, ENM_SCROLL|ENM_CHANGE );
  633. SendMessage ( mWndScript, EM_SETWORDBREAKPROC, 0, (LPARAM) ScriptWordBreakProc );
  634. mOldScriptProc = (WNDPROC)GetWindowLong ( mWndScript, GWL_WNDPROC );
  635. SetWindowLong ( mWndScript, GWL_USERDATA, (LONG)this );
  636. SetWindowLong ( mWndScript, GWL_WNDPROC, (LONG)ScriptWndProc );
  637. SendMessage ( mWndScript, EM_SETTABSTOPS, 1, (LPARAM)&tabsize );
  638. dc = GetDC ( mWndScript );
  639. GetTextMetrics ( dc, &tm );
  640. ZeroMemory ( &lf, sizeof(lf) );
  641. lf.lfHeight = tm.tmHeight;
  642. strcpy ( lf.lfFaceName, "Courier New" );
  643. SendMessage ( mWndScript, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );
  644. SendMessage ( mWndScript, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) );
  645. SendMessage ( mWndScript, EM_SETBKGNDCOLOR, 0, GetSysColor ( COLOR_3DFACE ) );
  646. mWndOutput = CreateWindow ( "RichEdit20A", "", WS_CHILD|ES_READONLY|ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|ES_AUTOHSCROLL|WS_VSCROLL|WS_HSCROLL|WS_VISIBLE, 0, 0, 100, 100, mWnd, (HMENU) IDC_DBG_OUTPUT, mInstance, 0 );
  647. SendMessage ( mWndOutput, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );
  648. SendMessage ( mWndOutput, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) );
  649. SendMessage ( mWndOutput, EM_SETBKGNDCOLOR, 0, GetSysColor ( COLOR_3DFACE ) );
  650. mWndConsole = CreateWindow ( "RichEdit20A", "", WS_CHILD|ES_READONLY|ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|ES_AUTOHSCROLL|WS_VSCROLL|WS_HSCROLL, 0, 0, 100, 100, mWnd, (HMENU) IDC_DBG_CONSOLE, mInstance, 0 );
  651. SendMessage ( mWndConsole, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );
  652. SendMessage ( mWndConsole, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) );
  653. SendMessage ( mWndConsole, EM_SETBKGNDCOLOR, 0, GetSysColor ( COLOR_3DFACE ) );
  654. mWndMargin = CreateWindow ( "STATIC", "", WS_VISIBLE|WS_CHILD, 0, 0, 0, 0, mWndScript, (HMENU)IDC_DBG_SPLITTER, mInstance, NULL );
  655. SetWindowLong ( mWndMargin, GWL_USERDATA, (LONG)this );
  656. SetWindowLong ( mWndMargin, GWL_WNDPROC, (LONG)MarginWndProc );
  657. mWndBorder = CreateWindow ( "STATIC", "", WS_VISIBLE|WS_CHILD|SS_GRAYFRAME, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_BORDER, mInstance, NULL );
  658. GetClientRect ( mWnd, &mSplitterRect );
  659. mSplitterRect.top = (mSplitterRect.bottom-mSplitterRect.top) / 2;
  660. mSplitterRect.bottom = mSplitterRect.top + 4;
  661. mWndTabs = CreateWindow ( WC_TABCONTROL, "", TCS_BOTTOM|WS_CHILD|WS_VISIBLE|TCS_FOCUSNEVER, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_TABS, mInstance, NULL );
  662. lf.lfHeight = -MulDiv(8, GetDeviceCaps(dc, LOGPIXELSY), 72);
  663. strcpy ( lf.lfFaceName, "Arial" );
  664. SendMessage ( mWndTabs, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );
  665. ReleaseDC ( mWndScript, dc );
  666. TCITEM item;
  667. item.mask = TCIF_TEXT;
  668. item.pszText = "Output";
  669. TabCtrl_InsertItem ( mWndTabs, 0, &item );
  670. item.pszText = "Console";
  671. TabCtrl_InsertItem ( mWndTabs, 1, &item );
  672. item.pszText = "Call Stack";
  673. TabCtrl_InsertItem ( mWndTabs, 2, &item );
  674. item.pszText = "Watch";
  675. TabCtrl_InsertItem ( mWndTabs, 3, &item );
  676. item.pszText = "Threads";
  677. TabCtrl_InsertItem ( mWndTabs, 4, &item );
  678. mWndCallstack = CreateWindow ( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_SHAREIMAGELISTS, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_CALLSTACK, mInstance, NULL );
  679. mWndWatch = CreateWindow ( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_EDITLABELS|LVS_OWNERDRAWFIXED, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_WATCH, mInstance, NULL );
  680. mWndThreads = CreateWindow ( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_SHAREIMAGELISTS, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_THREADS, mInstance, NULL );
  681. LVCOLUMN col;
  682. col.mask = LVCF_WIDTH|LVCF_TEXT;
  683. col.cx = 20;
  684. col.pszText = "";
  685. ListView_InsertColumn ( mWndCallstack, 0, &col );
  686. col.cx = 150;
  687. col.pszText = "Function";
  688. ListView_InsertColumn ( mWndCallstack, 1, &col );
  689. col.cx = 150;
  690. col.pszText = "Line";
  691. ListView_InsertColumn ( mWndCallstack, 2, &col );
  692. col.cx = 150;
  693. col.pszText = "Filename";
  694. ListView_InsertColumn ( mWndCallstack, 3, &col );
  695. col.cx = 20;
  696. col.pszText = "";
  697. ListView_InsertColumn ( mWndThreads, 0, &col );
  698. col.cx = 25;
  699. col.pszText = "ID";
  700. ListView_InsertColumn ( mWndThreads, 1, &col );
  701. col.cx = 150;
  702. col.pszText = "Name";
  703. ListView_InsertColumn ( mWndThreads, 2, &col );
  704. col.cx = 100;
  705. col.pszText = "State";
  706. ListView_InsertColumn ( mWndThreads, 3, &col );
  707. col.cx = 150;
  708. col.pszText = "Name";
  709. ListView_InsertColumn ( mWndWatch, 0, &col );
  710. col.cx = 200;
  711. col.pszText = "Value";
  712. ListView_InsertColumn ( mWndWatch, 1, &col );
  713. // Create the image list that is used by the threads window, callstack window, and
  714. // margin window
  715. mImageList = ImageList_Create ( 16, 16, ILC_COLOR|ILC_MASK, 0, 2 );
  716. ImageList_AddIcon ( mImageList, (HICON)LoadImage ( mInstance, MAKEINTRESOURCE(IDI_DBG_EMPTY), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_DEFAULTCOLOR) );
  717. ImageList_AddIcon ( mImageList, (HICON)LoadImage ( mInstance, MAKEINTRESOURCE(IDI_DBG_CURRENT), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_DEFAULTCOLOR) );
  718. ImageList_AddIcon ( mImageList, (HICON)LoadImage ( mInstance, MAKEINTRESOURCE(IDI_DBG_BREAKPOINT), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_DEFAULTCOLOR) );
  719. ImageList_AddIcon ( mImageList, (HICON)LoadImage ( mInstance, MAKEINTRESOURCE(IDI_DBG_CURRENTLINE), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_DEFAULTCOLOR) );
  720. ListView_SetImageList ( mWndThreads, mImageList, LVSIL_SMALL );
  721. ListView_SetImageList ( mWndCallstack, mImageList, LVSIL_SMALL );
  722. EnableWindows ( FALSE );
  723. ListView_SetExtendedListViewStyle ( mWndCallstack, LVS_EX_FULLROWSELECT );
  724. ListView_SetExtendedListViewStyle ( mWndThreads, LVS_EX_FULLROWSELECT );
  725. gDebuggerApp.GetOptions().GetColumnWidths ( "cw_callstack", mWndCallstack );
  726. gDebuggerApp.GetOptions().GetColumnWidths ( "cw_threads", mWndThreads );
  727. gDebuggerApp.GetOptions().GetColumnWidths ( "cw_watch", mWndWatch );
  728. mWndToolTips = CreateWindowEx ( WS_EX_TOPMOST,
  729. TOOLTIPS_CLASS,
  730. NULL,
  731. WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
  732. CW_USEDEFAULT,
  733. CW_USEDEFAULT,
  734. CW_USEDEFAULT,
  735. CW_USEDEFAULT,
  736. mWnd,
  737. NULL,
  738. mInstance,
  739. NULL
  740. );
  741. SendMessage ( mWndToolTips, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELONG(400,0) );
  742. SendMessage ( mWndToolTips, TTM_SETDELAYTIME, TTDT_RESHOW, MAKELONG(400,0) );
  743. SetWindowPos( mWndToolTips, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  744. LVITEM lvItem;
  745. lvItem.iItem = 0;
  746. lvItem.iSubItem = 0;
  747. lvItem.mask = LVIF_TEXT;
  748. lvItem.pszText = "";
  749. ListView_InsertItem ( mWndWatch, &lvItem );
  750. ListView_SetExtendedListViewStyle ( mWndWatch, LVS_EX_FULLROWSELECT );
  751. // Recent files
  752. InitRecentFiles ( );
  753. UpdateRecentFiles ( );
  754. HandleInitMenu ( (WPARAM)GetMenu ( mWnd ), 0 );
  755. // Read in the watches
  756. for ( i = 0; ; i ++ )
  757. {
  758. const char* s = gDebuggerApp.GetOptions().GetString ( va("watch%d", i ) );
  759. if ( !s || !s[0] )
  760. {
  761. break;
  762. }
  763. AddWatch ( s );
  764. }
  765. return 0;
  766. }
  767. /*
  768. ================
  769. rvDebuggerWindow::HandleCommand
  770. Handles the WM_COMMAND message for this window
  771. ================
  772. */
  773. int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
  774. {
  775. int event = HIWORD(wparam);
  776. int id = LOWORD(wparam);
  777. // The window menu list needs to be handled specially
  778. if ( id >= ID_DBG_WINDOWMIN && id <= ID_DBG_WINDOWMAX )
  779. {
  780. mActiveScript = id - ID_DBG_WINDOWMIN;
  781. UpdateScript ( );
  782. return 0;
  783. }
  784. // The recent file list needs to be handled specially
  785. if ( LOWORD(wparam) >= ID_DBG_FILE_MRU1 && LOWORD(wparam) < ID_DBG_FILE_MRU1 + rvRegistryOptions::MAX_MRU_SIZE )
  786. {
  787. idStr filename;
  788. filename = gDebuggerApp.GetOptions().GetRecentFile ( gDebuggerApp.GetOptions().GetRecentFileCount() - (LOWORD(wparam)-ID_DBG_FILE_MRU1) - 1 );
  789. if ( !OpenScript ( filename ) )
  790. {
  791. MessageBox ( mWnd, va("Failed to open script '%s'", filename.c_str() ), "Quake 4 Script Debugger", MB_OK );
  792. }
  793. return 0;
  794. }
  795. switch ( id )
  796. {
  797. case ID_DBG_EDIT_FINDSELECTED:
  798. {
  799. idStr text;
  800. GetSelectedText ( text );
  801. FindNext ( text );
  802. break;
  803. }
  804. case ID_DBG_EDIT_FINDSELECTEDPREV:
  805. {
  806. idStr text;
  807. GetSelectedText ( text );
  808. FindPrev ( text );
  809. break;
  810. }
  811. case ID_DBG_EDIT_FINDNEXT:
  812. FindNext ( );
  813. break;
  814. case ID_DBG_EDIT_FINDPREV:
  815. FindPrev ( );
  816. break;
  817. case ID_DBG_DEBUG_QUICKWATCH:
  818. {
  819. idStr text;
  820. GetSelectedText ( text );
  821. rvDebuggerQuickWatchDlg dlg;
  822. dlg.DoModal ( this, mCurrentStackDepth, text );
  823. break;
  824. }
  825. case ID_DBG_HELP_ABOUT:
  826. DialogBox ( mInstance, MAKEINTRESOURCE(IDD_DBG_ABOUT), mWnd, AboutDlgProc );
  827. break;
  828. case ID_DBG_DEBUG_BREAK:
  829. mClient->Break ( );
  830. break;
  831. case ID_DBG_DEBUG_STEPOVER:
  832. EnableWindows ( FALSE );
  833. mClient->StepOver ( );
  834. break;
  835. case ID_DBG_DEBUG_STEPINTO:
  836. EnableWindows ( FALSE );
  837. mClient->StepInto ( );
  838. break;
  839. case ID_DBG_DEBUG_RUN:
  840. // Run the game if its not running
  841. if ( !mClient->IsConnected ( ) )
  842. {
  843. char exeFile[MAX_PATH];
  844. char curDir[MAX_PATH];
  845. STARTUPINFO startup;
  846. PROCESS_INFORMATION process;
  847. ZeroMemory ( &startup, sizeof(startup) );
  848. startup.cb = sizeof(startup);
  849. GetCurrentDirectory ( MAX_PATH, curDir );
  850. GetModuleFileName ( NULL, exeFile, MAX_PATH );
  851. const char* s = va("%s +set fs_game %s +set fs_cdpath %s", exeFile, cvarSystem->GetCVarString( "fs_game" ), cvarSystem->GetCVarString( "fs_cdpath" ) );
  852. CreateProcess ( NULL, (LPSTR)s,
  853. NULL, NULL, FALSE, 0, NULL, curDir, &startup, &process );
  854. CloseHandle ( process.hThread );
  855. CloseHandle ( process.hProcess );
  856. }
  857. else if ( mClient->IsStopped() )
  858. {
  859. EnableWindows ( FALSE );
  860. mClient->Resume ( );
  861. UpdateToolbar ( );
  862. UpdateTitle ( );
  863. InvalidateRect ( mWnd, NULL, FALSE );
  864. }
  865. break;
  866. case IDC_DBG_SCRIPT:
  867. {
  868. RECT t;
  869. LONG num;
  870. LONG dem;
  871. SendMessage ( mWndScript, EM_GETZOOM, (LONG)&num, (LONG)&dem );
  872. if ( num != mZoomScaleNum || dem != mZoomScaleDem )
  873. {
  874. mZoomScaleNum = num;
  875. mZoomScaleDem = dem;
  876. GetClientRect ( mWndScript, &t );
  877. SendMessage ( mWnd, WM_SIZE, 0, MAKELPARAM(t.right-t.left,t.bottom-t.top) );
  878. }
  879. else
  880. {
  881. InvalidateRect ( mWndMargin, NULL, TRUE );
  882. }
  883. break;
  884. }
  885. case ID_DBG_DEBUG_TOGGLEBREAKPOINT:
  886. ToggleBreakpoint ( );
  887. break;
  888. case ID_DBG_FILE_EXIT:
  889. PostMessage ( mWnd, WM_CLOSE, 0, 0 );
  890. break;
  891. case ID_DBG_FILE_CLOSE:
  892. if ( mScripts.Num() )
  893. {
  894. delete mScripts[mActiveScript];
  895. mScripts.RemoveIndex ( mActiveScript );
  896. if ( mActiveScript >= mScripts.Num ( ) )
  897. {
  898. mActiveScript --;
  899. }
  900. UpdateWindowMenu ( );
  901. UpdateScript ( );
  902. }
  903. break;
  904. case ID_DBG_FILE_NEXT:
  905. if ( mScripts.Num ( ) > 0 )
  906. {
  907. mActiveScript++;
  908. if ( mActiveScript >= mScripts.Num ( ) )
  909. {
  910. mActiveScript = 0;
  911. }
  912. UpdateWindowMenu ( );
  913. UpdateScript ( );
  914. }
  915. break;
  916. case ID_DBG_FILE_OPEN:
  917. {
  918. rvOpenFileDialog dlg;
  919. dlg.SetTitle ( "Open Script" );
  920. dlg.SetFilter ( "*.script; *.gui; *.state" );
  921. dlg.SetFlags ( OFD_MUSTEXIST );
  922. if ( dlg.DoModal ( mWnd ) )
  923. {
  924. if ( !OpenScript ( dlg.GetFilename ( ) ) )
  925. {
  926. MessageBox ( mWnd, va("Failed to open script '%s'",dlg.GetFilename ( )), "Quake 4 Script Debugger", MB_OK );
  927. }
  928. }
  929. break;
  930. }
  931. case ID_DBG_EDIT_FIND:
  932. {
  933. rvDebuggerFindDlg dlg;
  934. if ( dlg.DoModal ( this ) )
  935. {
  936. FindNext ( dlg.GetFindText ( ) );
  937. }
  938. break;
  939. }
  940. case ID_DBG_WINDOW_CLOSEALL:
  941. {
  942. for ( int i = 0; i < mScripts.Num(); i ++ )
  943. {
  944. delete mScripts[i];
  945. }
  946. mScripts.Clear ( );
  947. mActiveScript = -1;
  948. UpdateWindowMenu ( );
  949. UpdateScript ( );
  950. break;
  951. }
  952. }
  953. return 0;
  954. }
  955. /*
  956. ================
  957. rvDebuggerWindow::WndProc
  958. Window procedure for the deubgger window
  959. ================
  960. */
  961. LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam )
  962. {
  963. rvDebuggerWindow* window = (rvDebuggerWindow*) GetWindowLong ( wnd, GWL_USERDATA );
  964. switch ( msg )
  965. {
  966. case WM_INITMENUPOPUP:
  967. window->HandleInitMenu ( wparam, lparam );
  968. break;
  969. case WM_DESTROY:
  970. {
  971. gDebuggerApp.GetOptions().SetColumnWidths ( "cw_callstack", window->mWndCallstack );
  972. gDebuggerApp.GetOptions().SetColumnWidths ( "cw_threads", window->mWndThreads );
  973. gDebuggerApp.GetOptions().SetColumnWidths ( "cw_watch", window->mWndWatch );
  974. gDebuggerApp.GetOptions().SetWindowPlacement ( "wp_main", wnd );
  975. int i;
  976. for ( i = 0; i < window->mWatches.Num ( ); i ++ )
  977. {
  978. gDebuggerApp.GetOptions().SetString ( va("watch%d", i ), window->mWatches[i]->mVariable );
  979. }
  980. gDebuggerApp.GetOptions().SetString ( va("watch%d", i ), "" );
  981. window->mWnd = NULL;
  982. SetWindowLong ( wnd, GWL_USERDATA, 0 );
  983. break;
  984. }
  985. case WM_ERASEBKGND:
  986. return 0;
  987. case WM_COMMAND:
  988. window->HandleCommand ( wparam, lparam );
  989. break;
  990. case WM_SETCURSOR:
  991. {
  992. POINT point;
  993. GetCursorPos ( &point );
  994. ScreenToClient ( wnd, &point );
  995. if ( PtInRect ( &window->mSplitterRect, point ) )
  996. {
  997. SetCursor ( LoadCursor ( NULL, IDC_SIZENS ) );
  998. return true;
  999. }
  1000. break;
  1001. }
  1002. case WM_SIZE:
  1003. {
  1004. RECT rect;
  1005. window->mMarginSize = window->mZoomScaleDem ? ((long)(18.0f * (float)window->mZoomScaleNum / (float)window->mZoomScaleDem)):18;
  1006. window->mSplitterRect.left = 0;
  1007. window->mSplitterRect.right = LOWORD(lparam);
  1008. SendMessage ( window->mWndToolbar, TB_AUTOSIZE, 0, 0 );
  1009. GetWindowRect ( window->mWndToolbar, &rect );
  1010. MoveWindow ( window->mWndScript, 0, rect.bottom-rect.top, LOWORD(lparam), window->mSplitterRect.top-(rect.bottom-rect.top), TRUE );
  1011. MoveWindow ( window->mWndMargin, 0, 0, window->mMarginSize, window->mSplitterRect.top-(rect.bottom-rect.top), TRUE );
  1012. SetRect ( &rect, 0, window->mSplitterRect.bottom, LOWORD(lparam), HIWORD(lparam) );
  1013. MoveWindow ( window->mWndTabs, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
  1014. SendMessage ( window->mWndTabs, TCM_ADJUSTRECT, FALSE, (LPARAM)&rect );
  1015. rect.bottom -= 4 ;
  1016. MoveWindow ( window->mWndBorder, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
  1017. InflateRect ( &rect, -1, -1 );
  1018. MoveWindow ( window->mWndOutput, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
  1019. MoveWindow ( window->mWndConsole, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
  1020. MoveWindow ( window->mWndCallstack, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
  1021. MoveWindow ( window->mWndWatch, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
  1022. MoveWindow ( window->mWndThreads, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
  1023. break;
  1024. }
  1025. case WM_PAINT:
  1026. {
  1027. PAINTSTRUCT ps;
  1028. HDC dc = BeginPaint ( wnd, &ps );
  1029. FillRect ( dc, &window->mSplitterRect, GetSysColorBrush ( COLOR_3DFACE ) );
  1030. if ( !window->mScripts.Num() )
  1031. {
  1032. RECT rect;
  1033. GetClientRect ( wnd, &rect );
  1034. rect.bottom = window->mSplitterRect.top;
  1035. FillRect ( dc, &rect, GetSysColorBrush ( COLOR_APPWORKSPACE ) );
  1036. }
  1037. EndPaint ( wnd, &ps );
  1038. break;
  1039. }
  1040. case WM_LBUTTONDOWN:
  1041. {
  1042. POINT pt = { LOWORD(lparam),HIWORD(lparam) };
  1043. if ( PtInRect ( &window->mSplitterRect, pt ) )
  1044. {
  1045. window->mSplitterDrag = true;
  1046. SetCapture ( wnd );
  1047. HDC dc = GetDC ( wnd );
  1048. DrawFocusRect ( dc, &window->mSplitterRect );
  1049. ReleaseDC ( wnd, dc );
  1050. }
  1051. break;
  1052. }
  1053. case WM_LBUTTONUP:
  1054. if ( window->mSplitterDrag )
  1055. {
  1056. HDC dc = GetDC ( wnd );
  1057. DrawFocusRect ( dc, &window->mSplitterRect );
  1058. ReleaseDC ( wnd, dc );
  1059. window->mSplitterDrag = false;
  1060. RECT client;
  1061. GetClientRect ( wnd, &client );
  1062. SendMessage ( wnd, WM_SIZE, 0, MAKELPARAM(client.right-client.left,client.bottom-client.top) );
  1063. InvalidateRect ( wnd, NULL, TRUE );
  1064. ReleaseCapture ( );
  1065. }
  1066. break;
  1067. case WM_MOUSEMOVE:
  1068. {
  1069. if ( window->mSplitterDrag )
  1070. {
  1071. HDC dc = GetDC ( wnd );
  1072. DrawFocusRect ( dc, &window->mSplitterRect );
  1073. ReleaseDC ( wnd, dc );
  1074. if ( GetCapture ( ) != wnd )
  1075. {
  1076. break;
  1077. }
  1078. RECT client;
  1079. GetClientRect ( wnd, &client );
  1080. window->mSplitterRect.top = HIWORD(lparam);
  1081. if ( window->mSplitterRect.top < client.top )
  1082. {
  1083. window->mSplitterRect.top = client.top;
  1084. }
  1085. else if ( window->mSplitterRect.top + 4 > client.bottom )
  1086. {
  1087. window->mSplitterRect.top = client.bottom - 4;
  1088. }
  1089. window->mSplitterRect.bottom = window->mSplitterRect.top + 4;
  1090. dc = GetDC ( wnd );
  1091. DrawFocusRect ( dc, &window->mSplitterRect );
  1092. ReleaseDC ( wnd, dc );
  1093. }
  1094. break;
  1095. }
  1096. case WM_DRAWITEM:
  1097. window->HandleDrawItem ( wparam, lparam );
  1098. break;
  1099. case WM_CREATE:
  1100. {
  1101. CREATESTRUCT* cs = (CREATESTRUCT*) lparam;
  1102. window = (rvDebuggerWindow*) cs->lpCreateParams;
  1103. SetWindowLong ( wnd, GWL_USERDATA, (LONG)cs->lpCreateParams );
  1104. window->mWnd = wnd;
  1105. window->HandleCreate ( wparam, lparam );
  1106. break;
  1107. }
  1108. case WM_ACTIVATE:
  1109. window->HandleActivate ( wparam, lparam );
  1110. break;
  1111. case WM_NOTIFY:
  1112. {
  1113. NMHDR* hdr;
  1114. hdr = (NMHDR*)lparam;
  1115. // Tool tips
  1116. if ( hdr->code == TTN_GETDISPINFO )
  1117. {
  1118. window->HandleTooltipGetDispInfo ( wparam, lparam );
  1119. break;
  1120. }
  1121. switch ( hdr->idFrom )
  1122. {
  1123. case IDC_DBG_WATCH:
  1124. switch ( hdr->code )
  1125. {
  1126. case LVN_KEYDOWN:
  1127. {
  1128. NMLVKEYDOWN* key = (NMLVKEYDOWN*) hdr;
  1129. switch ( key->wVKey )
  1130. {
  1131. case VK_DELETE:
  1132. {
  1133. int sel = ListView_GetNextItem ( hdr->hwndFrom, -1, LVNI_SELECTED );
  1134. if ( sel != -1 && sel != ListView_GetItemCount ( hdr->hwndFrom ) - 1 )
  1135. {
  1136. LVITEM item;
  1137. item.iItem = sel;
  1138. item.mask = LVIF_PARAM;
  1139. ListView_GetItem ( hdr->hwndFrom, &item );
  1140. ListView_DeleteItem ( hdr->hwndFrom, sel );
  1141. window->mWatches.Remove ( (rvDebuggerWatch*)item.lParam );
  1142. delete (rvDebuggerWatch*)item.lParam;
  1143. ListView_SetItemState ( hdr->hwndFrom,
  1144. sel,
  1145. LVIS_SELECTED, LVIS_SELECTED );
  1146. if ( ListView_GetNextItem ( hdr->hwndFrom, -1, LVNI_SELECTED ) == -1 )
  1147. {
  1148. ListView_SetItemState ( hdr->hwndFrom,
  1149. ListView_GetItemCount ( hdr->hwndFrom ) - 1,
  1150. LVIS_SELECTED, LVIS_SELECTED );
  1151. }
  1152. }
  1153. break;
  1154. }
  1155. case VK_RETURN:
  1156. {
  1157. int sel = ListView_GetNextItem ( hdr->hwndFrom, -1, LVNI_SELECTED );
  1158. if ( sel != -1 )
  1159. {
  1160. ListView_EditLabel ( hdr->hwndFrom, sel );
  1161. }
  1162. break;
  1163. }
  1164. }
  1165. break;
  1166. }
  1167. case LVN_BEGINLABELEDIT:
  1168. {
  1169. NMLVDISPINFO* di = (NMLVDISPINFO*)hdr;
  1170. DWORD style = GetWindowLong ( ListView_GetEditControl ( hdr->hwndFrom ), GWL_STYLE );
  1171. SetWindowLong ( ListView_GetEditControl ( hdr->hwndFrom ), GWL_STYLE, style & (~WS_BORDER) );
  1172. rvDebuggerWatch* watch = (rvDebuggerWatch*)di->item.lParam;
  1173. if ( watch )
  1174. {
  1175. SetWindowText ( ListView_GetEditControl ( hdr->hwndFrom ), watch->mVariable );
  1176. }
  1177. return FALSE;
  1178. }
  1179. case LVN_ENDLABELEDIT:
  1180. {
  1181. NMLVDISPINFO* di = (NMLVDISPINFO*)hdr;
  1182. if ( di->item.iItem == ListView_GetItemCount ( hdr->hwndFrom ) - 1 )
  1183. {
  1184. if ( !di->item.pszText || !di->item.pszText[0] )
  1185. {
  1186. return FALSE;
  1187. }
  1188. window->AddWatch ( ((NMLVDISPINFO*)hdr)->item.pszText );
  1189. window->UpdateWatch ( );
  1190. return FALSE;
  1191. }
  1192. else
  1193. {
  1194. rvDebuggerWatch* watch = (rvDebuggerWatch*) di->item.lParam;
  1195. if ( watch && di->item.pszText && di->item.pszText[0] )
  1196. {
  1197. watch->mVariable = di->item.pszText;
  1198. }
  1199. }
  1200. return TRUE;
  1201. }
  1202. }
  1203. break;
  1204. case IDC_DBG_CALLSTACK:
  1205. if ( hdr->code == NM_DBLCLK )
  1206. {
  1207. int sel = ListView_GetNextItem ( hdr->hwndFrom, -1, LVNI_SELECTED );
  1208. if ( sel != -1 )
  1209. {
  1210. window->mCurrentStackDepth = sel;
  1211. window->UpdateCallstack ( );
  1212. window->UpdateWatch ( );
  1213. window->OpenScript ( window->mClient->GetCallstack()[window->mCurrentStackDepth]->mFilename,
  1214. window->mClient->GetCallstack()[window->mCurrentStackDepth]->mLineNumber );
  1215. }
  1216. }
  1217. break;
  1218. case IDC_DBG_TABS:
  1219. if ( hdr->code == TCN_SELCHANGE )
  1220. {
  1221. ShowWindow ( window->mWndOutput, SW_HIDE );
  1222. ShowWindow ( window->mWndConsole, SW_HIDE );
  1223. ShowWindow ( window->mWndCallstack, SW_HIDE );
  1224. ShowWindow ( window->mWndWatch, SW_HIDE );
  1225. ShowWindow ( window->mWndThreads, SW_HIDE );
  1226. switch ( TabCtrl_GetCurSel ( hdr->hwndFrom ) )
  1227. {
  1228. case 0:
  1229. ShowWindow ( window->mWndOutput, SW_SHOW );
  1230. break;
  1231. case 1:
  1232. ShowWindow ( window->mWndConsole, SW_SHOW );
  1233. break;
  1234. case 2:
  1235. ShowWindow ( window->mWndCallstack, SW_SHOW );
  1236. break;
  1237. case 3:
  1238. ShowWindow ( window->mWndWatch, SW_SHOW );
  1239. break;
  1240. case 4:
  1241. ShowWindow ( window->mWndThreads, SW_SHOW );
  1242. break;
  1243. }
  1244. }
  1245. break;
  1246. }
  1247. break;
  1248. }
  1249. case WM_CLOSE:
  1250. if ( window->mClient->IsConnected ( ) )
  1251. {
  1252. if ( IDNO == MessageBox ( wnd, "The debugger is currently connected to a running version of the game. Are you sure you want to close now?", "Quake 4 Script Debugger", MB_YESNO|MB_ICONQUESTION ) )
  1253. {
  1254. return 0;
  1255. }
  1256. }
  1257. PostQuitMessage ( 0 );
  1258. break;
  1259. }
  1260. return DefWindowProc ( wnd, msg, wparam, lparam );
  1261. }
  1262. /*
  1263. ================
  1264. rvDebuggerWindow::Activate
  1265. Static method that will activate the currently running debugger. If one is found
  1266. and activated then true will be returned.
  1267. ================
  1268. */
  1269. bool rvDebuggerWindow::Activate ( void )
  1270. {
  1271. HWND find;
  1272. find = FindWindow ( DEBUGGERWINDOWCLASS, NULL );
  1273. if ( !find )
  1274. {
  1275. return false;
  1276. }
  1277. SetForegroundWindow ( find );
  1278. return true;
  1279. }
  1280. /*
  1281. ================
  1282. rvDebuggerWindow::ProcessNetMessage
  1283. Process an incoming network message
  1284. ================
  1285. */
  1286. void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg )
  1287. {
  1288. unsigned short command;
  1289. command = (unsigned short)MSG_ReadShort ( msg );
  1290. switch ( command )
  1291. {
  1292. case DBMSG_RESUMED:
  1293. UpdateTitle ( );
  1294. UpdateToolbar ( );
  1295. break;
  1296. case DBMSG_INSPECTVARIABLE:
  1297. {
  1298. char temp[1024];
  1299. char temp2[1024];
  1300. int i;
  1301. MSG_ReadShort ( msg );
  1302. MSG_ReadString ( msg, temp, 1024 );
  1303. MSG_ReadString ( msg, temp2, 1024 );
  1304. if ( mTooltipVar.Icmp ( temp ) == 0 )
  1305. {
  1306. mTooltipValue = temp2;
  1307. TOOLINFO info;
  1308. info.cbSize = sizeof(info);
  1309. info.hwnd = mWndScript;
  1310. info.uId = 0;
  1311. info.hinst = mInstance;
  1312. info.lpszText = va("%s=%s", mTooltipVar.c_str(), mTooltipValue.c_str() );
  1313. SendMessage ( mWndToolTips, TTM_UPDATETIPTEXT, 0, (LPARAM)&info );
  1314. SendMessage ( mWndToolTips, TTM_UPDATE, 0, 0 );
  1315. }
  1316. // See if any of the watches were updated by this inspect
  1317. for ( i = 0; i < mWatches.Num(); i ++ )
  1318. {
  1319. rvDebuggerWatch* watch = mWatches[i];
  1320. // See if the name matches the variable
  1321. if ( watch->mVariable.Cmp ( temp ) )
  1322. {
  1323. continue;
  1324. }
  1325. // Has the value changed?
  1326. if ( !watch->mValue.Cmp ( temp2 ) )
  1327. {
  1328. continue;
  1329. }
  1330. watch->mModified = true;
  1331. watch->mValue = temp2;
  1332. // Find the list view item that is storing this watch info and redraw it
  1333. for ( int l = 0; l < ListView_GetItemCount(mWndWatch); l ++ )
  1334. {
  1335. LVITEM item;
  1336. item.mask = LVIF_PARAM;
  1337. item.iItem = l;
  1338. ListView_GetItem ( mWndWatch, &item );
  1339. if ( item.lParam == (LPARAM) watch )
  1340. {
  1341. ListView_RedrawItems ( mWndWatch, l, l );
  1342. UpdateWindow ( mWndWatch );
  1343. break;
  1344. }
  1345. }
  1346. }
  1347. break;
  1348. }
  1349. case DBMSG_CONNECT:
  1350. case DBMSG_CONNECTED:
  1351. UpdateTitle ( );
  1352. UpdateToolbar ( );
  1353. Printf ( "Connected...\n" );
  1354. break;
  1355. case DBMSG_DISCONNECT:
  1356. UpdateTitle ( );
  1357. UpdateToolbar ( );
  1358. Printf ( "Disconnected...\n" );
  1359. break;
  1360. case DBMSG_PRINT:
  1361. SendMessage ( mWndConsole, EM_SETSEL, -1, -1 );
  1362. SendMessage ( mWndConsole, EM_REPLACESEL, 0, (LPARAM)(const char*)(msg->data) + msg->readcount );
  1363. SendMessage ( mWndConsole, EM_SCROLLCARET, 0, 0 );
  1364. break;
  1365. case DBMSG_BREAK:
  1366. {
  1367. Printf ( "Break: line=%d file='%s'\n", mClient->GetBreakLineNumber ( ), mClient->GetBreakFilename() );
  1368. mCurrentStackDepth = 0;
  1369. mClient->InspectVariable ( mTooltipVar, mCurrentStackDepth );
  1370. UpdateWatch ( );
  1371. EnableWindows ( TRUE );
  1372. OpenScript ( mClient->GetBreakFilename(), mClient->GetBreakLineNumber() - 1 );
  1373. UpdateTitle ( );
  1374. UpdateToolbar ( );
  1375. SetForegroundWindow ( mWnd );
  1376. break;
  1377. }
  1378. case DBMSG_INSPECTCALLSTACK:
  1379. {
  1380. UpdateCallstack ( );
  1381. break;
  1382. }
  1383. case DBMSG_INSPECTTHREADS:
  1384. {
  1385. LVITEM item;
  1386. ListView_DeleteAllItems ( mWndThreads );
  1387. ZeroMemory ( &item, sizeof(item) );
  1388. item.mask = LVIF_TEXT|LVIF_IMAGE;
  1389. for ( int i = 0; i < mClient->GetThreads().Num(); i ++ )
  1390. {
  1391. rvDebuggerThread* entry = mClient->GetThreads()[i];
  1392. item.iItem = ListView_GetItemCount ( mWndThreads );
  1393. item.pszText = "";
  1394. item.iImage = entry->mCurrent ? 1 : 0;
  1395. ListView_InsertItem ( mWndThreads, &item );
  1396. ListView_SetItemText ( mWndThreads, item.iItem, 1, (LPSTR)va("%d", entry->mID) );
  1397. ListView_SetItemText ( mWndThreads, item.iItem, 2, (LPSTR)entry->mName.c_str() );
  1398. if ( entry->mDying )
  1399. {
  1400. ListView_SetItemText ( mWndThreads, item.iItem, 3, "Dying" );
  1401. }
  1402. else if ( entry->mWaiting )
  1403. {
  1404. ListView_SetItemText ( mWndThreads, item.iItem, 3, "Waiting" );
  1405. }
  1406. else if ( entry->mDoneProcessing )
  1407. {
  1408. ListView_SetItemText ( mWndThreads, item.iItem, 3, "Stopped" );
  1409. }
  1410. else
  1411. {
  1412. ListView_SetItemText ( mWndThreads, item.iItem, 3, "Running" );
  1413. }
  1414. }
  1415. break;
  1416. }
  1417. }
  1418. }
  1419. /*
  1420. ================
  1421. rvDebuggerWindow::Printf
  1422. Sends a formatted message to the output window
  1423. ================
  1424. */
  1425. void rvDebuggerWindow::Printf ( const char* fmt, ... )
  1426. {
  1427. va_list argptr;
  1428. char msg[4096];
  1429. va_start (argptr,fmt);
  1430. vsprintf (msg,fmt,argptr);
  1431. va_end (argptr);
  1432. SendMessage ( mWndOutput, EM_SETSEL, -1, -1 );
  1433. SendMessage ( mWndOutput, EM_REPLACESEL, 0, (LPARAM)msg );
  1434. SendMessage ( mWndOutput, EM_SCROLLCARET, 0, 0 );
  1435. }
  1436. /*
  1437. ================
  1438. rvDebuggerWindow::OpenScript
  1439. Opens the script with the given filename and will scroll to the given line
  1440. number if one is specified
  1441. ================
  1442. */
  1443. bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber )
  1444. {
  1445. int i;
  1446. SetCursor ( LoadCursor ( NULL, IDC_WAIT ) );
  1447. mActiveScript = -1;
  1448. // See if the script is already loaded
  1449. for ( i = 0; i < mScripts.Num(); i ++ )
  1450. {
  1451. if ( !idStr::Icmp ( mScripts[i]->GetFilename ( ), filename ) )
  1452. {
  1453. if ( mActiveScript != i )
  1454. {
  1455. mActiveScript = i;
  1456. break;
  1457. }
  1458. }
  1459. }
  1460. // If the script isnt open already then open it
  1461. if ( mActiveScript == -1 )
  1462. {
  1463. rvDebuggerScript* script = new rvDebuggerScript;
  1464. // Load the script
  1465. if ( !script->Load ( filename ) )
  1466. {
  1467. delete script;
  1468. SetCursor ( LoadCursor ( NULL, IDC_ARROW ) );
  1469. return false;
  1470. }
  1471. gDebuggerApp.GetOptions().AddRecentFile ( filename );
  1472. UpdateRecentFiles ( );
  1473. mActiveScript = mScripts.Append ( script );
  1474. }
  1475. // Test code that will place a breakpoint on all valid lines of code
  1476. #if 0
  1477. for ( i = 0; i < mScripts[mActiveScript]->GetProgram().NumStatements(); i ++ )
  1478. {
  1479. dstatement_t* st = &mScripts[mActiveScript]->GetProgram().GetStatement ( i );
  1480. rvDebuggerBreakpoint* bp = new rvDebuggerBreakpoint ( filename, st->linenumber );
  1481. mBreakpoints.Append ( bp );
  1482. }
  1483. #endif
  1484. UpdateScript ( );
  1485. UpdateWindowMenu ( );
  1486. // Move to a specific line number?
  1487. if ( lineNumber != -1 )
  1488. {
  1489. int c;
  1490. // Put the caret on the line number specified and scroll it into position.
  1491. // This is a bit of a hack since we set the selection twice, but setting the
  1492. // selection to (c,c) didnt work for scrolling the caret so we do (c,c+1)
  1493. // and then scroll before going back to (c,c).
  1494. // NOTE: We scroll to the line before the one we want so its more visible
  1495. SetFocus ( mWndScript );
  1496. c = SendMessage ( mWndScript, EM_LINEINDEX, lineNumber - 1, 0 );
  1497. SendMessage ( mWndScript, EM_SETSEL, c, c + 1 );
  1498. SendMessage ( mWndScript, EM_SCROLLCARET, 0, 0 );
  1499. c = SendMessage ( mWndScript, EM_LINEINDEX, lineNumber, 0 );
  1500. SendMessage ( mWndScript, EM_SETSEL, c, c );
  1501. }
  1502. else
  1503. {
  1504. SendMessage ( mWndScript, EM_SETSEL, 0, 0 );
  1505. }
  1506. // Make sure the script window is visible
  1507. ShowWindow ( mWndScript, SW_SHOW );
  1508. UpdateWindow ( mWndScript );
  1509. SetCursor ( LoadCursor ( NULL, IDC_ARROW ) );
  1510. return true;
  1511. }
  1512. /*
  1513. ================
  1514. rvDebuggerWindow::ToggleBreakpoint
  1515. Toggles the breakpoint on the current script line
  1516. ================
  1517. */
  1518. void rvDebuggerWindow::ToggleBreakpoint ( void )
  1519. {
  1520. rvDebuggerBreakpoint* bp;
  1521. DWORD sel;
  1522. int line;
  1523. // Find the currently selected line
  1524. SendMessage ( mWndScript, EM_GETSEL, (WPARAM)&sel, 0 );
  1525. line = SendMessage ( mWndScript, EM_LINEFROMCHAR, sel, 0 ) + 1;
  1526. // If there is already a breakpoint there then just remove it, otherwise
  1527. // we need to create a new breakpoint
  1528. bp = mClient->FindBreakpoint ( mScripts[mActiveScript]->GetFilename ( ), line );
  1529. if ( !bp )
  1530. {
  1531. // Make sure the line is code before letting them add a breakpoint there
  1532. if ( !mScripts[mActiveScript]->IsLineCode ( line ) )
  1533. {
  1534. MessageBeep ( MB_ICONEXCLAMATION );
  1535. return;
  1536. }
  1537. mClient->AddBreakpoint ( mScripts[mActiveScript]->GetFilename(), line );
  1538. }
  1539. else
  1540. {
  1541. mClient->RemoveBreakpoint ( bp->GetID ( ) );
  1542. }
  1543. // Force a repaint of the script window
  1544. InvalidateRect ( mWndScript, NULL, FALSE );
  1545. }
  1546. /*
  1547. ================
  1548. rvDebuggerWindow::AboutDlgProc
  1549. Dialog box procedure for the about box
  1550. ================
  1551. */
  1552. INT_PTR CALLBACK rvDebuggerWindow::AboutDlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam )
  1553. {
  1554. switch ( msg )
  1555. {
  1556. case WM_COMMAND:
  1557. EndDialog ( wnd, 0 );
  1558. break;
  1559. }
  1560. return FALSE;
  1561. }
  1562. /*
  1563. ================
  1564. rvDebuggerWindow::CreateToolbar
  1565. Create the toolbar and and all of its buttons
  1566. ================
  1567. */
  1568. void rvDebuggerWindow::CreateToolbar ( void )
  1569. {
  1570. // Create the toolbar control
  1571. mWndToolbar = CreateWindowEx ( 0, TOOLBARCLASSNAME, "", WS_CHILD|WS_VISIBLE,0,0,0,0, mWnd, (HMENU)IDC_DBG_TOOLBAR, mInstance, NULL );
  1572. // Initialize the toolbar
  1573. SendMessage ( mWndToolbar, TB_BUTTONSTRUCTSIZE, ( WPARAM )sizeof( TBBUTTON ), 0 );
  1574. SendMessage ( mWndToolbar, TB_SETBUTTONSIZE, 0, MAKELONG(16,16) );
  1575. SendMessage ( mWndToolbar, TB_SETSTYLE, 0, SendMessage ( mWndToolbar, TB_GETSTYLE, 0, 0 ) | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS );
  1576. TBMETRICS tbmet;
  1577. tbmet.cbSize = sizeof(TBMETRICS);
  1578. SendMessage ( mWndToolbar, TB_GETMETRICS, 0, (LPARAM)&tbmet );
  1579. tbmet.cyPad = 0;
  1580. tbmet.cyBarPad = 0;
  1581. SendMessage ( mWndToolbar, TB_SETMETRICS, 0, (LPARAM)&tbmet );
  1582. // Add the bitmap containing button images to the toolbar.
  1583. TBADDBITMAP tbab;
  1584. tbab.hInst = mInstance;
  1585. tbab.nID = IDB_DBG_TOOLBAR;
  1586. SendMessage( mWndToolbar, TB_ADDBITMAP, (WPARAM)4, (LPARAM) &tbab );
  1587. // Add the buttons to the toolbar
  1588. TBBUTTON tbb[] = { { 0, 0, TBSTATE_ENABLED, BTNS_SEP, 0, 0, -1 },
  1589. { 8, ID_DBG_FILE_OPEN, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, -1 },
  1590. { 0, 0, TBSTATE_ENABLED, BTNS_SEP, 0, 0, -1 },
  1591. { 0, ID_DBG_DEBUG_RUN, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, -1 },
  1592. { 1, ID_DBG_DEBUG_BREAK, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, -1 },
  1593. { 0, 0, TBSTATE_ENABLED, BTNS_SEP, 0, 0, -1 },
  1594. { 4, ID_DBG_DEBUG_STEPINTO, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, -1 },
  1595. { 5, ID_DBG_DEBUG_STEPOVER, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, -1 },
  1596. { 6, ID_DBG_DEBUG_STEPOUT, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, -1 },
  1597. { 0, 0, TBSTATE_ENABLED, BTNS_SEP, 0, 0, -1 } };
  1598. SendMessage( mWndToolbar, TB_ADDBUTTONS, (WPARAM)sizeof(tbb)/sizeof(TBBUTTON), (LPARAM) tbb );
  1599. }
  1600. /*
  1601. ================
  1602. rvDebuggerWindow::HandleTooltipGetDispInfo
  1603. Handle the getdispinfo notification message for tooltips by responding with the
  1604. tooptip text for the given toolbar button.
  1605. ================
  1606. */
  1607. void rvDebuggerWindow::HandleTooltipGetDispInfo ( WPARAM wparam, LPARAM lparam )
  1608. {
  1609. NMTTDISPINFO* ttdi = (NMTTDISPINFO*) lparam;
  1610. switch ( ttdi->hdr.idFrom )
  1611. {
  1612. case ID_DBG_FILE_OPEN:
  1613. strcpy ( ttdi->szText, "Open Script" );
  1614. break;
  1615. case ID_DBG_DEBUG_STEPINTO:
  1616. strcpy ( ttdi->szText, "Step Into" );
  1617. break;
  1618. case ID_DBG_DEBUG_STEPOVER:
  1619. strcpy ( ttdi->szText, "Step Over" );
  1620. break;
  1621. case ID_DBG_DEBUG_STEPOUT:
  1622. strcpy ( ttdi->szText, "Step Out" );
  1623. break;
  1624. case ID_DBG_DEBUG_BREAK:
  1625. strcpy ( ttdi->szText, "Break" );
  1626. break;
  1627. case ID_DBG_DEBUG_RUN:
  1628. if ( mClient->IsConnected() )
  1629. {
  1630. strcpy ( ttdi->szText, "Continue" );
  1631. }
  1632. else
  1633. {
  1634. strcpy ( ttdi->szText, "Run" );
  1635. }
  1636. break;
  1637. default:
  1638. strcpy ( ttdi->szText, "" );
  1639. break;
  1640. }
  1641. }
  1642. /*
  1643. ================
  1644. rvDebuggerWindow::HandleActivate
  1645. When the main window is activated, check all the loaded scripts and see if any of them
  1646. have been modified since the last time they were loaded. If they have then reload
  1647. them and adjust all breakpoints that now fall on invalid lines.
  1648. ================
  1649. */
  1650. int rvDebuggerWindow::HandleActivate ( WPARAM wparam, LPARAM lparam )
  1651. {
  1652. int i;
  1653. // We are only interested in the activation, not deactivation
  1654. if ( !LOWORD(wparam) )
  1655. {
  1656. return 0;
  1657. }
  1658. // Run through all of the loaded scripts and see if any of them have been modified
  1659. for ( i = 0; i < mScripts.Num(); i ++ )
  1660. {
  1661. if ( mScripts[i]->IsFileModified ( true ) )
  1662. {
  1663. if ( IDYES == MessageBox ( mWnd, va("%s\n\nThis file has been modified outside of the debugger.\nDo you want to reload it?", mScripts[i]->GetFilename() ), "Quake 4 Script Debugger", MB_YESNO|MB_ICONQUESTION ) )
  1664. {
  1665. mScripts[i]->Reload ( );
  1666. // Update the script if it was the active one
  1667. if ( mActiveScript == i )
  1668. {
  1669. mLastActiveScript = -1;
  1670. UpdateScript ( );
  1671. }
  1672. // Loop through the breakpoints and see if any of them have
  1673. // moved to invalid lines within the script. If so then just remove
  1674. // them.
  1675. for ( int b = mClient->GetBreakpointCount() - 1; b >= 0 ; b-- )
  1676. {
  1677. rvDebuggerBreakpoint* bp = mClient->GetBreakpoint(b);
  1678. assert ( bp );
  1679. if ( !idStr::Icmp ( bp->GetFilename(), mScripts[i]->GetFilename() ) )
  1680. {
  1681. if ( !mScripts[i]->IsLineCode ( bp->GetLineNumber ( ) ) )
  1682. {
  1683. mClient->RemoveBreakpoint ( bp->GetID ( ) );
  1684. }
  1685. }
  1686. }
  1687. }
  1688. }
  1689. }
  1690. return 1;
  1691. }
  1692. /*
  1693. ================
  1694. rvDebuggerWindow::EnableWindows
  1695. Enables and disables all windows with a enable state dependent on the current
  1696. connected and paused state of the debugger.
  1697. ================
  1698. */
  1699. void rvDebuggerWindow::EnableWindows ( bool state )
  1700. {
  1701. EnableWindow ( mWndCallstack, state );
  1702. EnableWindow ( mWndThreads, state );
  1703. EnableWindow ( mWndWatch, state );
  1704. }
  1705. /*
  1706. ================
  1707. rvDebuggerWindow::AddWatch
  1708. Add a variable to the watch window. If update is set to true then also query the
  1709. debugger client for the value
  1710. ================
  1711. */
  1712. void rvDebuggerWindow::AddWatch ( const char* varname, bool update )
  1713. {
  1714. rvDebuggerWatch* watch;
  1715. watch = new rvDebuggerWatch;
  1716. watch->mVariable = varname;
  1717. watch->mModified = false;
  1718. watch->mValue = "???";
  1719. mWatches.Append ( watch );
  1720. // Add the variable to the watch control
  1721. LVITEM item;
  1722. item.mask = LVIF_PARAM;
  1723. item.iItem = ListView_GetItemCount ( mWndWatch ) - 1;
  1724. item.iSubItem = 0;
  1725. item.lParam = (LPARAM)watch;
  1726. ListView_InsertItem ( mWndWatch, &item );
  1727. // If update is set then request the value from the debugger client
  1728. if ( update )
  1729. {
  1730. mClient->InspectVariable ( varname, mCurrentStackDepth );
  1731. }
  1732. }
  1733. /*
  1734. ================
  1735. rvDebuggerWindow::InitRecentFiles
  1736. Finds the file menu and the location within it where the MRU should
  1737. be added.
  1738. ================
  1739. */
  1740. bool rvDebuggerWindow::InitRecentFiles ( void )
  1741. {
  1742. int i;
  1743. int count;
  1744. mRecentFileMenu = GetSubMenu ( GetMenu(mWnd), 0 );
  1745. count = GetMenuItemCount ( mRecentFileMenu );
  1746. for ( i = 0; i < count; i ++ )
  1747. {
  1748. if ( GetMenuItemID ( mRecentFileMenu, i ) == ID_DBG_FILE_MRU )
  1749. {
  1750. mRecentFileInsertPos = i;
  1751. DeleteMenu ( mRecentFileMenu, mRecentFileInsertPos, MF_BYPOSITION );
  1752. return true;
  1753. }
  1754. }
  1755. return false;
  1756. }
  1757. /*
  1758. ================
  1759. rvDebuggerWindow::UpdateRecentFiles
  1760. Updates the mru in the menu
  1761. ================
  1762. */
  1763. void rvDebuggerWindow::UpdateRecentFiles ( void )
  1764. {
  1765. int i;
  1766. int j;
  1767. // Make sure everything is initialized
  1768. if ( !mRecentFileMenu )
  1769. {
  1770. InitRecentFiles ( );
  1771. }
  1772. // Delete all the old recent files from the menu's
  1773. for ( i = 0; i < rvRegistryOptions::MAX_MRU_SIZE; i ++ )
  1774. {
  1775. DeleteMenu ( mRecentFileMenu, ID_DBG_FILE_MRU1 + i, MF_BYCOMMAND );
  1776. }
  1777. // Make sure there is a separator after the recent files
  1778. if ( gDebuggerApp.GetOptions().GetRecentFileCount() )
  1779. {
  1780. MENUITEMINFO info;
  1781. ZeroMemory ( &info, sizeof(info) );
  1782. info.cbSize = sizeof(info);
  1783. info.fMask = MIIM_FTYPE;
  1784. GetMenuItemInfo ( mRecentFileMenu, mRecentFileInsertPos+1,TRUE, &info );
  1785. if ( !(info.fType & MFT_SEPARATOR ) )
  1786. {
  1787. InsertMenu ( mRecentFileMenu, mRecentFileInsertPos, MF_BYPOSITION|MF_SEPARATOR|MF_ENABLED, 0, NULL );
  1788. }
  1789. }
  1790. // Add the recent files to the menu now
  1791. for ( j = 0, i = gDebuggerApp.GetOptions().GetRecentFileCount ( ) - 1; i >= 0; i --, j++ )
  1792. {
  1793. UINT id = ID_DBG_FILE_MRU1 + j;
  1794. idStr str = va("&%d ", j+1);
  1795. str.Append ( gDebuggerApp.GetOptions().GetRecentFile ( i ) );
  1796. InsertMenu ( mRecentFileMenu, mRecentFileInsertPos+j+1, MF_BYPOSITION|MF_STRING|MF_ENABLED, id, str );
  1797. }
  1798. }
  1799. /*
  1800. ================
  1801. rvDebuggerWindow::GetSelectedText
  1802. Function to retrieve the text that is currently selected in the
  1803. script control
  1804. ================
  1805. */
  1806. int rvDebuggerWindow::GetSelectedText ( idStr& text )
  1807. {
  1808. TEXTRANGE range;
  1809. int start;
  1810. int end;
  1811. char* temp;
  1812. text.Empty ( );
  1813. if ( mScripts.Num ( ) )
  1814. {
  1815. SendMessage ( mWndScript, EM_GETSEL, (WPARAM)&start, (LPARAM)&end );
  1816. if ( start == end )
  1817. {
  1818. end = SendMessage ( mWndScript, EM_FINDWORDBREAK, WB_RIGHT, start );
  1819. start = SendMessage ( mWndScript, EM_FINDWORDBREAK, WB_LEFT, start );
  1820. }
  1821. temp = new char[end-start+10];
  1822. range.chrg.cpMin = start;
  1823. range.chrg.cpMax = end;
  1824. range.lpstrText = temp;
  1825. SendMessage ( mWndScript, EM_GETTEXTRANGE, 0, (LPARAM) &range );
  1826. text = temp;
  1827. delete[] temp;
  1828. return start;
  1829. }
  1830. return -1;
  1831. }
  1832. /*
  1833. ================
  1834. rvDebuggerWindow::FindNext
  1835. Finds the next match of the find text in the active script. The next is
  1836. always relative to the current selection. If the text parameter is NULL
  1837. then the last text used will be searched for.
  1838. ================
  1839. */
  1840. bool rvDebuggerWindow::FindNext ( const char* text )
  1841. {
  1842. int start;
  1843. FINDTEXT ft;
  1844. if ( text )
  1845. {
  1846. mFind = text;
  1847. }
  1848. if ( !mFind.Length ( ) )
  1849. {
  1850. return false;
  1851. }
  1852. SendMessage ( mWndScript, EM_GETSEL, (WPARAM)&start, (LPARAM)0 );
  1853. if ( start < 0 )
  1854. {
  1855. start = 0;
  1856. }
  1857. ft.chrg.cpMin = start + 1;
  1858. ft.chrg.cpMax = -1;
  1859. ft.lpstrText = mFind.c_str();
  1860. start = SendMessage ( mWndScript, EM_FINDTEXT, FR_DOWN, (LPARAM)&ft );
  1861. if ( start < 0 )
  1862. {
  1863. ft.chrg.cpMin = 0;
  1864. ft.chrg.cpMax = -1;
  1865. ft.lpstrText = mFind.c_str();
  1866. start = SendMessage ( mWndScript, EM_FINDTEXT, FR_DOWN, (LPARAM)&ft );
  1867. if ( start < 0 )
  1868. {
  1869. return false;
  1870. }
  1871. }
  1872. SendMessage ( mWndScript, EM_SETSEL, start, start + mFind.Length() );
  1873. SendMessage ( mWndScript, EM_SCROLLCARET, 0, 0 );
  1874. return true;
  1875. }
  1876. /*
  1877. ================
  1878. rvDebuggerWindow::FindPrev
  1879. Finds the previous match of the find text in the active script. The previous is
  1880. always relative to the current selection. If the text parameter is NULL
  1881. then the last text used will be searched for.
  1882. ================
  1883. */
  1884. bool rvDebuggerWindow::FindPrev ( const char* text )
  1885. {
  1886. int start;
  1887. FINDTEXT ft;
  1888. if ( text )
  1889. {
  1890. mFind = text;
  1891. }
  1892. if ( !mFind.Length ( ) )
  1893. {
  1894. return false;
  1895. }
  1896. SendMessage ( mWndScript, EM_GETSEL, (WPARAM)&start, (LPARAM)0 );
  1897. if ( start < 0 )
  1898. {
  1899. start = 0;
  1900. }
  1901. ft.chrg.cpMin = start;
  1902. ft.chrg.cpMax = -1;
  1903. ft.lpstrText = mFind.c_str();
  1904. start = SendMessage ( mWndScript, EM_FINDTEXT, 0, (LPARAM)&ft );
  1905. if ( start < 0 )
  1906. {
  1907. GETTEXTLENGTHEX gtl;
  1908. gtl.flags = GTL_DEFAULT;
  1909. gtl.codepage = CP_ACP;
  1910. ft.chrg.cpMin = SendMessage ( mWndScript, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0 );
  1911. ft.chrg.cpMax = 0;
  1912. ft.lpstrText = mFind.c_str();
  1913. start = SendMessage ( mWndScript, EM_FINDTEXT, 0, (LPARAM)&ft );
  1914. if ( start < 0 )
  1915. {
  1916. return false;
  1917. }
  1918. }
  1919. SendMessage ( mWndScript, EM_SETSEL, start, start + mFind.Length() );
  1920. SendMessage ( mWndScript, EM_SCROLLCARET, 0, 0 );
  1921. return true;
  1922. }
  1923. /*
  1924. ================
  1925. rvDebuggerWindow::HandleDrawItem
  1926. Handled the WM_DRAWITEM message. The watch window is custom drawn so a grid can be displayed.
  1927. ================
  1928. */
  1929. int rvDebuggerWindow::HandleDrawItem ( WPARAM wparam, LPARAM lparam )
  1930. {
  1931. DRAWITEMSTRUCT* dis;
  1932. LVCOLUMN col;
  1933. int index;
  1934. idStr widths;
  1935. RECT rect;
  1936. rvDebuggerWatch* watch;
  1937. bool selected;
  1938. dis = (DRAWITEMSTRUCT*) lparam;
  1939. watch = (rvDebuggerWatch*)dis->itemData;
  1940. col.mask = LVCF_WIDTH;
  1941. rect = dis->rcItem;
  1942. rect.left = rect.left - 1;
  1943. rect.right = rect.left;
  1944. rect.bottom++;
  1945. selected = ((dis->itemState & ODS_SELECTED) && GetFocus()==mWndWatch);
  1946. // Set the colors based on the selected state and draw the item background
  1947. if ( selected )
  1948. {
  1949. FillRect ( dis->hDC, &dis->rcItem, GetSysColorBrush ( COLOR_HIGHLIGHT ) );
  1950. }
  1951. else
  1952. {
  1953. FillRect ( dis->hDC, &dis->rcItem, GetSysColorBrush ( IsWindowEnabled ( mWndWatch ) ? COLOR_WINDOW : COLOR_3DFACE ) );
  1954. }
  1955. // Run through the columns and draw each with a frame around it and the text
  1956. // vertically centered in it
  1957. for ( index = 0; ListView_GetColumn ( mWndWatch, index, &col ); index ++ )
  1958. {
  1959. rect.right = rect.left + col.cx;
  1960. FrameRect ( dis->hDC, &rect, GetSysColorBrush ( COLOR_3DFACE ) );
  1961. // Draw info on the watch if available
  1962. if ( watch )
  1963. {
  1964. RECT textrect;
  1965. textrect = rect;
  1966. textrect.left += 5;
  1967. switch ( index )
  1968. {
  1969. case 0:
  1970. SetTextColor ( dis->hDC, GetSysColor ( selected ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT ) );
  1971. DrawText ( dis->hDC, watch->mVariable, -1, &textrect, DT_LEFT|DT_VCENTER );
  1972. break;
  1973. case 1:
  1974. if ( watch && watch->mModified && (IsWindowEnabled ( mWndWatch ) ) )
  1975. {
  1976. SetTextColor ( dis->hDC, RGB(255,50,50) );
  1977. }
  1978. else
  1979. {
  1980. SetTextColor ( dis->hDC, GetSysColor ( selected ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT ) );
  1981. }
  1982. DrawText ( dis->hDC, watch->mValue, -1, &textrect, DT_LEFT|DT_VCENTER );
  1983. break;
  1984. }
  1985. }
  1986. rect.left = rect.right - 1;
  1987. }
  1988. return 0;
  1989. }