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