X11 Work Bench Toolkit  1.0
window_dressing.c
Go to the documentation of this file.
1 // _ _ _ _ //
3 // __ __(_) _ __ __| | ___ __ __ __| | _ __ ___ ___ ___ (_) _ __ __ _ ___ //
4 // \ \ /\ / /| || '_ \ / _` | / _ \\ \ /\ / / / _` || '__|/ _ \/ __|/ __|| || '_ \ / _` | / __| //
5 // \ V V / | || | | || (_| || (_) |\ V V / | (_| || | | __/\__ \\__ \| || | | || (_| | _| (__ //
6 // \_/\_/ |_||_| |_| \__,_| \___/ \_/\_/_____\__,_||_| \___||___/|___/|_||_| |_| \__, |(_)\___| //
7 // |_____| |___/ //
8 // //
9 // Utilities for painting and handling standard components of windows //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-2016 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17  all rights reserved
18 
19  DISCLAIMER: The X11workbench application and toolkit software are supplied
20  'as-is', with no warranties, either implied or explicit.
21  Any claims to alleged functionality or features should be
22  considered 'preliminary', and might not function as advertised.
23 
24  BSD-like license:
25 
26  There is no restriction as to what you can do with this software, so long
27  as you include the above copyright notice and DISCLAIMER for any distributed
28  work that is equal to or derived from this one, along with this paragraph
29  that explains the terms of the license if the source is also being made
30  available. A "derived work" describes a work that uses a significant portion
31  of the source files or algorithms that are included with this one.
32  Specifically excluded from this are files that were generated by the software,
33  or anything that is included with the software that is part of another package
34  (such as files that were created or added during the 'configure' process).
35  Specifically included is the use of part or all of any of the X11 workbench
36  toolkit source or header files in your distributed application. If you do not
37  ship the source, the above copyright statement is still required to be placed
38  in a reasonably prominent place, such as documentation, splash screens, and/or
39  'about the application' dialog boxes.
40 
41  Use and distribution are in accordance with GPL, LGPL, and/or the above
42  BSD-like license. See COPYING and README files for more information.
43 
44 
45  Additional information at http://sourceforge.net/projects/X11workbench
46 
47 ******************************************************************************/
48 
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <unistd.h>
55 #include <memory.h>
56 #include <string.h>
57 #include <strings.h>
58 #include <errno.h>
59 #include <X11/Xlib.h>
60 
61 #include "window_dressing.h"
62 #include "pixmap_helper.h"
63 #include "conf_help.h"
64 #include "draw_text.h"
65 
66 
67 static XColor clrScrollFG,
68  clrScrollBG,
69  clrScrollAFG,
70  clrScrollABG,
71  clrScrollHFG,
72  clrScrollHBG,
73  clrScrollBD,
74  clrScrollBD2,
75  clrScrollBD3;
76 
79 static int iInitScrollColorFlag = 0;
80 
83 #define LOAD_COLOR0(X,Y) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) > 0) { }
84 
86 #define LOAD_COLOR(X,Y,Z) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) <= 0){ WB_WARN_PRINT("%s - WARNING: can't find color %s, using default value %s\n", __FUNCTION__, X, Z); strcpy(Y,Z); }
87 
90 static void CheckInitScrollColors(void)
91 {
92  // TODO: consider freeing the colors with XFreeColors on exit
93  // TODO: consider not even bothering with 'XAllocColor' and just use PXM_RGBToPixel()
94 
96  {
97  static const char *szBorder2="#FFFFFF", *szBorder2W="#C8C6C0", *szBorder3="#9C9A94"; // for 3D borders
98  char szFG[16], szBG[16], szBD[16], szHFG[16], szHBG[16], szAFG[16], szABG[16]; // must be at least 14 characters
99  static const char szFGName[]="Scrollbar.foreground";
100  static const char szBGName[]="Scrollbar.background";
101  static const char szHFGName[]="Scrollbar.highlightForeground";
102  static const char szHBGName[]="Scrollbar.highlightBackground";
103  static const char szAFGName[]="Scrollbar.activeForeground";
104  static const char szABGName[]="Scrollbar.activeBackground";
105  static const char szBDName[]="Scrollbar.border";
106 
107  Colormap colormap = DefaultColormap(WBGetDefaultDisplay(), DefaultScreen(WBGetDefaultDisplay()));
108 
109 
110  LOAD_COLOR0(szFGName,szFG) else LOAD_COLOR0("*Dialog.foreground",szFG) else LOAD_COLOR0("*Form.foreground", szFG)
111  else LOAD_COLOR0("*WmDialogShell.foreground",szFG) else LOAD_COLOR0("*WmForm.foreground", szFG)
112  else LOAD_COLOR("*foreground", szFG, "#000000");
113 
114  LOAD_COLOR0(szBGName,szBG) else LOAD_COLOR0("*Dialog.background",szBG) else LOAD_COLOR0("*Form.background", szBG)
115  else LOAD_COLOR0("*WmDialogShell.background",szBG)
116  else LOAD_COLOR("*WmForm.background", szBG, "#dcdad5"); // default for gnome is dcdad5
117 
118  LOAD_COLOR(szHFGName,szHFG,szFG);
119  LOAD_COLOR(szHBGName,szHBG,szBG);
120  LOAD_COLOR(szAFGName,szAFG,szFG);
121  LOAD_COLOR(szABGName,szABG,szBG);
122 
123  LOAD_COLOR0(szBDName,szBD) else LOAD_COLOR0("*Dialog.border",szBD) else LOAD_COLOR0("*Form.border", szBD)
124  else LOAD_COLOR0("*WmDialogShell.border",szBD) else LOAD_COLOR0("*WmForm.border", szBD)
125  else LOAD_COLOR0("*borderColor", szBD)
126  else LOAD_COLOR("*border", szBD, "black"); // default for gnome
127 
128  XParseColor(WBGetDefaultDisplay(), colormap, szFG, &clrScrollFG);
129  XAllocColor(WBGetDefaultDisplay(), colormap, &clrScrollFG);
130  XParseColor(WBGetDefaultDisplay(), colormap, szBG, &clrScrollBG);
131  XAllocColor(WBGetDefaultDisplay(), colormap, &clrScrollBG);
132  XParseColor(WBGetDefaultDisplay(), colormap, szAFG, &clrScrollAFG);
133  XAllocColor(WBGetDefaultDisplay(), colormap, &clrScrollAFG);
134  XParseColor(WBGetDefaultDisplay(), colormap, szABG, &clrScrollABG);
135  XAllocColor(WBGetDefaultDisplay(), colormap, &clrScrollABG);
136  XParseColor(WBGetDefaultDisplay(), colormap, szHFG, &clrScrollHFG);
137  XAllocColor(WBGetDefaultDisplay(), colormap, &clrScrollHFG);
138  XParseColor(WBGetDefaultDisplay(), colormap, szHBG, &clrScrollHBG);
139  XAllocColor(WBGetDefaultDisplay(), colormap, &clrScrollHBG);
140  XParseColor(WBGetDefaultDisplay(), colormap, szBD, &clrScrollBD);
141 
142  // 3D border colors for now these are hard-coded - later derive them from FG and BG colors
143  if(clrScrollBG.red >= 60000 && clrScrollBG.green >= 60000 &&
144  clrScrollBG.blue >= 60000) // note see man page on XColor, values 0 through 65535 for RGB
145  {
146  XParseColor(WBGetDefaultDisplay(), colormap, szBorder2W, &clrScrollBD2);
147  }
148  else
149  {
150  XParseColor(WBGetDefaultDisplay(), colormap, szBorder2, &clrScrollBD2);
151  }
152 
153  XAllocColor(WBGetDefaultDisplay(), colormap, &clrScrollBD2);
154  XParseColor(WBGetDefaultDisplay(), colormap, szBorder3, &clrScrollBD3);
155  XAllocColor(WBGetDefaultDisplay(), colormap, &clrScrollBD3);
156 
158  }
159 }
160 
161 
164 static unsigned char isqrt(unsigned char iVal)
165 {
166 unsigned char aAnswers[256] =
167 {
168  0,1,1,2,2,2,2,3,3,3,3,3,3,4,4,4,
169  4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,6,
170  6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,
171  7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,
172  8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,
173  9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,
174  10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,
175  11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
176  11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,
177  12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,
178  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
179  13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,
180  14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
181  14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,
182  15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
183  15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16
184 };
185 
186 
187  return aAnswers[iVal & 0xff];
188 }
189 
192 static unsigned char icos(unsigned char iVal)
193 {
194 unsigned char aAnswers[256] =
195 {
196 255,255,255,255,255,255,255,255,255,255,255,254,254,254,254,254,
197 254,254,253,253,253,253,253,252,252,252,252,252,251,251,251,250,
198 250,250,249,249,249,248,248,248,247,247,247,246,246,245,245,244,
199 244,244,243,243,242,242,241,241,240,240,239,238,238,237,237,236,
200 236,235,234,234,233,232,232,231,231,230,229,228,228,227,226,226,
201 225,224,223,223,222,221,220,220,219,218,217,216,215,215,214,213,
202 212,211,210,209,208,208,207,206,205,204,203,202,201,200,199,198,
203 197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,181,
204 180,179,178,177,176,175,174,172,171,170,169,168,167,165,164,163,
205 162,161,159,158,157,156,154,153,152,151,149,148,147,146,144,143,
206 142,140,139,138,136,135,134,132,131,130,128,127,126,124,123,122,
207 120,119,117,116,115,113,112,110,109,108,106,105,103,102,100,99,
208 98,96,95,93,92,90,89,87,86,84,83,81,80,79,77,76,
209 74,73,71,70,68,67,65,63,62,60,59,57,56,54,53,51,
210 50,48,47,45,44,42,41,39,37,36,34,33,31,30,28,27,
211 25,23,22,20,19,17,16,14,13,11,9,8,6,5,3,2
212 };
213 
214 
215  return aAnswers[iVal & 0xff];
216 }
217 
218 
219 #if 0 /* this function not currently used. consider removeing it in a refactor */
220 static char ilog2n(unsigned char y)
221 {
222  if(y & 0x08)
223  return 4;
224  else if(y & 0x04)
225  return 3;
226  else if(y & 0x02)
227  return 2;
228  else if(y & 0x01)
229  return 1;
230  else
231  return 0;
232 }
233 #endif // 0
234 
235 #if 0 /* this function not currently used. consider removeing it in a refactor */
236 static char ilog2c(unsigned char y)
237 {
238  if(y & 0xf0)
239  return ilog2n(y >> 4) + 4;
240 
241  return ilog2n(y);
242 }
243 #endif // 0
244 
245 #if 0 /* this function not currently used. consider removeing it in a refactor */
246 static int ilog2s(unsigned short y)
247 {
248  if(y & 0xff00)
249  {
250  return ilog2c((unsigned char)(y >> 8)) + 8;
251  }
252 
253  return ilog2c((unsigned char)y);
254 }
255 #endif // 0
256 
257 #if 0 /* this function not currently used. consider removeing it in a refactor */
258 static int ilog2(unsigned int y)
259 {
260  if(y & 0xffff0000)
261  {
262  return ilog2s((unsigned short)(y >> 16)) + 16;
263  }
264 
265  return ilog2s((unsigned short)y);
266 }
267 #endif // 0
268 
269 #if 0 /* this function not currently used. consider removeing it in a refactor */
270 static int ilog2ll(WB_UINT64 y)
271 {
272  if(y & 0xffffffff00000000LL)
273  {
274  return ilog2((unsigned int)(y >> 32)) + 32;
275  }
276 
277  return ilog2((unsigned int)y);
278 }
279 #endif // 0
280 
281 
282 void WBSetVScrollRange(WB_SCROLLINFO *pSI, int iMin, int iMax)
283 {
284  // TODO: data validation
285  pSI->iVMin = iMin;
286  pSI->iVMax = iMax;
287 }
288 
289 void WBSetHScrollRange(WB_SCROLLINFO *pSI, int iMin, int iMax)
290 {
291  // TODO: data validation
292  pSI->iHMin = iMin;
293  pSI->iHMax = iMax;
294 }
295 
296 void WBSetVScrollPos(WB_SCROLLINFO *pSI, int iPos)
297 {
298  // TODO: data validation
299  pSI->iVPos = iPos;
300 }
301 
302 void WBSetHScrollPos(WB_SCROLLINFO *pSI, int iPos)
303 {
304  // TODO: data validation
305  pSI->iHPos = iPos;
306 }
307 
308 static void InternalNotifySelf(Window wID, Atom aNotify, long lData0, long lData1, long lData2, long lData3, long lData4)
309 {
310 Display *pDisplay = WBGetWindowDisplay(wID);
311 
312  XClientMessageEvent evt = {
313  .type=ClientMessage,
314  .serial=0,
315  .send_event=0,
316  .display=pDisplay,
317  .window=wID,
318  .message_type=aNotify,
319  .format=32
320  };
321  evt.data.l[0] = lData0;
322  evt.data.l[1] = lData1;
323  evt.data.l[2] = lData2;
324  evt.data.l[3] = lData3;
325  evt.data.l[4] = lData4;
326 
327  WBWindowDispatch(wID, (XEvent *)&evt);
328 }
329 
330 
331 static void InternalCalcVScrollBar(WB_SCROLLINFO *pScrollInfo, WB_GEOM *pgeomClient, int iVScrollWidth, int iHScrollHeight)
332 {
333 int iKnobSize, iKnobPos, iBarHeight, iBarWidth;
334 int i1, i2;
335 int nListItems;
336 
337 
338  iBarHeight = pgeomClient->height;
339  iBarWidth = pgeomClient->width;
340 
341  pScrollInfo->iVScrollWidth = iVScrollWidth;
342  pScrollInfo->iVBarHeight = iBarHeight;
343 
344 
345  if(pScrollInfo->iVPos < pScrollInfo->iVMin || pScrollInfo->iVPos > pScrollInfo->iVMax)
346  {
347  pScrollInfo->iVPos = -1; // for now
348  }
349 
350  nListItems = pScrollInfo->iVMax - pScrollInfo->iVMin + 1;
351 
352 
353  // knob height equals 'nListItems / nListItems!' multiplied by
354  // the available knob height, for a minimum value of 'iHScrollHeight'
355 
356  iKnobSize = (iBarHeight - 4 * iHScrollHeight - 2);
357 
358  if(iKnobSize > iHScrollHeight / 2)
359  {
360  i1 = iHScrollHeight * (2 * iHScrollHeight + 1); // 'twice scroll height' factorial
361  i2 = iHScrollHeight * 2;
362 
363  while(i2 > 2 && i1 > iKnobSize) // using the above starting point get 'max factorial less than height'
364  {
365  i1 -= i2;
366  i2--;
367  }
368 
369  if(nListItems >= i2) // more than twice scroll height?
370  {
371  if(nListItems > iKnobSize || iKnobSize - i1 < iHScrollHeight / 2)
372  {
373  iKnobSize = -1; // to fix later
374  }
375  else
376  {
377  iKnobSize -= i1; // twice scroll height factorial
378  iKnobSize -= (nListItems - iHScrollHeight * 2 + 1);
379  }
380  }
381  else
382  {
383  iKnobSize -= i1 // 'twice knob height' factorial
384  - (nListItems + 1) * nListItems / 2; // 'nListItems' factorial
385  }
386 
387  if(iKnobSize < 3 * iHScrollHeight / 4)
388  {
389  iKnobSize = 3 * iHScrollHeight / 4; // minimum size
390  }
391  }
392  else
393  {
394  iKnobSize = 3 * iHScrollHeight / 4; // worst case use this anyway
395  }
396 
397  // cache these geometries because I need them for mouse handling
398 
399  pScrollInfo->geomVBar.y = pgeomClient->y + 1;
400  pScrollInfo->geomVBar.height = pgeomClient->height - 2;
401  pScrollInfo->geomVBar.width = pScrollInfo->iVScrollWidth;
402  pScrollInfo->geomVBar.x = pgeomClient->x + pgeomClient->width
403  - pScrollInfo->geomVBar.width - 1;
404 
405  pScrollInfo->geomVDown.x = pScrollInfo->geomVUp.x
406  = pScrollInfo->geomVBar.x + 1;
407  pScrollInfo->geomVDown.width = pScrollInfo->geomVUp.width
408  = pScrollInfo->geomVBar.width - 2;
409  pScrollInfo->geomVDown.height = pScrollInfo->geomVUp.height
410  = iHScrollHeight - 1;
411 
412  pScrollInfo->geomVUp.y = pScrollInfo->geomVBar.y + 1;
413  pScrollInfo->geomVDown.y = pScrollInfo->geomVBar.y
414  + pScrollInfo->geomVBar.height
415  - pScrollInfo->geomVDown.height
416  - 1;
417 
418  pScrollInfo->geomVKnob.x = pScrollInfo->geomVDown.x;
419  pScrollInfo->geomVKnob.width = pScrollInfo->geomVDown.width;
420  pScrollInfo->geomVKnob.height = pScrollInfo->iVKnobSize
421  = iKnobSize;
422 
423  // now that I have a reasonable estimate of the knob size, calculate
424  // the starting vertical position of the knob
425 
426  if(pScrollInfo->iVPos <= 0 || nListItems <= 0)
427  {
428  iKnobPos = 0;
429  }
430  else
431  {
432  int iBarScrollArea = pScrollInfo->geomVDown.y
433  - (pScrollInfo->geomVUp.y + pScrollInfo->geomVUp.height)
434  - iKnobSize
435  - 1; // inclusive value
436 
437  if(pScrollInfo->iVPos >= (nListItems - 1))
438  {
439  iKnobPos = iBarScrollArea;
440  }
441  else
442  {
443  iKnobPos = (WB_INT64)pScrollInfo->iVPos * (WB_INT64)iBarScrollArea
444  / (WB_INT64)(nListItems - 1);
445  }
446  }
447 
448 
449  pScrollInfo->geomVKnob.y = (pScrollInfo->iVKnob = iKnobPos)
450  + pScrollInfo->geomVUp.y + pScrollInfo->geomVUp.height;
451 
452  // fixed values go here
453  pScrollInfo->geomVBar.border = 1;
454  pScrollInfo->geomVUp.border = 0;
455  pScrollInfo->geomVDown.border = 0;
456  pScrollInfo->geomVKnob.border = 0;
457 
458 }
459 
460 
461 static void InternalCalcHScrollBar(WB_SCROLLINFO *pScrollInfo, WB_GEOM *pgeomClient, int iVScrollWidth, int iHScrollHeight)
462 {
463 int iKnobSize, iKnobPos, iBarHeight, iBarWidth;
464 int i1, i2;
465 int nListItems;
466 
467 
468 
469  iBarHeight = pgeomClient->height;
470  iBarWidth = pgeomClient->width;
471 
472  pScrollInfo->iHScrollHeight = iHScrollHeight;
473  pScrollInfo->iHBarWidth = iBarWidth;
474 
475 
476  if(pScrollInfo->iHPos < pScrollInfo->iHMin || pScrollInfo->iHPos > pScrollInfo->iHMax)
477  {
478  pScrollInfo->iHPos = -1; // for now
479  }
480 
481  nListItems = pScrollInfo->iHMax - pScrollInfo->iHMin + 1;
482 
483 
484  // knob height equals 'nListItems / nListItems!' multiplied by
485  // the available knob height, for a minimum value of 'iHScrollHeight'
486 
487  iKnobSize = (iBarHeight - 4 * iHScrollHeight - 2);
488 
489  if(iKnobSize > iVScrollWidth / 2)
490  {
491  i1 = iVScrollWidth * (2 * iVScrollWidth + 1); // 'twice scroll height' factorial
492  i2 = iVScrollWidth * 2;
493 
494  while(i2 > 2 && i1 > iKnobSize) // using the above starting point get 'max factorial less than height'
495  {
496  i1 -= i2;
497  i2--;
498  }
499 
500  if(nListItems >= i2) // more than twice scroll height?
501  {
502  if(nListItems > iKnobSize || iKnobSize - i1 < iVScrollWidth / 2)
503  {
504  iKnobSize = -1; // to fix later
505  }
506  else
507  {
508  iKnobSize -= i1; // twice scroll height factorial
509  iKnobSize -= (nListItems - iVScrollWidth * 2 + 1);
510  }
511  }
512  else
513  {
514  iKnobSize -= i1 // 'twice knob height' factorial
515  - (nListItems + 1) * nListItems / 2; // 'nListItems' factorial
516  }
517 
518  if(iKnobSize < 3 * iVScrollWidth / 4)
519  {
520  iKnobSize = 3 * iVScrollWidth / 4; // minimum size
521  }
522  }
523  else
524  {
525  iKnobSize = 3 * iVScrollWidth / 4; // worst case use this anyway
526  }
527 
528  // cache these geometries because I need them for mouse handling
529 
530  pScrollInfo->geomHBar.x = pgeomClient->x + 1;
531  pScrollInfo->geomVBar.width = pgeomClient->width - 2;
532  pScrollInfo->geomVBar.height = pScrollInfo->iHScrollHeight;
533  pScrollInfo->geomVBar.y = pgeomClient->y + pgeomClient->height
534  - pScrollInfo->geomVBar.height - 1;
535 
536  pScrollInfo->geomHLeft.y = pScrollInfo->geomHRight.y
537  = pScrollInfo->geomHBar.y + 1;
538  pScrollInfo->geomHLeft.height = pScrollInfo->geomHRight.height
539  = pScrollInfo->geomHBar.height - 2;
540  pScrollInfo->geomHLeft.width = pScrollInfo->geomHRight.width
541  = iVScrollWidth - 1;
542 
543  pScrollInfo->geomHLeft.x = pScrollInfo->geomHBar.x + 1;
544  pScrollInfo->geomHRight.x = pScrollInfo->geomHBar.x
545  + pScrollInfo->geomHBar.width
546  - pScrollInfo->geomHRight.width
547  - 1;
548 
549  pScrollInfo->geomHKnob.y = pScrollInfo->geomHLeft.y;
550  pScrollInfo->geomHKnob.height = pScrollInfo->geomHLeft.height;
551  pScrollInfo->geomHKnob.width = pScrollInfo->iHKnobSize
552  = iKnobSize;
553 
554  // now that I have a reasonable estimate of the knob size, calculate
555  // the starting vertical position of the knob
556 
557  if(pScrollInfo->iHPos <= 0 || nListItems <= 0)
558  {
559  iKnobPos = 0;
560  }
561  else
562  {
563  int iBarScrollArea = pScrollInfo->geomHRight.y
564  - (pScrollInfo->geomHLeft.y + pScrollInfo->geomHLeft.width)
565  - iKnobSize
566  - 1; // inclusive value
567 
568  if(pScrollInfo->iHPos >= (nListItems - 1))
569  {
570  iKnobPos = iBarScrollArea;
571  }
572  else
573  {
574  iKnobPos = (WB_INT64)pScrollInfo->iHPos * (WB_INT64)iBarScrollArea
575  / (WB_INT64)(nListItems - 1);
576  }
577  }
578 
579 
580  pScrollInfo->geomHKnob.x = (pScrollInfo->iHKnob = iKnobPos)
581  + pScrollInfo->geomHLeft.x + pScrollInfo->geomHLeft.width;
582 
583  // fixed values go here
584  pScrollInfo->geomVBar.border = 1;
585  pScrollInfo->geomVUp.border = 0;
586  pScrollInfo->geomVDown.border = 0;
587  pScrollInfo->geomVKnob.border = 0;
588 
589 }
590 
591 
592 
593 
594 void WBCalcVScrollBar(WB_SCROLLINFO *pScrollInfo, WB_GEOM *pgeomClient, int iVScrollWidth,
595  int iHScrollHeight, int nListItems, int nPos)
596 {
597 int iBarHeight, iBarWidth;
598 
599 
600  iBarHeight = pgeomClient->height;
601  iBarWidth = pgeomClient->width;
602 
603  if(pScrollInfo->iVBarHeight == iBarHeight &&
604  pScrollInfo->iHBarWidth == iBarWidth &&
605  pScrollInfo->iVKnob >= 0 &&
606  pScrollInfo->iVKnobSize >= 0 &&
607  pScrollInfo->iVPos == nPos &&
608  pScrollInfo->iVMin == 0 &&
609  pScrollInfo->iVMax == (nListItems - 1))
610  {
611  // assume it's set up correctly
612 
613 // WB_ERROR_TEXT("TEMPORARY: short cycling calc scroll info\n");
614 // return;
615  }
616 
617  if(pScrollInfo->iHBarWidth != iBarWidth ||
618  pScrollInfo->iVBarHeight != iBarHeight) // TODO: do i need this???
619  {
620  // client size change detected
621  // zero out the H Scroll info so that it forces re-calculation next time
622  pScrollInfo->iHKnob = pScrollInfo->iHKnobSize = pScrollInfo->iHPos = pScrollInfo->iHMin = pScrollInfo->iHMax = 0;
623  }
624 
625 // WB_ERROR_PRINT("TEMPORARY: %d %d %d %d %d\n",
626 // iBarHeight, iVScrollWidth, iHScrollHeight, nListItems, nPos);
627 
628  pScrollInfo->iVMin = 0;
629  pScrollInfo->iVMax = nListItems - 1;
630 
631  if(nPos == -1 ||
632  (nPos >= pScrollInfo->iVMin && nPos <= pScrollInfo->iVMax))
633  {
634  pScrollInfo->iVPos = nPos;
635  }
636  else
637  {
638  pScrollInfo->iVPos = nPos = -1;
639  }
640 
641  InternalCalcVScrollBar(pScrollInfo, pgeomClient, iVScrollWidth, iHScrollHeight);
642 
643  pScrollInfo->iHScrollHeight = iHScrollHeight;
644  pScrollInfo->iHBarWidth = iBarWidth;
645 
646 }
647 
648 void WBCalcHScrollBar(WB_SCROLLINFO *pScrollInfo, WB_GEOM *pgeomClient, int iVScrollWidth,
649  int iHScrollHeight, int nListItems, int nPos)
650 {
651 int /*iKnobSize, iKnobPos,*/ iBarHeight, iBarWidth;
652 //int i1, i2;
653 
654  iBarHeight = pgeomClient->height;
655  iBarWidth = pgeomClient->width;
656 
657  if(pScrollInfo->iVBarHeight == iBarHeight &&
658  pScrollInfo->iHBarWidth == iBarWidth &&
659  pScrollInfo->iVKnob >= 0 &&
660  pScrollInfo->iVKnobSize >= 0 &&
661  pScrollInfo->iVPos == nPos &&
662  pScrollInfo->iVMin == 0 &&
663  pScrollInfo->iVMax == (nListItems - 1))
664  {
665  // assume it's set up correctly
666 
667 // WB_ERROR_TEXT("TEMPORARY: short cycling calc scroll info\n");
668 // return;
669  }
670 
671  if(pScrollInfo->iVBarHeight != iBarHeight ||
672  pScrollInfo->iHBarWidth != iBarWidth) // TODO: do I need this??
673  {
674  // client size change detected
675  // zero out the V Scroll info so that it forces re-calculation next time
676  pScrollInfo->iVKnob = pScrollInfo->iVKnobSize = pScrollInfo->iVPos = pScrollInfo->iVMin = pScrollInfo->iVMax = 0;
677  }
678 
679  pScrollInfo->iHMin = 0;
680  pScrollInfo->iHMax = nListItems - 1;
681 
682 
683  if(nPos == -1 ||
684  (nPos >= 0 && nPos < nListItems))
685  {
686  pScrollInfo->iHPos = nPos;
687  }
688  else
689  {
690  pScrollInfo->iHPos = nPos = -1;
691  }
692 
693  InternalCalcHScrollBar(pScrollInfo, pgeomClient, iVScrollWidth, iHScrollHeight);
694 
695  pScrollInfo->iVScrollWidth = iVScrollWidth;
696  pScrollInfo->iVBarHeight = iBarHeight;
697 
698 }
699 
700 
701 
702 int WBCalcVScrollDragPos(WB_SCROLLINFO *pScrollInfo, int iY)
703 {
704  if(pScrollInfo &&
705  (pScrollInfo->iVMax >= pScrollInfo->iVMin))
706  {
707  int nListItems = pScrollInfo->iVMax - pScrollInfo->iVMin + 1;
708  int iKnobSize = pScrollInfo->geomVKnob.height;
709  int iKnobPos = iY - iKnobSize / 3
710  - (pScrollInfo->geomVUp.y + pScrollInfo->geomVUp.height);
711 
712  int iBarScrollArea = pScrollInfo->geomVDown.y
713  - (pScrollInfo->geomVUp.y + pScrollInfo->geomVUp.height)
714  - iKnobSize
715  - 1; // inclusive value
716 
717  if(iBarScrollArea <= 0)
718  {
719  return pScrollInfo->iVMin; // always assume 1st item's index
720  }
721  else
722  {
723  int nPos = (WB_INT64)iKnobPos * (WB_INT64)(nListItems - 1)
724  / (WB_INT64)iBarScrollArea;
725 
726  WB_ERROR_PRINT("TEMPORARY: iY=%d; nLI=%d KS=%d KP=%d BSA=%d nP=%d\n",
727  iY, nListItems, iKnobSize, iKnobPos, iBarScrollArea, nPos);
728 
729  if(nPos < pScrollInfo->iVMin)
730  {
731  return pScrollInfo->iVMin;
732  }
733  else if(nPos > pScrollInfo->iVMax)
734  {
735  return pScrollInfo->iVMax;
736  }
737  else
738  {
739  return nPos;
740  }
741  }
742  }
743 
744  return -1; // can't evaluate, so return -1
745 }
746 
747 int WBCalcHScrollDragPos(WB_SCROLLINFO *pScrollInfo, int iX)
748 {
749  return -1; // for now, until implemented
750 }
751 
752 
753 void WBUpdateScrollBarGeometry(WB_SCROLLINFO *pSI, XFontSet fontSetRef,
754  WB_GEOM *pgeomClient, WB_GEOM *pgeomUsable)
755 {
756 WB_GEOM geom;
757 int iVScrollWidth, iHScrollHeight;
758 XCharStruct xBounds;
759 
760  // TODO: data validation
761 
762  geom.x = pgeomClient->x;
763  geom.y = pgeomClient->y;
764  geom.width = pgeomClient->width;
765  geom.height = pgeomClient->height;
766 
767  xBounds = WBFontSetMaxBounds(/*pDisplay*/WBGetDefaultDisplay(), fontSetRef);
768 
769  iVScrollWidth = WBTextWidth(fontSetRef, "X", 1) * 2 + 4; // standard width of vertical scrollbar
770  iHScrollHeight = xBounds.ascent + xBounds.descent + 4;
771 
772  // calculate the rectangles for the scroll bars
773 
774  if(pSI->iVMin < pSI->iVMax)
775  {
776 // InternalCalcVScrollBar(pSI, pgeomClient, iVScrollWidth, pScrollInfo->iVMin,
777 // pScrollInfo->iVMax, pScrollInfo->iVPos);
778  InternalCalcVScrollBar(pSI, pgeomClient, iVScrollWidth, geom.height - iHScrollHeight);
779 
780  geom.width -= iVScrollWidth;
781  }
782 
783  if(pSI->iHMin < pSI->iHMax)
784  {
785 // InternalCalcHScrollBar(pSI, pgeomClient, iHScrollHeight, pScrollInfo->iHMin,
786 // pScrollInfo->iHMax, pScrollInfo->iHPos);
787  InternalCalcHScrollBar(pSI, pgeomClient, iHScrollHeight, geom.width - iVScrollWidth);
788 
789 
790  geom.height -= iHScrollHeight;
791  }
792 
793  if(pgeomUsable)
794  {
795  memcpy(pgeomUsable, &geom, sizeof(*pgeomUsable));
796  }
797 }
798 
799 
800 int WBScrollBarEvent(Window wID, XEvent *pEvent, WB_SCROLLINFO *pScrollInfo)
801 {
802 int iX, iY, iDirection, iPosition;
803 
804 
805  if(wID == None || !pEvent || pEvent->type != ClientMessage || !pScrollInfo)
806  {
807  return 0; // only client message events and valid parameters
808  }
809 
810  if(pEvent->xclient.message_type == aWM_POINTER)
811  {
812  if(pEvent->xclient.data.l[0] == WB_POINTER_CLICK)
813  {
814  // TODO: handle shift-click, ctrl-click, alt-click
815 
816  if(pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
817  !pEvent->xclient.data.l[2])
818  {
819  iX = pEvent->xclient.data.l[3];
820  iY = pEvent->xclient.data.l[4];
821 
822  if(WB_LIKELY(pScrollInfo != NULL))
823  {
824  iDirection = WB_SCROLL_NA;
825  iPosition = 0;
826 
827  if(WBPointInGeom(iX, iY, pScrollInfo->geomVBar))
828  {
829  if(WBPointInGeom(iX, iY, pScrollInfo->geomVUp))
830  {
831  iDirection = WB_SCROLL_BACKWARD;
832  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (up)\n", __FUNCTION__);
833  }
834  else if(WBPointInGeom(iX, iY, pScrollInfo->geomVDown))
835  {
836  iDirection = WB_SCROLL_FORWARD;
837  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (down)\n", __FUNCTION__);
838  }
839  else if(WBPointInGeom(iX, iY, pScrollInfo->geomVKnob))
840  {
841  // ON THE KNOB - VScroll
842 
843  iDirection = WB_SCROLL_KNOB;
844 // iPosition = pScrollInfo->iVMin; //pListInfo->nTop; // NO!
845  iPosition = WBCalcVScrollDragPos(pScrollInfo, iY);
846 
847  if(iPosition < 0)
848  {
849  iPosition = pScrollInfo->iVMin; //pListInfo->nTop;
850  }
851 
852  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (knob)\n", __FUNCTION__);
853 
854  // TODO: determine position of knob
855  }
856  else if(iY >= pScrollInfo->geomVUp.y + pScrollInfo->geomVUp.height &&
857  iY < pScrollInfo->geomVKnob.y)
858  {
859  iDirection = WB_SCROLL_PAGEBACK;
860  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (page up)\n", __FUNCTION__);
861  }
862  else if(iY >= pScrollInfo->geomVKnob.y + pScrollInfo->geomVKnob.height &&
863  iY < pScrollInfo->geomVDown.y)
864  {
865  iDirection = WB_SCROLL_PAGEFWD;
866  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (page down)\n", __FUNCTION__);
867  }
868  else
869  {
870  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (unknown)\n", __FUNCTION__);
871  }
872 
873  InternalNotifySelf(wID, aSCROLL_NOTIFY, WB_SCROLL_VERTICAL, iDirection, iPosition, 0, 0);
874 
875  return 1; // handled
876  }
877  else if(WBPointInGeom(iX, iY, pScrollInfo->geomHBar))
878  {
879  if(WBPointInGeom(iX, iY, pScrollInfo->geomHLeft))
880  {
881  iDirection = WB_SCROLL_BACKWARD;
882  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (left)\n", __FUNCTION__);
883  }
884  else if(WBPointInGeom(iX, iY, pScrollInfo->geomHRight))
885  {
886  iDirection = WB_SCROLL_FORWARD;
887  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (right)\n", __FUNCTION__);
888  }
889  else if(WBPointInGeom(iX, iY, pScrollInfo->geomHKnob))
890  {
891  // ON THE KNOB - HScroll
892 
893  iDirection = WB_SCROLL_KNOB;
894 // iPosition = pScrollInfo->iHMin; // NO!
895  iPosition = WBCalcHScrollDragPos(pScrollInfo, iY);
896 
897  if(iPosition < 0)
898  {
899  iPosition = pScrollInfo->iHMin; //pListInfo->nTop;
900  }
901 
902  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (knob)\n", __FUNCTION__);
903 
904  // TODO: determine position of knob
905  }
906  else if(iX >= pScrollInfo->geomHLeft.x + pScrollInfo->geomHLeft.width &&
907  iX < pScrollInfo->geomHKnob.x)
908  {
909  iDirection = WB_SCROLL_PAGEBACK;
910  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (page left)\n", __FUNCTION__);
911  }
912  else if(iX >= pScrollInfo->geomHKnob.x + pScrollInfo->geomHKnob.width &&
913  iX < pScrollInfo->geomHRight.x)
914  {
915  iDirection = WB_SCROLL_PAGEFWD;
916  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (page right)\n", __FUNCTION__);
917  }
918  else
919  {
920  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (unknown)\n", __FUNCTION__);
921  }
922 
923  InternalNotifySelf(wID, aSCROLL_NOTIFY, WB_SCROLL_HORIZONTAL, iDirection, iPosition, 0, 0);
924 
925  return 1; // handled
926  }
927  }
928  }
929  }
930  else if(pEvent->xclient.data.l[0] == WB_POINTER_DBLCLICK)
931  {
932  if(pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
933  !pEvent->xclient.data.l[2])
934  {
935  iX = pEvent->xclient.data.l[3];
936  iY = pEvent->xclient.data.l[4];
937  // assume selection already done, so notify owner
938 
939  if(WB_LIKELY(pScrollInfo != NULL))
940  {
941  if(WBPointInGeom(iX, iY, pScrollInfo->geomVBar))
942  {
943  // if not within knob, re-post to self as single-click
944  if(!WBPointInGeom(iX, iY, pScrollInfo->geomVKnob))
945  {
946  XClientMessageEvent evt;
947  memcpy(&evt, pEvent, sizeof(evt));
948  evt.data.l[0] = WB_POINTER_CLICK;
949 
950  return WBScrollBarEvent(wID, (XEvent *)&evt, pScrollInfo);
951  }
952  }
953  else if(WBPointInGeom(iX, iY, pScrollInfo->geomHBar))
954  {
955  // if not within knob, re-post to self as single-click
956  if(!WBPointInGeom(iX, iY, pScrollInfo->geomHKnob))
957  {
958  XClientMessageEvent evt;
959  memcpy(&evt, pEvent, sizeof(evt));
960  evt.data.l[0] = WB_POINTER_CLICK;
961 
962  return WBScrollBarEvent(wID, (XEvent *)&evt, pScrollInfo);
963  }
964  }
965  }
966  }
967  }
968  else if(pEvent->xclient.data.l[0] == WB_POINTER_CANCEL)
969  {
970  // canceling drag (as appropriate)
971 
972 // if(pScrollInfo &&
973 // pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
974 // !pEvent->xclient.data.l[2])
975 // {
976 // pScrollInfo->iScrollState &= ~WBScrollState_LDRAG;
977 // }
978  if((pScrollInfo->iScrollState & WBScrollState_LDRAG) ||
979  (pScrollInfo->iScrollState & WBScrollState_MDRAG) ||
980  (pScrollInfo->iScrollState & WBScrollState_RDRAG))
981  {
983 
984  return 1; // "handled"
985  }
986  }
987  else if(pEvent->xclient.data.l[0] == WB_POINTER_DRAG ||
988  pEvent->xclient.data.l[0] == WB_POINTER_MOVE)
989  {
990  if(pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
991  !pEvent->xclient.data.l[2])
992  {
993  iX = pEvent->xclient.data.l[3];
994  iY = pEvent->xclient.data.l[4];
995 
996  if(WB_LIKELY(pScrollInfo != NULL))
997  {
998  if(!WBPointInGeom(iX, iY, pScrollInfo->geomVBar) &&
999  !WBPointInGeom(iX, iY, pScrollInfo->geomHBar) &&
1000  (!(pScrollInfo->iScrollState & WBScrollState_LDRAG) ||
1001  pEvent->xclient.data.l[0] == WB_POINTER_DRAG))
1002  {
1003  // TODO: this is for multi-select listboxes, doing a drag-select
1004 
1005  pScrollInfo->iScrollState &= ~WBScrollState_LDRAG; // make sure
1006 
1007  return 0; // "not handled" (allow drag-select to be handled by scrollbar owner)
1008  }
1009  else if(WBPointInGeom(iX, iY, pScrollInfo->geomVKnob) ||
1010  WB_LIKELY(pEvent->xclient.data.l[0] == WB_POINTER_MOVE &&
1011  (pScrollInfo->iScrollState & WBScrollState_LDRAG)))
1012  {
1013  if(pEvent->xclient.data.l[0] == WB_POINTER_DRAG) // begin drag, return window ID
1014  {
1015  WB_ERROR_PRINT("TEMPORARY - %s Mouse drag in scroll bar (knob)\n", __FUNCTION__);
1016 
1017  pScrollInfo->iScrollState |= WBScrollState_LDRAG; // set the state bit for left-drag
1018  return((int)wID); // enabling the drag
1019  }
1020 
1021  iPosition = WBCalcVScrollDragPos(pScrollInfo, iY);
1022 
1023  if(iPosition < 0)
1024  {
1025  iPosition = pScrollInfo->iVMin; //pListInfo->nTop;
1026  }
1027 
1028  // track the mouse position along the center of the knob, if possible
1029 
1030  WB_ERROR_PRINT("TEMPORARY - %s Mouse motion in scroll bar (knob)\n", __FUNCTION__);
1031 
1032  InternalNotifySelf(wID, aSCROLL_NOTIFY, WB_SCROLL_VERTICAL,
1033  WB_SCROLL_KNOB, iPosition, 0, 0);
1034  }
1035  else if(WBPointInGeom(iX, iY, pScrollInfo->geomHKnob) ||
1036  WB_LIKELY(pEvent->xclient.data.l[0] == WB_POINTER_MOVE &&
1037  (pScrollInfo->iScrollState & WBScrollState_HLDRAG)))
1038  {
1039  if(pEvent->xclient.data.l[0] == WB_POINTER_DRAG) // begin drag, return window ID
1040  {
1041  WB_ERROR_PRINT("TEMPORARY - %s Mouse drag in scroll bar (knob)\n", __FUNCTION__);
1042 
1043  pScrollInfo->iScrollState |= WBScrollState_HLDRAG; // set the state bit for left-drag
1044  return((int)wID); // enabling the drag (window ID cannot be 'None')
1045  }
1046 
1047  iPosition = WBCalcHScrollDragPos(pScrollInfo, iX);
1048 
1049  if(iPosition < 0)
1050  {
1051  iPosition = pScrollInfo->iHMin; // pListInfo->nTop;
1052  }
1053 
1054  // track the mouse position along the center of the knob, if possible
1055 
1056  WB_ERROR_PRINT("TEMPORARY - %s Mouse motion in scroll bar (knob)\n", __FUNCTION__);
1057 
1058  InternalNotifySelf(wID, aSCROLL_NOTIFY, WB_SCROLL_HORIZONTAL,
1059  WB_SCROLL_KNOB, iPosition, 0, 0);
1060  }
1061  else
1062  {
1063  WB_ERROR_PRINT("TEMPORARY - %s mouse motion in scroll bar outside of knob\n", __FUNCTION__);
1064  }
1065  }
1066  }
1067  }
1068  else if(pEvent->xclient.data.l[0] == WB_POINTER_DROP)
1069  {
1070  if(WB_LIKELY(pScrollInfo != NULL))
1071  {
1072  if((pScrollInfo->iScrollState & WBScrollState_LDRAG) ||
1073  (pScrollInfo->iScrollState & WBScrollState_MDRAG) ||
1074  (pScrollInfo->iScrollState & WBScrollState_RDRAG))
1075  {
1077 
1078  return 1; // "handled" (just a notification anyway)
1079  }
1080  }
1081  }
1082  else if(pEvent->xclient.data.l[0] == WB_POINTER_SCROLLUP)
1083  {
1084  InternalNotifySelf(wID, aSCROLL_NOTIFY, WB_SCROLL_VERTICAL, WB_SCROLL_BACKWARD, 0, 0, 0);
1085  }
1086  else if(pEvent->xclient.data.l[0] == WB_POINTER_SCROLLDOWN)
1087  {
1088  InternalNotifySelf(wID, aSCROLL_NOTIFY, WB_SCROLL_VERTICAL, WB_SCROLL_FORWARD, 0, 0, 0);
1089  }
1090 
1091  // TODO: anything else?
1092  }
1093  else if(pEvent->xclient.message_type == aWM_CHAR)
1094  {
1095  // handle cursors only - up, down, left, right, home, end, page up, page down, etc.
1096 
1097  long lKey = pEvent->xclient.data.l[0]; // return from WBKeyEventProcessKey
1098  long lAltCtrlShift = pEvent->xclient.data.l[1]; // *piAltCtrlShift from WBKeyEventProcessKey
1099 #ifndef NO_DEBUG
1100  int nChar = (int)pEvent->xclient.data.l[2]; // # of characters decoded into pBuf (below)
1101  char *pBuf = (char *)&(pEvent->xclient.data.l[3]); // decode buffer (at least 8 chars in length)
1102 #endif // !NO_DEBUG
1103 
1104 
1105  // TODO: scroll bar hotkeys
1106 
1107 
1108  }
1109 
1110  return 0;
1111 }
1112 
1113 
1114 // this assumes WB_SCROLLINFO is valid. To make it so, call WBUpdateScrollBarGeometry() or similar
1115 
1116 void WBPaintHScrollBar(WB_SCROLLINFO *pScrollInfo, Display *pDisplay, Drawable wID,
1117  GC gc, WB_GEOM *pgeomClient)
1118 {
1120 
1121  // fill scrollbar with background color
1122  XSetForeground(pDisplay, gc, clrScrollBG.pixel);
1123  XFillRectangle(pDisplay, wID, gc, pScrollInfo->geomHBar.x - 1, pScrollInfo->geomHBar.y,
1124  pScrollInfo->geomHBar.width + 1, pScrollInfo->geomHBar.height);
1125 
1126  // draw 3D borders around everything
1127 
1128  WBDraw3DBorderRect(pDisplay, wID, gc, &(pScrollInfo->geomHBar),
1129  clrScrollBD3.pixel, clrScrollBD2.pixel);
1130 
1131  // if the scrollbar is DISABLED, do not draw the rest of it
1132  if(pScrollInfo->iHPos < pScrollInfo->iHMin ||
1133  pScrollInfo->iHPos > pScrollInfo->iHMax ||
1134  pScrollInfo->iHMax < pScrollInfo->iHMin)
1135  {
1136 // WB_ERROR_PRINT("TEMPORARY: grey out %d %d %d\n",
1137 // pScrollInfo->iVPos, pScrollInfo->iVMin, pScrollInfo->iVMax);
1138  return; // I am done (greyed out bar)
1139  }
1140 
1141  WBDraw3DBorderRect(pDisplay, wID, gc, &(pScrollInfo->geomHLeft),
1142  clrScrollBD2.pixel, clrScrollBD3.pixel);
1143  WBDraw3DBorderRect(pDisplay, wID, gc, &(pScrollInfo->geomHRight),
1144  clrScrollBD2.pixel, clrScrollBD3.pixel);
1145  WBDraw3DBorderRect(pDisplay, wID, gc, &(pScrollInfo->geomHKnob),
1146  clrScrollBD2.pixel, clrScrollBD3.pixel);
1147 
1148  // draw arrows
1149 
1150  WBDrawLeftArrow(pDisplay, wID, gc, &(pScrollInfo->geomHLeft), clrScrollFG.pixel);
1151  WBDrawRightArrow(pDisplay, wID, gc, &(pScrollInfo->geomHRight), clrScrollFG.pixel);
1152 }
1153 
1154 // NOTE: this assumes WB_SCROLLINFO is valid. To make it so, call WBUpdateScrollBarGeometry() or similar
1155 
1156 void WBPaintVScrollBar(WB_SCROLLINFO *pScrollInfo, Display *pDisplay, Drawable wID,
1157  GC gc, WB_GEOM *pgeomClient)
1158 {
1160 
1161  // fill scrollbar with background color
1162  XSetForeground(pDisplay, gc, clrScrollBG.pixel);
1163  XFillRectangle(pDisplay, wID, gc, pScrollInfo->geomVBar.x - 1, pScrollInfo->geomVBar.y,
1164  pScrollInfo->geomVBar.width + 1, pScrollInfo->geomVBar.height);
1165 
1166  // draw 3D borders around everything
1167 
1168  WBDraw3DBorderRect(pDisplay, wID, gc, &(pScrollInfo->geomVBar),
1169  clrScrollBD3.pixel, clrScrollBD2.pixel);
1170 
1171  // if the scrollbar is DISABLED, do not draw the rest of it
1172  if(pScrollInfo->iVPos < pScrollInfo->iVMin ||
1173  pScrollInfo->iVPos > pScrollInfo->iVMax ||
1174  pScrollInfo->iVMax < pScrollInfo->iVMin)
1175  {
1176 // WB_ERROR_PRINT("TEMPORARY: grey out %d %d %d\n",
1177 // pScrollInfo->iVPos, pScrollInfo->iVMin, pScrollInfo->iVMax);
1178  return; // I am done (greyed out bar)
1179  }
1180 
1181  WBDraw3DBorderRect(pDisplay, wID, gc, &(pScrollInfo->geomVUp),
1182  clrScrollBD2.pixel, clrScrollBD3.pixel);
1183  WBDraw3DBorderRect(pDisplay, wID, gc, &(pScrollInfo->geomVDown),
1184  clrScrollBD2.pixel, clrScrollBD3.pixel);
1185  WBDraw3DBorderRect(pDisplay, wID, gc, &(pScrollInfo->geomVKnob),
1186  clrScrollBD2.pixel, clrScrollBD3.pixel);
1187 
1188  // draw arrows
1189 
1190  WBDrawUpArrow(pDisplay, wID, gc, &(pScrollInfo->geomVUp), clrScrollFG.pixel);
1191  WBDrawDownArrow(pDisplay, wID, gc, &(pScrollInfo->geomVDown), clrScrollFG.pixel);
1192 }
1193 
1194 
1195 
1197 // //
1198 // ____ _ ____ _ _ _ //
1199 // | _ \ _ __ __ _ __ __(_) _ __ __ _ | _ \ _ __ (_) _ __ ___ __ _ | |_ (_)__ __ ___ ___ //
1200 // | | | || '__|/ _` |\ \ /\ / /| || '_ \ / _` | | |_) || '__|| || '_ ` _ \ / _` || __|| |\ \ / // _ \/ __| //
1201 // | |_| || | | (_| | \ V V / | || | | || (_| | | __/ | | | || | | | | || (_| || |_ | | \ V /| __/\__ \ //
1202 // |____/ |_| \__,_| \_/\_/ |_||_| |_| \__, | |_| |_| |_||_| |_| |_| \__,_| \__||_| \_/ \___||___/ //
1203 // |___/ //
1204 // //
1206 
1207 void WBDrawBorderRect(Display *pDisplay, Drawable wID, GC gc,
1208  WB_GEOM *pgeomBorder, unsigned long lBorderColor)
1209 {
1210 XPoint xpt[5];
1211 
1212  XSetForeground(pDisplay, gc, lBorderColor);
1213 
1214  xpt[0].x=pgeomBorder->x;
1215  xpt[0].y=pgeomBorder->y;
1216 
1217  xpt[1].x = xpt[0].x
1218  + pgeomBorder->width
1219  - 1; // 'inclusive' values
1220 
1221  xpt[1].y = xpt[0].y;
1222 
1223  xpt[2].x = xpt[1].x;
1224  xpt[2].y = xpt[1].y
1225  + pgeomBorder->height
1226  - 1;
1227 
1228  xpt[3].x = xpt[0].x;
1229  xpt[3].y = xpt[2].y;
1230 
1231  xpt[4].x = xpt[0].x;
1232  xpt[4].y = xpt[0].y + 1; // exclude final point
1233 
1234  XDrawLines(pDisplay, wID, gc, xpt, 5, CoordModeOrigin);
1235 }
1236 
1237 void WBDraw3DBorderRect(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomBorder,
1238  unsigned long lBorderColor1, unsigned long lBorderColor2)
1239 {
1240 XPoint xpt[4];
1241 XColor clr;
1242 int iR, iG, iB;
1243 
1244 
1245  XSetForeground(pDisplay, gc, lBorderColor1);
1246  xpt[0].x = pgeomBorder->x;
1247  xpt[0].y = pgeomBorder->y
1248  + pgeomBorder->height - 1 - 1; // exclude first point
1249 
1250  xpt[1].x = xpt[0].x;
1251  xpt[1].y = pgeomBorder->y;
1252 
1253  xpt[2].x = xpt[0].x
1254  + pgeomBorder->width - 1 - 1; // exclude last point
1255  xpt[2].y = xpt[1].y;
1256 
1257  XDrawLines(pDisplay, wID, gc, xpt, 3, CoordModeOrigin);
1258 
1259  XSetForeground(pDisplay, gc, lBorderColor2);
1260 
1261  xpt[0].x = pgeomBorder->x
1262  + pgeomBorder->width - 1;
1263  xpt[0].y = pgeomBorder->y + 1; // exclude first point
1264 
1265  xpt[1].x = xpt[0].x;
1266  xpt[1].y = pgeomBorder->y
1267  + pgeomBorder->height - 1;
1268 
1269  xpt[2].x = pgeomBorder->x + 1; // exclude final point
1270  xpt[2].y = xpt[1].y;
1271 
1272  XDrawLines(pDisplay, wID, gc, xpt, 3, CoordModeOrigin);
1273 
1274  // Use the RGB info to calculate an 'average' color for the corners
1275 
1276  bzero(&clr, sizeof(clr));
1277  clr.pixel = lBorderColor1;
1278 
1279  PXM_PixelToRGB(NULL, &clr);
1280 
1281  iR = (clr.flags & DoRed) ? (unsigned int)clr.red : 0;
1282  iG = (clr.flags & DoGreen) ? (unsigned int)clr.green : 0;
1283  iB = (clr.flags & DoBlue) ? (unsigned int)clr.blue : 0;
1284 
1285  bzero(&clr, sizeof(clr));
1286  clr.pixel = lBorderColor2;
1287 
1288  PXM_PixelToRGB(NULL, &clr);
1289 
1290  iR += (clr.flags & DoRed) ? (unsigned int)clr.red : 0;
1291  iG += (clr.flags & DoGreen) ? (unsigned int)clr.green : 0;
1292  iB += (clr.flags & DoBlue) ? (unsigned int)clr.blue : 0;
1293 
1294  bzero(&clr, sizeof(clr));
1295 
1296  clr.red = iR >> 1;
1297  clr.green = iG >> 1;
1298  clr.blue = iB >> 1;
1299 
1300  PXM_RGBToPixel(NULL, &clr); // TODO: alloc the color as well?
1301 
1302 // WB_ERROR_PRINT("TEMPORARY: %s - average of %08xH and %08xH is %08xH (%d, %d, %d)\n", __FUNCTION__,
1303 // (unsigned int)lBorderColor1, (unsigned int)lBorderColor2,
1304 // (unsigned int)clr.pixel, clr.red, clr.green, clr.blue);
1305 
1306  XSetForeground(pDisplay, gc, clr.pixel);
1307 
1308  xpt[0].x = pgeomBorder->x;
1309  xpt[1].y = pgeomBorder->y;
1310 
1311  xpt[1].x = xpt[0].x + pgeomBorder->width - 1;
1312  xpt[0].y = xpt[1].y + pgeomBorder->height - 1;
1313 
1314  XDrawPoints(pDisplay, wID, gc, xpt, 2, CoordModeOrigin);
1315 
1316  XSetForeground(pDisplay, gc, lBorderColor1);
1317 
1318 }
1319 
1320 void WBDrawDashedRect(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
1321 {
1322 static const char dash_list[4]={1,2,2,1};
1323 GC gc2;
1324 
1325 
1326  gc2 = WBGetWindowCopyGC2(wID, gc);
1327 
1328  if(gc2)
1329  {
1330  WBDrawBorderRect(pDisplay, wID, gc2, pgeomRect, WhitePixel(pDisplay, DefaultScreen(pDisplay)));
1331  XSetDashes(pDisplay, gc2, 1, dash_list, 4);
1332  XSetLineAttributes(pDisplay, gc2, 1, LineOnOffDash, CapNotLast, JoinBevel);
1333  XSetBackground(pDisplay, gc2, WhitePixel(pDisplay, DefaultScreen(pDisplay)));
1334  WBDrawBorderRect(pDisplay, wID, gc2, pgeomRect, lColor);
1335 
1336  XFreeGC(pDisplay, gc2);
1337  }
1338  else
1339  {
1340  WB_ERROR_PRINT("%s:%d - unable to create GC\n", __FUNCTION__, __LINE__);
1341  }
1342 }
1343 
1344 void WBDrawLeftArrow(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
1345 {
1346 XPoint xpt[5];
1347 long lBG, lFG;
1348 
1349  lBG = WBGetGCBGColor(pDisplay, gc);
1350  lFG = WBGetGCFGColor(pDisplay, gc); // save color context
1351 
1352  XSetForeground(pDisplay, gc, lColor);
1353  XSetBackground(pDisplay, gc, lColor);
1354 
1355  // LEFT ARROW
1356  xpt[0].x = pgeomRect->x + (pgeomRect->width >> 2);
1357  xpt[0].y = pgeomRect->y + (pgeomRect->height >> 2) + (pgeomRect->height >> 2);
1358  xpt[1].x = xpt[0].x + (pgeomRect->width >> 1) - 1;
1359  xpt[1].y = pgeomRect->y + (pgeomRect->height >> 2);
1360  xpt[2].x = xpt[1].x;
1361  xpt[2].y = pgeomRect->y + pgeomRect->height - 1 - (pgeomRect->height >> 2);
1362  xpt[3].x = xpt[0].x;
1363  xpt[3].y = xpt[2].y - (pgeomRect->height >> 2);
1364  xpt[4].x = xpt[0].x;
1365  xpt[4].y = xpt[0].y;
1366 
1367  XDrawLines(pDisplay, wID, gc, xpt, 5, CoordModeOrigin);
1368  XFillPolygon(pDisplay, wID, gc, xpt, 5, /*Convex*/Nonconvex, CoordModeOrigin);
1369 
1370  XSetForeground(pDisplay, gc, lFG);
1371  XSetBackground(pDisplay, gc, lBG); // restore color context
1372 }
1373 
1374 void WBDrawUpArrow(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
1375 {
1376 XPoint xpt[5];
1377 long lBG, lFG;
1378 
1379  lBG = WBGetGCBGColor(pDisplay, gc);
1380  lFG = WBGetGCFGColor(pDisplay, gc); // save color context
1381 
1382  XSetForeground(pDisplay, gc, lColor);
1383  XSetBackground(pDisplay, gc, lColor);
1384 
1385  xpt[0].x = pgeomRect->x + (pgeomRect->width >> 2) + (pgeomRect->width >> 2);
1386  xpt[0].y = pgeomRect->y + (pgeomRect->height >> 2);
1387  xpt[1].x = pgeomRect->x + (pgeomRect->width >> 2);
1388  xpt[1].y = xpt[0].y + (pgeomRect->height >> 1) - 1;
1389  xpt[2].x = pgeomRect->x + pgeomRect->width - 1 - (pgeomRect->width >> 2);
1390  xpt[2].y = xpt[1].y;
1391  xpt[3].x = xpt[2].x - (pgeomRect->width >> 2);
1392  xpt[3].y = xpt[0].y;
1393  xpt[4].x = xpt[0].x;
1394  xpt[4].y = xpt[0].y;
1395 
1396  XDrawLines(pDisplay, wID, gc, xpt, 5, CoordModeOrigin);
1397  XFillPolygon(pDisplay, wID, gc, xpt, 5, /*Convex*/Nonconvex, CoordModeOrigin);
1398 
1399  XSetForeground(pDisplay, gc, lFG);
1400  XSetBackground(pDisplay, gc, lBG); // restore color context
1401 }
1402 
1403 void WBDrawRightArrow(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
1404 {
1405 XPoint xpt[5];
1406 long lBG, lFG;
1407 
1408  lBG = WBGetGCBGColor(pDisplay, gc);
1409  lFG = WBGetGCFGColor(pDisplay, gc); // save color context
1410 
1411  XSetForeground(pDisplay, gc, lColor);
1412  XSetBackground(pDisplay, gc, lColor);
1413 
1414  // RIGHT ARROW
1415  xpt[0].x = pgeomRect->x + pgeomRect->width - 1 - (pgeomRect->width >> 2);
1416  xpt[0].y = pgeomRect->y + (pgeomRect->height >> 2) + (pgeomRect->height >> 2);
1417  xpt[1].x = xpt[0].x - (pgeomRect->width >> 1) + 1;
1418  xpt[1].y = pgeomRect->y + (pgeomRect->height >> 2);
1419  xpt[2].x = xpt[1].x;
1420  xpt[2].y = pgeomRect->y + pgeomRect->height - 1 - (pgeomRect->height >> 2);
1421  xpt[3].x = xpt[0].x;
1422  xpt[3].y = xpt[2].y - (pgeomRect->height >> 2);
1423  xpt[4].x = xpt[0].x;
1424  xpt[4].y = xpt[0].y;
1425 
1426  XDrawLines(pDisplay, wID, gc, xpt, 5, CoordModeOrigin);
1427  XFillPolygon(pDisplay, wID, gc, xpt, 5, /*Convex*/Nonconvex, CoordModeOrigin);
1428 
1429  XSetForeground(pDisplay, gc, lFG);
1430  XSetBackground(pDisplay, gc, lBG); // restore color context
1431 }
1432 
1433 void WBDrawDownArrow(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
1434 {
1435 XPoint xpt[5];
1436 long lBG, lFG;
1437 
1438  lBG = WBGetGCBGColor(pDisplay, gc);
1439  lFG = WBGetGCFGColor(pDisplay, gc); // save color context
1440 
1441  XSetForeground(pDisplay, gc, lColor);
1442  XSetBackground(pDisplay, gc, lColor);
1443 
1444  xpt[0].x = pgeomRect->x + (pgeomRect->width >> 2) + (pgeomRect->width >> 2);
1445  xpt[0].y = pgeomRect->y + pgeomRect->height - 1 - (pgeomRect->height >> 2);
1446  xpt[1].x = pgeomRect->x + (pgeomRect->width >> 2);
1447  xpt[1].y = xpt[0].y - (pgeomRect->height >> 1) + 1;
1448  xpt[2].x = pgeomRect->x + pgeomRect->width - 1 - (pgeomRect->width >> 2);
1449  xpt[2].y = xpt[1].y;
1450  xpt[3].x = xpt[2].x - (pgeomRect->width >> 2);
1451  xpt[3].y = xpt[0].y;
1452  xpt[4].x = xpt[0].x;
1453  xpt[4].y = xpt[0].y;
1454 
1455  XDrawLines(pDisplay, wID, gc, xpt, 5, CoordModeOrigin);
1456  XFillPolygon(pDisplay, wID, gc, xpt, 5, /*Convex*/Nonconvex, CoordModeOrigin);
1457 
1458  XSetForeground(pDisplay, gc, lFG);
1459  XSetBackground(pDisplay, gc, lBG); // restore color context
1460 }
1461 
1462 
1463 void WBDraw3DBorderTab(Display *pDisplay, Drawable dw, GC gc, WB_GEOM *pgeomOutline,
1464  int fFocus, unsigned long lFGColor, unsigned long lBGColor,
1465  unsigned long lBorderColor1, unsigned long lBorderColor2,
1466  unsigned long lHighlightColor,
1467  XFontSet fontSet, XFontSet fontSetBold,
1468  Atom aGraphic, const char *szText)
1469 {
1470 XPoint xpt[13];
1471 XColor clrAvg, clrTemp;
1472 int iFontHeight, bFocus;
1473 int i1, i2, iR, iG, iB, iY, iU, iV, iYBG, iY0, iU0, iV0;
1474 Region rgnClip;
1475 GC gc2;
1476 WB_RECT rctTemp;
1477 
1478 
1479  if(fontSet == None)
1480  {
1481  fontSet = WBGetDefaultFontSet(pDisplay);
1482  }
1483 
1484  if(fontSetBold == None)
1485  {
1486  fontSetBold = WBGetDefaultFontSet(pDisplay);
1487  }
1488 
1489  iFontHeight = WBFontSetHeight(pDisplay, fontSet);
1490 
1491 
1492  // begin by creating a region that consists of my 'rounded rect' polygon
1493 
1494  // create a rounded-corner trapezoid the encompasses the tab. corner pixels will
1495  // be averages of the border colors.
1496 
1497  // lower left corner
1498  xpt[0].x = pgeomOutline->x;
1499  xpt[0].y = pgeomOutline->y + pgeomOutline->height + 1; // for clip rgn, I do this
1500 
1501  // left side
1502  xpt[1].x = xpt[0].x + 2; // slight trapezoid
1503  xpt[1].y = pgeomOutline->y + 3; // room for corner
1504 
1505  // upper left corner
1506  xpt[2].x = xpt[1].x + 1;
1507  xpt[2].y = xpt[1].y - 1; // 45 degrees
1508  xpt[3].x = xpt[2].x + 1;
1509  xpt[3].y = xpt[2].y - 1; // again
1510  xpt[4].x = xpt[3].x + 1;
1511  xpt[4].y = xpt[3].y - 1; // now we're "on point"
1512 
1513  // top row
1514  xpt[5].x = xpt[0].x + pgeomOutline->width - 5;
1515  xpt[5].y = xpt[4].y;
1516 
1517  // upper right corner
1518  xpt[6].x = xpt[5].x + 1;
1519  xpt[6].y = xpt[5].y + 1; // 45 degrees
1520  xpt[7].x = xpt[6].x + 1;
1521  xpt[7].y = xpt[6].y + 1; // 45 degrees
1522  xpt[8].x = xpt[7].x + 1;
1523  xpt[8].y = xpt[7].y + 1; // 45 degrees
1524 
1525  // right side
1526  xpt[9].x = pgeomOutline->x + pgeomOutline->width;
1527  xpt[9].y = xpt[0].y; // same y as 1st point
1528 
1529  xpt[10].x = xpt[0].x;
1530  xpt[10].y = xpt[0].y; // close up the polygon
1531 
1532  rgnClip = XPolygonRegion(xpt, 11, WindingRule);
1533  if(rgnClip == None)
1534  {
1535  WB_ERROR_PRINT("ERROR: %s - unable to create polygon region\n", __FUNCTION__);
1536  return;
1537  }
1538 
1539  // create GC copy and select the clipping region
1540 
1541  gc2 = WBCopyDrawableGC(pDisplay, dw, gc);
1542 
1543  if(gc2 == None)
1544  {
1545  XDestroyRegion(rgnClip);
1546 
1547  WB_ERROR_PRINT("ERROR: %s - unable to create GC\n", __FUNCTION__);
1548  return;
1549  }
1550 
1551  // select the clip region
1552  XSetRegion(pDisplay, gc2, rgnClip);
1553 
1554  // set 'bFocus' to indicate if I have focus. 'fFocus' also indicates 'x' button state
1555  // 0 or < -1 is "I do not have focus". -1 or > 0 is "I have focus". negative is 'x button clicked'
1556 
1557  bFocus = fFocus > 0 || fFocus == -1;
1558 
1559  if(bFocus)
1560  {
1561  // do the background color in the tab. this will use the clip region to help me
1562 
1563  clrTemp.pixel = lBGColor; // default background color
1564  PXM_PixelToRGB(NULL, &clrTemp);
1565 
1566  iR = clrTemp.red >> 8;
1567  iG = clrTemp.green >> 8;
1568  iB = clrTemp.blue >> 8;
1569 
1570  PXM_RGBToYUV(iR, iG, iB, &iYBG, NULL, NULL); // get 'Y' for the background (assume grey)
1571  // if the background Y is less than 'pure white', split the difference
1572  if(iYBG < 255)
1573  {
1574  iYBG = (iYBG + 256) / 2;
1575  }
1576 
1577  i2 = 6 * (xpt[0].y - xpt[5].y - 2) / 7; // calculate 6/7 of the height
1578 
1579 
1580 // WB_ERROR_PRINT("TEMPORARY: %s - i2 is %d, from %d and %d\n", __FUNCTION__,
1581 // i2, xpt[0].y, xpt[5].y);
1582 
1583  iY0 = iYBG; // initialize this way
1584 
1585  clrTemp.pixel = lHighlightColor;
1586  PXM_PixelToRGB(NULL, &clrTemp);
1587 
1588  iR = clrTemp.red >> 8;
1589  iG = clrTemp.green >> 8;
1590  iB = clrTemp.blue >> 8;
1591 
1592  PXM_RGBToYUV(iR, iG, iB, &iY0, &iU0, &iV0); // cache YUV as iY0, iU0, iV0
1593 
1594  if(iY0 > 3 * iYBG / 4) // restrict the range of the brightness of the highlight color vs background
1595  {
1596  iY0 = 3 * iYBG / 4;
1597  }
1598  else if(iY0 < 3 * iYBG / 7)
1599  {
1600  iY0 = 3 * iYBG / 7;
1601  }
1602 
1603  for(i1=xpt[5].y; i1 < xpt[0].y - 2; i1++)
1604  {
1605  XPoint xpt2[2];
1606  int iR2 = abs(i1 - (xpt[5].y + i2 / 2)); // 'i2 / 2' is 3/7 of the height...
1607 
1608  iR2 = icos(iR2 * 232 / i2); // 255 would be pi/2, so go slightly less than that
1609  iR2 = ((int)iR2 * (int)iR2) / 256; // use cos^2 - 'icos' returns a value between 0 and 255
1610 // iR2 *= iR2; old way, squared it
1611 // iR2 = isqrt(iR2); old way, square root
1612 
1613  iY = iY0; // grab cached YUV values for highlight color
1614  iU = iU0;
1615  iV = iV0;
1616 
1617  // NOTE: 'iY' is the brightness of the highlight color, 'iYBG' the brightness of the background (averaged with white)
1618 
1619 // iY = iYBG * 3 / 4 + ((384 - iYBG) * iR2) / 384; // allows brightness to drop to a bit less than the background's brightness
1620  iY = (iYBG - iY + iY / 2) * iR2 / 256 + iY + iY / 2 + 4;
1621 
1622  if(iY > 255)
1623  {
1624  iU = 128 + (iU - 128) * 255 / iY * 255 / iY;
1625  iV = 128 + (iV - 128) * 255 / iY * 255 / iY;
1626  iY = 255;
1627  }
1628 
1629  PXM_YUVToRGB(iY, iU, iV, &iR, &iG, &iB);
1630 
1631  clrTemp.red = (iR << 8) + 128;
1632  clrTemp.green = (iG << 8) + 128;
1633  clrTemp.blue = (iB << 8) + 128;
1634  clrTemp.flags = DoRed | DoGreen | DoBlue;
1635 
1636  PXM_RGBToPixel(NULL, &clrTemp);
1637 
1638 // WB_ERROR_PRINT("TEMPORARY: %s - YUV is %d, %d, %d, RGB is %d, %d, %d for %lu (%08lxH)\n", __FUNCTION__,
1639 // iY, iU, iV,
1640 // clrTemp.red, clrTemp.green, clrTemp.blue, clrTemp.pixel, clrTemp.pixel);
1641 
1642  XSetForeground(pDisplay, gc2, clrTemp.pixel); // select this color
1643 
1644  xpt2[0].x = pgeomOutline->x + 1;
1645  xpt2[0].y = i1;
1646  xpt2[1].x = pgeomOutline->x + pgeomOutline->width - 2;
1647  xpt2[1].y = xpt2[0].y;
1648 
1649  if(i1 < xpt[5].y + 2) // corner hack, based on observation
1650  {
1651  xpt2[0].x += (xpt[5].y + 2) - i1;
1652  xpt2[1].x -= (xpt[5].y + 2) - i1;
1653  }
1654 
1655  // draw the line
1656  XDrawLines(pDisplay, dw, gc2, xpt2, 2, CoordModeOrigin); // stop at point 6 (don't paint 6 to 7)
1657  }
1658  }
1659 
1660 
1661  // next, squeeze in the right/left edges of my points. I've observed I need to do this
1662  // as a hack, don't do the left squeeze for the 'focus' tab
1663 
1664  if(!bFocus)
1665  {
1666  for(i1=0; i1 < 5; i1++)
1667  {
1668  xpt[i1].x ++;
1669  }
1670  }
1671 
1672  for(i1=5; i1 < 10; i1++)
1673  {
1674  xpt[i1].x --;
1675  }
1676 
1677  if(!bFocus)
1678  {
1679  xpt[0].y -= 3; // one above 'bottom' (where I'll draw a line)
1680  }
1681  else
1682  {
1683  xpt[0].y -= 2;
1684  }
1685 
1686  xpt[9].y = xpt[0].y; // same y as 1st point
1687 
1688  xpt[10].x = xpt[0].x; // re-close up polygon (again)
1689  xpt[10].y = xpt[0].y; // close up the polygon
1690 
1691 
1692  // make a copy of the GC
1693 
1694  XSetForeground(pDisplay, gc2, lFGColor);
1695  XSetBackground(pDisplay, gc2, lBGColor);
1696 
1697  // Use the RGB info to calculate an 'average' color for the corner transition
1698 
1699  bzero(&clrAvg, sizeof(clrAvg));
1700  clrAvg.pixel = lBorderColor1;
1701 
1702  PXM_PixelToRGB(NULL, &clrAvg);
1703 
1704  iR = (clrAvg.flags & DoRed) ? (unsigned int)clrAvg.red : 0;
1705  iG = (clrAvg.flags & DoGreen) ? (unsigned int)clrAvg.green : 0;
1706  iB = (clrAvg.flags & DoBlue) ? (unsigned int)clrAvg.blue : 0;
1707 
1708  bzero(&clrAvg, sizeof(clrAvg));
1709  clrAvg.pixel = lBorderColor2;
1710 
1711  PXM_PixelToRGB(NULL, &clrAvg);
1712 
1713  iR += (clrAvg.flags & DoRed) ? (unsigned int)clrAvg.red : 0;
1714  iG += (clrAvg.flags & DoGreen) ? (unsigned int)clrAvg.green : 0;
1715  iB += (clrAvg.flags & DoBlue) ? (unsigned int)clrAvg.blue : 0;
1716 
1717  bzero(&clrAvg, sizeof(clrAvg));
1718 
1719  clrAvg.red = iR >> 1;
1720  clrAvg.green = iG >> 1;
1721  clrAvg.blue = iB >> 1;
1722 
1723  PXM_RGBToPixel(NULL, &clrAvg); // TODO: alloc the color as well?
1724 
1725  // next, draw polygon using 3D colors
1726 
1727  XSetForeground(pDisplay, gc2, lBorderColor1);
1728  XDrawLines(pDisplay, dw, gc2, xpt, 7, CoordModeOrigin); // stop at point 6 (don't paint 6 to 7)
1729 
1730  XSetForeground(pDisplay, gc2, lBorderColor2);
1731  XDrawLines(pDisplay, dw, gc2, xpt + 8, 2, CoordModeOrigin); // stop at point 6 (don't paint 6 or 7)
1732 
1733  if(bFocus)
1734  {
1735  // for non-focus, draw the bottom (7 to 8) using border color 2. for focus, draw as background color
1736 
1737  XSetForeground(pDisplay, gc2, lBGColor);
1738  }
1739 
1740  XDrawLines(pDisplay, dw, gc2, xpt + 9, 2, CoordModeOrigin); // the bottom line
1741 
1742  // paint pixels 6 and 7 with the 'average' color
1743  XSetForeground(pDisplay, gc2, clrAvg.pixel);
1744  XDrawPoints(pDisplay, dw, gc2, xpt + 6, 2, CoordModeOrigin);
1745 
1746  if(!bFocus)
1747  {
1748  // when not in focus, also do avg color for the first pixel. this completes the 3D effect 'color transition'
1749  XDrawPoints(pDisplay, dw, gc2, xpt, 1, CoordModeOrigin);
1750  }
1751 
1752 
1753 
1754  // TAB TEXT (TODO: image atom)
1755 
1756  rctTemp.left = pgeomOutline->x;
1757  rctTemp.top = pgeomOutline->y;
1758  rctTemp.right = rctTemp.left + pgeomOutline->width;
1759  rctTemp.bottom = rctTemp.top + pgeomOutline->height;
1760 
1761  if(!szText)
1762  {
1763  szText = "{untitled}";
1764  }
1765 
1766  XSetForeground(pDisplay, gc2, lFGColor);
1767 
1768  // for now just do centered text
1769  DTDrawSingleLineText(fontSet, szText, pDisplay, gc2, dw, 0, 0, &rctTemp,
1771 
1772 
1773  // NOW, I need to draw the 'x' for the close button. Fist, I calculate its rect
1774 
1775  rctTemp.top = pgeomOutline->y + 2; // top plus 2 pixels
1776  rctTemp.bottom = rctTemp.top + iFontHeight; // height is font height
1777  rctTemp.right = pgeomOutline->x + pgeomOutline->width - 6; // right is 6 pixels from right edge
1778  rctTemp.left = rctTemp.right - iFontHeight + 2; // left is 2 pixels + font height from right
1779 
1780  if(fFocus < 0) // clicking 'x' ?
1781  {
1782  XSetForeground(pDisplay, gc2, lHighlightColor);
1783  XSetBackground(pDisplay, gc2, lHighlightColor);
1784  }
1785  else
1786  {
1787  XSetForeground(pDisplay, gc2, lBGColor);
1788  XSetBackground(pDisplay, gc2, lBGColor);
1789  }
1790 
1791  // fill in with selected colors
1792 
1793  XFillRectangle(pDisplay, dw, gc2, rctTemp.left, rctTemp.top, rctTemp.right - rctTemp.left, rctTemp.bottom - rctTemp.top);
1794 
1795  if(fFocus < 0) // clicking 'x' ?
1796  {
1797  XSetForeground(pDisplay, gc2, lBGColor);
1798  }
1799  else
1800  {
1801  XSetForeground(pDisplay, gc2, lHighlightColor);
1802  }
1803 
1804  // now draw the '+' using a BOLD font
1805 
1806  rctTemp.left -=2;
1807  rctTemp.right += 2;
1808  rctTemp.top -= 2;
1809  rctTemp.bottom += 2; // big enough to 'center' properly
1810 
1811  DTDrawSingleLineText(fontSetBold, "x", pDisplay, gc2, dw, 0, 0, &rctTemp,
1813 
1814 
1815  XFreeGC(pDisplay, gc2);
1816  XDestroyRegion(rgnClip);
1817 
1818 
1819 // WB_ERROR_PRINT("TEMPORARY: %s - only partially implemented\n", __FUNCTION__);
1820 }
1821 
1822 
1823 
WB_GEOM geomVKnob
geometry for the vertical scroll bar &#39;knob&#39; (empty if not visible)
unsigned long WBGetGCBGColor(Display *pDisplay, GC gc)
returns the currently assigned background color for a GC
int iVBarHeight
calculated height of vertical scroll bar (re-calculate on window size change)
void PXM_RGBToYUV(int iR, int iG, int iB, int *piY, int *piU, int *piV)
Convert R, G, B values to Y, U, V with 0-255 range.
#define WB_POINTER_DBLCLICK
WM_POINTER &#39;double-click&#39; event, send in lieu of WB_POINTER_CLICK for double-click.
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
int iHMin
minimum horizontal range (0 if no bar)
#define WB_POINTER_CLICK
Mouse &#39;click&#39; event.
static unsigned char icos(unsigned char iVal)
integer 255 * cos(iVal * pi / 512) calculation via lookup table
static XColor clrScrollABG
active background scroll bar color
1st parameter (bar) - The horizontal scroll bar for the control or window
2nd parameter (direction) - down, right
#define WB_POINTER_DROP
WM_POINTER &#39;drop&#39; event, only sent if drag/drop supported AND was not &#39;canceled&#39;; see WB_POINTER_CANC...
Utilities for copying and drawing text, determining text extents, and so on.
static XColor clrScrollBG
background scroll bar color
int iVKnob
calculated relative Y pixel position of vertical scroll &#39;knob&#39;
1st parameter (bar) - The vertical scroll bar for the control or window.
#define LOAD_COLOR0(X, Y)
macro to load a color, mostly for readability
void DTDrawSingleLineText(XFontSet fontSet, const char *szText, Display *pDisplay, GC gc, Drawable dw, int iTabWidth, int iTabOrigin, const WB_RECT *prcBounds, int iAlignment)
draw single-line text
Definition: draw_text.c:1282
#define WB_POINTER_SCROLLDOWN
WM_POINTER &#39;scroll down&#39; event, caused by mouse button 5.
void PXM_YUVToRGB(int iY, int iU, int iV, int *piR, int *piG, int *piB)
Convert Y, U, V values to R, G, B with 0-255 range.
GC WBCopyDrawableGC(Display *pDisplay, Drawable dw, GC gcSrc)
makes a copy of the specified GC for the desired &#39;Drawable&#39;
int WBScrollBarEvent(Window wID, XEvent *pEvent, WB_SCROLLINFO *pScrollInfo)
Event handler for scroll bars.
WB_GEOM geomVBar
geometry for the vertical scroll bar excluding border (empty if not visible)
void WBDraw3DBorderRect(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomBorder, unsigned long lBorderColor1, unsigned long lBorderColor2)
Draw a 3D &#39;border&#39; rectangle.
int WBTextWidth(XFontSet fontSet, const char *szText, int cbText)
Obtain the pixel width of specified text for a specified XFontSet.
Definition: font_helper.c:2066
int WBWindowDispatch(Window wID, XEvent *pEvent)
Dispatches a window XEvent. May be called directly.
int WBCalcHScrollDragPos(WB_SCROLLINFO *pScrollInfo, int iX)
Calculate and assign the correct horizontal scroll bar position from mouse coordinates.
void WBSetVScrollRange(WB_SCROLLINFO *pSI, int iMin, int iMax)
Set the scroll range for a vertical scrollbar in the WB_SCROLLINFO structure.
WB_GEOM geomVUp
geometry for the vertical scroll bar &#39;up&#39; button (empty if not visible)
int iVPos
current vertical scroll position (N/A if outside of min/max range)
static XColor clrScrollBD3
3D highlight scroll bar border color (dark)
unsigned int border
Atom aWM_CHAR
keystroke/character notifications generated by API
horizontally centered text. tabs are treated as white space
Definition: draw_text.h:78
static int iInitScrollColorFlag
initialization flag for scroll colors. when zero, colors not yet initialized
2nd parameter (direction) - pgup, pgleft
&#39;configuration helper&#39; main header file for the X11 Work Bench Toolkit API
void PXM_RGBToPixel(XStandardColormap *pMap, XColor *pColor)
Icon Registration for application &#39;large&#39; and &#39;small&#39; icons.
int iScrollState
scroll state flags - see enumeration WBScrollState_ENUM
void PXM_PixelToRGB(XStandardColormap *pMap, XColor *pColor)
Convert the pixel menber of an XColor to RGB.
unsigned long long WB_UINT64
Platform abstract unsigned 64-bit integer.
2nd parameter (direction) - up, left
void WBDrawBorderRect(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomBorder, unsigned long lBorderColor)
Draw a &#39;border&#39; rectangle.
int iHScrollHeight
calculated height of horizontal scroll bar (in pixels); 0 if not known
Structure that defines scroll bar info for both horizontal and vertical scroll bars.
int iHPos
current horozontal scroll position (N/A if outside of min/max range)
static XColor clrScrollBD2
3D highlight scroll bar border color (light)
left button in &#39;drag&#39; state on vertical scroll bar (relies on drag cancel)
center using entire text height (ascent + descent for single line)
Definition: draw_text.h:86
unsigned int width
#define WB_POINTER_SCROLLUP
WM_POINTER &#39;scroll up&#39; event, caused by mouse button 4.
int iHMax
maximum horizontal range (0 if no bar)
static unsigned char isqrt(unsigned char iVal)
integer square root of a value 0-255
int iHBarWidth
calculated width of horizontal scroll bar (re-calculate on window size change)
unsigned int height
int WBFontSetHeight(Display *pDisplay, XFontSet fontSet)
Get the maximum character height from a font set.
Definition: font_helper.c:1178
#define WB_ERROR_PRINT(...)
Preferred method of implementing an &#39;error level&#39; debug message for all subsystems.
Definition: debug_helper.h:268
void WBSetHScrollPos(WB_SCROLLINFO *pSI, int iPos)
Set the scroll range for a horizontal scrollbar in the WB_SCROLLINFO structure.
void WBDraw3DBorderTab(Display *pDisplay, Drawable dw, GC gc, WB_GEOM *pgeomOutline, int fFocus, unsigned long lFGColor, unsigned long lBGColor, unsigned long lBorderColor1, unsigned long lBorderColor2, unsigned long lHighlightColor, XFontSet fontSet, XFontSet fontSetBold, Atom aGraphic, const char *szText)
Draw a &#39;tab&#39; within a specified &#39;outline&#39; rectangle.
#define WBGetWindowCopyGC2(wID, gcSrc)
makes a copy of the specified GC for the desired window
#define WB_POINTER_CANCEL
WM_POINTER &#39;cancel&#39; event, cancels an ongoing operation, such as drag/drop (useful for resource clean...
middle button in &#39;drag&#39; state on vertical scroll bar
2nd parameter (direction) - &#39;knob track&#39; - pos in data.l[2]
Atom aSCROLL_NOTIFY
#define WB_POINTER_MOVE
WM_POINTER &#39;move&#39; event, for motion notification during drag/drop.
#define WB_POINTER_DRAG
WM_POINTER &#39;drag&#39; event, window proc MUST return the window ID to auto-support drag/drop.
WB_GEOM geomHLeft
geometry for the horizontal scroll bar &#39;left&#39; button (empty if not visible)
void WBDrawDownArrow(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
Draw a down arrow in a window within a specified geometry.
#define WB_POINTER_BUTTON1
WM_POINTER button bitmask indicating that button 1 is pressed.
internal wrapper struct for &#39;rectangle&#39; definition
int WBCalcVScrollDragPos(WB_SCROLLINFO *pScrollInfo, int iY)
Calculate and assign the correct vertical scroll bar position from mouse coordinates.
WB_GEOM geomHBar
geometry for the horizontal scroll bar excluding border (empty if not visible)
int iVKnobSize
calculated pixel height of vertical scroll &#39;knob&#39;
void WBSetVScrollPos(WB_SCROLLINFO *pSI, int iPos)
Set the scroll range for a vertical scrollbar in the WB_SCROLLINFO structure.
#define LOAD_COLOR(X, Y, Z)
macro to load a color with a fallback, mostly for readability
2nd parameter (direction) - pgdn, pgright
Atom aWM_POINTER
pointer click/double-click/drag notifications generated by API
void WBDrawLeftArrow(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
Draw a left arrow in a window within a specified geometry.
static XColor clrScrollFG
foreground scroll bar color
generic &#39;NA&#39; or &#39;UNDEFINED&#39; value
static XColor clrScrollHBG
highlight background scroll bar color
static XColor clrScrollBD
standard scroll bar border color
static XColor clrScrollAFG
active foreground scroll bar color
int iHKnobSize
calculated pixel width of horizontal scroll &#39;knob&#39;
void WBSetHScrollRange(WB_SCROLLINFO *pSI, int iMin, int iMax)
Set the scroll range for a horizontal scrollbar in the WB_SCROLLINFO structure.
Display * WBGetWindowDisplay(Window wID)
returns the Display associated with a window
void WBPaintVScrollBar(WB_SCROLLINFO *pScrollInfo, Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomClient)
Paint the vertical scroll bar within a window based on WB_SCROLLINFO.
XCharStruct WBFontSetMaxBounds(Display *pDisplay, XFontSet fontSet)
Get a &#39;maximized&#39; copy of the &#39;max_bounds&#39; member for the font set.
Definition: font_helper.c:1287
int iVMax
maximum vertical range (0 if no bar)
WB_GEOM geomHKnob
geometry for the horizontal scroll bar &#39;knob&#39; (empty if not visible)
static void CheckInitScrollColors(void)
internal utility to check and initialize scroll bar standard colors
int iVMin
minimum vertical range (0 if no bar)
long long WB_INT64
Platform abstract 64-bit integer.
unsigned long WBGetGCFGColor(Display *pDisplay, GC gc)
returns the currently assigned foreground color for a GC
#define WB_LIKELY(x)
static XColor clrScrollHFG
highlight foreground scroll bar color
void WBCalcHScrollBar(WB_SCROLLINFO *pScrollInfo, WB_GEOM *pgeomClient, int iVScrollWidth, int iHScrollHeight, int nListItems, int nPos)
Calculate the parameters for a horizontal scroll bar.
void WBCalcVScrollBar(WB_SCROLLINFO *pScrollInfo, WB_GEOM *pgeomClient, int iVScrollWidth, int iHScrollHeight, int nListItems, int nPos)
Calculate the parameters for a vertical scroll bar.
WB_GEOM geomHRight
geometry for the horizontal scroll bar &#39;right&#39; button (empty if not visible)
void WBDrawUpArrow(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
Draw an up arrow in a window within a specified geometry.
#define WBPointInGeom(X, Y, G)
Returns logical TRUE if the point (X,Y) is within the borders of the geometry &#39;G&#39;.
int iHKnob
calculated relative X pixel position of horizontal scroll &#39;knob&#39;
WB_GEOM geomVDown
geometry for the vertical scroll bar &#39;down&#39; button (empty if not visible)
void WBDrawRightArrow(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
Draw a right arrow in a window within a specified geometry.
internal wrapper struct for X11 &#39;geometry&#39; definition
left button in &#39;drag&#39; state on horizontal scroll bar (relies on drag cancel)
right button in &#39;drag&#39; state on vertical scroll bar
int iVScrollWidth
calculated width of vertical scroll bar (in pixels); 0 if not known
XFontSet WBGetDefaultFontSet(Display *pDisplay)
Returns an XFontSet for the default font.
void WBUpdateScrollBarGeometry(WB_SCROLLINFO *pSI, XFontSet fontSetRef, WB_GEOM *pgeomClient, WB_GEOM *pgeomUsable)
Update the scroll bar geometry within the WB_SCROLLINFO structure.
void WBPaintHScrollBar(WB_SCROLLINFO *pScrollInfo, Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomClient)
Paint the horizontal scroll bar within a window based on WB_SCROLLINFO.
void WBDrawDashedRect(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
Draw a &#39;dashed&#39; rectangle.