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