1 /********************************************************************************
2 *                                                                               *
3 *                      G r a d i e n t B a r   W i d g e t                      *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2002,2005 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (at your option) any later version.            *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: FXGradientBar.cpp,v 1.68 2005/01/16 16:06:07 fox Exp $                   *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "fxkeys.h"
28 #include "FXHash.h"
29 #include "FXThread.h"
30 #include "FXStream.h"
31 #include "FXString.h"
32 #include "FXSize.h"
33 #include "FXPoint.h"
34 #include "FXRectangle.h"
35 #include "FXSettings.h"
36 #include "FXRegistry.h"
37 #include "FXApp.h"
38 #include "FXDCWindow.h"
39 #include "FXDrawable.h"
40 #include "FXImage.h"
41 #include "FXGradientBar.h"
42 #include "FXColorDialog.h"
43 #include "FXPopup.h"
44 #include "FXMenuPane.h"
45 #include "FXMenuCommand.h"
46 
47 /*
48   Notes:
49 
50   - Shoot for compatibility with GIMP, to the extent feasible;
51     format is:
52 
53     "GIMP Gradient"
54     number_of_segments
55     left middle right r0 g0 b0 a0 r1 g1 b1 a1 type coloring
56     left middle right r0 g0 b0 a0 r1 g1 b1 a1 type coloring
57 
58     Where type is:
59 
60     0 = linear
61     1 = curved
62     2 = sine
63     3 = sphere increasing
64     4 = sphere decreasing
65 
66     and coloring is:
67 
68     0 = RGB
69     1 = counter clockwise hue, sat, value
70     2 = clockwise hue, sat, value
71 
72     FXGradientBar will not implement coloring #1 and #2.
73 
74   - Need ability to change alpha for all pivots (not the colors!).
75 
76   - Set/get all segmemts API.
77 
78   - Drop left of pivot changes left color, right changes right color,
79     and on changes both.
80 
81   - Pass -1 if many segments are affected; otherwise pass affected segment
82     index only.
83 
84 */
85 
86 #define CONTROL_SIZE        9
87 #define BAR_WIDTH           64
88 #define BAR_HEIGHT          16
89 
90 #define TAB_SIZE            9
91 #define TAB_DIST            5
92 #define TOP_PAD             3
93 #define BOTTOM_PAD          3
94 #define SIDE_PAD            6
95 #define TAB_PROXIMITY       20
96 #define ARROW_WIDTH         12
97 #define ARROW_HEIGHT        6
98 #define PICK_EXTRA          3
99 
100 #define INT(x)              ((int)((x)+0.5))
101 #define BLEND(ch,bg,alpha)  (((bg)*(255-(alpha))+(ch)*(alpha))/255)
102 #define EPSILON             1.0E-10
103 #define GRADIENTBAR_MASK    (GRADIENTBAR_HORIZONTAL|GRADIENTBAR_VERTICAL|GRADIENTBAR_NO_CONTROLS|GRADIENTBAR_CONTROLS_TOP|GRADIENTBAR_CONTROLS_BOTTOM|GRADIENTBAR_CONTROLS_LEFT|GRADIENTBAR_CONTROLS_RIGHT)
104 
105 using namespace FX;
106 
107 /*******************************************************************************/
108 
109 namespace FX {
110 
111 
112 // Map
113 FXDEFMAP(FXGradientBar) FXGradientBarMap[]={
114   FXMAPFUNC(SEL_PAINT,0,FXGradientBar::onPaint),
115   FXMAPFUNC(SEL_MOTION,0,FXGradientBar::onMotion),
116   FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXGradientBar::onLeftBtnPress),
117   FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXGradientBar::onLeftBtnRelease),
118   FXMAPFUNC(SEL_DND_ENTER,0,FXGradientBar::onDNDEnter),
119   FXMAPFUNC(SEL_DND_LEAVE,0,FXGradientBar::onDNDLeave),
120   FXMAPFUNC(SEL_DND_DROP,0,FXGradientBar::onDNDDrop),
121   FXMAPFUNC(SEL_DND_MOTION,0,FXGradientBar::onDNDMotion),
122   FXMAPFUNC(SEL_QUERY_TIP,0,FXGradientBar::onQueryTip),
123   FXMAPFUNC(SEL_QUERY_HELP,0,FXGradientBar::onQueryHelp),
124   FXMAPFUNC(SEL_UPDATE,FXGradientBar::ID_RECENTER,FXGradientBar::onUpdRecenter),
125   FXMAPFUNC(SEL_COMMAND,FXGradientBar::ID_RECENTER,FXGradientBar::onCmdRecenter),
126   FXMAPFUNC(SEL_UPDATE,FXGradientBar::ID_SPLIT,FXGradientBar::onUpdSplit),
127   FXMAPFUNC(SEL_COMMAND,FXGradientBar::ID_SPLIT,FXGradientBar::onCmdSplit),
128   FXMAPFUNC(SEL_UPDATE,FXGradientBar::ID_MERGE,FXGradientBar::onUpdMerge),
129   FXMAPFUNC(SEL_COMMAND,FXGradientBar::ID_MERGE,FXGradientBar::onCmdMerge),
130   FXMAPFUNC(SEL_UPDATE,FXGradientBar::ID_UNIFORM,FXGradientBar::onUpdUniform),
131   FXMAPFUNC(SEL_COMMAND,FXGradientBar::ID_UNIFORM,FXGradientBar::onCmdUniform),
132   FXMAPFUNCS(SEL_COMMAND,FXGradientBar::ID_BLEND_LINEAR,FXGradientBar::ID_BLEND_DECREASING,FXGradientBar::onCmdBlending),
133   FXMAPFUNCS(SEL_UPDATE,FXGradientBar::ID_BLEND_LINEAR,FXGradientBar::ID_BLEND_DECREASING,FXGradientBar::onUpdBlending),
134   FXMAPFUNCS(SEL_UPDATE,FXGradientBar::ID_LOWER_COLOR,FXGradientBar::ID_UPPER_COLOR,FXGradientBar::onUpdSegColor),
135   FXMAPFUNCS(SEL_CHANGED,FXGradientBar::ID_LOWER_COLOR,FXGradientBar::ID_UPPER_COLOR,FXGradientBar::onCmdSegColor),
136   FXMAPFUNCS(SEL_COMMAND,FXGradientBar::ID_LOWER_COLOR,FXGradientBar::ID_UPPER_COLOR,FXGradientBar::onCmdSegColor),
137   FXMAPFUNC(SEL_COMMAND,FXGradientBar::ID_SETHELPSTRING,FXGradientBar::onCmdSetHelp),
138   FXMAPFUNC(SEL_COMMAND,FXGradientBar::ID_GETHELPSTRING,FXGradientBar::onCmdGetHelp),
139   FXMAPFUNC(SEL_COMMAND,FXGradientBar::ID_SETTIPSTRING,FXGradientBar::onCmdSetTip),
140   FXMAPFUNC(SEL_COMMAND,FXGradientBar::ID_GETTIPSTRING,FXGradientBar::onCmdGetTip),
141   };
142 
143 
144 // Object implementation
FXIMPLEMENT(FXGradientBar,FXFrame,FXGradientBarMap,ARRAYNUMBER (FXGradientBarMap))145 FXIMPLEMENT(FXGradientBar,FXFrame,FXGradientBarMap,ARRAYNUMBER(FXGradientBarMap))
146 
147 
148 // For serialization
149 FXGradientBar::FXGradientBar(){
150   flags|=FLAG_ENABLED|FLAG_DROPTARGET;
151   sellower=-1;
152   selupper=-1;
153   dropped=-1;
154   current=-1;
155   anchor=-1;
156   grip=GRIP_NONE;
157   where=GRIP_NONE;
158   offset=0;
159   }
160 
161 
162 // Construct gradient editor
FXGradientBar(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb)163 FXGradientBar::FXGradientBar(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb):
164   FXFrame(p,opts,x,y,w,h,pl,pr,pt,pb){
165   flags|=FLAG_ENABLED|FLAG_DROPTARGET;
166   target=tgt;
167   message=sel;
168   backColor=getApp()->getHiliteColor();
169   selectColor=FXRGB((92*FXREDVAL(backColor))/100,(92*FXGREENVAL(backColor))/100,(92*FXBLUEVAL(backColor))/100);
170   bar=new FXImage(getApp(),NULL,IMAGE_DITHER|IMAGE_KEEP|IMAGE_OWNED|IMAGE_SHMI|IMAGE_SHMP,2,2);
171   nsegs=3;
172   FXMALLOC(&seg,FXGradient,nsegs);
173   seg[0].lower=0.0;
174   seg[0].middle=0.2;
175   seg[0].upper=0.4;
176   seg[0].lowerColor=FXRGBA(255,0,0,255);
177   seg[0].upperColor=FXRGBA(0,255,0,255);
178   seg[0].blend=GRADIENT_BLEND_LINEAR;
179   seg[1].lower=0.4;
180   seg[1].middle=0.5;
181   seg[1].upper=0.6;
182   seg[1].lowerColor=FXRGBA(0,0,0,0);
183   seg[1].upperColor=FXRGBA(255,255,0,255);
184   seg[1].blend=GRADIENT_BLEND_LINEAR;
185   seg[2].lower=0.6;
186   seg[2].middle=0.8;
187   seg[2].upper=1.0;
188   seg[2].lowerColor=FXRGBA(0,0,0,0);
189   seg[2].upperColor=FXRGBA(255,0,0,255);
190   seg[2].blend=GRADIENT_BLEND_LINEAR;
191   sellower=-1;
192   selupper=-1;
193   dropped=-1;
194   current=-1;
195   anchor=-1;
196   grip=GRIP_NONE;
197   where=GRIP_NONE;
198   offset=0;
199   }
200 
201 
202 // Create window
create()203 void FXGradientBar::create(){
204   FXFrame::create();
205   if(!colorType){colorType=getApp()->registerDragType(colorTypeName);}
206   updatebar();
207   bar->create();
208   }
209 
210 
211 // Get default width
getDefaultWidth()212 FXint FXGradientBar::getDefaultWidth(){
213   register FXint w=BAR_WIDTH;
214   if(options&GRADIENTBAR_VERTICAL){
215     w=BAR_HEIGHT;
216     if(options&GRADIENTBAR_CONTROLS_LEFT) w+=CONTROL_SIZE+1;
217     if(options&GRADIENTBAR_CONTROLS_RIGHT) w+=CONTROL_SIZE+1;
218     }
219   return w+4+padleft+padright+(border<<1);
220   }
221 
222 
223 // Get default height
getDefaultHeight()224 FXint FXGradientBar::getDefaultHeight(){
225   register FXint h=BAR_WIDTH;
226   if(!(options&GRADIENTBAR_VERTICAL)){
227     h=BAR_HEIGHT;
228     if(options&GRADIENTBAR_CONTROLS_TOP) h+=CONTROL_SIZE+1;
229     if(options&GRADIENTBAR_CONTROLS_BOTTOM) h+=CONTROL_SIZE+1;
230     }
231   return h+4+padtop+padbottom+(border<<1);
232   }
233 
234 
235 // Resize the bar
layout()236 void FXGradientBar::layout(){
237   register FXint ww,hh;
238   ww=width-padleft-padright-(border<<1)-4;
239   hh=height-padtop-padbottom-(border<<1)-4;
240   if(options&GRADIENTBAR_VERTICAL){
241     if(options&GRADIENTBAR_CONTROLS_LEFT) ww-=CONTROL_SIZE+1;
242     if(options&GRADIENTBAR_CONTROLS_RIGHT) ww-=CONTROL_SIZE+1;
243     }
244   else{
245     if(options&GRADIENTBAR_CONTROLS_TOP) hh-=CONTROL_SIZE+1;
246     if(options&GRADIENTBAR_CONTROLS_BOTTOM) hh-=CONTROL_SIZE+1;
247     }
248   if(ww<2) ww=2;
249   if(hh<2) hh=2;
250   if((bar->getWidth()!=ww) || (bar->getHeight()!=hh) || (flags&FLAG_DIRTY)){
251     if((bar->getWidth()!=ww) || (bar->getHeight()!=hh)){
252       bar->resize(ww,hh);
253       }
254     updatebar();
255     bar->render();
256     update();
257     }
258   flags&=~FLAG_DIRTY;
259   }
260 
261 
262 typedef FXdouble (*BLENDFUNC)(FXdouble,FXdouble);
263 
264 
265 // Linear blend
blendlinear(FXdouble middle,FXdouble pos)266 FXdouble FXGradientBar::blendlinear(FXdouble middle,FXdouble pos){
267   register FXdouble factor;
268   if(pos<=middle){
269     factor=(middle<EPSILON) ? 0.0 : 0.5*pos/middle;
270     }
271   else{
272     pos-=middle;
273     middle=1.0-middle;
274     factor=(middle<EPSILON) ? 1.0 : 0.5+0.5*pos/middle;
275     }
276   return factor;
277   }
278 
279 
280 // Power blend
blendpower(FXdouble middle,FXdouble pos)281 FXdouble FXGradientBar::blendpower(FXdouble middle,FXdouble pos){
282   if(middle<EPSILON) middle=EPSILON;
283   return pow(pos,log(0.5)/log(middle));
284   }
285 
286 
287 // Sinusoidal blend
blendsine(FXdouble middle,FXdouble pos)288 FXdouble FXGradientBar::blendsine(FXdouble middle,FXdouble pos){
289   pos=blendlinear(middle,pos);
290   return (sin((-PI/2.0)+PI*pos)+1.0)/2.0;
291   }
292 
293 
294 // Quadratic increasing blend
blendincreasing(FXdouble middle,FXdouble pos)295 FXdouble FXGradientBar::blendincreasing(FXdouble middle,FXdouble pos){
296   pos=blendlinear(middle,pos)-1.0;
297   return sqrt(1.0-pos*pos);       // Works for convex increasing and concave decreasing
298   }
299 
300 
301 // Quadratic decreasing blend
blenddecreasing(FXdouble middle,FXdouble pos)302 FXdouble FXGradientBar::blenddecreasing(FXdouble middle,FXdouble pos){
303   pos=blendlinear(middle,pos);
304   return 1.0-sqrt(1.0-pos*pos);   // Works for convex decreasing and concave increasing
305   }
306 
307 
308 // Fill with gradient ramp
gradient(FXColor * ramp,FXint nramp)309 void FXGradientBar::gradient(FXColor *ramp,FXint nramp){
310   register FXint s,lr,lg,lb,la,ur,ug,ub,ua,d,l,h,m,i;
311   register FXdouble len=seg[nsegs-1].upper-seg[0].lower;
312   register FXdouble del=nramp-1;
313   register BLENDFUNC blend=NULL;
314   register FXdouble f,t;
315 
316   FXASSERT(len>0.0);
317 
318   // Loop over segments
319   for(s=0; s<nsegs; s++){
320 
321     // Lower color components
322     lr=((FXuchar*)&seg[s].lowerColor)[0];
323     lg=((FXuchar*)&seg[s].lowerColor)[1];
324     lb=((FXuchar*)&seg[s].lowerColor)[2];
325     la=((FXuchar*)&seg[s].lowerColor)[3];
326 
327     // Upper color components
328     ur=((FXuchar*)&seg[s].upperColor)[0];
329     ug=((FXuchar*)&seg[s].upperColor)[1];
330     ub=((FXuchar*)&seg[s].upperColor)[2];
331     ua=((FXuchar*)&seg[s].upperColor)[3];
332 
333     // Pixel range of segment
334     l=(FXint)(0.5+(del*(seg[s].lower-seg[0].lower))/len);
335     m=(FXint)(0.5+(del*(seg[s].middle-seg[0].lower))/len);
336     h=(FXint)(0.5+(del*(seg[s].upper-seg[0].lower))/len);
337     FXASSERT(0<=l && l<=m && m<=h && h<nramp);
338 
339     // Get blend function
340     switch(seg[s].blend){
341       case GRADIENT_BLEND_LINEAR:     blend=blendlinear;     break;
342       case GRADIENT_BLEND_POWER:      blend=blendpower;      break;
343       case GRADIENT_BLEND_SINE:       blend=blendsine;       break;
344       case GRADIENT_BLEND_INCREASING: blend=blendincreasing; break;
345       case GRADIENT_BLEND_DECREASING: blend=blenddecreasing; break;
346       }
347 
348     d=h-l;
349     if(0<d){
350       for(i=l; i<=h; i++){
351         FXASSERT(0<=i && i<nramp);
352         f=blend(((FXdouble)m-(FXdouble)l)/(FXdouble)d,((FXdouble)i-(FXdouble)l)/(FXdouble)d);
353         t=1.0-f;
354         ((FXuchar*)&ramp[i])[0]=(FXuchar)(t*lr+f*ur);
355         ((FXuchar*)&ramp[i])[1]=(FXuchar)(t*lg+f*ug);
356         ((FXuchar*)&ramp[i])[2]=(FXuchar)(t*lb+f*ub);
357         ((FXuchar*)&ramp[i])[3]=(FXuchar)(t*la+f*ua);
358         }
359       }
360     }
361   }
362 
363 
364 // Update bar
updatebar()365 void FXGradientBar::updatebar(){
366   register FXint barw=bar->getWidth();
367   register FXint barh=bar->getHeight();
368   register FXint x,y,r,g,b,a;
369   register FXColor clr;
370   FXColor *ramp=NULL;
371 
372   // Vertical
373   if(options&GRADIENTBAR_VERTICAL){
374 
375     // Allocate ramp
376     FXMALLOC(&ramp,FXColor,barh);
377 
378     // Fill with gradient
379     gradient(ramp,barh);
380 
381     // Fill image
382     for(y=0; y<barh; y++){
383       r=((FXuchar*)&ramp[y])[0];
384       g=((FXuchar*)&ramp[y])[1];
385       b=((FXuchar*)&ramp[y])[2];
386       a=((FXuchar*)&ramp[y])[3];
387       clr=FXRGB(BLEND(r,255,a), BLEND(g,255,a), BLEND(b,255,a));
388       for(x=0; x<barw/2; x++){
389         bar->setPixel(x,barh-y-1,clr);
390         }
391       clr=FXRGB(BLEND(r,0,a), BLEND(g,0,a), BLEND(b,0,a));
392       for(x=barw/2; x<barw; x++){
393         bar->setPixel(x,barh-y-1,clr);
394         }
395       }
396     }
397 
398   // Horizontal
399   else{
400 
401     // Allocate ramp
402     FXMALLOC(&ramp,FXColor,barw);
403 
404     // Fill with gradient
405     gradient(ramp,barw);
406 
407     // Fill image
408     for(x=0; x<barw; x++){
409       r=((FXuchar*)&ramp[x])[0];
410       g=((FXuchar*)&ramp[x])[1];
411       b=((FXuchar*)&ramp[x])[2];
412       a=((FXuchar*)&ramp[x])[3];
413       clr=FXRGB(BLEND(r,255,a), BLEND(g,255,a), BLEND(b,255,a));
414       for(y=0; y<barh/2; y++){
415         bar->setPixel(x,y,clr);
416         }
417       clr=FXRGB(BLEND(r,0,a), BLEND(g,0,a), BLEND(b,0,a));
418       for(y=barh/2; y<barh; y++){
419         bar->setPixel(x,y,clr);
420         }
421       }
422     }
423 
424   // Free ramp
425   FXFREE(&ramp);
426   }
427 
428 
429 // Draw up arrow
drawUpArrow(FXDCWindow & dc,FXint x,FXint y,FXColor clr)430 void FXGradientBar::drawUpArrow(FXDCWindow& dc,FXint x,FXint y,FXColor clr){
431   FXPoint arrow[3];
432   arrow[0].x=x;                arrow[0].y=y;
433   arrow[1].x=x-CONTROL_SIZE/2; arrow[1].y=y+CONTROL_SIZE;
434   arrow[2].x=x+CONTROL_SIZE/2; arrow[2].y=y+CONTROL_SIZE;
435   dc.setForeground(clr);
436   dc.fillPolygon(arrow,3);
437   dc.setForeground(FXRGB(0,0,0));
438   dc.drawLine(x,y,x+CONTROL_SIZE/2,y+CONTROL_SIZE);
439   dc.drawLine(x-CONTROL_SIZE/2,y+CONTROL_SIZE,x+CONTROL_SIZE/2,y+CONTROL_SIZE);
440   dc.drawLine(x,y,x-CONTROL_SIZE/2,y+CONTROL_SIZE);
441   }
442 
443 
444 // Draw down arrow
drawDnArrow(FXDCWindow & dc,FXint x,FXint y,FXColor clr)445 void FXGradientBar::drawDnArrow(FXDCWindow& dc,FXint x,FXint y,FXColor clr){
446   FXPoint arrow[3];
447   arrow[0].x=x-CONTROL_SIZE/2; arrow[0].y=y;
448   arrow[1].x=x+CONTROL_SIZE/2; arrow[1].y=y;
449   arrow[2].x=x;                arrow[2].y=y+CONTROL_SIZE;
450   dc.setForeground(clr);
451   dc.fillPolygon(arrow,3);
452   dc.setForeground(FXRGB(0,0,0));
453   dc.drawLine(x-CONTROL_SIZE/2,y,x+CONTROL_SIZE/2,y);
454   dc.drawLine(x,y+CONTROL_SIZE,x-CONTROL_SIZE/2,y);
455   dc.drawLine(x,y+CONTROL_SIZE,x+CONTROL_SIZE/2,y);
456   }
457 
458 
459 // Draw right arrow
drawRtArrow(FXDCWindow & dc,FXint x,FXint y,FXColor clr)460 void FXGradientBar::drawRtArrow(FXDCWindow& dc,FXint x,FXint y,FXColor clr){
461   FXPoint arrow[3];
462   arrow[0].x=x;              arrow[0].y=y-CONTROL_SIZE/2;
463   arrow[1].x=x;              arrow[1].y=y+CONTROL_SIZE/2;
464   arrow[2].x=x+CONTROL_SIZE; arrow[2].y=y;
465   dc.setForeground(clr);
466   dc.fillPolygon(arrow,3);
467   dc.setForeground(FXRGB(0,0,0));
468   dc.drawLine(x+CONTROL_SIZE,y,x,y-CONTROL_SIZE/2);
469   dc.drawLine(x+CONTROL_SIZE,y,x,y+CONTROL_SIZE/2);
470   dc.drawLine(x,y-CONTROL_SIZE/2,x,y+CONTROL_SIZE/2);
471   }
472 
473 
474 // Draw left arrow
drawLtArrow(FXDCWindow & dc,FXint x,FXint y,FXColor clr)475 void FXGradientBar::drawLtArrow(FXDCWindow& dc,FXint x,FXint y,FXColor clr){
476   FXPoint arrow[3];
477   arrow[0].x=x+CONTROL_SIZE; arrow[0].y=y-CONTROL_SIZE/2;
478   arrow[1].x=x+CONTROL_SIZE; arrow[1].y=y+CONTROL_SIZE/2;
479   arrow[2].x=x;              arrow[2].y=y;
480   dc.setForeground(clr);
481   dc.fillPolygon(arrow,3);
482   dc.setForeground(FXRGB(0,0,0));
483   dc.drawLine(x,y,x+CONTROL_SIZE,y-CONTROL_SIZE/2);
484   dc.drawLine(x,y,x+CONTROL_SIZE,y+CONTROL_SIZE/2);
485   dc.drawLine(x+CONTROL_SIZE,y-CONTROL_SIZE/2,x+CONTROL_SIZE,y+CONTROL_SIZE/2);
486   }
487 
488 
489 // Draw top arrows
drawTopArrows(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)490 void FXGradientBar::drawTopArrows(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
491   register FXdouble len=seg[nsegs-1].upper-seg[0].lower;
492   register FXint s,l,m,r;
493   FXASSERT(len>0.0);
494   for(s=0; s<nsegs; s++){
495     l=(FXint)(0.5+((w-1)*(seg[s].lower-seg[0].lower))/len);
496     m=(FXint)(0.5+((w-1)*(seg[s].middle-seg[0].lower))/len);
497     r=(FXint)(0.5+((w-1)*(seg[s].upper-seg[0].lower))/len);
498     dc.setForeground(isSegmentSelected(s)?selectColor:backColor);
499     dc.fillRectangle(x+l,y,r-l,h);
500     if(0<s) drawDnArrow(dc,x+l,y,FXRGB(0,0,0));
501     drawDnArrow(dc,x+m,y,FXRGB(255,255,255));
502     }
503   drawDnArrow(dc,x,y,FXRGB(0,0,0));
504   drawDnArrow(dc,x+w-1,y,FXRGB(0,0,0));
505   }
506 
507 
508 // Draw bottom arrows
drawBottomArrows(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)509 void FXGradientBar::drawBottomArrows(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
510   register FXdouble len=seg[nsegs-1].upper-seg[0].lower;
511   register FXint s,l,m,r;
512   FXASSERT(len>0.0);
513   for(s=0; s<nsegs; s++){
514     l=(FXint)(0.5+((w-1)*(seg[s].lower-seg[0].lower))/len);
515     m=(FXint)(0.5+((w-1)*(seg[s].middle-seg[0].lower))/len);
516     r=(FXint)(0.5+((w-1)*(seg[s].upper-seg[0].lower))/len);
517     dc.setForeground(isSegmentSelected(s)?selectColor:backColor);
518     dc.fillRectangle(x+l,y,r-l,h);
519     if(0<s) drawUpArrow(dc,x+l,y,FXRGB(0,0,0));
520     drawUpArrow(dc,x+m,y,FXRGB(255,255,255));
521     }
522   drawUpArrow(dc,x,y,FXRGB(0,0,0));
523   drawUpArrow(dc,x+w-1,y,FXRGB(0,0,0));
524   }
525 
526 
527 // Draw left arrows
drawLeftArrows(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)528 void FXGradientBar::drawLeftArrows(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
529   register FXdouble len=seg[nsegs-1].upper-seg[0].lower;
530   register FXint s,t,m,b;
531   FXASSERT(len>0.0);
532   for(s=0; s<nsegs; s++){
533     t=(FXint)(0.5+((h-1)*(seg[s].upper-seg[0].lower))/len);
534     m=(FXint)(0.5+((h-1)*(seg[s].middle-seg[0].lower))/len);
535     b=(FXint)(0.5+((h-1)*(seg[s].lower-seg[0].lower))/len);
536     dc.setForeground(isSegmentSelected(s)?selectColor:backColor);
537     dc.fillRectangle(x,y+h-t-1,w,t-b);
538     if(0<s) drawRtArrow(dc,x,y+h-b-1,FXRGB(0,0,0));
539     drawRtArrow(dc,x,y+h-m-1,FXRGB(255,255,255));
540     }
541   drawRtArrow(dc,x,y,FXRGB(0,0,0));
542   drawRtArrow(dc,x,y+h-1,FXRGB(0,0,0));
543   }
544 
545 
546 // Draw right arrows
drawRightArrows(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)547 void FXGradientBar::drawRightArrows(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
548   register FXdouble len=seg[nsegs-1].upper-seg[0].lower;
549   register FXint s,t,m,b;
550   FXASSERT(len>0.0);
551   for(s=0; s<nsegs; s++){
552     t=(FXint)(0.5+((h-1)*(seg[s].upper-seg[0].lower))/len);
553     m=(FXint)(0.5+((h-1)*(seg[s].middle-seg[0].lower))/len);
554     b=(FXint)(0.5+((h-1)*(seg[s].lower-seg[0].lower))/len);
555     dc.setForeground(isSegmentSelected(s)?selectColor:backColor);
556     dc.fillRectangle(x,y+h-t-1,w,t-b);
557     if(0<s) drawLtArrow(dc,x,y+h-b-1,FXRGB(0,0,0));
558     drawLtArrow(dc,x,y+h-m-1,FXRGB(255,255,255));
559     }
560   drawLtArrow(dc,x,y,FXRGB(0,0,0));
561   drawLtArrow(dc,x,y+h-1,FXRGB(0,0,0));
562   }
563 
564 
565 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)566 long FXGradientBar::onPaint(FXObject*,FXSelector,void* ptr){
567   FXEvent *event=(FXEvent*)ptr;
568   FXDCWindow dc(this,event);
569   FXint barx,bary,barw,barh;
570 
571   // Frame
572   drawFrame(dc,0,0,width,height);
573 
574   // Background
575   dc.setForeground(baseColor);
576   dc.fillRectangle(border,border,padleft,height-(border<<1));
577   dc.fillRectangle(width-padright-border,border,padright,height-(border<<1));
578   dc.fillRectangle(border+padleft,border,width-padleft-padright-(border<<1),padtop);
579   dc.fillRectangle(border+padleft,height-padbottom-border,width-padleft-padright-(border<<1),padbottom);
580 
581   barx=padleft+border;
582   bary=padtop+border;
583   barw=width-padright-padleft-(border<<1);
584   barh=height-padbottom-padtop-(border<<1);
585 
586   // Sunken well for gradient
587   drawDoubleSunkenRectangle(dc,barx,bary,barw,barh);
588 
589   barx+=2;
590   bary+=2;
591   barw-=4;
592   barh-=4;
593 
594   // Set clip rectangle
595   dc.setClipRectangle(barx,bary,barw,barh);
596 
597   // Vertical gradient
598   if(options&GRADIENTBAR_VERTICAL){
599 
600     // Left controls
601     if(options&GRADIENTBAR_CONTROLS_LEFT){
602       drawLeftArrows(dc,barx,bary,CONTROL_SIZE+1,barh);
603       barx+=CONTROL_SIZE+1;
604       }
605 
606     // Draw the bar itself
607     dc.drawImage(bar,barx,bary);
608     barx+=bar->getWidth();
609 
610     // Right controls
611     if(options&GRADIENTBAR_CONTROLS_RIGHT){
612       drawRightArrows(dc,barx,bary,CONTROL_SIZE+1,barh);
613       }
614     }
615 
616   // Horizontal gradient
617   else{
618 
619     // Top controls
620     if(options&GRADIENTBAR_CONTROLS_TOP){
621       drawTopArrows(dc,barx,bary,barw,CONTROL_SIZE+1);
622       bary+=CONTROL_SIZE+1;
623       }
624 
625     // Draw the bar itself
626     dc.drawImage(bar,barx,bary);
627     bary+=bar->getHeight();
628 
629     // Bottom controls
630     if(options&GRADIENTBAR_CONTROLS_BOTTOM){
631       drawBottomArrows(dc,barx,bary,barw,CONTROL_SIZE+1);
632       }
633     }
634   return 1;
635   }
636 
637 
638 // Replace the current gradient segments
setGradients(const FXGradient * segments,FXint nsegments)639 void FXGradientBar::setGradients(const FXGradient *segments,FXint nsegments){
640   if(!segments || nsegments<1){ fxerror("FXGradientBar::setGradients: bad argument."); }
641   if(nsegments!=nsegs){
642     FXRESIZE(&seg,FXGradient,nsegments);
643     nsegs=nsegments;
644     if(selupper>=nsegs) selupper=nsegs-1;
645     if(sellower>=nsegs) sellower=nsegs-1;
646     if(current>=nsegs) current=nsegs-1;
647     if(anchor>=nsegs) anchor=nsegs-1;
648     }
649   memcpy(seg,segments,sizeof(FXGradient)*nsegments);
650   recalc();
651   }
652 
653 
654 // Return the gradient segments
getGradients(FXGradient * & segments,FXint & nsegments) const655 void FXGradientBar::getGradients(FXGradient*& segments,FXint& nsegments) const {
656   nsegments=0;
657   if(FXMALLOC(&segments,FXGradient,nsegs)){
658     memcpy(segments,seg,sizeof(FXGradient)*nsegs);
659     nsegments=nsegs;
660     }
661   }
662 
663 
664 // Select segment
selectSegments(FXint fm,FXint to,FXbool notify)665 FXbool FXGradientBar::selectSegments(FXint fm,FXint to,FXbool notify){
666   if(fm>to || fm<0 || to>=nsegs){ fxerror("FXGradientBar::selectSegments: argument out of range."); }
667   if(sellower!=fm || selupper!=to){
668     sellower=fm;
669     selupper=to;
670     update();
671     if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),NULL);}
672     return TRUE;
673     }
674   return FALSE;
675   }
676 
677 
678 // Deselect all segments
deselectSegments(FXbool notify)679 FXbool FXGradientBar::deselectSegments(FXbool notify){
680   if(0<=sellower && 0<=selupper){
681     sellower=selupper=-1;
682     update();
683     if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),NULL);}
684     return TRUE;
685     }
686   return FALSE;
687   }
688 
689 
690 // Is segment selected
isSegmentSelected(FXint s) const691 FXbool FXGradientBar::isSegmentSelected(FXint s) const {
692   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::isSegmentSelected: argument out of range."); }
693   return sellower<=s && s<=selupper;
694   }
695 
696 
697 // Set current item
setCurrentSegment(FXint index,FXbool notify)698 void FXGradientBar::setCurrentSegment(FXint index,FXbool notify){
699   if(index<-1 || nsegs<=index){ fxerror("%s::setCurrentSegment: index out of range.\n",getClassName()); }
700   if(index!=current){
701     current=index;
702     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);}
703     }
704   }
705 
706 
707 // Change anchor segment
setAnchorSegment(FXint index)708 void FXGradientBar::setAnchorSegment(FXint index){
709   if(index<-1 || nsegs<=index){ fxerror("%s::setAnchorSegment: index out of range.\n",getClassName()); }
710   anchor=index;
711   }
712 
713 
714 // Move lower point of segment
moveSegmentLower(FXint sg,FXdouble val,FXbool notify)715 void FXGradientBar::moveSegmentLower(FXint sg,FXdouble val,FXbool notify){
716   if(0<sg && sg<nsegs){
717     if(val<seg[sg-1].middle) val=seg[sg-1].middle;
718     if(seg[sg].middle<val) val=seg[sg].middle;
719     if(seg[sg].lower!=val){
720       seg[sg-1].upper=seg[sg].lower=val;
721       recalc();
722       if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)sg);}
723       }
724     }
725   }
726 
727 
728 // Move middle point of segment
moveSegmentMiddle(FXint sg,FXdouble val,FXbool notify)729 void FXGradientBar::moveSegmentMiddle(FXint sg,FXdouble val,FXbool notify){
730   if(0<=sg && sg<nsegs){
731     if(val<seg[sg].lower) val=seg[sg].lower;
732     if(seg[sg].upper<val) val=seg[sg].upper;
733     if(seg[sg].middle!=val){
734       seg[sg].middle=val;
735       recalc();
736       if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)sg);}
737       }
738     }
739   }
740 
741 
742 // Move upper point of segment
moveSegmentUpper(FXint sg,FXdouble val,FXbool notify)743 void FXGradientBar::moveSegmentUpper(FXint sg,FXdouble val,FXbool notify){
744   if(0<=sg && sg<nsegs-1){
745     if(val<seg[sg].middle) val=seg[sg].middle;
746     if(seg[sg+1].middle<val) val=seg[sg+1].middle;
747     if(seg[sg].upper!=val){
748       seg[sg+1].lower=seg[sg].upper=val;
749       recalc();
750       if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)sg);}
751       }
752     }
753   }
754 
755 
756 // Move segments
moveSegments(FXint sglo,FXint sghi,FXdouble val,FXbool notify)757 void FXGradientBar::moveSegments(FXint sglo,FXint sghi,FXdouble val,FXbool notify){
758   register FXdouble delta, below,above,room;
759   register FXint i;
760   if(0<=sglo && sghi<nsegs && sglo<=sghi){
761     FXTRACE((1,"sglo=%d sghi=%d val=%.10f\n",sglo,sghi,val));
762     below=seg[sglo].middle-seg[sglo].lower;
763     above=seg[sghi].upper-seg[sglo].middle;
764     room=seg[sghi].middle-seg[sglo].middle;
765     if(sglo==0){
766       if(val<seg[0].lower) val=seg[0].lower;
767       }
768     else{
769       if(val-below<seg[sglo-1].middle) val=seg[sglo-1].middle+below;
770       }
771     if(sghi==nsegs-1){
772       if(val+room>seg[nsegs-1].upper) val=seg[nsegs-1].upper-room;
773       }
774     else{
775       if(val+above>seg[sghi+1].middle) val=seg[sghi+1].middle-above;
776       }
777     delta=val-seg[sglo].middle;
778     if(delta!=0.0){
779       for(i=sglo; i<=sghi; i++){
780         if(0<i) seg[i].lower+=delta;
781         seg[i].middle+=delta;
782         if(i<nsegs-1) seg[i].upper+=delta;
783         }
784       if(0<sglo){
785         seg[sglo-1].upper=seg[sglo].lower;
786         }
787       if(sghi<nsegs-1){
788         seg[sghi+1].lower=seg[sghi].upper;
789         }
790       recalc();
791       if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);}
792       }
793     }
794   }
795 
796 
797 // Split segment at the midpoint
splitSegments(FXint sglo,FXint sghi,FXbool notify)798 void FXGradientBar::splitSegments(FXint sglo,FXint sghi,FXbool notify){
799   register FXint n=sghi-sglo+1;
800   register FXint i,j;
801   if(0<=sglo && sghi<nsegs && 0<n){
802     FXRESIZE(&seg,FXGradient,nsegs+n);
803     memmove(&seg[sghi+n],&seg[sghi],sizeof(FXGradient)*(nsegs-sghi));
804     for(i=sghi,j=sghi+n-1; sglo<=i; i-=1,j-=2){
805       seg[j+1].upper=seg[i].upper;
806       seg[j+1].lower=seg[i].middle;
807       seg[j+1].middle=0.5*(seg[j+1].upper+seg[j+1].lower);
808       seg[j+1].lowerColor=seg[i].upperColor;// FIXME
809       seg[j+1].upperColor=seg[i].upperColor;
810       seg[j+1].blend=seg[i].blend;
811       seg[j+0].upper=seg[i].middle;
812       seg[j+0].lower=seg[i].lower;
813       seg[j+0].middle=0.5*(seg[j+0].upper+seg[j+0].lower);
814       seg[j+0].lowerColor=seg[i].lowerColor;
815       seg[j+0].upperColor=seg[i].upperColor;// FIXME
816       seg[j+0].blend=seg[i].blend;
817       }
818     nsegs+=n;
819     recalc();
820     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);}
821     }
822   }
823 
824 
825 // Merge segments
mergeSegments(FXint sglo,FXint sghi,FXbool notify)826 void FXGradientBar::mergeSegments(FXint sglo,FXint sghi,FXbool notify){
827   register FXint n=sghi-sglo;
828   if(0<=sglo && sghi<nsegs && 0<n){
829     seg[sglo].middle=(n&1)?seg[(sghi+sglo)/2].upper:seg[(sghi+sglo)/2].middle;
830     seg[sglo].upper=seg[sghi].upper;
831     seg[sglo].upperColor=seg[sghi].upperColor;
832     memmove(&seg[sglo+1],&seg[sghi+1],sizeof(FXGradient)*(nsegs-sghi-1));
833     FXRESIZE(&seg,FXGradient,nsegs-n);
834     nsegs-=n;
835     if(selupper>=nsegs) selupper=nsegs-1;
836     if(sellower>=nsegs) sellower=nsegs-1;
837     if(current>=nsegs) current=nsegs-1;
838     if(anchor>=nsegs) anchor=nsegs-1;
839     recalc();
840     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);}
841     }
842   }
843 
844 
845 // Make segments uniformly distributed
uniformSegments(FXint sglo,FXint sghi,FXbool notify)846 void FXGradientBar::uniformSegments(FXint sglo,FXint sghi,FXbool notify){
847   register FXdouble m,d,a;
848   register FXint s;
849   if(0<=sglo && sghi<nsegs && sglo<=sghi){
850     d=sghi-sglo+1;
851     m=seg[sghi].upper-seg[sglo].lower;
852     a=seg[sglo].lower;
853     for(s=sglo; s<=sghi; s++){
854       seg[s].lower=a+(FXdouble)(s-sglo)*m/d;
855       seg[s].upper=a+(FXdouble)(s-sglo+1)*m/d;
856       seg[s].middle=0.5*(seg[s].lower+seg[s].upper);
857       }
858     recalc();
859     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);}
860     }
861   }
862 
863 
864 // Change blend curve of segment
blendSegments(FXint sglo,FXint sghi,FXuint blend,FXbool notify)865 void FXGradientBar::blendSegments(FXint sglo,FXint sghi,FXuint blend,FXbool notify){
866   register FXint s;
867   if(0<=sglo && sghi<nsegs && sglo<=sghi){
868     for(s=sglo; s<=sghi; s++){
869       seg[s].blend=blend;
870       }
871     recalc();
872     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);}
873     }
874   }
875 
876 
877 // Determine which segment got hit
getSegment(FXint x,FXint y) const878 FXint FXGradientBar::getSegment(FXint x,FXint y) const {
879   register FXdouble shi=seg[nsegs-1].upper;
880   register FXdouble slo=seg[0].lower;
881   register FXdouble len=shi-slo;
882   register FXdouble del;
883   register FXint lo,hi,v,s;
884   FXASSERT(len>0.0);
885   if(options&GRADIENTBAR_VERTICAL){
886     if(y<border+padtop+2) return nsegs-1;
887     if(y>height-border-padbottom-2) return 0;
888     v=height-border-padbottom-y-3;
889     del=bar->getHeight()-1;
890     }
891   else{
892     if(x<border+padleft+2) return 0;
893     if(x>width-border-padright-2) return nsegs-1;
894     v=x-border-padleft-2;
895     del=bar->getWidth()-1;
896     }
897   for(s=0; s<nsegs; s++){
898     lo=(FXint)(0.5+(del*(seg[s].lower-slo))/len);
899     hi=(FXint)(0.5+(del*(seg[s].upper-slo))/len);
900     if(lo<=v && v<=hi) return s;
901     }
902   return -1;
903   }
904 
905 
906 // Get grip in segment
getGrip(FXint sg,FXint x,FXint y) const907 FXint FXGradientBar::getGrip(FXint sg,FXint x,FXint y) const {
908   if(0<=sg && sg<nsegs){
909     register FXdouble shi=seg[nsegs-1].upper;
910     register FXdouble slo=seg[0].lower;
911     register FXdouble len=shi-slo;
912     register FXdouble del;
913     register FXint lo,hi,md,v;
914     FXASSERT(len>0.0);
915     if(options&GRADIENTBAR_VERTICAL){
916       v=height-border-padbottom-y-3;
917       del=bar->getHeight()-1;
918       }
919     else{
920       v=x-border-padleft-2;
921       del=bar->getWidth()-1;
922       }
923     lo=(FXint)(0.5+(del*(seg[sg].lower-slo))/len);
924     hi=(FXint)(0.5+(del*(seg[sg].upper-slo))/len);
925     if((lo-CONTROL_SIZE/2-PICK_EXTRA)<=v && v<=(hi+CONTROL_SIZE/2+PICK_EXTRA)){
926       if(v<=(lo+CONTROL_SIZE/2+PICK_EXTRA)) return GRIP_LOWER;
927       if((hi-CONTROL_SIZE/2-PICK_EXTRA)<=v) return GRIP_UPPER;
928       md=(FXint)(0.5+(del*(seg[sg].middle-slo))/len);
929       if(v<(md-CONTROL_SIZE/2-PICK_EXTRA)) return GRIP_SEG_LOWER;
930       if(v>(md+CONTROL_SIZE/2+PICK_EXTRA)) return GRIP_SEG_UPPER;
931       return GRIP_MIDDLE;
932       }
933     }
934   return GRIP_NONE;
935   }
936 
937 
938 // Get value given position x,y
getValue(FXint x,FXint y) const939 FXdouble FXGradientBar::getValue(FXint x,FXint y) const {
940   register FXdouble slo=seg[0].lower;
941   register FXdouble shi=seg[nsegs-1].upper;
942   register FXdouble val;
943   if(options&GRADIENTBAR_VERTICAL)
944     val=slo+(height-padbottom-border-3-y)*(shi-slo)/(bar->getHeight()-1);
945   else
946     val=slo+(x-padleft-border-2)*(shi-slo)/(bar->getWidth()-1);
947   return FXCLAMP(slo,val,shi);
948   }
949 
950 
951 // Get position of lower edge of segment
getSegmentLowerPos(FXint sg) const952 FXint FXGradientBar::getSegmentLowerPos(FXint sg) const {
953   register FXdouble shi=seg[nsegs-1].upper;
954   register FXdouble slo=seg[0].lower;
955   register FXdouble len=shi-slo;
956   register FXint pos;
957   FXASSERT(0<=sg && sg<nsegs);
958   FXASSERT(0<len);
959   if(options&GRADIENTBAR_VERTICAL){
960     pos=height-padbottom-border-3-(FXint)(0.5+((bar->getHeight()-1)*(seg[sg].lower-slo))/len);
961     }
962   else{
963     pos=padleft+border+2+(FXint)(0.5+((bar->getWidth()-1)*(seg[sg].lower-slo))/len);
964     }
965   return pos;
966   }
967 
968 
969 // Get position of upper edge of segment
getSegmentUpperPos(FXint sg) const970 FXint FXGradientBar::getSegmentUpperPos(FXint sg) const {
971   register FXdouble shi=seg[nsegs-1].upper;
972   register FXdouble slo=seg[0].lower;
973   register FXdouble len=shi-slo;
974   register FXint pos;
975   FXASSERT(0<=sg && sg<nsegs);
976   FXASSERT(0<len);
977   if(options&GRADIENTBAR_VERTICAL){
978     pos=height-padbottom-border-3-(FXint)(0.5+((bar->getHeight()-1)*(seg[sg].upper-slo))/len);
979     }
980   else{
981     pos=padleft+border+2+(FXint)(0.5+((bar->getWidth()-1)*(seg[sg].upper-slo))/len);
982     }
983   return pos;
984   }
985 
986 
987 // Get position of middle of segment
getSegmentMiddlePos(FXint sg) const988 FXint FXGradientBar::getSegmentMiddlePos(FXint sg) const {
989   register FXdouble shi=seg[nsegs-1].upper;
990   register FXdouble slo=seg[0].lower;
991   register FXdouble len=shi-slo;
992   register FXint pos;
993   FXASSERT(0<=sg && sg<nsegs);
994   FXASSERT(0<len);
995   if(options&GRADIENTBAR_VERTICAL){
996     pos=height-padbottom-border-3-(FXint)(0.5+((bar->getHeight()-1)*(seg[sg].middle-slo))/len);
997     }
998   else{
999     pos=padleft+border+2+(FXint)(0.5+((bar->getWidth()-1)*(seg[sg].middle-slo))/len);
1000     }
1001   return pos;
1002   }
1003 
1004 
1005 // Mouse moved
onMotion(FXObject *,FXSelector,void * ptr)1006 long FXGradientBar::onMotion(FXObject*,FXSelector,void* ptr){
1007   FXEvent* event=(FXEvent*)ptr;
1008   FXdouble value;
1009   FXint g,s;
1010   if(options&GRADIENTBAR_VERTICAL){
1011     value=getValue(event->win_x,event->win_y+offset);
1012     }
1013   else{
1014     value=getValue(event->win_x+offset,event->win_y);
1015     }
1016   switch(grip){
1017     case GRIP_LOWER:
1018       if(0<current) moveSegmentLower(current,value,TRUE);
1019       return 1;
1020     case GRIP_MIDDLE:
1021       moveSegmentMiddle(current,value,TRUE);
1022       return 1;
1023     case GRIP_UPPER:
1024       if(current<nsegs-1) moveSegmentUpper(current,value,TRUE);
1025       return 1;
1026     case GRIP_SEG_LOWER:
1027     case GRIP_SEG_UPPER:
1028       moveSegments(sellower,selupper,value,TRUE);
1029       return 1;
1030     case GRIP_NONE:
1031       s=getSegment(event->win_x,event->win_y);
1032       if(0<=s){
1033         g=getGrip(s,event->win_x,event->win_y);
1034         if((g==GRIP_MIDDLE) || (g==GRIP_LOWER && 0<s) || (g==GRIP_UPPER && s<nsegs-1)){
1035           if(options&GRADIENTBAR_VERTICAL){
1036             setDefaultCursor(getApp()->getDefaultCursor(DEF_DRAGH_CURSOR));
1037             }
1038           else{
1039             setDefaultCursor(getApp()->getDefaultCursor(DEF_DRAGV_CURSOR));
1040             }
1041           return 1;
1042           }
1043         }
1044       setDefaultCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
1045       return 1;
1046     }
1047   return 0;
1048   }
1049 
1050 
1051 // Pressed button
onLeftBtnPress(FXObject *,FXSelector,void * ptr)1052 long FXGradientBar::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
1053   FXEvent* event=(FXEvent*)ptr;
1054   flags&=~FLAG_TIP;
1055   if(isEnabled()){
1056     grab();
1057     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
1058     setCurrentSegment(getSegment(event->win_x,event->win_y),TRUE);
1059     if(0<=current){
1060       grip=getGrip(current,event->win_x,event->win_y);
1061       if(grip==GRIP_SEG_LOWER || grip==GRIP_SEG_UPPER){
1062         if((0<=anchor) && (event->state&SHIFTMASK)){
1063           selectSegments(FXMIN(current,anchor),FXMAX(current,anchor),TRUE);
1064           }
1065         else if(!isSegmentSelected(current)){
1066           selectSegments(current,current,TRUE);
1067           setAnchorSegment(current);
1068           }
1069         offset=getSegmentMiddlePos(sellower);
1070         }
1071       else{
1072         deselectSegments(TRUE);
1073         if(grip==GRIP_LOWER){
1074           offset=getSegmentLowerPos(current);
1075           }
1076         else if(grip==GRIP_MIDDLE){
1077           offset=getSegmentMiddlePos(current);
1078           }
1079         else if(grip==GRIP_UPPER){
1080           offset=getSegmentUpperPos(current);
1081           }
1082         }
1083       if(grip!=GRIP_NONE){
1084         if(options&GRADIENTBAR_VERTICAL){
1085           setDragCursor(getApp()->getDefaultCursor(DEF_DRAGH_CURSOR));
1086           offset=offset-event->win_y;
1087           }
1088         else{
1089           setDragCursor(getApp()->getDefaultCursor(DEF_DRAGV_CURSOR));
1090           offset=offset-event->win_x;
1091           }
1092         }
1093       flags&=~FLAG_UPDATE;
1094       }
1095     else{
1096       deselectSegments(TRUE);
1097       }
1098     return 1;
1099     }
1100   return 0;
1101   }
1102 
1103 
1104 // Released button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)1105 long FXGradientBar::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
1106   FXEvent* event=(FXEvent*)ptr;
1107   FXint g=grip;
1108   if(isEnabled()){
1109     ungrab();
1110     flags&=~FLAG_CHANGED;
1111     flags|=FLAG_UPDATE;
1112     grip=GRIP_NONE;
1113     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
1114     if((0<=current) && (g==GRIP_SEG_LOWER || g==GRIP_SEG_UPPER) && !(event->state&SHIFTMASK) && !event->moved){
1115       selectSegments(current,current,TRUE);
1116       }
1117     setAnchorSegment(current);
1118     setDragCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
1119     return 1;
1120     }
1121   return 0;
1122   }
1123 
1124 
1125 // Handle drag-and-drop enter
onDNDEnter(FXObject * sender,FXSelector sel,void * ptr)1126 long FXGradientBar::onDNDEnter(FXObject* sender,FXSelector sel,void* ptr){
1127   FXFrame::onDNDEnter(sender,sel,ptr);
1128   dropped=-1;
1129   return 1;
1130   }
1131 
1132 
1133 // Handle drag-and-drop leave
onDNDLeave(FXObject * sender,FXSelector sel,void * ptr)1134 long FXGradientBar::onDNDLeave(FXObject* sender,FXSelector sel,void* ptr){
1135   FXFrame::onDNDLeave(sender,sel,ptr);
1136   dropped=-1;
1137   return 1;
1138   }
1139 
1140 
1141 // Handle drag-and-drop motion
onDNDMotion(FXObject * sender,FXSelector sel,void * ptr)1142 long FXGradientBar::onDNDMotion(FXObject* sender,FXSelector sel,void* ptr){
1143   FXEvent* event=(FXEvent*)ptr;
1144 
1145   // Handle base class first
1146   if(FXFrame::onDNDMotion(sender,sel,ptr)) return 1;
1147 
1148   // Is it a color being dropped?
1149   if(offeredDNDType(FROM_DRAGNDROP,colorType)){
1150     dropped=getSegment(event->win_x,event->win_y);
1151     if(0<=dropped){
1152       where=getGrip(dropped,event->win_x,event->win_y);
1153       if(where!=GRIP_NONE){
1154         acceptDrop(DRAG_COPY);
1155         }
1156       }
1157     return 1;
1158     }
1159   return 0;
1160   }
1161 
1162 
1163 // Handle drag-and-drop drop
onDNDDrop(FXObject * sender,FXSelector sel,void * ptr)1164 long FXGradientBar::onDNDDrop(FXObject* sender,FXSelector sel,void* ptr){
1165   FXushort *clr; FXuint len; FXColor color;
1166 
1167   // Try handling it in base class first
1168   if(FXFrame::onDNDDrop(sender,sel,ptr)) return 1;
1169 
1170   // Try handle here
1171   if(0<=dropped){
1172     if(getDNDData(FROM_DRAGNDROP,colorType,(FXuchar*&)clr,len)){
1173       color=FXRGBA((clr[0]+128)/257,(clr[1]+128)/257,(clr[2]+128)/257,(clr[3]+128)/257);
1174       FXFREE(&clr);
1175       if(where!=GRIP_NONE){
1176         if(where<=GRIP_SEG_LOWER){
1177           setSegmentLowerColor(dropped,color,TRUE);
1178           if(where==GRIP_LOWER && 0<dropped) setSegmentUpperColor(dropped-1,color,TRUE);
1179           }
1180         else if(where>=GRIP_SEG_UPPER){
1181           setSegmentUpperColor(dropped,color,TRUE);
1182           if(where==GRIP_UPPER && dropped<nsegs-1) setSegmentLowerColor(dropped+1,color,TRUE);
1183           }
1184         else{
1185           setSegmentLowerColor(dropped,color,TRUE);
1186           setSegmentUpperColor(dropped,color,TRUE);
1187           }
1188         }
1189       return 1;
1190       }
1191     }
1192   return 0;
1193   }
1194 
1195 
1196 // Update upper or lower color of current segment
onUpdSegColor(FXObject * sender,FXSelector sel,void *)1197 long FXGradientBar::onUpdSegColor(FXObject* sender,FXSelector sel,void*){
1198   if(0<=current){
1199     if(FXSELID(sel)==ID_LOWER_COLOR){
1200       sender->handle(this,FXSEL(SEL_COMMAND,ID_SETINTVALUE),(void*)&seg[current].lowerColor);
1201       }
1202     else if(FXSELID(sel)==ID_UPPER_COLOR){
1203       sender->handle(this,FXSEL(SEL_COMMAND,ID_SETINTVALUE),(void*)&seg[current].upperColor);
1204       }
1205     }
1206   return 1;
1207   }
1208 
1209 
1210 // Change upper or lower color of current segment
onCmdSegColor(FXObject * sender,FXSelector sel,void *)1211 long FXGradientBar::onCmdSegColor(FXObject* sender,FXSelector sel,void*){
1212   FXColor color;
1213   if(0<=current){
1214     if(FXSELID(sel)==ID_LOWER_COLOR){
1215       sender->handle(this,FXSEL(SEL_COMMAND,ID_GETINTVALUE),(void*)&color);
1216       setSegmentLowerColor(current,color,TRUE);
1217       }
1218     else if(FXSELID(sel)==ID_UPPER_COLOR){
1219       sender->handle(this,FXSEL(SEL_COMMAND,ID_GETINTVALUE),(void*)&color);
1220       setSegmentUpperColor(current,color,TRUE);
1221       }
1222     }
1223   return 1;
1224   }
1225 
1226 
1227 // Update recenter midpoint
onUpdRecenter(FXObject * sender,FXSelector,void *)1228 long FXGradientBar::onUpdRecenter(FXObject* sender,FXSelector,void*){
1229   sender->handle(this,(0<=current)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1230   return 1;
1231   }
1232 
1233 
1234 // Recenter midpoint
onCmdRecenter(FXObject *,FXSelector,void *)1235 long FXGradientBar::onCmdRecenter(FXObject*,FXSelector,void*){
1236   if(0<=current){
1237     moveSegmentMiddle(current,0.5*(seg[current].lower+seg[current].upper),TRUE);
1238     }
1239   return 1;
1240   }
1241 
1242 
1243 // Update split segment
onUpdSplit(FXObject * sender,FXSelector,void *)1244 long FXGradientBar::onUpdSplit(FXObject* sender,FXSelector,void*){
1245   sender->handle(this,(0<=sellower && 0<=selupper)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1246   return 1;
1247   }
1248 
1249 
1250 // Split segment
onCmdSplit(FXObject *,FXSelector,void *)1251 long FXGradientBar::onCmdSplit(FXObject*,FXSelector,void*){
1252   if(0<=sellower && 0<=selupper){
1253     splitSegments(sellower,selupper,TRUE);
1254     selectSegments(sellower,selupper+selupper-sellower+1,TRUE);
1255     }
1256   return 1;
1257   }
1258 
1259 
1260 // Update merge segments
onUpdMerge(FXObject * sender,FXSelector,void *)1261 long FXGradientBar::onUpdMerge(FXObject* sender,FXSelector,void*){
1262   sender->handle(this,(0<=sellower && 0<=selupper && sellower<selupper)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1263   return 1;
1264   }
1265 
1266 
1267 // Merge selection into one segment
onCmdMerge(FXObject *,FXSelector,void *)1268 long FXGradientBar::onCmdMerge(FXObject*,FXSelector,void*){
1269   if(0<=sellower && 0<=selupper){
1270     mergeSegments(sellower,selupper,TRUE);
1271     selectSegments(sellower,sellower,TRUE);
1272     }
1273   return 1;
1274   }
1275 
1276 
1277 // Update make uniform
onUpdUniform(FXObject * sender,FXSelector,void *)1278 long FXGradientBar::onUpdUniform(FXObject* sender,FXSelector,void*){
1279   sender->handle(this,(0<=sellower && 0<=selupper)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1280   return 1;
1281   }
1282 
1283 
1284 // Make selected segments uniform
onCmdUniform(FXObject *,FXSelector,void *)1285 long FXGradientBar::onCmdUniform(FXObject*,FXSelector,void*){
1286   if(0<=sellower && 0<=selupper) uniformSegments(sellower,selupper,TRUE);
1287   return 1;
1288   }
1289 
1290 
1291 // Update blending
onUpdBlending(FXObject * sender,FXSelector sel,void *)1292 long FXGradientBar::onUpdBlending(FXObject* sender,FXSelector sel,void*){
1293   FXuint blend=FXSELID(sel)-ID_BLEND_LINEAR;
1294   if(0<=sellower && 0<=selupper){
1295     sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL);
1296     for(FXint s=sellower; s<=selupper; s++){
1297       if(seg[s].blend!=blend){ sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); return 1; }
1298       }
1299     sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL);
1300     }
1301   else{
1302     sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1303     }
1304   return 1;
1305   }
1306 
1307 
1308 // Change blending
onCmdBlending(FXObject *,FXSelector sel,void *)1309 long FXGradientBar::onCmdBlending(FXObject*,FXSelector sel,void*){
1310   FXuint blend=FXSELID(sel)-ID_BLEND_LINEAR;
1311   if(0<=sellower && 0<=selupper){
1312     blendSegments(sellower,selupper,blend,TRUE);
1313     }
1314   return 1;
1315   }
1316 
1317 
1318 // Set help using a message
onCmdSetHelp(FXObject *,FXSelector,void * ptr)1319 long FXGradientBar::onCmdSetHelp(FXObject*,FXSelector,void* ptr){
1320   setHelpText(*((FXString*)ptr));
1321   return 1;
1322   }
1323 
1324 
1325 // Get help using a message
onCmdGetHelp(FXObject *,FXSelector,void * ptr)1326 long FXGradientBar::onCmdGetHelp(FXObject*,FXSelector,void* ptr){
1327   *((FXString*)ptr)=getHelpText();
1328   return 1;
1329   }
1330 
1331 
1332 // Set tip using a message
onCmdSetTip(FXObject *,FXSelector,void * ptr)1333 long FXGradientBar::onCmdSetTip(FXObject*,FXSelector,void* ptr){
1334   setTipText(*((FXString*)ptr));
1335   return 1;
1336   }
1337 
1338 
1339 // Get tip using a message
onCmdGetTip(FXObject *,FXSelector,void * ptr)1340 long FXGradientBar::onCmdGetTip(FXObject*,FXSelector,void* ptr){
1341   *((FXString*)ptr)=getTipText();
1342   return 1;
1343   }
1344 
1345 
1346 // We were asked about tip text
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)1347 long FXGradientBar::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){
1348   if(FXWindow::onQueryTip(sender,sel,ptr)) return 1;
1349   if((flags&FLAG_TIP) && !tip.empty()){
1350     sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&tip);
1351     return 1;
1352     }
1353   return 0;
1354   }
1355 
1356 
1357 // We were asked about status text
onQueryHelp(FXObject * sender,FXSelector sel,void * ptr)1358 long FXGradientBar::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){
1359   if(FXWindow::onQueryHelp(sender,sel,ptr)) return 1;
1360   if((flags&FLAG_HELP) && !help.empty()){
1361     sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help);
1362     return 1;
1363     }
1364   return 0;
1365   }
1366 
1367 
1368 // Get blend cuve of segment
getSegmentBlend(FXint s) const1369 FXuint FXGradientBar::getSegmentBlend(FXint s) const {
1370   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::getSegmentBlend: argument out of range."); }
1371   return seg[s].blend;
1372   }
1373 
1374 
1375 // Set colors of a segment
setSegmentLowerColor(FXint s,FXColor clr,FXbool notify)1376 void FXGradientBar::setSegmentLowerColor(FXint s,FXColor clr,FXbool notify){
1377   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::setSegmentLowerColor: argument out of range."); }
1378   if(seg[s].lowerColor!=clr){
1379     seg[s].lowerColor=clr;
1380     recalc();
1381     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)s);}
1382     }
1383   }
1384 
1385 
1386 // Set colors of a segment
setSegmentUpperColor(FXint s,FXColor clr,FXbool notify)1387 void FXGradientBar::setSegmentUpperColor(FXint s,FXColor clr,FXbool notify){
1388   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::setSegmentUpperColor: argument out of range."); }
1389   if(seg[s].upperColor!=clr){
1390     seg[s].upperColor=clr;
1391     recalc();
1392     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)s);}
1393     }
1394   }
1395 
1396 
1397 // Get colors of a segment
getSegmentLowerColor(FXint s) const1398 FXColor FXGradientBar::getSegmentLowerColor(FXint s) const {
1399   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::getSegmentLowerColor: argument out of range."); }
1400   return seg[s].lowerColor;
1401   }
1402 
1403 
1404 // Get colors of a segment
getSegmentUpperColor(FXint s) const1405 FXColor FXGradientBar::getSegmentUpperColor(FXint s) const {
1406   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::getSegmentUpperColor: argument out of range."); }
1407   return seg[s].upperColor;
1408   }
1409 
1410 
1411 // Get lower value of segment sg
getSegmentLower(FXint sg) const1412 FXdouble FXGradientBar::getSegmentLower(FXint sg) const {
1413   if(sg<0 || sg>=nsegs){ fxerror("FXGradientBar::getSegmentLower: argument out of range."); }
1414   return seg[sg].lower;
1415   }
1416 
1417 
1418 // Get middle value of segment sg
getSegmentMiddle(FXint sg) const1419 FXdouble FXGradientBar::getSegmentMiddle(FXint sg) const {
1420   if(sg<0 || sg>=nsegs){ fxerror("FXGradientBar::getSegmentMiddle: argument out of range."); }
1421   return seg[sg].middle;
1422   }
1423 
1424 
1425 // Get upper value of segment sg
getSegmentUpper(FXint sg) const1426 FXdouble FXGradientBar::getSegmentUpper(FXint sg) const {
1427   if(sg<0 || sg>=nsegs){ fxerror("FXGradientBar::getSegmentUpper: argument out of range."); }
1428   return seg[sg].upper;
1429   }
1430 
1431 
1432 // Set color bar options
setBarStyle(FXuint style)1433 void FXGradientBar::setBarStyle(FXuint style){
1434   FXuint opts=(options&~GRADIENTBAR_MASK) | (style&GRADIENTBAR_MASK);
1435   if(options!=opts){
1436     options=opts;
1437     recalc();
1438     update();
1439     }
1440   }
1441 
1442 
1443 // Get color bar options
getBarStyle() const1444 FXuint FXGradientBar::getBarStyle() const {
1445   return (options&GRADIENTBAR_MASK);
1446   }
1447 
1448 
1449 // Set base color
setSelectColor(FXColor clr)1450 void FXGradientBar::setSelectColor(FXColor clr){
1451   if(clr!=selectColor){
1452     selectColor=clr;
1453     update();
1454     }
1455   }
1456 
1457 
1458 // Save data
save(FXStream & store) const1459 void FXGradientBar::save(FXStream& store) const {
1460   FXFrame::save(store);
1461   store << bar;
1462   store << tip;
1463   store << help;
1464   store << selectColor;
1465   }
1466 
1467 
1468 // Load data
load(FXStream & store)1469 void FXGradientBar::load(FXStream& store){
1470   FXFrame::load(store);
1471   store >> bar;
1472   store >> tip;
1473   store >> help;
1474   store >> selectColor;
1475   }
1476 
1477 
1478 // Zap it
~FXGradientBar()1479 FXGradientBar::~FXGradientBar(){
1480   delete bar;
1481   FXFREE(&seg);
1482   bar=(FXImage*)-1L;
1483   seg=(FXGradient*)-1L;
1484   }
1485 
1486 }
1487