Posted to tcl by Emiliano at Wed Sep 19 21:13:22 GMT 2007view raw

  1. /*
  2. * tkUnixScrollbar.c --
  3. *
  4. * This file implements the Unix specific portion of the scrollbar
  5. * widget.
  6. *
  7. * Copyright (c) 1996 by Sun Microsystems, Inc.
  8. *
  9. * See the file "license.terms" for information on usage and redistribution of
  10. * this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11. *
  12. * RCS: @(#) $Id: tkUnixScrlbr.c,v 1.4 2005/11/13 21:00:17 dkf Exp $
  13. */
  14.  
  15. #include "tkScrollbar.h"
  16.  
  17. /*
  18. * Minimum slider length, in pixels (designed to make sure that the slider is
  19. * always easy to grab with the mouse).
  20. */
  21.  
  22. #define MIN_SLIDER_LENGTH 5
  23.  
  24. /*
  25. * Declaration of Unix specific scrollbar structure.
  26. */
  27.  
  28. typedef struct UnixScrollbar {
  29. TkScrollbar info; /* Generic scrollbar info. */
  30. GC troughGC; /* For drawing trough. */
  31. GC copyGC; /* Used for copying from pixmap onto screen. */
  32. } UnixScrollbar;
  33.  
  34. /*
  35. * The class procedure table for the scrollbar widget. All fields except size
  36. * are left initialized to NULL, which should happen automatically since the
  37. * variable is declared at this scope.
  38. */
  39.  
  40. Tk_ClassProcs tkpScrollbarProcs = {
  41. sizeof(Tk_ClassProcs) /* size */
  42. };
  43.  
  44. /*
  45. *----------------------------------------------------------------------
  46. *
  47. * TkpCreateScrollbar --
  48. *
  49. * Allocate a new TkScrollbar structure.
  50. *
  51. * Results:
  52. * Returns a newly allocated TkScrollbar structure.
  53. *
  54. * Side effects:
  55. * Registers an event handler for the widget.
  56. *
  57. *----------------------------------------------------------------------
  58. */
  59.  
  60. TkScrollbar *
  61. TkpCreateScrollbar(
  62. Tk_Window tkwin)
  63. {
  64. UnixScrollbar *scrollPtr = (UnixScrollbar *)ckalloc(sizeof(UnixScrollbar));
  65. scrollPtr->troughGC = None;
  66. scrollPtr->copyGC = None;
  67.  
  68. Tk_CreateEventHandler(tkwin,
  69. ExposureMask|StructureNotifyMask|FocusChangeMask,
  70. TkScrollbarEventProc, (ClientData) scrollPtr);
  71.  
  72. return (TkScrollbar *) scrollPtr;
  73. }
  74.  
  75. /*
  76. *--------------------------------------------------------------
  77. *
  78. * TkpDisplayScrollbar --
  79. *
  80. * This procedure redraws the contents of a scrollbar window. It is
  81. * invoked as a do-when-idle handler, so it only runs when there's
  82. * nothing else for the application to do.
  83. *
  84. * Results:
  85. * None.
  86. *
  87. * Side effects:
  88. * Information appears on the screen.
  89. *
  90. *--------------------------------------------------------------
  91. */
  92.  
  93. void
  94. TkpDisplayScrollbar(
  95. ClientData clientData) /* Information about window. */
  96. {
  97. register TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
  98. register Tk_Window tkwin = scrollPtr->tkwin;
  99. XPoint points[7];
  100. Tk_3DBorder border;
  101. int relief, width, elementBorderWidth;
  102. Pixmap pixmap;
  103.  
  104. if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  105. goto done;
  106. }
  107.  
  108. if (scrollPtr->vertical) {
  109. width = Tk_Width(tkwin) - 2*scrollPtr->inset;
  110. } else {
  111. width = Tk_Height(tkwin) - 2*scrollPtr->inset;
  112. }
  113. elementBorderWidth = scrollPtr->elementBorderWidth;
  114. if (elementBorderWidth < 0) {
  115. elementBorderWidth = scrollPtr->borderWidth;
  116. }
  117.  
  118. /*
  119. * In order to avoid screen flashes, this procedure redraws the scrollbar
  120. * in a pixmap, then copies the pixmap to the screen in a single
  121. * operation. This means that there's no point in time where the on-sreen
  122. * image has been cleared.
  123. */
  124.  
  125. pixmap = Tk_GetPixmap(scrollPtr->display, Tk_WindowId(tkwin),
  126. Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  127.  
  128. if (scrollPtr->highlightWidth != 0) {
  129. GC gc;
  130.  
  131. if (scrollPtr->flags & GOT_FOCUS) {
  132. gc = Tk_GCForColor(scrollPtr->highlightColorPtr, pixmap);
  133. } else {
  134. gc = Tk_GCForColor(scrollPtr->highlightBgColorPtr, pixmap);
  135. }
  136. Tk_DrawFocusHighlight(tkwin, gc, scrollPtr->highlightWidth, pixmap);
  137. }
  138. Tk_Draw3DRectangle(tkwin, pixmap, scrollPtr->bgBorder,
  139. scrollPtr->highlightWidth, scrollPtr->highlightWidth,
  140. Tk_Width(tkwin) - 2*scrollPtr->highlightWidth,
  141. Tk_Height(tkwin) - 2*scrollPtr->highlightWidth,
  142. scrollPtr->borderWidth, scrollPtr->relief);
  143. XFillRectangle(scrollPtr->display, pixmap,
  144. ((UnixScrollbar*)scrollPtr)->troughGC,
  145. scrollPtr->inset, scrollPtr->inset,
  146. (unsigned) (Tk_Width(tkwin) - 2*scrollPtr->inset),
  147. (unsigned) (Tk_Height(tkwin) - 2*scrollPtr->inset));
  148.  
  149. /*
  150. * Draw the top or left arrow. The coordinates of the polygon points
  151. * probably seem odd, but they were carefully chosen with respect to X's
  152. * rules for filling polygons. These point choices cause the arrows to
  153. * just fill the narrow dimension of the scrollbar and be properly
  154. * centered.
  155. */
  156.  
  157. if (scrollPtr->activeField == TOP_ARROW) {
  158. border = scrollPtr->activeBorder;
  159. relief = scrollPtr->activeField == TOP_ARROW ? scrollPtr->activeRelief
  160. : TK_RELIEF_RAISED;
  161. } else {
  162. border = scrollPtr->bgBorder;
  163. relief = TK_RELIEF_RAISED;
  164. }
  165.  
  166. GC arrowGC;
  167. int x, y, w, h;
  168. XColor *black;
  169.  
  170. black = Tk_GetColor(scrollPtr->interp, tkwin, "black");
  171. arrowGC = Tk_GCForColor(black, pixmap);
  172.  
  173. /*
  174. * Both vertical and horizontal scrollbars happens to have
  175. * the same coordinates for the initial arrow, so we draw
  176. * it here
  177. */
  178.  
  179. Tk_Fill3DRectangle(tkwin, pixmap, border,
  180. scrollPtr->inset, scrollPtr->inset,
  181. width, width,
  182. elementBorderWidth, relief);
  183.  
  184. w = (width - 2*elementBorderWidth)*0.8;
  185. if (w%2==0) w++;
  186. h = w/2;
  187.  
  188. if (scrollPtr->vertical) {
  189. /* draw the inner arrow
  190. * vertical top
  191. */
  192.  
  193. x = scrollPtr->inset + (width - w)/2;
  194. y = scrollPtr->inset + width/2 + h/2;
  195.  
  196. if (relief != TK_RELIEF_RAISED) {
  197. x++;
  198. y++;
  199. }
  200.  
  201. points[0].x = x;
  202. points[0].y = y;
  203. points[1].x = points[0].x + w/2;
  204. points[1].y = points[0].y - h - 1;
  205. points[2].x = points[0].x + w;
  206. points[2].y = points[0].y;
  207.  
  208. XFillPolygon(scrollPtr->display, pixmap, arrowGC,
  209. points, 3, Convex, CoordModeOrigin);
  210.  
  211. } else {
  212. /* draw the inner arrow
  213. * horizontal left
  214. */
  215.  
  216. x = scrollPtr->inset + (width + h)/2;
  217. y = scrollPtr->inset + (width + w)/2;
  218.  
  219. if (relief != TK_RELIEF_RAISED) {
  220. x++;
  221. y++;
  222. }
  223.  
  224. points[0].x = x;
  225. points[0].y = y - 1;
  226. points[1].x = points[0].x - h;
  227. points[1].y = points[0].y - w/2;
  228. points[2].x = points[0].x;
  229. points[2].y = points[0].y - w + 1;
  230.  
  231. XFillPolygon(scrollPtr->display, pixmap, arrowGC,
  232. points, 3, Convex, CoordModeOrigin);
  233.  
  234. }
  235.  
  236. /*
  237. * Display the bottom or right arrow.
  238. */
  239.  
  240. if (scrollPtr->activeField == BOTTOM_ARROW) {
  241. border = scrollPtr->activeBorder;
  242. relief = scrollPtr->activeField == BOTTOM_ARROW
  243. ? scrollPtr->activeRelief : TK_RELIEF_RAISED;
  244. } else {
  245. border = scrollPtr->bgBorder;
  246. relief = TK_RELIEF_RAISED;
  247. }
  248. if (scrollPtr->vertical) {
  249.  
  250. Tk_Fill3DRectangle(tkwin, pixmap, border,
  251. scrollPtr->inset,
  252. Tk_Height(tkwin) - scrollPtr->arrowLength - scrollPtr->inset + 1,
  253. width, width,
  254. elementBorderWidth, relief);
  255.  
  256. x = scrollPtr->inset + (width - w)/2 + 1;
  257. y = Tk_Height(tkwin) - scrollPtr->inset - (width + h)/2;
  258.  
  259. if (relief != TK_RELIEF_RAISED) {
  260. x++;
  261. y++;
  262. }
  263.  
  264. points[0].x = x;
  265. points[0].y = y;
  266. points[1].x = points[0].x + w/2 - 1;
  267. points[1].y = points[0].y + h;
  268. points[2].x = points[0].x + w - 2;
  269. points[2].y = points[0].y;
  270.  
  271. XFillPolygon(scrollPtr->display, pixmap, arrowGC,
  272. points, 3, Convex, CoordModeOrigin);
  273.  
  274. } else {
  275.  
  276. Tk_Fill3DRectangle(tkwin, pixmap, border,
  277. Tk_Width(tkwin) - scrollPtr->inset - scrollPtr->arrowLength + 1,
  278. scrollPtr->inset, width, width,
  279. elementBorderWidth, relief);
  280.  
  281. x = Tk_Width(tkwin) - scrollPtr->inset - (width + h)/2;
  282. y = scrollPtr->inset + (width - w)/2;
  283.  
  284. if (relief != TK_RELIEF_RAISED) {
  285. x++;
  286. y++;
  287. }
  288.  
  289. points[0].x = x;
  290. points[0].y = y;
  291. points[1].x = points[0].x + h;
  292. points[1].y = points[0].y + w/2;
  293. points[2].x = points[0].x;
  294. points[2].y = points[0].y + w - 1;
  295.  
  296. XFillPolygon(scrollPtr->display, pixmap, arrowGC,
  297. points, 3, Convex, CoordModeOrigin);
  298.  
  299. }
  300.  
  301. Tk_FreeColor(black); /*Release the color of arrow*/
  302. /*release the GC ????*/
  303.  
  304. /*
  305. * Display the slider.
  306. */
  307.  
  308. if (scrollPtr->activeField == SLIDER) {
  309. border = scrollPtr->activeBorder;
  310. relief = scrollPtr->activeField == SLIDER ? scrollPtr->activeRelief
  311. : TK_RELIEF_RAISED;
  312. } else {
  313. border = scrollPtr->bgBorder;
  314. relief = TK_RELIEF_RAISED;
  315. }
  316. if (scrollPtr->vertical) {
  317. Tk_Fill3DRectangle(tkwin, pixmap, border,
  318. scrollPtr->inset, scrollPtr->sliderFirst,
  319. width, scrollPtr->sliderLast - scrollPtr->sliderFirst,
  320. elementBorderWidth, relief);
  321. } else {
  322. Tk_Fill3DRectangle(tkwin, pixmap, border,
  323. scrollPtr->sliderFirst, scrollPtr->inset,
  324. scrollPtr->sliderLast - scrollPtr->sliderFirst, width,
  325. elementBorderWidth, relief);
  326. }
  327.  
  328. /*
  329. * Copy the information from the off-screen pixmap onto the screen, then
  330. * delete the pixmap.
  331. */
  332.  
  333. XCopyArea(scrollPtr->display, pixmap, Tk_WindowId(tkwin),
  334. ((UnixScrollbar*)scrollPtr)->copyGC, 0, 0,
  335. (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0);
  336. Tk_FreePixmap(scrollPtr->display, pixmap);
  337.  
  338. done:
  339. scrollPtr->flags &= ~REDRAW_PENDING;
  340. }
  341.  
  342. /*
  343. *----------------------------------------------------------------------
  344. *
  345. * TkpComputeScrollbarGeometry --
  346. *
  347. * After changes in a scrollbar's size or configuration, this procedure
  348. * recomputes various geometry information used in displaying the
  349. * scrollbar.
  350. *
  351. * Results:
  352. * None.
  353. *
  354. * Side effects:
  355. * The scrollbar will be displayed differently.
  356. *
  357. *----------------------------------------------------------------------
  358. */
  359.  
  360. extern void
  361. TkpComputeScrollbarGeometry(
  362. register TkScrollbar *scrollPtr)
  363. /* Scrollbar whose geometry may have
  364. * changed. */
  365. {
  366. int width, fieldLength;
  367.  
  368. if (scrollPtr->highlightWidth < 0) {
  369. scrollPtr->highlightWidth = 0;
  370. }
  371. scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth;
  372. width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin)
  373. : Tk_Height(scrollPtr->tkwin);
  374. scrollPtr->arrowLength = width - 2*scrollPtr->inset + 1;
  375. fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
  376. : Tk_Width(scrollPtr->tkwin))
  377. - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  378. if (fieldLength < 0) {
  379. fieldLength = 0;
  380. }
  381. scrollPtr->sliderFirst = fieldLength*scrollPtr->firstFraction;
  382. scrollPtr->sliderLast = fieldLength*scrollPtr->lastFraction;
  383.  
  384. /*
  385. * Adjust the slider so that some piece of it is always displayed in the
  386. * scrollbar and so that it has at least a minimal width (so it can be
  387. * grabbed with the mouse).
  388. */
  389.  
  390. if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) {
  391. scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth;
  392. }
  393. if (scrollPtr->sliderFirst < 0) {
  394. scrollPtr->sliderFirst = 0;
  395. }
  396. if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
  397. + MIN_SLIDER_LENGTH)) {
  398. scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH;
  399. }
  400. if (scrollPtr->sliderLast > fieldLength) {
  401. scrollPtr->sliderLast = fieldLength;
  402. }
  403. scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset;
  404. scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset;
  405.  
  406. /*
  407. * Register the desired geometry for the window (leave enough space for
  408. * the two arrows plus a minimum-size slider, plus border around the whole
  409. * window, if any). Then arrange for the window to be redisplayed.
  410. */
  411.  
  412. if (scrollPtr->vertical) {
  413. Tk_GeometryRequest(scrollPtr->tkwin,
  414. scrollPtr->width + 2*scrollPtr->inset,
  415. 2*(scrollPtr->arrowLength + scrollPtr->borderWidth
  416. + scrollPtr->inset));
  417. } else {
  418. Tk_GeometryRequest(scrollPtr->tkwin,
  419. 2*(scrollPtr->arrowLength + scrollPtr->borderWidth
  420. + scrollPtr->inset), scrollPtr->width + 2*scrollPtr->inset);
  421. }
  422. Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset);
  423. }
  424.  
  425. /*
  426. *----------------------------------------------------------------------
  427. *
  428. * TkpDestroyScrollbar --
  429. *
  430. * Free data structures associated with the scrollbar control.
  431. *
  432. * Results:
  433. * None.
  434. *
  435. * Side effects:
  436. * Frees the GCs associated with the scrollbar.
  437. *
  438. *----------------------------------------------------------------------
  439. */
  440.  
  441. void
  442. TkpDestroyScrollbar(
  443. TkScrollbar *scrollPtr)
  444. {
  445. UnixScrollbar *unixScrollPtr = (UnixScrollbar *)scrollPtr;
  446.  
  447. if (unixScrollPtr->troughGC != None) {
  448. Tk_FreeGC(scrollPtr->display, unixScrollPtr->troughGC);
  449. }
  450. if (unixScrollPtr->copyGC != None) {
  451. Tk_FreeGC(scrollPtr->display, unixScrollPtr->copyGC);
  452. }
  453. }
  454.  
  455. /*
  456. *----------------------------------------------------------------------
  457. *
  458. * TkpConfigureScrollbar --
  459. *
  460. * This procedure is called after the generic code has finished
  461. * processing configuration options, in order to configure platform
  462. * specific options.
  463. *
  464. * Results:
  465. * None.
  466. *
  467. * Side effects:
  468. * Configuration info may get changed.
  469. *
  470. *----------------------------------------------------------------------
  471. */
  472.  
  473. void
  474. TkpConfigureScrollbar(
  475. register TkScrollbar *scrollPtr)
  476. /* Information about widget; may or may not
  477. * already have values for some fields. */
  478. {
  479. XGCValues gcValues;
  480. GC new;
  481. UnixScrollbar *unixScrollPtr = (UnixScrollbar *) scrollPtr;
  482.  
  483. Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder);
  484.  
  485. gcValues.foreground = scrollPtr->troughColorPtr->pixel;
  486. new = Tk_GetGC(scrollPtr->tkwin, GCForeground, &gcValues);
  487. if (unixScrollPtr->troughGC != None) {
  488. Tk_FreeGC(scrollPtr->display, unixScrollPtr->troughGC);
  489. }
  490. unixScrollPtr->troughGC = new;
  491. if (unixScrollPtr->copyGC == None) {
  492. gcValues.graphics_exposures = False;
  493. unixScrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures,
  494. &gcValues);
  495. }
  496. }
  497.  
  498. /*
  499. *--------------------------------------------------------------
  500. *
  501. * TkpScrollbarPosition --
  502. *
  503. * Determine the scrollbar element corresponding to a given position.
  504. *
  505. * Results:
  506. * One of TOP_ARROW, TOP_GAP, etc., indicating which element of the
  507. * scrollbar covers the position given by (x, y). If (x,y) is outside the
  508. * scrollbar entirely, then OUTSIDE is returned.
  509. *
  510. * Side effects:
  511. * None.
  512. *
  513. *--------------------------------------------------------------
  514. */
  515.  
  516. int
  517. TkpScrollbarPosition(
  518. register TkScrollbar *scrollPtr,
  519. /* Scrollbar widget record. */
  520. int x, int y) /* Coordinates within scrollPtr's window. */
  521. {
  522. int length, width, tmp;
  523.  
  524. if (scrollPtr->vertical) {
  525. length = Tk_Height(scrollPtr->tkwin);
  526. width = Tk_Width(scrollPtr->tkwin);
  527. } else {
  528. tmp = x;
  529. x = y;
  530. y = tmp;
  531. length = Tk_Width(scrollPtr->tkwin);
  532. width = Tk_Height(scrollPtr->tkwin);
  533. }
  534.  
  535. if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset))
  536. || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) {
  537. return OUTSIDE;
  538. }
  539.  
  540. /*
  541. * All of the calculations in this procedure mirror those in
  542. * TkpDisplayScrollbar. Be sure to keep the two consistent.
  543. */
  544.  
  545. if (y < (scrollPtr->inset + scrollPtr->arrowLength)) {
  546. return TOP_ARROW;
  547. }
  548. if (y < scrollPtr->sliderFirst) {
  549. return TOP_GAP;
  550. }
  551. if (y < scrollPtr->sliderLast) {
  552. return SLIDER;
  553. }
  554. if (y >= (length - (scrollPtr->arrowLength + scrollPtr->inset))) {
  555. return BOTTOM_ARROW;
  556. }
  557. return BOTTOM_GAP;
  558. }
  559.  
  560. /*
  561. * Local Variables:
  562. * mode: c
  563. * c-basic-offset: 4
  564. * fill-column: 78
  565. * End:
  566. */