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