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,2006 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 4937 2019-03-10 19:59:30Z arthurcnorman $                   *
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   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   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   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   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   FXint s,lr,lg,lb,la,ur,ug,ub,ua,d,l,h,m,i;
311   FXdouble len=seg[nsegs-1].upper-seg[0].lower;
312   FXdouble del=nramp-1;
313   BLENDFUNC blend=NULL;
314   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   FXint barw=bar->getWidth();
367   FXint barh=bar->getHeight();
368   FXint x,y,r,g,b,a;
369   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   FXdouble len=seg[nsegs-1].upper-seg[0].lower;
492   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   FXdouble len=seg[nsegs-1].upper-seg[0].lower;
511   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   FXdouble len=seg[nsegs-1].upper-seg[0].lower;
530   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   FXdouble len=seg[nsegs-1].upper-seg[0].lower;
549   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   FXdouble delta, below,above,room;
759   FXint i;
760   if(0<=sglo && sghi<nsegs && sglo<=sghi){
761     below=seg[sglo].middle-seg[sglo].lower;
762     above=seg[sghi].upper-seg[sglo].middle;
763     room=seg[sghi].middle-seg[sglo].middle;
764     if(sglo==0){
765       if(val<seg[0].lower) val=seg[0].lower;
766       }
767     else{
768       if(val-below<seg[sglo-1].middle) val=seg[sglo-1].middle+below;
769       }
770     if(sghi==nsegs-1){
771       if(val+room>seg[nsegs-1].upper) val=seg[nsegs-1].upper-room;
772       }
773     else{
774       if(val+above>seg[sghi+1].middle) val=seg[sghi+1].middle-above;
775       }
776     delta=val-seg[sglo].middle;
777     if(delta!=0.0){
778       for(i=sglo; i<=sghi; i++){
779         if(0<i) seg[i].lower+=delta;
780         seg[i].middle+=delta;
781         if(i<nsegs-1) seg[i].upper+=delta;
782         }
783       if(0<sglo){
784         seg[sglo-1].upper=seg[sglo].lower;
785         }
786       if(sghi<nsegs-1){
787         seg[sghi+1].lower=seg[sghi].upper;
788         }
789       recalc();
790       if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);}
791       }
792     }
793   }
794 
795 
796 // Split segment at the midpoint
splitSegments(FXint sglo,FXint sghi,FXbool notify)797 void FXGradientBar::splitSegments(FXint sglo,FXint sghi,FXbool notify){
798   FXint n=sghi-sglo+1;
799   FXint i,j;
800   if(0<=sglo && sghi<nsegs && 0<n){
801     FXRESIZE(&seg,FXGradient,nsegs+n);
802     memmove(&seg[sghi+n],&seg[sghi],sizeof(FXGradient)*(nsegs-sghi));
803     for(i=sghi,j=sghi+n-1; sglo<=i; i-=1,j-=2){
804       seg[j+1].upper=seg[i].upper;
805       seg[j+1].lower=seg[i].middle;
806       seg[j+1].middle=0.5*(seg[j+1].upper+seg[j+1].lower);
807       seg[j+1].lowerColor=seg[i].upperColor;// FIXME
808       seg[j+1].upperColor=seg[i].upperColor;
809       seg[j+1].blend=seg[i].blend;
810       seg[j+0].upper=seg[i].middle;
811       seg[j+0].lower=seg[i].lower;
812       seg[j+0].middle=0.5*(seg[j+0].upper+seg[j+0].lower);
813       seg[j+0].lowerColor=seg[i].lowerColor;
814       seg[j+0].upperColor=seg[i].upperColor;// FIXME
815       seg[j+0].blend=seg[i].blend;
816       }
817     nsegs+=n;
818     recalc();
819     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);}
820     }
821   }
822 
823 
824 // Merge segments
mergeSegments(FXint sglo,FXint sghi,FXbool notify)825 void FXGradientBar::mergeSegments(FXint sglo,FXint sghi,FXbool notify){
826   FXint n=sghi-sglo;
827   if(0<=sglo && sghi<nsegs && 0<n){
828     seg[sglo].middle=(n&1)?seg[(sghi+sglo)/2].upper:seg[(sghi+sglo)/2].middle;
829     seg[sglo].upper=seg[sghi].upper;
830     seg[sglo].upperColor=seg[sghi].upperColor;
831     memmove(&seg[sglo+1],&seg[sghi+1],sizeof(FXGradient)*(nsegs-sghi-1));
832     FXRESIZE(&seg,FXGradient,nsegs-n);
833     nsegs-=n;
834     if(selupper>=nsegs) selupper=nsegs-1;
835     if(sellower>=nsegs) sellower=nsegs-1;
836     if(current>=nsegs) current=nsegs-1;
837     if(anchor>=nsegs) anchor=nsegs-1;
838     recalc();
839     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);}
840     }
841   }
842 
843 
844 // Make segments uniformly distributed
uniformSegments(FXint sglo,FXint sghi,FXbool notify)845 void FXGradientBar::uniformSegments(FXint sglo,FXint sghi,FXbool notify){
846   FXdouble m,d,a;
847   FXint s;
848   if(0<=sglo && sghi<nsegs && sglo<=sghi){
849     d=sghi-sglo+1;
850     m=seg[sghi].upper-seg[sglo].lower;
851     a=seg[sglo].lower;
852     for(s=sglo; s<=sghi; s++){
853       seg[s].lower=a+(FXdouble)(s-sglo)*m/d;
854       seg[s].upper=a+(FXdouble)(s-sglo+1)*m/d;
855       seg[s].middle=0.5*(seg[s].lower+seg[s].upper);
856       }
857     recalc();
858     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);}
859     }
860   }
861 
862 
863 // Change blend curve of segment
blendSegments(FXint sglo,FXint sghi,FXuint blend,FXbool notify)864 void FXGradientBar::blendSegments(FXint sglo,FXint sghi,FXuint blend,FXbool notify){
865   FXint s;
866   if(0<=sglo && sghi<nsegs && sglo<=sghi){
867     for(s=sglo; s<=sghi; s++){
868       seg[s].blend=blend;
869       }
870     recalc();
871     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);}
872     }
873   }
874 
875 
876 // Determine which segment got hit
getSegment(FXint x,FXint y) const877 FXint FXGradientBar::getSegment(FXint x,FXint y) const {
878   FXdouble shi=seg[nsegs-1].upper;
879   FXdouble slo=seg[0].lower;
880   FXdouble len=shi-slo;
881   FXdouble del;
882   FXint lo,hi,v,s;
883   FXASSERT(len>0.0);
884   if(options&GRADIENTBAR_VERTICAL){
885     if(y<border+padtop+2) return nsegs-1;
886     if(y>height-border-padbottom-2) return 0;
887     v=height-border-padbottom-y-3;
888     del=bar->getHeight()-1;
889     }
890   else{
891     if(x<border+padleft+2) return 0;
892     if(x>width-border-padright-2) return nsegs-1;
893     v=x-border-padleft-2;
894     del=bar->getWidth()-1;
895     }
896   for(s=0; s<nsegs; s++){
897     lo=(FXint)(0.5+(del*(seg[s].lower-slo))/len);
898     hi=(FXint)(0.5+(del*(seg[s].upper-slo))/len);
899     if(lo<=v && v<=hi) return s;
900     }
901   return -1;
902   }
903 
904 
905 // Get grip in segment
getGrip(FXint sg,FXint x,FXint y) const906 FXint FXGradientBar::getGrip(FXint sg,FXint x,FXint y) const {
907   if(0<=sg && sg<nsegs){
908     FXdouble shi=seg[nsegs-1].upper;
909     FXdouble slo=seg[0].lower;
910     FXdouble len=shi-slo;
911     FXdouble del;
912     FXint lo,hi,md,v;
913     FXASSERT(len>0.0);
914     if(options&GRADIENTBAR_VERTICAL){
915       v=height-border-padbottom-y-3;
916       del=bar->getHeight()-1;
917       }
918     else{
919       v=x-border-padleft-2;
920       del=bar->getWidth()-1;
921       }
922     lo=(FXint)(0.5+(del*(seg[sg].lower-slo))/len);
923     hi=(FXint)(0.5+(del*(seg[sg].upper-slo))/len);
924     if((lo-CONTROL_SIZE/2-PICK_EXTRA)<=v && v<=(hi+CONTROL_SIZE/2+PICK_EXTRA)){
925       if(v<=(lo+CONTROL_SIZE/2+PICK_EXTRA)) return GRIP_LOWER;
926       if((hi-CONTROL_SIZE/2-PICK_EXTRA)<=v) return GRIP_UPPER;
927       md=(FXint)(0.5+(del*(seg[sg].middle-slo))/len);
928       if(v<(md-CONTROL_SIZE/2-PICK_EXTRA)) return GRIP_SEG_LOWER;
929       if(v>(md+CONTROL_SIZE/2+PICK_EXTRA)) return GRIP_SEG_UPPER;
930       return GRIP_MIDDLE;
931       }
932     }
933   return GRIP_NONE;
934   }
935 
936 
937 // Get value given position x,y
getValue(FXint x,FXint y) const938 FXdouble FXGradientBar::getValue(FXint x,FXint y) const {
939   FXdouble slo=seg[0].lower;
940   FXdouble shi=seg[nsegs-1].upper;
941   FXdouble val;
942   if(options&GRADIENTBAR_VERTICAL)
943     val=slo+(height-padbottom-border-3-y)*(shi-slo)/(bar->getHeight()-1);
944   else
945     val=slo+(x-padleft-border-2)*(shi-slo)/(bar->getWidth()-1);
946   return FXCLAMP(slo,val,shi);
947   }
948 
949 
950 // Get position of lower edge of segment
getSegmentLowerPos(FXint sg) const951 FXint FXGradientBar::getSegmentLowerPos(FXint sg) const {
952   FXdouble shi=seg[nsegs-1].upper;
953   FXdouble slo=seg[0].lower;
954   FXdouble len=shi-slo;
955   FXint pos;
956   FXASSERT(0<=sg && sg<nsegs);
957   FXASSERT(0<len);
958   if(options&GRADIENTBAR_VERTICAL){
959     pos=height-padbottom-border-3-(FXint)(0.5+((bar->getHeight()-1)*(seg[sg].lower-slo))/len);
960     }
961   else{
962     pos=padleft+border+2+(FXint)(0.5+((bar->getWidth()-1)*(seg[sg].lower-slo))/len);
963     }
964   return pos;
965   }
966 
967 
968 // Get position of upper edge of segment
getSegmentUpperPos(FXint sg) const969 FXint FXGradientBar::getSegmentUpperPos(FXint sg) const {
970   FXdouble shi=seg[nsegs-1].upper;
971   FXdouble slo=seg[0].lower;
972   FXdouble len=shi-slo;
973   FXint pos;
974   FXASSERT(0<=sg && sg<nsegs);
975   FXASSERT(0<len);
976   if(options&GRADIENTBAR_VERTICAL){
977     pos=height-padbottom-border-3-(FXint)(0.5+((bar->getHeight()-1)*(seg[sg].upper-slo))/len);
978     }
979   else{
980     pos=padleft+border+2+(FXint)(0.5+((bar->getWidth()-1)*(seg[sg].upper-slo))/len);
981     }
982   return pos;
983   }
984 
985 
986 // Get position of middle of segment
getSegmentMiddlePos(FXint sg) const987 FXint FXGradientBar::getSegmentMiddlePos(FXint sg) const {
988   FXdouble shi=seg[nsegs-1].upper;
989   FXdouble slo=seg[0].lower;
990   FXdouble len=shi-slo;
991   FXint pos;
992   FXASSERT(0<=sg && sg<nsegs);
993   FXASSERT(0<len);
994   if(options&GRADIENTBAR_VERTICAL){
995     pos=height-padbottom-border-3-(FXint)(0.5+((bar->getHeight()-1)*(seg[sg].middle-slo))/len);
996     }
997   else{
998     pos=padleft+border+2+(FXint)(0.5+((bar->getWidth()-1)*(seg[sg].middle-slo))/len);
999     }
1000   return pos;
1001   }
1002 
1003 
1004 // Mouse moved
onMotion(FXObject *,FXSelector,void * ptr)1005 long FXGradientBar::onMotion(FXObject*,FXSelector,void* ptr){
1006   FXEvent* event=(FXEvent*)ptr;
1007   FXdouble value;
1008   FXint g,s;
1009   if(options&GRADIENTBAR_VERTICAL){
1010     value=getValue(event->win_x,event->win_y+offset);
1011     }
1012   else{
1013     value=getValue(event->win_x+offset,event->win_y);
1014     }
1015   switch(grip){
1016     case GRIP_LOWER:
1017       if(0<current) moveSegmentLower(current,value,TRUE);
1018       return 1;
1019     case GRIP_MIDDLE:
1020       moveSegmentMiddle(current,value,TRUE);
1021       return 1;
1022     case GRIP_UPPER:
1023       if(current<nsegs-1) moveSegmentUpper(current,value,TRUE);
1024       return 1;
1025     case GRIP_SEG_LOWER:
1026     case GRIP_SEG_UPPER:
1027       moveSegments(sellower,selupper,value,TRUE);
1028       return 1;
1029     case GRIP_NONE:
1030       s=getSegment(event->win_x,event->win_y);
1031       if(0<=s){
1032         g=getGrip(s,event->win_x,event->win_y);
1033         if((g==GRIP_MIDDLE) || (g==GRIP_LOWER && 0<s) || (g==GRIP_UPPER && s<nsegs-1)){
1034           if(options&GRADIENTBAR_VERTICAL){
1035             setDefaultCursor(getApp()->getDefaultCursor(DEF_DRAGH_CURSOR));
1036             }
1037           else{
1038             setDefaultCursor(getApp()->getDefaultCursor(DEF_DRAGV_CURSOR));
1039             }
1040           return 1;
1041           }
1042         }
1043       setDefaultCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
1044       return 1;
1045     }
1046   return 0;
1047   }
1048 
1049 
1050 // Pressed button
onLeftBtnPress(FXObject *,FXSelector,void * ptr)1051 long FXGradientBar::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
1052   FXEvent* event=(FXEvent*)ptr;
1053   flags&=~FLAG_TIP;
1054   if(isEnabled()){
1055     grab();
1056     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
1057     setCurrentSegment(getSegment(event->win_x,event->win_y),TRUE);
1058     if(0<=current){
1059       grip=getGrip(current,event->win_x,event->win_y);
1060       if(grip==GRIP_SEG_LOWER || grip==GRIP_SEG_UPPER){
1061         if((0<=anchor) && (event->state&SHIFTMASK)){
1062           selectSegments(FXMIN(current,anchor),FXMAX(current,anchor),TRUE);
1063           }
1064         else if(!isSegmentSelected(current)){
1065           selectSegments(current,current,TRUE);
1066           setAnchorSegment(current);
1067           }
1068         offset=getSegmentMiddlePos(sellower);
1069         }
1070       else{
1071         deselectSegments(TRUE);
1072         if(grip==GRIP_LOWER){
1073           offset=getSegmentLowerPos(current);
1074           }
1075         else if(grip==GRIP_MIDDLE){
1076           offset=getSegmentMiddlePos(current);
1077           }
1078         else if(grip==GRIP_UPPER){
1079           offset=getSegmentUpperPos(current);
1080           }
1081         }
1082       if(grip!=GRIP_NONE){
1083         if(options&GRADIENTBAR_VERTICAL){
1084           setDragCursor(getApp()->getDefaultCursor(DEF_DRAGH_CURSOR));
1085           offset=offset-event->win_y;
1086           }
1087         else{
1088           setDragCursor(getApp()->getDefaultCursor(DEF_DRAGV_CURSOR));
1089           offset=offset-event->win_x;
1090           }
1091         }
1092       flags&=~FLAG_UPDATE;
1093       }
1094     else{
1095       deselectSegments(TRUE);
1096       }
1097     return 1;
1098     }
1099   return 0;
1100   }
1101 
1102 
1103 // Released button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)1104 long FXGradientBar::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
1105   FXEvent* event=(FXEvent*)ptr;
1106   FXint g=grip;
1107   if(isEnabled()){
1108     ungrab();
1109     flags&=~FLAG_CHANGED;
1110     flags|=FLAG_UPDATE;
1111     grip=GRIP_NONE;
1112     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
1113     if((0<=current) && (g==GRIP_SEG_LOWER || g==GRIP_SEG_UPPER) && !(event->state&SHIFTMASK) && !event->moved){
1114       selectSegments(current,current,TRUE);
1115       }
1116     setAnchorSegment(current);
1117     setDragCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
1118     return 1;
1119     }
1120   return 0;
1121   }
1122 
1123 
1124 // Handle drag-and-drop enter
onDNDEnter(FXObject * sender,FXSelector sel,void * ptr)1125 long FXGradientBar::onDNDEnter(FXObject* sender,FXSelector sel,void* ptr){
1126   FXFrame::onDNDEnter(sender,sel,ptr);
1127   dropped=-1;
1128   return 1;
1129   }
1130 
1131 
1132 // Handle drag-and-drop leave
onDNDLeave(FXObject * sender,FXSelector sel,void * ptr)1133 long FXGradientBar::onDNDLeave(FXObject* sender,FXSelector sel,void* ptr){
1134   FXFrame::onDNDLeave(sender,sel,ptr);
1135   dropped=-1;
1136   return 1;
1137   }
1138 
1139 
1140 // Handle drag-and-drop motion
onDNDMotion(FXObject * sender,FXSelector sel,void * ptr)1141 long FXGradientBar::onDNDMotion(FXObject* sender,FXSelector sel,void* ptr){
1142   FXEvent* event=(FXEvent*)ptr;
1143 
1144   // Handle base class first
1145   if(FXFrame::onDNDMotion(sender,sel,ptr)) return 1;
1146 
1147   // Is it a color being dropped?
1148   if(offeredDNDType(FROM_DRAGNDROP,colorType)){
1149     dropped=getSegment(event->win_x,event->win_y);
1150     if(0<=dropped){
1151       where=getGrip(dropped,event->win_x,event->win_y);
1152       if(where!=GRIP_NONE){
1153         acceptDrop(DRAG_COPY);
1154         }
1155       }
1156     return 1;
1157     }
1158   return 0;
1159   }
1160 
1161 
1162 // Handle drag-and-drop drop
onDNDDrop(FXObject * sender,FXSelector sel,void * ptr)1163 long FXGradientBar::onDNDDrop(FXObject* sender,FXSelector sel,void* ptr){
1164   FXushort *clr; FXuint len; FXColor color;
1165 
1166   // Try handling it in base class first
1167   if(FXFrame::onDNDDrop(sender,sel,ptr)) return 1;
1168 
1169   // Try handle here
1170   if(0<=dropped){
1171     if(getDNDData(FROM_DRAGNDROP,colorType,(FXuchar*&)clr,len)){
1172       color=FXRGBA((clr[0]+128)/257,(clr[1]+128)/257,(clr[2]+128)/257,(clr[3]+128)/257);
1173       FXFREE(&clr);
1174       if(where!=GRIP_NONE){
1175         if(where<=GRIP_SEG_LOWER){
1176           setSegmentLowerColor(dropped,color,TRUE);
1177           if(where==GRIP_LOWER && 0<dropped) setSegmentUpperColor(dropped-1,color,TRUE);
1178           }
1179         else if(where>=GRIP_SEG_UPPER){
1180           setSegmentUpperColor(dropped,color,TRUE);
1181           if(where==GRIP_UPPER && dropped<nsegs-1) setSegmentLowerColor(dropped+1,color,TRUE);
1182           }
1183         else{
1184           setSegmentLowerColor(dropped,color,TRUE);
1185           setSegmentUpperColor(dropped,color,TRUE);
1186           }
1187         }
1188       return 1;
1189       }
1190     }
1191   return 0;
1192   }
1193 
1194 
1195 // Update upper or lower color of current segment
onUpdSegColor(FXObject * sender,FXSelector sel,void *)1196 long FXGradientBar::onUpdSegColor(FXObject* sender,FXSelector sel,void*){
1197   if(0<=current){
1198     if(FXSELID(sel)==ID_LOWER_COLOR){
1199       sender->handle(this,FXSEL(SEL_COMMAND,ID_SETINTVALUE),(void*)&seg[current].lowerColor);
1200       }
1201     else if(FXSELID(sel)==ID_UPPER_COLOR){
1202       sender->handle(this,FXSEL(SEL_COMMAND,ID_SETINTVALUE),(void*)&seg[current].upperColor);
1203       }
1204     }
1205   return 1;
1206   }
1207 
1208 
1209 // Change upper or lower color of current segment
onCmdSegColor(FXObject * sender,FXSelector sel,void *)1210 long FXGradientBar::onCmdSegColor(FXObject* sender,FXSelector sel,void*){
1211   FXColor color;
1212   if(0<=current){
1213     if(FXSELID(sel)==ID_LOWER_COLOR){
1214       sender->handle(this,FXSEL(SEL_COMMAND,ID_GETINTVALUE),(void*)&color);
1215       setSegmentLowerColor(current,color,TRUE);
1216       }
1217     else if(FXSELID(sel)==ID_UPPER_COLOR){
1218       sender->handle(this,FXSEL(SEL_COMMAND,ID_GETINTVALUE),(void*)&color);
1219       setSegmentUpperColor(current,color,TRUE);
1220       }
1221     }
1222   return 1;
1223   }
1224 
1225 
1226 // Update recenter midpoint
onUpdRecenter(FXObject * sender,FXSelector,void *)1227 long FXGradientBar::onUpdRecenter(FXObject* sender,FXSelector,void*){
1228   sender->handle(this,(0<=current)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1229   return 1;
1230   }
1231 
1232 
1233 // Recenter midpoint
onCmdRecenter(FXObject *,FXSelector,void *)1234 long FXGradientBar::onCmdRecenter(FXObject*,FXSelector,void*){
1235   if(0<=current){
1236     moveSegmentMiddle(current,0.5*(seg[current].lower+seg[current].upper),TRUE);
1237     }
1238   return 1;
1239   }
1240 
1241 
1242 // Update split segment
onUpdSplit(FXObject * sender,FXSelector,void *)1243 long FXGradientBar::onUpdSplit(FXObject* sender,FXSelector,void*){
1244   sender->handle(this,(0<=sellower && 0<=selupper)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1245   return 1;
1246   }
1247 
1248 
1249 // Split segment
onCmdSplit(FXObject *,FXSelector,void *)1250 long FXGradientBar::onCmdSplit(FXObject*,FXSelector,void*){
1251   if(0<=sellower && 0<=selupper){
1252     splitSegments(sellower,selupper,TRUE);
1253     selectSegments(sellower,selupper+selupper-sellower+1,TRUE);
1254     }
1255   return 1;
1256   }
1257 
1258 
1259 // Update merge segments
onUpdMerge(FXObject * sender,FXSelector,void *)1260 long FXGradientBar::onUpdMerge(FXObject* sender,FXSelector,void*){
1261   sender->handle(this,(0<=sellower && 0<=selupper && sellower<selupper)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1262   return 1;
1263   }
1264 
1265 
1266 // Merge selection into one segment
onCmdMerge(FXObject *,FXSelector,void *)1267 long FXGradientBar::onCmdMerge(FXObject*,FXSelector,void*){
1268   if(0<=sellower && 0<=selupper){
1269     mergeSegments(sellower,selupper,TRUE);
1270     selectSegments(sellower,sellower,TRUE);
1271     }
1272   return 1;
1273   }
1274 
1275 
1276 // Update make uniform
onUpdUniform(FXObject * sender,FXSelector,void *)1277 long FXGradientBar::onUpdUniform(FXObject* sender,FXSelector,void*){
1278   sender->handle(this,(0<=sellower && 0<=selupper)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1279   return 1;
1280   }
1281 
1282 
1283 // Make selected segments uniform
onCmdUniform(FXObject *,FXSelector,void *)1284 long FXGradientBar::onCmdUniform(FXObject*,FXSelector,void*){
1285   if(0<=sellower && 0<=selupper) uniformSegments(sellower,selupper,TRUE);
1286   return 1;
1287   }
1288 
1289 
1290 // Update blending
onUpdBlending(FXObject * sender,FXSelector sel,void *)1291 long FXGradientBar::onUpdBlending(FXObject* sender,FXSelector sel,void*){
1292   FXuint blend=FXSELID(sel)-ID_BLEND_LINEAR;
1293   if(0<=sellower && 0<=selupper){
1294     sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL);
1295     for(FXint s=sellower; s<=selupper; s++){
1296       if(seg[s].blend!=blend){ sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); return 1; }
1297       }
1298     sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL);
1299     }
1300   else{
1301     sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1302     }
1303   return 1;
1304   }
1305 
1306 
1307 // Change blending
onCmdBlending(FXObject *,FXSelector sel,void *)1308 long FXGradientBar::onCmdBlending(FXObject*,FXSelector sel,void*){
1309   FXuint blend=FXSELID(sel)-ID_BLEND_LINEAR;
1310   if(0<=sellower && 0<=selupper){
1311     blendSegments(sellower,selupper,blend,TRUE);
1312     }
1313   return 1;
1314   }
1315 
1316 
1317 // Set help using a message
onCmdSetHelp(FXObject *,FXSelector,void * ptr)1318 long FXGradientBar::onCmdSetHelp(FXObject*,FXSelector,void* ptr){
1319   setHelpText(*((FXString*)ptr));
1320   return 1;
1321   }
1322 
1323 
1324 // Get help using a message
onCmdGetHelp(FXObject *,FXSelector,void * ptr)1325 long FXGradientBar::onCmdGetHelp(FXObject*,FXSelector,void* ptr){
1326   *((FXString*)ptr)=getHelpText();
1327   return 1;
1328   }
1329 
1330 
1331 // Set tip using a message
onCmdSetTip(FXObject *,FXSelector,void * ptr)1332 long FXGradientBar::onCmdSetTip(FXObject*,FXSelector,void* ptr){
1333   setTipText(*((FXString*)ptr));
1334   return 1;
1335   }
1336 
1337 
1338 // Get tip using a message
onCmdGetTip(FXObject *,FXSelector,void * ptr)1339 long FXGradientBar::onCmdGetTip(FXObject*,FXSelector,void* ptr){
1340   *((FXString*)ptr)=getTipText();
1341   return 1;
1342   }
1343 
1344 
1345 // We were asked about tip text
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)1346 long FXGradientBar::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){
1347   if(FXWindow::onQueryTip(sender,sel,ptr)) return 1;
1348   if((flags&FLAG_TIP) && !tip.empty()){
1349     sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&tip);
1350     return 1;
1351     }
1352   return 0;
1353   }
1354 
1355 
1356 // We were asked about status text
onQueryHelp(FXObject * sender,FXSelector sel,void * ptr)1357 long FXGradientBar::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){
1358   if(FXWindow::onQueryHelp(sender,sel,ptr)) return 1;
1359   if((flags&FLAG_HELP) && !help.empty()){
1360     sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help);
1361     return 1;
1362     }
1363   return 0;
1364   }
1365 
1366 
1367 // Get blend cuve of segment
getSegmentBlend(FXint s) const1368 FXuint FXGradientBar::getSegmentBlend(FXint s) const {
1369   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::getSegmentBlend: argument out of range."); }
1370   return seg[s].blend;
1371   }
1372 
1373 
1374 // Set colors of a segment
setSegmentLowerColor(FXint s,FXColor clr,FXbool notify)1375 void FXGradientBar::setSegmentLowerColor(FXint s,FXColor clr,FXbool notify){
1376   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::setSegmentLowerColor: argument out of range."); }
1377   if(seg[s].lowerColor!=clr){
1378     seg[s].lowerColor=clr;
1379     recalc();
1380     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)s);}
1381     }
1382   }
1383 
1384 
1385 // Set colors of a segment
setSegmentUpperColor(FXint s,FXColor clr,FXbool notify)1386 void FXGradientBar::setSegmentUpperColor(FXint s,FXColor clr,FXbool notify){
1387   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::setSegmentUpperColor: argument out of range."); }
1388   if(seg[s].upperColor!=clr){
1389     seg[s].upperColor=clr;
1390     recalc();
1391     if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)s);}
1392     }
1393   }
1394 
1395 
1396 // Get colors of a segment
getSegmentLowerColor(FXint s) const1397 FXColor FXGradientBar::getSegmentLowerColor(FXint s) const {
1398   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::getSegmentLowerColor: argument out of range."); }
1399   return seg[s].lowerColor;
1400   }
1401 
1402 
1403 // Get colors of a segment
getSegmentUpperColor(FXint s) const1404 FXColor FXGradientBar::getSegmentUpperColor(FXint s) const {
1405   if(s<0 || s>=nsegs){ fxerror("FXGradientBar::getSegmentUpperColor: argument out of range."); }
1406   return seg[s].upperColor;
1407   }
1408 
1409 
1410 // Get lower value of segment sg
getSegmentLower(FXint sg) const1411 FXdouble FXGradientBar::getSegmentLower(FXint sg) const {
1412   if(sg<0 || sg>=nsegs){ fxerror("FXGradientBar::getSegmentLower: argument out of range."); }
1413   return seg[sg].lower;
1414   }
1415 
1416 
1417 // Get middle value of segment sg
getSegmentMiddle(FXint sg) const1418 FXdouble FXGradientBar::getSegmentMiddle(FXint sg) const {
1419   if(sg<0 || sg>=nsegs){ fxerror("FXGradientBar::getSegmentMiddle: argument out of range."); }
1420   return seg[sg].middle;
1421   }
1422 
1423 
1424 // Get upper value of segment sg
getSegmentUpper(FXint sg) const1425 FXdouble FXGradientBar::getSegmentUpper(FXint sg) const {
1426   if(sg<0 || sg>=nsegs){ fxerror("FXGradientBar::getSegmentUpper: argument out of range."); }
1427   return seg[sg].upper;
1428   }
1429 
1430 
1431 // Set color bar options
setBarStyle(FXuint style)1432 void FXGradientBar::setBarStyle(FXuint style){
1433   FXuint opts=(options&~GRADIENTBAR_MASK) | (style&GRADIENTBAR_MASK);
1434   if(options!=opts){
1435     options=opts;
1436     recalc();
1437     update();
1438     }
1439   }
1440 
1441 
1442 // Get color bar options
getBarStyle() const1443 FXuint FXGradientBar::getBarStyle() const {
1444   return (options&GRADIENTBAR_MASK);
1445   }
1446 
1447 
1448 // Set base color
setSelectColor(FXColor clr)1449 void FXGradientBar::setSelectColor(FXColor clr){
1450   if(clr!=selectColor){
1451     selectColor=clr;
1452     update();
1453     }
1454   }
1455 
1456 
1457 // Save data
save(FXStream & store) const1458 void FXGradientBar::save(FXStream& store) const {
1459   FXFrame::save(store);
1460   store << bar;
1461   store << tip;
1462   store << help;
1463   store << selectColor;
1464   }
1465 
1466 
1467 // Load data
load(FXStream & store)1468 void FXGradientBar::load(FXStream& store){
1469   FXFrame::load(store);
1470   store >> bar;
1471   store >> tip;
1472   store >> help;
1473   store >> selectColor;
1474   }
1475 
1476 
1477 // Zap it
~FXGradientBar()1478 FXGradientBar::~FXGradientBar(){
1479   delete bar;
1480   FXFREE(&seg);
1481   bar=(FXImage*)-1L;
1482   seg=(FXGradient*)-1L;
1483   }
1484 
1485 }
1486