Leaked source code of windows server 2003
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.

782 lines
20 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 < 0 || line >= ptab->nlines)
  60. return;
  61. pline = &ptab->pdata[line];
  62. cell1 = 0;
  63. cellcount = 0;
  64. for (i = 0; i < ptab->hdr.ncols; i++) {
  65. ppos = &ptab->pcellpos[i];
  66. cd = &pline->pdata[i];
  67. if (ppos->clipstart < ppos->clipend) {
  68. if ((cd->flags & CELL_VALID) == 0) {
  69. /* add a cell to the list to be updated*/
  70. if (cellcount++ == 0) {
  71. cell1 = i;
  72. }
  73. } else {
  74. /* this cell already valid - so end of
  75. * a contig block. if the contig
  76. * block just ended contained cells to update,
  77. * do it now
  78. */
  79. if (cellcount > 0) {
  80. gtab_updatecontig(hwnd, ptab,
  81. line, cell1, cellcount);
  82. }
  83. cellcount = 0;
  84. }
  85. }
  86. /* cell not visible - end of a contig block. If it was a
  87. * non-empty contig block, then update it now.
  88. */
  89. if (cellcount > 0) {
  90. gtab_updatecontig(hwnd, ptab, line, cell1, cellcount);
  91. cellcount = 0;
  92. }
  93. }
  94. if (cellcount > 0) {
  95. gtab_updatecontig(hwnd, ptab, line, cell1, cellcount);
  96. cellcount = 0;
  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, BOOL fPrinting)
  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. DWORD etoFlags = ETO_CLIPPED;
  256. fcol = 0; bkcol = 0; /* eliminate spurious diagnostic, generate worse code */
  257. hfont = 0; /* eliminate spurious diagnostic, generate worse code */
  258. /* init pointers to cell text and properties */
  259. pline = &ptab->pdata[line];
  260. cd = &pline->pdata[cell];
  261. ppos = &ptab->pcellpos[cell];
  262. /* draw gutter */
  263. rc.top = pline->linepos.clipstart;
  264. rc.bottom = pline->linepos.clipend;
  265. rc.left = (cell > 0) ? ptab->pcellpos[cell - 1].clipend : 0;
  266. rc.right = ppos->clipstart;
  267. if (cell > ptab->hdr.fixedcols && ptab->hdr.fixedcols < ptab->hdr.ncols) {
  268. rc.left = max(rc.left, ptab->pcellpos[ptab->hdr.fixedcols].clipstart);
  269. }
  270. if (ptab->hdr.fixedcols > 0 && cell == ptab->hdr.fixedcols) {
  271. rc.right--;
  272. }
  273. if (!fPrinting && rc.right > rc.left) {
  274. FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
  275. }
  276. /* clip all output to this rectangle */
  277. rc.top = pline->linepos.clipstart;
  278. rc.bottom = pline->linepos.clipend;
  279. rc.left = ppos->clipstart;
  280. rc.right = ppos->clipend;
  281. /* check cell properties and colours */
  282. if (cd->props.valid & P_ALIGN) {
  283. align = cd->props.alignment;
  284. } else {
  285. align = P_LEFT;
  286. }
  287. //$ FUTURE: This prevents the user's font from being used when printing.
  288. // For now, that's better than having the output be really tiny, but this
  289. // needs to be fixed right eventually.
  290. if (!fPrinting && cd->props.valid & P_FONT) {
  291. hfontOld = SelectObject(hdc, cd->props.hFont);
  292. }
  293. // get y offset to center text vertically within cell
  294. GetTextMetrics(hdc, &tm);
  295. yOfs = (rc.bottom - rc.top - tm.tmHeight) / 2;
  296. /* set replacement chars and char widths */
  297. cxSpaceReplace = GetTextExtent(hdc, szSpaceReplace, 1);
  298. cxTabReplace = cxSpaceReplace * ptab->tabchars;
  299. /* set colours if not default */
  300. if (!fPrinting) {
  301. if (cd->props.valid & P_FCOLOUR) {
  302. fcol = cd->props.forecolour;
  303. fcolOld = SetTextColor(hdc, fcol);
  304. }
  305. if (cd->props.valid & P_FCOLOURWS) {
  306. fcolws = cd->props.forecolourws;
  307. }
  308. else {
  309. fcolws = fcol;
  310. }
  311. if (cd->props.valid & P_BCOLOUR) {
  312. /* there is a non-default background colour.
  313. * create a brush and fill the entire cell with it
  314. */
  315. hbr = CreateSolidBrush(cd->props.backcolour);
  316. if (hbr)
  317. {
  318. FillRect(hdc, &rc, hbr);
  319. DeleteObject(hbr);
  320. }
  321. /* also set colour as background colour for the text */
  322. bkcolOld = SetBkColor(hdc, cd->props.backcolour);
  323. }
  324. }
  325. /* calc offset of text within cell for right-align or centering */
  326. if (align == P_LEFT) {
  327. cx = ptab->avewidth/2;
  328. } else {
  329. cx = 0;
  330. if (cd->pwzText) {
  331. GetTextExtentPoint32W(hdc, cd->pwzText, wcslen(cd->pwzText), &size);
  332. } else if (cd->ptext) {
  333. GetTextExtentPoint32A(hdc, cd->ptext, lstrlen(cd->ptext), &size);
  334. }
  335. cx = size.cx;
  336. if (align == P_CENTRE) {
  337. cx = (ppos->size - cx) / 2;
  338. } else {
  339. cx = ppos->size - cx - (ptab->avewidth/2);
  340. }
  341. }
  342. cx += ppos->start;
  343. /* expand tabs on output and show whitespace on output */
  344. x = 0;
  345. y = pline->linepos.start + yOfs;
  346. /* set search string for strpbrk fn;
  347. don't search for space chars unless we're showing whitespace */
  348. if (!show_whitespace)
  349. {
  350. szCharSet[1] = '\0';
  351. wzCharSet[1] = '\0';
  352. }
  353. // determine the string to display (ansi/unicode). if we have a string
  354. // and it's not empty, then loop and display it.
  355. chp = cd->ptext;
  356. pwch = cd->pwzText;
  357. if (pwch ? *pwch : (chp && *chp))
  358. {
  359. while (TRUE)
  360. {
  361. if (pwch)
  362. {
  363. pwchBreak = wcspbrk(pwch, wzCharSet);
  364. if (!pwchBreak)
  365. pwchBreak = pwch + wcslen(pwch);
  366. }
  367. else
  368. {
  369. tabp = My_mbspbrk(chp, szCharSet);
  370. if (!tabp)
  371. tabp = chp + lstrlen(chp);
  372. }
  373. /* perform output up to tab/space char */
  374. if (pwch)
  375. ExtTextOutW(hdc, x+cx, y, etoFlags, &rc, pwch, (UINT)(pwchBreak-pwch), NULL);
  376. else
  377. ExtTextOutA(hdc, x+cx, y, etoFlags, &rc, chp, (UINT)(tabp-chp), NULL);
  378. /* advance past the tab */
  379. if (pwch)
  380. {
  381. GetTextExtentPoint32W(hdc, pwch, (UINT)(pwchBreak - pwch), &size);
  382. pwch = pwchBreak;
  383. }
  384. else
  385. {
  386. GetTextExtentPoint32A(hdc, chp, (UINT)(tabp - chp), &size);
  387. chp = tabp;
  388. }
  389. x += size.cx;
  390. // bail if we hit null terminator
  391. if (pwch ? !*pwch : !*chp)
  392. break;
  393. /* handle tab chars */
  394. while (pwch ? (*pwch == '\t') : (*chp == '\t'))
  395. {
  396. /* print replacement char */
  397. if (show_whitespace)
  398. {
  399. if (!fPrinting)
  400. SetTextColor(hdc, fcolws);
  401. ExtTextOut(hdc, x + cx, y, etoFlags, &rc, szTabReplace, 1, NULL);
  402. if (!fPrinting)
  403. SetTextColor(hdc, fcol);
  404. }
  405. /* advance past the tab */
  406. if (cxTabReplace > 0)
  407. x += cxTabReplace - (x % cxTabReplace);
  408. if (pwch)
  409. pwch = ++pwchBreak;
  410. else
  411. chp = ++tabp;
  412. }
  413. /* handle space chars */
  414. if (show_whitespace)
  415. {
  416. while (pwch ? (*pwch == ' ') : (*chp == ' '))
  417. {
  418. /* replace space char with visible char */
  419. if (!fPrinting)
  420. SetTextColor(hdc, fcolws);
  421. ExtTextOut(hdc, x + cx, y, etoFlags, &rc, szSpaceReplace, 1, NULL);
  422. if (!fPrinting)
  423. SetTextColor(hdc, fcol);
  424. x += cxSpaceReplace;
  425. if (pwch)
  426. pwch = ++pwchBreak;
  427. else
  428. chp = ++tabp;
  429. }
  430. }
  431. }
  432. }
  433. /* reset colours to original if not default */
  434. if (!fPrinting) {
  435. if (cd->props.valid & P_FCOLOUR) {
  436. SetTextColor(hdc, fcolOld);
  437. }
  438. if (cd->props.valid & P_BCOLOUR) {
  439. SetBkColor(hdc, bkcolOld);
  440. }
  441. if (cd->props.valid & P_FONT) {
  442. SelectObject(hdc, hfontOld);
  443. }
  444. }
  445. /* now box cell if marked */
  446. if (!fPrinting) {
  447. if ((cd->props.valid & P_BOX)) {
  448. if (cd->props.box != 0) {
  449. // rcbox.top = pline->linepos.start;
  450. rcbox.top = y;
  451. rcbox.bottom = rcbox.top + pline->linepos.size;
  452. rcbox.left = ppos->start;
  453. rcbox.right = ppos->start + ppos->size;
  454. gtab_boxcell(hwnd, hdc, &rcbox, &rc, cd->props.box);
  455. }
  456. }
  457. }
  458. }
  459. /* fetch and paint the specified line */
  460. void
  461. gtab_paintline(HWND hwnd, HDC hdc, lpTable ptab, int line, BOOL show_whitespace, BOOL fPrinting)
  462. {
  463. lpCellPos ppos = NULL;
  464. int i;
  465. RECT rc;
  466. if (line < 0 || line >= ptab->nlines)
  467. return;
  468. if (!fPrinting)
  469. GetClientRect(hwnd, &rc);
  470. gtab_updateline(hwnd, ptab, line);
  471. for (i = 0; i < ptab->hdr.ncols; i++) {
  472. ppos = &ptab->pcellpos[i];
  473. /* show whitespace iff the flag is set
  474. and we're painting the main text column */
  475. if (ppos->clipstart < ppos->clipend) {
  476. gtab_paintcell(hwnd, hdc, ptab, line, i,
  477. (show_whitespace && (i == 2)), fPrinting);
  478. }
  479. }
  480. if (!fPrinting && ppos) {
  481. rc.top = ptab->pdata[line].linepos.clipstart;
  482. rc.bottom = ptab->pdata[line].linepos.clipend;
  483. rc.left = ppos->clipend;
  484. FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
  485. }
  486. }
  487. void
  488. gtab_paint(HWND hwnd)
  489. {
  490. PAINTSTRUCT ps;
  491. int y, y2, i;
  492. HDC hDC = BeginPaint(hwnd, &ps);
  493. lpTable ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE);
  494. if (!ptab) {
  495. FillRect(hDC, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
  496. } else {
  497. /* separator lines between fixed rows/columns
  498. * (ie headers) and the rest - if enabled
  499. */
  500. /* paint here first for good impression,
  501. * and again after to clean up!!
  502. */
  503. if (ptab->hdr.vseparator) {
  504. gtab_vsep(hwnd, ptab, hDC);
  505. }
  506. if (ptab->hdr.hseparator) {
  507. gtab_hsep(hwnd, ptab, hDC);
  508. }
  509. /* paint only the rows that need painting */
  510. for (i = 0; i < ptab->nlines; i++) {
  511. y = ptab->pdata[i].linepos.start;
  512. y2 = y + ptab->pdata[i].linepos.size;
  513. if ( (y <= ps.rcPaint.bottom) &&
  514. (y2 >= ps.rcPaint.top)) {
  515. gtab_paintline(hwnd, hDC, ptab, i, ptab->show_whitespace, FALSE);
  516. }
  517. }
  518. if (ptab->hdr.vseparator) {
  519. gtab_vsep(hwnd, ptab, hDC);
  520. }
  521. if (ptab->hdr.hseparator) {
  522. gtab_hsep(hwnd, ptab, hDC);
  523. }
  524. if (ptab->selvisible) {
  525. gtab_invertsel(hwnd, ptab, hDC);
  526. }
  527. }
  528. EndPaint(hwnd, &ps);
  529. }
  530. void
  531. gtab_vsep(HWND hwnd, lpTable ptab, HDC hdc)
  532. {
  533. int x;
  534. RECT rc;
  535. if (ptab->hdr.fixedcols < 1) {
  536. return;
  537. }
  538. x = ptab->pcellpos[ptab->hdr.fixedcols - 1].clipend+1;
  539. GetClientRect(hwnd, &rc);
  540. MoveToEx(hdc, x, rc.top, NULL);
  541. LineTo(hdc, x, rc.bottom);
  542. }
  543. void
  544. gtab_hsep(HWND hwnd, lpTable ptab, HDC hdc)
  545. {
  546. int y;
  547. RECT rc;
  548. if (ptab->hdr.fixedrows < 1) {
  549. return;
  550. }
  551. y = ptab->rowheight * ptab->hdr.fixedrows;
  552. GetClientRect(hwnd, &rc);
  553. MoveToEx(hdc, rc.left, y-1, NULL);
  554. LineTo(hdc, rc.right, y-1);
  555. }
  556. /* draw in (inverting) the dotted selection lines for tracking a col width
  557. */
  558. void
  559. gtab_drawvertline(HWND hwnd, lpTable ptab)
  560. {
  561. RECT rc;
  562. HDC hdc;
  563. HPEN hpen;
  564. hdc = GetDC(hwnd);
  565. if (hdc)
  566. {
  567. SetROP2(hdc, R2_XORPEN);
  568. hpen = SelectObject(hdc, hpenDotted);
  569. GetClientRect(hwnd, &rc);
  570. MoveToEx(hdc, ptab->trackline1, rc.top, NULL);
  571. LineTo(hdc, ptab->trackline1, rc.bottom);
  572. if (ptab->trackline2 != -1) {
  573. MoveToEx(hdc, ptab->trackline2, rc.top, NULL);
  574. LineTo(hdc, ptab->trackline2, rc.bottom);
  575. }
  576. SelectObject(hdc, hpen);
  577. ReleaseDC(hwnd, hdc);
  578. }
  579. }
  580. /*
  581. * mark the selected line, if visible, in the style chosen by the
  582. * client app. This can be TM_SOLID, meaning an inversion of
  583. * the whole selected area or TM_FOCUS, meaning, inversion of the first
  584. * cell, and then a dotted focus rectangle for the rest.
  585. *
  586. * this function inverts either style, and so will turn the selection
  587. * both on and off.
  588. */
  589. void
  590. gtab_invertsel(HWND hwnd, lpTable ptab, HDC hdc_in)
  591. {
  592. HDC hdc;
  593. int firstline, lastline;
  594. long startrow, lastrow, toprow, bottomrow;
  595. RECT rc;
  596. int lastcell;
  597. /* get the selection start and end rows ordered vertically */
  598. if (ptab->select.nrows == 0) {
  599. return;
  600. } else if (ptab->select.nrows < 0) {
  601. startrow = ptab->select.startrow + ptab->select.nrows + 1;
  602. lastrow = ptab->select.startrow;
  603. } else {
  604. startrow = ptab->select.startrow;
  605. lastrow = ptab->select.startrow + ptab->select.nrows -1;
  606. }
  607. /* is selected area (or part of it) visible on screen ? */
  608. firstline = gtab_rowtoline(hwnd, ptab, startrow);
  609. lastline = gtab_rowtoline(hwnd, ptab, lastrow);
  610. if (firstline < 0) {
  611. toprow = gtab_linetorow(hwnd, ptab,
  612. ptab->hdr.fixedselectable ? 0: ptab->hdr.fixedrows);
  613. if ((toprow >= startrow) &&
  614. (toprow <= lastrow)) {
  615. firstline = gtab_rowtoline(hwnd, ptab, toprow);
  616. } else {
  617. return;
  618. }
  619. } else {
  620. toprow = 0;
  621. }
  622. if (lastline < 0) {
  623. bottomrow = gtab_linetorow(hwnd, ptab, ptab->nlines-1);
  624. if ((bottomrow <= lastrow) &&
  625. (bottomrow >=startrow)) {
  626. lastline = gtab_rowtoline(hwnd, ptab, bottomrow);
  627. } else {
  628. return;
  629. }
  630. }
  631. rc.top = ptab->pdata[firstline].linepos.clipstart;
  632. rc.bottom = ptab->pdata[lastline].linepos.clipend;
  633. /* selection mode includes a flag TM_FOCUS indicating we should
  634. * use a focus rect instead of the traditional inversion for
  635. * selections in this table. This interferes with multiple backgrnd
  636. * colours less. However we still do inversion for fixedcols.
  637. */
  638. lastcell = (int)(ptab->select.startcell + ptab->select.ncells - 1);
  639. /*
  640. * invert the whole area for TM_SOLID or just the first
  641. * cell for TM_FOCUS
  642. */
  643. rc.left = ptab->pcellpos[ptab->select.startcell].clipstart;
  644. if (ptab->hdr.selectmode & TM_FOCUS) {
  645. rc.right = ptab->pcellpos[ptab->select.startcell].clipend;
  646. }else {
  647. rc.right = ptab->pcellpos[lastcell].clipend;
  648. }
  649. if (hdc_in == NULL) {
  650. hdc = GetDC(hwnd);
  651. if (!hdc)
  652. return;
  653. } else {
  654. hdc = hdc_in;
  655. }
  656. InvertRect(hdc, &rc);
  657. /*
  658. * draw focus rectangle around remaining cells on this line, if there
  659. * are any
  660. */
  661. if (ptab->hdr.selectmode & TM_FOCUS) {
  662. /*
  663. * now this is a real fudge. if we are drawing TM_FOCUS
  664. * selection, and the real top line is off the top of the
  665. * window, then the top of the focus rect will be drawn at
  666. * the top of our window. If we then scroll up one line,
  667. * a new focus rect will be drawn, but the old top of focus
  668. * rect line will still be there as junk on the
  669. * screen. To fix this, we have 2 choices: we undo the selection
  670. * before every scroll (too slow) or we set the focus rect a little
  671. * bigger if the real top line is off-window, so that the top line
  672. * is clipped (as it should be). This latter is what we do here
  673. */
  674. if (toprow > startrow) {
  675. rc.top--;
  676. }
  677. if (ptab->select.ncells > 1) {
  678. rc.left = ptab->pcellpos[ptab->select.startcell+1].clipstart;
  679. rc.right = ptab->pcellpos[lastcell].clipend;
  680. DrawFocusRect(hdc, &rc);
  681. }
  682. }
  683. if (hdc_in == NULL) {
  684. ReleaseDC(hwnd, hdc);
  685. }
  686. }