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