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

752 lines
18 KiB

  1. /*
  2. * standard table class.
  3. *
  4. * paint functions.
  5. *
  6. * see table.h for interface description
  7. */
  8. #include <string.h>
  9. #include "windows.h"
  10. #include "commdlg.h"
  11. #include "gutils.h"
  12. #include "table.h"
  13. #include "tpriv.h"
  14. #ifdef WIN32
  15. int
  16. GetTextExtent(HDC hdc, LPSTR text, INT len)
  17. {
  18. SIZE sz;
  19. GetTextExtentPoint(hdc, text, len, &sz);
  20. return(sz.cx);
  21. }
  22. #endif
  23. void gtab_updatecontig(HWND hwnd, lpTable ptab, int line, int cell1, int count);
  24. /* change all cr/lf chars in input text to nul chars (used to be spaces, not sure why) */
  25. void gtab_delcr(LPSTR ptext)
  26. {
  27. LPSTR chp;
  28. if (ptext == NULL) {
  29. return;
  30. }
  31. for(chp = ptext; (chp = My_mbschr(chp, '\r')) != NULL; ) {
  32. *chp = '\0';
  33. }
  34. for(chp = ptext; (chp = My_mbschr(chp, '\n')) != NULL; ) {
  35. *chp = '\0';
  36. }
  37. }
  38. void gtab_delcrW(LPWSTR pwzText)
  39. {
  40. LPWSTR pwch;
  41. if (pwzText)
  42. for(pwch = pwzText; *pwch; pwch++)
  43. if (*pwch == '\r' || *pwch == '\n')
  44. *pwch = 0;
  45. }
  46. /* ensure that all visible cells in the given line have valid
  47. * text and property contents. loop through the cells, picking out
  48. * contiguous blocks of visible, invalid cells and call
  49. * gtab_updatecontig to update these from the owner window.
  50. */
  51. void
  52. gtab_updateline(HWND hwnd, lpTable ptab, int line)
  53. {
  54. lpCellPos ppos;
  55. int cell1, cellcount;
  56. lpLineData pline;
  57. lpCellData cd;
  58. int i;
  59. if (line < ptab->hdr.nrows) {
  60. pline = &ptab->pdata[line];
  61. cell1 = 0;
  62. cellcount = 0;
  63. for (i = 0; i < ptab->hdr.ncols; i++) {
  64. ppos = &ptab->pcellpos[i];
  65. cd = &pline->pdata[i];
  66. if (ppos->clipstart < ppos->clipend) {
  67. if ((cd->flags & CELL_VALID) == 0) {
  68. /* add a cell to the list to be updated*/
  69. if (cellcount++ == 0) {
  70. cell1 = i;
  71. }
  72. } else {
  73. /* this cell already valid - so end of
  74. * a contig block. if the contig
  75. * block just ended contained cells to update,
  76. * do it now
  77. */
  78. if (cellcount > 0) {
  79. gtab_updatecontig(hwnd, ptab,
  80. line, cell1, cellcount);
  81. }
  82. cellcount = 0;
  83. }
  84. }
  85. /* cell not visible - end of a contig block. If it was a
  86. * non-empty contig block, then update it now.
  87. */
  88. if (cellcount > 0) {
  89. gtab_updatecontig(hwnd, ptab, line, cell1, cellcount);
  90. cellcount = 0;
  91. }
  92. }
  93. if (cellcount > 0) {
  94. gtab_updatecontig(hwnd, ptab, line, cell1, cellcount);
  95. cellcount = 0;
  96. }
  97. }
  98. }
  99. /*
  100. * update a contiguous block of invalid cells by calling the owner window
  101. */
  102. void
  103. gtab_updatecontig(HWND hwnd, lpTable ptab, int line, int cell1, int count)
  104. {
  105. lpLineData pline;
  106. lpCellData cd;
  107. CellDataList list;
  108. lpProps colprops;
  109. int i;
  110. COLORREF rgb;
  111. pline = &ptab->pdata[line];
  112. cd = &pline->pdata[cell1];
  113. list.id = ptab->hdr.id;
  114. list.row = gtab_linetorow(hwnd, ptab, line);
  115. list.startcell = cell1;
  116. list.ncells = count;
  117. list.plist = cd;
  118. /* clear out prop flags */
  119. rgb = GetSysColor(COLOR_WINDOW);
  120. for (i = 0; i < count; i++) {
  121. cd[i].props.valid = P_BCOLOUR;
  122. cd[i].props.backcolour = rgb;
  123. if (cd[i].nchars > 0) {
  124. cd[i].ptext[0] = '\0';
  125. if (cd[i].pwzText) {
  126. cd[i].pwzText[0] = '\0';
  127. }
  128. }
  129. }
  130. if (list.row < ptab->hdr.nrows) {
  131. gtab_sendtq(hwnd, TQ_GETDATA, (LPARAM) &list);
  132. }
  133. /* for each cell, mark valid and set properties */
  134. for (i = 0; i < count; i++) {
  135. cd[i].flags |= CELL_VALID;
  136. gtab_delcr(cd[i].ptext);
  137. gtab_delcrW(cd[i].pwzText);
  138. /* fetch properties from hdr and colhdr */
  139. colprops = &ptab->pcolhdr[i + cell1].props;
  140. if (!(cd[i].props.valid & P_FCOLOUR)) {
  141. if (colprops->valid & P_FCOLOUR) {
  142. cd[i].props.valid |= P_FCOLOUR;
  143. cd[i].props.forecolour = colprops->forecolour;
  144. } else if (ptab->hdr.props.valid & P_FCOLOUR) {
  145. cd[i].props.valid |= P_FCOLOUR;
  146. cd[i].props.forecolour =
  147. ptab->hdr.props.forecolour;
  148. }
  149. }
  150. if (!(cd[i].props.valid & P_FCOLOURWS)) {
  151. if (colprops->valid & P_FCOLOURWS) {
  152. cd[i].props.valid |= P_FCOLOURWS;
  153. cd[i].props.forecolourws = colprops->forecolourws;
  154. } else if (ptab->hdr.props.valid & P_FCOLOURWS) {
  155. cd[i].props.valid |= P_FCOLOURWS;
  156. cd[i].props.forecolourws =
  157. ptab->hdr.props.forecolourws;
  158. }
  159. }
  160. if (!(cd[i].props.valid & P_BCOLOUR)) {
  161. if (colprops->valid & P_BCOLOUR) {
  162. cd[i].props.valid |= P_BCOLOUR;
  163. cd[i].props.backcolour = colprops->backcolour;
  164. } else if (ptab->hdr.props.valid & P_BCOLOUR) {
  165. cd[i].props.valid |= P_BCOLOUR;
  166. cd[i].props.backcolour =
  167. ptab->hdr.props.backcolour;
  168. }
  169. }
  170. if (!(cd[i].props.valid & P_FONT)) {
  171. if (colprops->valid & P_FONT) {
  172. cd[i].props.valid |= P_FONT;
  173. cd[i].props.hFont = colprops->hFont;
  174. } else if (ptab->hdr.props.valid & P_FONT) {
  175. cd[i].props.valid |= P_FONT;
  176. cd[i].props.hFont = ptab->hdr.props.hFont;
  177. }
  178. }
  179. if (!(cd[i].props.valid & P_ALIGN)) {
  180. if (colprops->valid & P_ALIGN) {
  181. cd[i].props.valid |= P_ALIGN;
  182. cd[i].props.alignment = colprops->alignment;
  183. } else if (ptab->hdr.props.valid & P_ALIGN) {
  184. cd[i].props.valid |= P_ALIGN;
  185. cd[i].props.alignment =
  186. ptab->hdr.props.alignment;
  187. }
  188. }
  189. if (!(cd[i].props.valid & P_BOX)) {
  190. if (colprops->valid & P_BOX) {
  191. cd[i].props.valid |= P_BOX;
  192. cd[i].props.box = colprops->box;
  193. } else if (ptab->hdr.props.valid & P_BOX) {
  194. cd[i].props.valid |= P_BOX;
  195. cd[i].props.box = ptab->hdr.props.box;
  196. }
  197. }
  198. /* you can't set width/height per cell - this
  199. * is ignored at cell level.
  200. */
  201. }
  202. }
  203. void
  204. gtab_boxcell(HWND hwnd, HDC hdc, LPRECT rcp, LPRECT pclip, UINT boxmode)
  205. {
  206. if (boxmode & P_BOXTOP) {
  207. MoveToEx(hdc, max(rcp->left, pclip->left),
  208. max(rcp->top, pclip->top), NULL);
  209. LineTo(hdc, min(rcp->right, pclip->right),
  210. max(rcp->top, pclip->top));
  211. }
  212. if (boxmode & P_BOXBOTTOM) {
  213. MoveToEx(hdc, max(rcp->left, pclip->left),
  214. min(rcp->bottom, pclip->bottom), NULL);
  215. LineTo(hdc, min(rcp->right, pclip->right),
  216. min(rcp->bottom, pclip->bottom));
  217. }
  218. if (boxmode & P_BOXLEFT) {
  219. MoveToEx(hdc, max(rcp->left, pclip->left),
  220. max(rcp->top, pclip->top), NULL);
  221. MoveToEx(hdc, max(rcp->left, pclip->left),
  222. min(rcp->bottom, pclip->bottom), NULL);
  223. }
  224. if (boxmode & P_BOXRIGHT) {
  225. MoveToEx(hdc, min(rcp->right, pclip->right),
  226. max(rcp->top, pclip->top), NULL);
  227. LineTo(hdc, min(rcp->right, pclip->right),
  228. min(rcp->bottom, pclip->bottom));
  229. }
  230. }
  231. void
  232. gtab_paintcell(HWND hwnd, HDC hdc, lpTable ptab, int line, int cell, BOOL show_whitespace)
  233. {
  234. lpLineData pline;
  235. lpCellData cd;
  236. lpCellPos ppos;
  237. RECT rc, rcbox;
  238. int cx, x, y;
  239. UINT align;
  240. LPSTR chp, tabp;
  241. LPWSTR pwch, pwchBreak;
  242. DWORD fcol, fcolOld, fcolws;
  243. DWORD bkcol, bkcolOld;
  244. HFONT hfont, hfontOld;
  245. HBRUSH hbr;
  246. char szCharSet[] = "\t ";
  247. WCHAR wzCharSet[] = L"\t ";
  248. char szSpaceReplace[] = { (char) 183, (char) 0 };
  249. char szTabReplace[] = { (char) 187, (char) 0 };
  250. int cxSpaceReplace;
  251. int cxTabReplace;
  252. SIZE size;
  253. TEXTMETRIC tm;
  254. int yOfs;
  255. fcol = 0; bkcol = 0; /* eliminate spurious diagnostic, generate worse code */
  256. hfont = 0; /* eliminate spurious diagnostic, generate worse code */
  257. /* init pointers to cell text and properties */
  258. pline = &ptab->pdata[line];
  259. cd = &pline->pdata[cell];
  260. ppos = &ptab->pcellpos[cell];
  261. /* draw gutter */
  262. rc.top = pline->linepos.clipstart;
  263. rc.bottom = pline->linepos.clipend;
  264. rc.left = (cell > 0) ? ptab->pcellpos[cell - 1].clipend : 0;
  265. rc.right = ppos->clipstart;
  266. if (cell > ptab->hdr.fixedcols && ptab->hdr.fixedcols < ptab->hdr.ncols) {
  267. rc.left = max(rc.left, ptab->pcellpos[ptab->hdr.fixedcols].clipstart);
  268. }
  269. if (ptab->hdr.fixedcols > 0 && cell == ptab->hdr.fixedcols) {
  270. rc.right--;
  271. }
  272. if (rc.right > rc.left) {
  273. FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
  274. }
  275. /* clip all output to this rectangle */
  276. rc.top = pline->linepos.clipstart;
  277. rc.bottom = pline->linepos.clipend;
  278. rc.left = ppos->clipstart;
  279. rc.right = ppos->clipend;
  280. /* check cell properties and colours */
  281. if (cd->props.valid & P_ALIGN) {
  282. align = cd->props.alignment;
  283. } else {
  284. align = P_LEFT;
  285. }
  286. if (cd->props.valid & P_FONT) {
  287. hfontOld = SelectObject(hdc, cd->props.hFont);
  288. }
  289. // get y offset to center text vertically within cell
  290. GetTextMetrics(hdc, &tm);
  291. yOfs = (rc.bottom - rc.top - tm.tmHeight) / 2;
  292. /* set replacement chars and char widths */
  293. cxSpaceReplace = GetTextExtent(hdc, szSpaceReplace, 1);
  294. cxTabReplace = cxSpaceReplace * ptab->tabchars;
  295. /* set colours if not default */
  296. if (cd->props.valid & P_FCOLOUR) {
  297. fcol = cd->props.forecolour;
  298. fcolOld = SetTextColor(hdc, fcol);
  299. }
  300. if (cd->props.valid & P_FCOLOURWS) {
  301. fcolws = cd->props.forecolourws;
  302. }
  303. else {
  304. fcolws = fcol;
  305. }
  306. if (cd->props.valid & P_BCOLOUR) {
  307. /* there is a non-default background colour.
  308. * create a brush and fill the entire cell with it
  309. */
  310. hbr = CreateSolidBrush(cd->props.backcolour);
  311. if (hbr)
  312. {
  313. FillRect(hdc, &rc, hbr);
  314. DeleteObject(hbr);
  315. }
  316. /* also set colour as background colour for the text */
  317. bkcolOld = SetBkColor(hdc, cd->props.backcolour);
  318. }
  319. /* calc offset of text within cell for right-align or centering */
  320. if (align == P_LEFT) {
  321. cx = ptab->avewidth/2;
  322. } else {
  323. cx = 0;
  324. if (cd->pwzText) {
  325. GetTextExtentPoint32W(hdc, cd->pwzText, wcslen(cd->pwzText), &size);
  326. } else if (cd->ptext) {
  327. GetTextExtentPoint32A(hdc, cd->ptext, lstrlen(cd->ptext), &size);
  328. }
  329. cx = size.cx;
  330. if (align == P_CENTRE) {
  331. cx = (ppos->size - cx) / 2;
  332. } else {
  333. cx = ppos->size - cx - (ptab->avewidth/2);
  334. }
  335. }
  336. cx += ppos->start;
  337. /* expand tabs on output and show whitespace on output */
  338. x = 0;
  339. y = pline->linepos.start + yOfs;
  340. /* set search string for strpbrk fn;
  341. don't search for space chars unless we're showing whitespace */
  342. if (!show_whitespace)
  343. {
  344. szCharSet[1] = '\0';
  345. wzCharSet[1] = '\0';
  346. }
  347. // determine the string to display (ansi/unicode). if we have a string
  348. // and it's not empty, then loop and display it.
  349. chp = cd->ptext;
  350. pwch = cd->pwzText;
  351. if (pwch ? *pwch : (chp && *chp))
  352. {
  353. while (TRUE)
  354. {
  355. if (pwch)
  356. {
  357. pwchBreak = wcspbrk(pwch, wzCharSet);
  358. if (!pwchBreak)
  359. pwchBreak = pwch + wcslen(pwch);
  360. }
  361. else
  362. {
  363. tabp = My_mbspbrk(chp, szCharSet);
  364. if (!tabp)
  365. tabp = chp + lstrlen(chp);
  366. }
  367. /* perform output up to tab/space char */
  368. if (pwch)
  369. ExtTextOutW(hdc, x+cx, y, ETO_CLIPPED, &rc, pwch, (UINT)(pwchBreak-pwch), NULL);
  370. else
  371. ExtTextOutA(hdc, x+cx, y, ETO_CLIPPED, &rc, chp, (UINT)(tabp-chp), NULL);
  372. /* advance past the tab */
  373. if (pwch)
  374. {
  375. GetTextExtentPoint32W(hdc, pwch, (UINT)(pwchBreak - pwch), &size);
  376. pwch = pwchBreak;
  377. }
  378. else
  379. {
  380. GetTextExtentPoint32A(hdc, chp, (UINT)(tabp - chp), &size);
  381. chp = tabp;
  382. }
  383. x += size.cx;
  384. // bail if we hit null terminator
  385. if (pwch ? !*pwch : !*chp)
  386. break;
  387. /* handle tab chars */
  388. while (pwch ? (*pwch == '\t') : (*chp == '\t'))
  389. {
  390. /* print replacement char */
  391. if (show_whitespace)
  392. {
  393. SetTextColor(hdc, fcolws);
  394. ExtTextOut(hdc, x + cx, y, ETO_CLIPPED, &rc, szTabReplace, 1, NULL);
  395. SetTextColor(hdc, fcol);
  396. }
  397. /* advance past the tab */
  398. x += cxTabReplace - (x % cxTabReplace);
  399. if (pwch)
  400. pwch = ++pwchBreak;
  401. else
  402. chp = ++tabp;
  403. }
  404. /* handle space chars */
  405. if (show_whitespace)
  406. {
  407. while (pwch ? (*pwch == ' ') : (*chp == ' '))
  408. {
  409. /* replace space char with visible char */
  410. SetTextColor(hdc, fcolws);
  411. ExtTextOut(hdc, x + cx, y, ETO_CLIPPED, &rc, szSpaceReplace, 1, NULL);
  412. SetTextColor(hdc, fcol);
  413. x += cxSpaceReplace;
  414. if (pwch)
  415. pwch = ++pwchBreak;
  416. else
  417. chp = ++tabp;
  418. }
  419. }
  420. }
  421. }
  422. /* reset colours to original if not default */
  423. if (cd->props.valid & P_FCOLOUR) {
  424. SetTextColor(hdc, fcolOld);
  425. }
  426. if (cd->props.valid & P_BCOLOUR) {
  427. SetBkColor(hdc, bkcolOld);
  428. }
  429. if (cd->props.valid & P_FONT) {
  430. SelectObject(hdc, hfontOld);
  431. }
  432. /* now box cell if marked */
  433. if ((cd->props.valid & P_BOX)) {
  434. if (cd->props.box != 0) {
  435. // rcbox.top = pline->linepos.start;
  436. rcbox.top = y;
  437. rcbox.bottom = rcbox.top + pline->linepos.size;
  438. rcbox.left = ppos->start;
  439. rcbox.right = ppos->start + ppos->size;
  440. gtab_boxcell(hwnd, hdc, &rcbox, &rc, cd->props.box);
  441. }
  442. }
  443. }
  444. /* fetch and paint the specified line */
  445. void
  446. gtab_paintline(HWND hwnd, HDC hdc, lpTable ptab, int line, BOOL show_whitespace)
  447. {
  448. lpCellPos ppos;
  449. int i;
  450. RECT rc;
  451. if (line < ptab->hdr.nrows) {
  452. GetClientRect(hwnd, &rc);
  453. gtab_updateline(hwnd, ptab, line);
  454. for (i = 0; i < ptab->hdr.ncols; i++) {
  455. ppos = &ptab->pcellpos[i];
  456. /* show whitespace iff the flag is set
  457. and we're painting the main text column */
  458. if (ppos->clipstart < ppos->clipend) {
  459. gtab_paintcell(hwnd, hdc, ptab, line, i,
  460. (show_whitespace && (i == 2)));
  461. }
  462. }
  463. rc.top = ptab->pdata[line].linepos.clipstart;
  464. rc.bottom = ptab->pdata[line].linepos.clipend;
  465. rc.left = ppos->clipend;
  466. FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
  467. }
  468. }
  469. void
  470. gtab_paint(HWND hwnd)
  471. {
  472. PAINTSTRUCT ps;
  473. HDC hDC = BeginPaint(hwnd, &ps);
  474. int y, y2, i;
  475. lpTable ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE);
  476. if (!ptab || (ptab->hdr.nrows == 0)) {
  477. FillRect(hDC, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
  478. } else {
  479. /* separator lines between fixed rows/columns
  480. * (ie headers) and the rest - if enabled
  481. */
  482. /* paint here first for good impression,
  483. * and again after to clean up!!
  484. */
  485. if (ptab->hdr.vseparator) {
  486. gtab_vsep(hwnd, ptab, hDC);
  487. }
  488. if (ptab->hdr.hseparator) {
  489. gtab_hsep(hwnd, ptab, hDC);
  490. }
  491. /* paint only the rows that need painting */
  492. for (i = 0; i < ptab->nlines; i++) {
  493. y = ptab->pdata[i].linepos.start;
  494. y2 = y + ptab->pdata[i].linepos.size;
  495. if ( (y <= ps.rcPaint.bottom) &&
  496. (y2 >= ps.rcPaint.top)) {
  497. gtab_paintline(hwnd, hDC, ptab, i, ptab->show_whitespace);
  498. }
  499. }
  500. if (ptab->hdr.vseparator) {
  501. gtab_vsep(hwnd, ptab, hDC);
  502. }
  503. if (ptab->hdr.hseparator) {
  504. gtab_hsep(hwnd, ptab, hDC);
  505. }
  506. if (ptab->selvisible) {
  507. gtab_invertsel(hwnd, ptab, hDC);
  508. }
  509. }
  510. EndPaint(hwnd, &ps);
  511. }
  512. void
  513. gtab_vsep(HWND hwnd, lpTable ptab, HDC hdc)
  514. {
  515. int x;
  516. RECT rc;
  517. if (ptab->hdr.fixedcols < 1) {
  518. return;
  519. }
  520. x = ptab->pcellpos[ptab->hdr.fixedcols - 1].clipend+1;
  521. GetClientRect(hwnd, &rc);
  522. MoveToEx(hdc, x, rc.top, NULL);
  523. LineTo(hdc, x, rc.bottom);
  524. }
  525. void
  526. gtab_hsep(HWND hwnd, lpTable ptab, HDC hdc)
  527. {
  528. int y;
  529. RECT rc;
  530. if (ptab->hdr.fixedrows < 1) {
  531. return;
  532. }
  533. y = ptab->rowheight * ptab->hdr.fixedrows;
  534. GetClientRect(hwnd, &rc);
  535. MoveToEx(hdc, rc.left, y-1, NULL);
  536. LineTo(hdc, rc.right, y-1);
  537. }
  538. /* draw in (inverting) the dotted selection lines for tracking a col width
  539. */
  540. void
  541. gtab_drawvertline(HWND hwnd, lpTable ptab)
  542. {
  543. RECT rc;
  544. HDC hdc;
  545. HPEN hpen;
  546. hdc = GetDC(hwnd);
  547. if (hdc)
  548. {
  549. SetROP2(hdc, R2_XORPEN);
  550. hpen = SelectObject(hdc, hpenDotted);
  551. GetClientRect(hwnd, &rc);
  552. MoveToEx(hdc, ptab->trackline1, rc.top, NULL);
  553. LineTo(hdc, ptab->trackline1, rc.bottom);
  554. if (ptab->trackline2 != -1) {
  555. MoveToEx(hdc, ptab->trackline2, rc.top, NULL);
  556. LineTo(hdc, ptab->trackline2, rc.bottom);
  557. }
  558. SelectObject(hdc, hpen);
  559. ReleaseDC(hwnd, hdc);
  560. }
  561. }
  562. /*
  563. * mark the selected line, if visible, in the style chosen by the
  564. * client app. This can be TM_SOLID, meaning an inversion of
  565. * the whole selected area or TM_FOCUS, meaning, inversion of the first
  566. * cell, and then a dotted focus rectangle for the rest.
  567. *
  568. * this function inverts either style, and so will turn the selection
  569. * both on and off.
  570. */
  571. void
  572. gtab_invertsel(HWND hwnd, lpTable ptab, HDC hdc_in)
  573. {
  574. HDC hdc;
  575. int firstline, lastline;
  576. long startrow, lastrow, toprow, bottomrow;
  577. RECT rc;
  578. int lastcell;
  579. /* get the selection start and end rows ordered vertically */
  580. if (ptab->select.nrows == 0) {
  581. return;
  582. } else if (ptab->select.nrows < 0) {
  583. startrow = ptab->select.startrow + ptab->select.nrows + 1;
  584. lastrow = ptab->select.startrow;
  585. } else {
  586. startrow = ptab->select.startrow;
  587. lastrow = ptab->select.startrow + ptab->select.nrows -1;
  588. }
  589. /* is selected area (or part of it) visible on screen ? */
  590. firstline = gtab_rowtoline(hwnd, ptab, startrow);
  591. lastline = gtab_rowtoline(hwnd, ptab, lastrow);
  592. if (firstline < 0) {
  593. toprow = gtab_linetorow(hwnd, ptab,
  594. ptab->hdr.fixedselectable ? 0: ptab->hdr.fixedrows);
  595. if ((toprow >= startrow) &&
  596. (toprow <= lastrow)) {
  597. firstline = gtab_rowtoline(hwnd, ptab, toprow);
  598. } else {
  599. return;
  600. }
  601. } else {
  602. toprow = 0;
  603. }
  604. if (lastline < 0) {
  605. bottomrow = gtab_linetorow(hwnd, ptab, ptab->nlines-1);
  606. if ((bottomrow <= lastrow) &&
  607. (bottomrow >=startrow)) {
  608. lastline = gtab_rowtoline(hwnd, ptab, bottomrow);
  609. } else {
  610. return;
  611. }
  612. }
  613. rc.top = ptab->pdata[firstline].linepos.clipstart;
  614. rc.bottom = ptab->pdata[lastline].linepos.clipend;
  615. /* selection mode includes a flag TM_FOCUS indicating we should
  616. * use a focus rect instead of the traditional inversion for
  617. * selections in this table. This interferes with multiple backgrnd
  618. * colours less. However we still do inversion for fixedcols.
  619. */
  620. lastcell = (int)(ptab->select.startcell + ptab->select.ncells - 1);
  621. /*
  622. * invert the whole area for TM_SOLID or just the first
  623. * cell for TM_FOCUS
  624. */
  625. rc.left = ptab->pcellpos[ptab->select.startcell].clipstart;
  626. if (ptab->hdr.selectmode & TM_FOCUS) {
  627. rc.right = ptab->pcellpos[ptab->select.startcell].clipend;
  628. }else {
  629. rc.right = ptab->pcellpos[lastcell].clipend;
  630. }
  631. if (hdc_in == NULL) {
  632. hdc = GetDC(hwnd);
  633. if (!hdc)
  634. return;
  635. } else {
  636. hdc = hdc_in;
  637. }
  638. InvertRect(hdc, &rc);
  639. /*
  640. * draw focus rectangle around remaining cells on this line, if there
  641. * are any
  642. */
  643. if (ptab->hdr.selectmode & TM_FOCUS) {
  644. /*
  645. * now this is a real fudge. if we are drawing TM_FOCUS
  646. * selection, and the real top line is off the top of the
  647. * window, then the top of the focus rect will be drawn at
  648. * the top of our window. If we then scroll up one line,
  649. * a new focus rect will be drawn, but the old top of focus
  650. * rect line will still be there as junk on the
  651. * screen. To fix this, we have 2 choices: we undo the selection
  652. * before every scroll (too slow) or we set the focus rect a little
  653. * bigger if the real top line is off-window, so that the top line
  654. * is clipped (as it should be). This latter is what we do here
  655. */
  656. if (toprow > startrow) {
  657. rc.top--;
  658. }
  659. if (ptab->select.ncells > 1) {
  660. rc.left = ptab->pcellpos[ptab->select.startcell+1].clipstart;
  661. rc.right = ptab->pcellpos[lastcell].clipend;
  662. DrawFocusRect(hdc, &rc);
  663. }
  664. }
  665. if (hdc_in == NULL) {
  666. ReleaseDC(hwnd, hdc);
  667. }
  668. }