53416b7ed68c5a31a00f61c01fcbc37b9e1ff44f
[sdk] / ecere / src / gui / controls / Button.ec
1 namespace gui::controls;
2
3 import "Window"
4
5 class ButtonBits { bool bevel:1, scale:1, offset:1, stayDown:1, checkBox:1, toggle:1, radio:1, keyRepeat:1, bevelOver:1, ellipsis:1, stayUp:1; };
6
7 #define CAPTION_DISTANCE   18
8
9 public enum ButtonState /*: byte*/ { up, down, over, disabled, upChecked, downChecked, overChecked, disabledChecked };
10 //use and extend preexisting enum? -> public enum Alignment { left, right, center };
11 public enum BoxAlignment { center, left, right, top, bottom }; // topLeftCorner, topRightCorner, bottomLeftCorner, bottomRightCorner
12
13 public class Button : CommonControl
14 {
15    /*
16    private(opacity);
17    private(drawBehind);
18    */
19    public property Percentage opacity
20    {
21       isset { return (buttonStyle.checkBox || buttonStyle.radio) ? (Window::opacity != 0) : Window::opacity != 1; }
22       get { return Window::opacity; }
23       set { Window::opacity = value; }
24    }
25    public property bool drawBehind
26    {
27       isset { return (buttonStyle.checkBox || buttonStyle.radio) ? (Window::drawBehind != drawBehind) : Window::drawBehind != false; }
28       get { return Window::drawBehind; }
29       set { Window::drawBehind = value; }
30    }
31    class_property(icon) = "<:ecere>controls/button.png";
32
33    watch(background)
34    {
35       Color background = this.background;
36
37       if(colors[down] == colors[up])      colors[down] = background;
38       if(colors[over] == colors[up])      colors[over] = background;
39       if(colors[disabled] == colors[up])  colors[disabled] = background;
40       colors[0] = background;
41    };
42
43    watch(foreground)
44    {
45       Color foreground = this.foreground;
46
47       if(textColor[down] == textColor[up])      textColor[down] = foreground;
48       if(textColor[over] == textColor[up])      textColor[over] = foreground;
49       if(textColor[disabled] == textColor[up])  textColor[disabled] = foreground;
50       textColor[0] = foreground;
51    };
52
53    watch(font)
54    {
55       OnLoadGraphics();
56       SetInitSize(initSize);
57    };
58
59    watch(caption)
60    {
61       OnLoadGraphics();
62       SetInitSize(initSize);
63    };
64
65    ButtonBits buttonStyle;
66    ButtonState state;
67    BitmapResource bitmaps[ButtonState];
68         int captionHeight, captionWidth;
69    unichar symbol;
70    Color textColor[ButtonState];
71    Font font;
72    Color colors[ButtonState];
73    Alignment alignment;
74    BoxAlignment bitmapAlignment;
75    struct { bool hint:1, key:1, captured:1, over:1, checked:1; };
76
77    Button()
78    {
79       background = formColor;
80       foreground = black;
81       //opacity = 0;
82       //drawBehind = true;
83       borderStyle = 0;
84       bevel = true;
85       buttonStyle.offset = true;
86
87       // SetCursor(eInterface_GetCursor(__ecereModule->app, CUR_ARROW));
88       
89       alignment = center;
90       state = up;
91       hint = false;
92       textColor[up] = textColor[down] =
93       textColor[over] = black;
94       textColor[disabled] = Color { 85, 85, 85 };
95
96       colors[up] = formColor;
97       colors[down] = formColor;
98       colors[over] = formColor;
99       colors[disabled] = white;
100       colors[upChecked] = Color { 233,229,221 };
101       colors[overChecked] = formColor;
102       colors[downChecked] = Color { 233,229,221 };
103       colors[disabledChecked] = formColor;
104    }
105
106    Bitmap GetButtonBitmap()
107    {
108       ButtonState state = this.state;
109       Bitmap buttonBitmap = bitmaps[(int)checked*4 + state].bitmap;
110       if(!buttonBitmap) buttonBitmap = bitmaps[(int)checked * 4+up].bitmap;
111       if(!buttonBitmap) buttonBitmap = bitmaps[up].bitmap;
112       if(!isEnabled && bitmaps[disabled]) buttonBitmap = bitmaps[disabled].bitmap;
113       return buttonBitmap;
114    }
115
116    void OnRedraw(Surface surface)
117    {
118       ButtonState state = this.state;
119       Bitmap buttonBitmap = GetButtonBitmap();
120       char * text = this.text;
121       int offset = (state == down && buttonStyle.offset) ? 1 : 0;
122       Color backColor = colors[(int)checked*4 + state];
123       int isDefault = this.isDefault;
124       int tw = 0, th = 0;
125       int bw = 0, bh = 0;
126       
127       if(!backColor) backColor = colors[up];
128       
129       font = fontObject;
130       surface.TextFont(font);
131       if(text)
132          surface.TextExtent(text, strlen(text),&tw, &th);
133
134       if(buttonStyle.bevelOver && checked)
135          offset = 1;
136
137       if(!isEnabled)
138       {
139          if(bitmaps[disabled]) buttonBitmap = bitmaps[disabled].bitmap;
140          state = disabled;
141       }
142
143       if(buttonBitmap && buttonStyle.bevelOver && !buttonStyle.checkBox && !buttonStyle.radio && text)
144          isDefault = 0;
145
146       // Background
147       if((buttonStyle.bevel || buttonStyle.bevelOver) /* && opacity && backColor*/)
148       {
149          if(!buttonStyle.scale || !buttonBitmap)
150          {
151             surface.SetBackground({(byte)(opacity * 255), backColor});
152             //surface.Area(1, 1,clientSize.w-3+offset,clientSize.h-3+offset);
153             surface.Area(0, 0,clientSize.w-1,clientSize.h-1);
154          }
155       }
156
157       // Checkbox
158       if(buttonStyle.checkBox && !buttonBitmap)
159       {
160          int height = 16;
161          int start = (clientSize.h - height) / 2;
162
163          if(!isEnabled)
164             // surface.SetBackground(formColor);
165             surface.SetBackground(gainsboro);
166          else if(active && !text)
167             surface.SetBackground((offset ? formColor : Color { 0,0,170 }));
168          else
169             surface.SetBackground((offset ? formColor : background /*white*/));
170          surface.Area(2, start+2,height-3,start+height-3);
171
172          surface.SetForeground(Color { 85, 85, 85 });
173          surface.HLine(0, height - 2, start + 0);
174          surface.VLine(start+1, start+height - 2, 0);
175
176          surface.SetForeground(Color { 64,64,64 });
177          surface.HLine(1, height - 3, start + 1);
178          surface.VLine(start+2, start+height - 3, 1);
179
180          surface.SetForeground(Color { 212,208,200 });
181          surface.HLine(1, height - 2, start + height-2);
182          surface.VLine(start+1, start+height - 3, height-2);
183
184          surface.SetForeground(white);
185          surface.HLine(0, height - 1, start + height-1);
186          surface.VLine(start+0, start+height - 2, height-1);
187
188          if(checked)
189          {
190             if(active && !text)
191                surface.SetForeground(white);
192             else if(!isEnabled)
193                surface.SetForeground(Color { 85, 85, 85 });
194             else
195                surface.SetForeground(foreground /*black*/);
196             surface.DrawLine(4, start+8, 7,start+11);
197             surface.DrawLine(4, start+9, 7,start+12);
198             surface.DrawLine(7, start+11, 11,start+3);
199             surface.DrawLine(7, start+12, 11,start+4);
200          }
201       }
202
203       // Bitmaps
204       if(buttonBitmap)
205       {
206          surface.SetForeground(white);
207          if(buttonStyle.radio || buttonStyle.checkBox)
208          {
209             int x = 0, y = (clientSize.h-buttonBitmap.height)/2;
210             if(buttonStyle.bevelOver && text)
211             {
212                x = (CAPTION_DISTANCE-buttonBitmap.width)/2 + offset;
213                y = (clientSize.h-buttonBitmap.height)/2 + offset;
214             }
215
216             // Radio Buttons and Checkboxes
217             surface.Blit(buttonBitmap,
218                x, y,
219                0,0,buttonBitmap.width,buttonBitmap.height);
220          }
221          else 
222          {
223             // Push Buttons
224             if(buttonStyle.scale)
225             {
226                if(buttonStyle.bevel || buttonStyle.offset)
227                   surface.Stretch(buttonBitmap, 
228                      1 + offset, 1 + offset,0,0,
229                      clientSize.w-3,clientSize.h-3,buttonBitmap.width,buttonBitmap.height);
230                else
231                   surface.Stretch(buttonBitmap, 0,0, 0,0,
232                      clientSize.w,clientSize.h,buttonBitmap.width,buttonBitmap.height);
233             }
234             else
235             {
236                int x, y;
237                bw = buttonBitmap.width;
238                bh = buttonBitmap.height;
239                
240                if(bitmapAlignment == left || bitmapAlignment == right)
241                {
242                   if(bitmapAlignment == left)
243                      x = 2;
244                   else
245                      x = clientSize.w-bw-2;
246                   y = (clientSize.h-bh)/2;
247                }
248                else if(bitmapAlignment == top || bitmapAlignment == bottom)
249                {
250                   x = (clientSize.w-bw)/2;
251                   if(bitmapAlignment == top)
252                      y = 2;
253                   else
254                      y = clientSize.h-bh-2;
255                }
256                else
257                {
258                   x = (clientSize.w-bw)/2;
259                   y = (clientSize.h-bh - (int)(buttonStyle.bevelOver && text) * th)/2;
260                }
261                if(buttonStyle.bevel || buttonStyle.offset)
262                {
263                   x += offset;
264                   y += offset;
265                }
266                surface.Blit(buttonBitmap, x,y, 0,0, bw,bh);
267             }
268          }
269       }
270
271       // Shadows
272       if(buttonStyle.bevel || 
273          (buttonStyle.bevelOver && 
274             (state == down || state == over || checked)))
275       {
276          if(state == down || checked)
277          {
278             surface.SetForeground(Color { 85, 85, 85 });
279             surface.HLine(isDefault + 0, clientSize.w-2-isDefault, 0);
280             surface.VLine(isDefault + 1, clientSize.h-2-isDefault, 0);
281             surface.SetForeground(white);
282             surface.HLine(isDefault + 0, clientSize.w-1-isDefault, clientSize.h-1-isDefault);
283             surface.VLine(isDefault + 0, clientSize.h-2-isDefault, clientSize.w-1-isDefault);
284          }
285          else
286          {
287             surface.SetForeground(white);
288             surface.HLine(0 + isDefault, clientSize.w-2 - isDefault,  isDefault);
289             surface.VLine(1 + isDefault, clientSize.h-2 - isDefault,  isDefault);
290             surface.SetForeground(Color { 85, 85, 85 });
291             surface.HLine(1 + isDefault, clientSize.w-2 - isDefault, clientSize.h-2 - isDefault);
292             surface.VLine(1 + isDefault, clientSize.h-3 - isDefault, clientSize.w-2 - isDefault);
293
294             if(buttonStyle.bevel)
295             {
296                surface.SetForeground(black);
297                surface.HLine( isDefault, clientSize.w-1 - isDefault, clientSize.h-1 - isDefault);
298                surface.VLine( isDefault, clientSize.h-2 - isDefault, clientSize.w-1 - isDefault);
299             }
300          }
301       }
302
303       // Text
304       surface.TextOpacity(false);
305       surface.SetForeground((textColor[state] ? textColor[state] : textColor[up]));
306       if(text)
307       {
308          if((buttonStyle.radio || buttonStyle.checkBox) && !(buttonStyle.bevelOver))
309             WriteCaption(surface, /*clientSize.h +*/ CAPTION_DISTANCE + 3, 
310                (clientSize.h - th - 4)/2);
311          else 
312          {
313             int x, y;
314
315             if(buttonStyle.bevelOver && buttonBitmap && !buttonStyle.checkBox && !buttonStyle.radio)
316             {
317                if(bitmapAlignment == top)
318                   y = (clientSize.h - bh - 4 - th - 5)/2 + offset + bh + 4;
319                else if(bitmapAlignment == bottom)
320                   y = (clientSize.h - bh - 4 - th - 5)/2 + offset;
321                else//if(bitmapAlignment == left || bitmapAlignment == right)
322                   y = clientSize.h - th - 5 + offset;
323             }
324             else
325                y = (clientSize.h - th - 1)/2 + offset;
326
327             if(buttonStyle.ellipsis)
328             {
329                int width = clientSize.w - 2*6;
330                int x = 6 + offset;
331
332                surface.WriteTextDots(alignment, x, y, width, text, strlen(text));
333             }
334             else
335             {
336                int width;
337                x = 6 + offset;
338                width = clientSize.w - 2 * 6;
339                if(bitmapAlignment == left || bitmapAlignment == right)
340                {
341                   if(bitmapAlignment == left)
342                      x += bw + 4;
343                   width -= bw + 4;
344                }
345                if(buttonStyle.checkBox || ((buttonStyle.radio /*|| buttonStyle.bevelOver*/) && buttonBitmap))
346                {
347                   x += CAPTION_DISTANCE + 3;
348                }
349
350                if(tw < width)
351                {
352                   if(alignment == right)
353                      x += width - tw - 1;
354                   else if(alignment == center)
355                      x += (width - tw) / 2;
356                }
357                WriteCaption(surface, x, y);
358             }
359          }
360       }
361
362       // Activation Highlight
363       if(isDefault)
364       {
365          surface.SetForeground(black);
366          surface.Rectangle(0,0,clientSize.w-1,clientSize.h-1);
367       }
368       if(!(buttonStyle.bevelOver) && !isRemote)
369       {
370          if(active/* && (text || !(buttonStyle.radio || buttonStyle.checkBox))*/)
371          {
372             int x1,y1,x2,y2;
373             surface.SetForeground(black);
374             surface.LineStipple(0x5555);
375
376             if((buttonStyle.radio || buttonStyle.checkBox) && text)
377             {
378                x1 = /*clientSize.h + */CAPTION_DISTANCE;
379                y1 = 0;
380                x2 = clientSize.w-4;
381                y2 = clientSize.h-4;
382             }
383             else
384             {
385                x1 = 3+offset;
386                y1 = 3+offset;
387                x2 = clientSize.w - 5+offset;
388                y2 = clientSize.h - 5+offset;
389
390                if(buttonStyle.radio || buttonStyle.checkBox)
391                {
392                   x1-=3;
393                   y1-=3;
394                   x2+=1;
395                   y2+=1;
396                }
397             }
398             if((x2 - x1) & 1) x2++;
399             if((y2 - y1) & 1) y2++;
400
401             surface.Rectangle(x1, y1, x2, y2);
402             surface.LineStipple(0);
403          }
404       }
405    }
406
407    /*
408    void TVisionOnRedraw(Window window,Surface surface)
409    {
410       Button * button = (Button *)window->data;
411       int state = this.state;
412       int tw, th;
413
414       int offset = (buttonStyle.bevel && state == down) ? textCellW : 0;
415
416       if(clientSize.h <= textCellH || clientSize.w <= 2*textCellW) offset = 0;
417
418       if(!isEnabled)
419          state = disabled;
420
421       surface.TextOpacity(false);
422       surface.SetForeground((active ? white : black) );
423
424       surface.SetBackground(GREEN);
425       // Background
426       if(buttonStyle.bevel)
427       {
428          if(state == down)
429          {
430             if(clientSize.h > textCellH && clientSize.w > 2*textCellW)
431                surface.Area(textCellW,0,clientSize.w-textCellW,clientSize.h-textCellH-1);
432             else if(!window->background)
433                surface.Area(0,0,clientSize.w-1,clientSize.h-1);
434          }
435          else
436          {
437             if(clientSize.h > textCellH && clientSize.w > 2*textCellW)
438             {
439                surface.Area(0,0,clientSize.w-2*textCellW,clientSize.h-textCellH-1);
440                surface.SetForeground(black);
441                eSurface_DrawingChar(surface, 223);
442                surface.HLine(textCellW,clientSize.w-textCellW,window->h-textCellH);
443                if(clientSize.h>32)
444                {
445                   eSurface_DrawingChar(surface, 219);
446                   surface.VLine(1,clientSize.h-textCellH,clientSize.w-textCellW);
447                }
448                eSurface_DrawingChar(surface, 220);
449                surface.HLine(clientSize.w - textCellW, clientSize.w - textCellW, 0);
450             }
451             else if(!window->background)
452                surface.Area(0,0,clientSize.w-1,clientSize.h-1);
453          }
454       }
455
456       surface.SetForeground(((active || state == down) ? ((BITwindow->buttonStyle, ES_DEFAULT)) ? LT_RED : white) : ((BITwindow->buttonStyle, ES_DEFAULT)) ? LT_CYAN : textColor[state])) );
457
458       if(buttonStyle.checkBox)
459          surface.WriteText(0,0, checked ? "[\373]" : "[ ] = 3);
460       if(buttonStyle.radio)
461          surface.WriteText(0,0, checked ? "(\007)" : "( ) = 3);
462
463       if(text)
464          surface.TextExtent(text,strlen(text),&tw, &th);
465
466       if(buttonStyle.radio || buttonStyle.checkBox)
467          surface.SetForeground(((state == down) ? ((BITwindow->buttonStyle, ES_DEFAULT)) ? LT_RED : white) : ((BITwindow->buttonStyle, ES_DEFAULT)) ? LT_CYAN : textColor[state])));
468
469       if(buttonStyle.radio || buttonStyle.checkBox)
470          WriteCaption(surface, 4*textCellW, (clientSize.h - th - 2)/2);
471       else if(!text)
472       {
473          if(symbol)
474          {
475             tw = textCellW; th = textCellH;   
476             surface.WriteTextf((clientSize.w - tw)/2 + offset, (clientSize.h - th - 2)/2 + offset.%c = symbol);
477          }
478       }
479       else
480          WriteCaption(surface,(clientSize.w - tw)/2 + offset,(clientSize.h - th - 2)/2 + offset);
481    }
482    */
483
484    bool OnMouseMove(int x, int y, Modifiers mods)
485    {
486       if(!mods.isSideEffect)
487          return NotifyMouseMove(master, this, x,y, mods);
488       return true;
489    }
490
491    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
492    {
493       //eSystem_Logf("%s %s\n = text, active ? "Activated" : "Deactivated;
494       if(!active)
495       {
496          if(!buttonStyle.toggle)
497          {
498             if(captured)
499             {
500                captured = false;
501                ReleaseCapture();
502             }
503             if(hint && NotifyReleased(master, this, 0, 0, 0))
504             {
505                state = up;
506                hint = false;
507                key = false;
508             }
509          }
510       }
511       Update(null);
512       return true;
513    }
514
515    bool OnLeftButtonDown(int x, int y, Modifiers mods)
516    {
517       if(x >= 0 && y >= 0 && x < size.w && y < size.h)
518       {
519          if(buttonStyle.toggle)
520          {
521             state = (state == down) ? up : down;
522             if(NotifyPushed(master, this, x,y, mods) &&
523                NotifyClicked(master, this, x,y, mods))
524                Update(null);
525          }
526          else
527          {
528             if(key || Capture())
529             {
530                Window master = this.master;
531                if(!key)
532                   captured = true;
533                if(NotifyPushed(master, this, x,y, mods))
534                {
535                   hint = true;
536                   if(!buttonStyle.stayUp)
537                      state = down;
538                   Update(null);
539                }
540             }
541          }
542       }
543       return true;
544    }
545
546    bool OnLeftButtonUp(int x, int y, Modifiers mods)
547    {
548       if(!(buttonStyle.toggle))
549       {
550          bool releasedResult;
551          if(captured)
552          {
553             captured = false;
554             ReleaseCapture();
555          }
556
557          releasedResult = NotifyReleased(master, this, x,y, mods);
558          if(hint)
559          {
560             if(x >= 0 && y >= 0 && x < size.w && y < size.h)
561             {
562                if(buttonStyle.checkBox)
563                   property::checked = !checked;
564                else if(buttonStyle.radio)
565                   property::checked = true;
566
567                // state = mods ? up : over;
568                // TESTING THIS OUT:
569                state = over ? over : up;
570                hint = false;
571                Update(null);
572                if(releasedResult)
573                {
574                   if(NotifyClicked(master, this, x,y, mods))
575                      return true;
576                   else
577                      return false;
578                }
579             }
580             else
581             {
582                state = up;
583                hint = false;
584                Update(null);
585             }
586          }
587       }
588       return true;
589    }
590
591    bool OnLeftDoubleClick(int x, int y, Modifiers mods)
592    {
593       return NotifyDoubleClick(master, this, x,y, mods);
594    }
595
596    bool OnMouseOver(int x, int y, Modifiers mods)
597    {
598       over = true;
599       if(!key)
600       {
601          if(!(buttonStyle.toggle || buttonStyle.stayUp))
602          {
603             if(!hint)
604                state = over;
605             else
606             {
607                state = down;
608             }
609          }
610          else
611          {
612             if(state != down)
613                state = over;
614          }
615
616          if(NotifyMouseOver(master, this, x,y, mods))
617             Update(null);
618       }
619       return true;
620    }
621
622    void OnMouseCaptureLost()
623    {
624       if(captured)
625       {
626          captured = false;
627          if(hint)
628          {
629             state = up;
630             hint = false;
631             Update(null);
632          }
633       }
634    }
635
636    bool OnMouseLeave(Modifiers mods)
637    {
638       if(!key)
639       {
640          if( (!buttonStyle.stayDown || !hint) /*&&  !buttonStyle.toggle */) 
641          {
642             if(!(buttonStyle.toggle) || state == over)
643                state = up;
644          }
645
646          if(NotifyMouseLeave(master, this, mods))
647             Update(null);
648       }
649       over = false;
650       return true;
651    }
652
653    bool OnLoadGraphics()
654    {
655       font = fontObject;
656       if(text)
657          display.FontExtent(font,text,strlen(text), &captionWidth, &captionHeight);
658       else
659       {
660          display.FontExtent(font," ", 1, &captionWidth, &captionHeight);
661          captionWidth = 80;
662       }
663       if(bitmapAlignment != center && !buttonStyle.checkBox && !buttonStyle.radio && !buttonStyle.scale)
664       {
665          Bitmap buttonBitmap = GetButtonBitmap();
666          if(buttonBitmap)
667          {
668             if(bitmapAlignment == left || bitmapAlignment == right)
669                captionWidth += buttonBitmap.width + 4;
670             else
671                captionHeight += buttonBitmap.height + 4;
672          }
673       }
674       return true;
675    }
676
677    bool OnResizing(int *width, int *height)
678    {
679       if(buttonStyle.checkBox || buttonStyle.radio || buttonStyle.bevelOver)
680       {
681          Bitmap bitmap0 = bitmaps[0] ? bitmaps[0].bitmap : null;
682          if(!*width && bitmap0) *width = bitmap0.width;
683          if(!*height && bitmap0) *height = bitmap0.height;
684
685          *height = Max(*height, captionHeight + 2);
686          if(text)
687          {
688             if(guiApp.textMode)
689                *width = Max(*width, /**height + */captionWidth + CAPTION_DISTANCE + 16);
690             else
691                *width = Max(*width, /**height + */captionWidth + CAPTION_DISTANCE + 8);
692          }
693          else
694          {
695             *width = Max(*width, *height);
696             if(guiApp.textMode)
697                *width = Max(*width, 32);
698          }
699       }
700       else
701       {
702          if(guiApp.textMode && buttonStyle.bevel)
703          {
704             if(!*width)
705                *width = Max(*width, captionWidth + 8);
706             if(!*height)
707                *height = Max(*height, captionHeight + 16);
708          }
709          else
710          {
711             if(!*width)
712                *width = Max(*width, captionWidth + /*4*/12);
713             if(!*height)
714                *height = Max(*height, captionHeight + /*6*/ 8);
715          }
716       }
717       return true;
718    }
719
720
721    bool OnKeyUp(Key key, unichar character)
722    {
723       if(key == space || key.code == enter || key.code == keyPadEnter || (key == hotKey && !buttonStyle.keyRepeat))
724       {
725          if(this.key)
726          {
727             this.key = false;
728             /*return */OnLeftButtonUp(0,0, key.modifiers);
729             if(key == hotKey)
730                return false;
731          }
732       }
733       return true;
734    }
735
736    bool OnKeyDown(Key key, unichar character)
737    {
738       if(!key.alt && !key.ctrl)
739       {
740          switch(key.code)
741          {
742             case hotKey:
743                if(buttonStyle.keyRepeat) break;
744             case space:
745             case defaultKey:
746                this.key = true;
747                OnLeftButtonDown(0,0, key.modifiers);
748                if(key.code == hotKey)
749                   return true;
750                else
751                   return false;
752             case escape:
753                if(this.key)
754                {
755                   OnActivate(false, null, null, true);
756                   return false;
757                }
758                break;
759          }
760       }
761       return true;
762    }
763
764    bool OnKeyHit(Key key, unichar character)
765    {
766       switch(key)
767       {
768          case left: case up:
769             if(parent.CycleChildren(false, false, false, !(buttonStyle.bevelOver && buttonStyle.radio)))
770             {
771                if(buttonStyle.bevelOver && buttonStyle.radio)
772                {
773                   Button b = (Button)parent.activeChild;
774                   if(b && b != this && eClass_IsDerived(b._class, class(Button)) && b.buttonStyle.radio && b.buttonStyle.bevelOver)
775                   {
776                      b.OnLeftButtonDown(0,0,(Modifiers)key);   // Casting Key to Modifiers is bad
777                      b.OnLeftButtonUp(0,0,(Modifiers)key);
778                   }
779                }
780                return false;
781             }
782             break;
783          case right: case down:
784             if(parent.CycleChildren(true, false, false, !(buttonStyle.bevelOver && buttonStyle.radio)))
785             {
786                if(buttonStyle.bevelOver && buttonStyle.radio)
787                {
788                   Button b = (Button)parent.activeChild;
789                   if(b && b != this && eClass_IsDerived(b._class, class(Button)) && b.buttonStyle.radio && b.buttonStyle.bevelOver)
790                   {
791                      b.OnLeftButtonDown(0,0,(Modifiers)key);
792                      b.OnLeftButtonUp(0,0,(Modifiers)key);
793                   }
794                }
795                return false;
796             }
797             break;
798          case hotKey:
799             if(buttonStyle.keyRepeat)
800             {
801                this.key = true;
802                OnLeftButtonDown(0,0, key.modifiers);
803                this.key = false;
804                /*return */OnLeftButtonUp(0,0, key.modifiers);
805                return false;
806             }
807             break;
808       }
809       return true;
810    }
811 public:
812    void SetColor(ButtonState state, Color value)
813    {
814       colors[state] = value;
815    }
816
817    void SetTextColor(ButtonState state, Color value)
818    {
819       textColor[state] = value;
820    }
821
822    void RemoveRadio()
823    {
824       if(buttonStyle.radio)
825       {
826          RemoveResource(bitmaps[up]);
827          RemoveResource(bitmaps[down]);
828          RemoveResource(bitmaps[disabled]);
829          RemoveResource(bitmaps[upChecked]);
830          RemoveResource(bitmaps[downChecked]);
831          RemoveResource(bitmaps[disabledChecked]);
832
833          bitmaps[up] = null;
834          bitmaps[down] = null;
835          bitmaps[disabled] = null;
836          bitmaps[upChecked] = null;
837          bitmaps[downChecked] = null;
838          bitmaps[disabledChecked] = null;
839       }
840       buttonStyle.radio = false;
841    }
842
843    // Notifications
844    virtual bool Window::NotifyClicked(Button button, int x, int y, Modifiers mods);
845    virtual bool Window::NotifyDoubleClick(Button button, int x, int y, Modifiers mods);
846    virtual bool Window::NotifyPushed(Button button, int x, int y, Modifiers mods);
847    virtual bool Window::NotifyReleased(Button button, int x, int y, Modifiers mods);
848    virtual bool Window::NotifyMouseMove(Button button, int x, int y, Modifiers mods);
849    virtual bool Window::NotifyMouseOver(Button button, int x, int y, Modifiers mods);
850    virtual bool Window::NotifyMouseLeave(Button button, Modifiers mods);
851
852    // Properties
853    property bool isRadio
854    {
855       property_category $"Behavior" 
856       set
857       {
858          if(value)
859          {
860             if(!bitmaps[up])
861             {
862                RemoveResource(bitmaps[up]);
863                RemoveResource(bitmaps[down]);
864                RemoveResource(bitmaps[disabled]);
865                RemoveResource(bitmaps[upChecked]);
866                RemoveResource(bitmaps[downChecked]);
867                RemoveResource(bitmaps[disabledChecked]);
868
869                bitmaps[up] = BitmapResource { };
870                bitmaps[down] = BitmapResource { };
871                bitmaps[disabled] = BitmapResource { };
872                bitmaps[upChecked] = BitmapResource { };
873                bitmaps[downChecked] = BitmapResource { };
874                bitmaps[disabledChecked] = BitmapResource { };
875                bitmaps[up].fileName = "<:ecere>elements/optionBoxUp.png";
876                bitmaps[down].fileName = "<:ecere>elements/optionBoxDown.png";
877                bitmaps[disabled].fileName = "<:ecere>elements/optionBoxDisabled.png";
878                bitmaps[upChecked].fileName = "<:ecere>elements/optionBoxSelectedUp.png";
879                bitmaps[downChecked].fileName = "<:ecere>elements/optionBoxSelectedDown.png";
880                bitmaps[disabledChecked].fileName = "<:ecere>elements/optionBoxDisabledSelected.png";
881
882                AddResource(bitmaps[up]);
883                AddResource(bitmaps[down]);
884                AddResource(bitmaps[disabled]);
885                AddResource(bitmaps[upChecked]);
886                AddResource(bitmaps[downChecked]);
887                AddResource(bitmaps[disabledChecked]);
888             }
889
890             buttonStyle.radio = true;
891             buttonStyle.checkBox = false;
892             buttonStyle.bevel = false;
893             opacity = 0;
894             drawBehind = true;
895          }
896          else
897          {
898             RemoveRadio();
899          }
900          OnLoadGraphics();
901
902          SetInitSize(initSize);
903       }
904       get { return buttonStyle.radio; }
905    };
906    property bool isCheckbox
907    {
908       property_category $"Behavior" 
909       set
910       {
911          if(value)
912          {
913             buttonStyle.checkBox = true;
914             background = white;
915             RemoveRadio();
916             buttonStyle.radio = false;
917             buttonStyle.bevel = false;
918             opacity = 0;
919             drawBehind = true;
920          }
921          else
922             buttonStyle.checkBox = false;
923          OnLoadGraphics();
924
925          SetInitSize(initSize);
926       }
927       get { return buttonStyle.checkBox; }
928    };
929    property bool bevel
930    {
931       property_category $"Behavior" 
932       set
933       {
934          if(value)
935          {
936             buttonStyle.bevel = true;
937             RemoveRadio();
938             buttonStyle.checkBox = false;
939             buttonStyle.radio = false;
940             buttonStyle.bevelOver = false;
941             opacity = 1;
942             drawBehind = false;
943          }
944          else
945          {
946             buttonStyle.bevel = false;
947             opacity = 0;
948             drawBehind = true;
949          }
950       }
951       get { return buttonStyle.bevel; }
952    };
953    property bool bevelOver
954    {
955       property_category $"Behavior" 
956       set
957       {
958          if(value)
959          {
960             buttonStyle.bevelOver = true;
961             buttonStyle.offset = true;
962             buttonStyle.bevel = false;
963          }
964          else
965          {
966             buttonStyle.bevelOver = false;
967             buttonStyle.offset = false;
968          }
969       }
970       get { return buttonStyle.bevelOver; }
971    };
972    property bool toggle { property_category $"Behavior" set { buttonStyle.toggle = value; } get { return buttonStyle.toggle; } };
973    property bool checked
974    {
975       property_category $"Appearance" 
976       set
977       {
978          if(buttonStyle.toggle)
979          {
980             state = value ? down : up;
981          }
982          else 
983          {
984             if(buttonStyle.radio && value)
985             {
986                Window parent = this.parent;
987                if(parent)
988                {
989                   Window otherWindow;
990                   for(otherWindow = parent.firstChild; otherWindow; otherWindow = otherWindow.next)
991                   {
992                      if(otherWindow != this && eClass_IsDerived(otherWindow._class, _class))
993                      {
994                         Button otherButton = (Button)otherWindow;
995                         if(otherButton.buttonStyle.radio)
996                            otherButton.checked = false;
997                      }
998                   }
999                }
1000             }
1001             checked = value;
1002          }
1003          Update(null);
1004       }
1005       get { return buttonStyle.toggle ? (state == down) : checked; }
1006    };
1007    property Alignment alignment { property_category $"Appearance" set { alignment = value; } get { return alignment; } };
1008    property BitmapResource bitmap
1009    {
1010       property_category $"Appearance" 
1011       set
1012       {
1013          AddResource(value);
1014          RemoveResource(bitmaps[up]);
1015          bitmaps[up] = value;
1016
1017          RemoveResource(bitmaps[over]);
1018          RemoveResource(bitmaps[down]);
1019          RemoveResource(bitmaps[disabled]);
1020          RemoveResource(bitmaps[upChecked]);
1021          RemoveResource(bitmaps[overChecked]);
1022          RemoveResource(bitmaps[downChecked]);
1023          RemoveResource(bitmaps[disabledChecked]);
1024          
1025          bitmaps[over] = null;
1026          bitmaps[down] = null;
1027          bitmaps[disabled] = null;
1028          bitmaps[upChecked] = null;
1029          bitmaps[overChecked] = null;
1030          bitmaps[downChecked] = null;
1031          bitmaps[disabledChecked] = null;
1032
1033          /*
1034          if(buttonStyle.radio && !value)
1035          {
1036             AddResource((bitmaps[up] = BitmapResource { fileName = "<:ecere>elements/optionBox.png" }));
1037             AddResource((bitmaps[down] = BitmapResource { fileName = "<:ecere>elements/optionBox.png" }));
1038             AddResource((bitmaps[disabled] = BitmapResource { fileName = "<:ecere>elements/optionBoxDisabled.png" }));
1039             AddResource((bitmaps[upChecked] = BitmapResource { fileName = "<:ecere>elements/optionBoxSelected.png" }));
1040             AddResource((bitmaps[downChecked] = BitmapResource { fileName = "<:ecere>elements/optionBoxSelected.png" }));
1041             AddResource((bitmaps[disabledChecked] = BitmapResource { fileName = "<:ecere>elements/optionBoxDisabledSelected.png" }));
1042          }
1043          else */if(!bitmaps[disabled])
1044          {
1045             bitmaps[disabled] = BitmapResource { fileName = bitmap.fileName, grayed = true };
1046             AddResource(bitmaps[disabled]);
1047          }
1048       }
1049       get { return bitmaps[up]; }
1050    };
1051    property bool stayUp { property_category $"Behavior" set { buttonStyle.stayUp = value; } get { return buttonStyle.stayUp; } };
1052    property bool scaleBitmap { property_category $"Appearance" set { buttonStyle.scale = value; } get { return buttonStyle.scale; } };
1053    property bool keyRepeat { property_category $"Behavior" set { buttonStyle.keyRepeat = value; } get { return buttonStyle.keyRepeat; } };
1054    property unichar symbol { property_category $"Appearance" set { this.symbol = value; } get { return symbol; } };
1055    property bool ellipsis { property_category $"Appearance" set { buttonStyle.ellipsis = value; } get { return buttonStyle.ellipsis; } };
1056    property bool stayDown { property_category $"Behavior" set { buttonStyle.stayDown = value; } get { return buttonStyle.stayDown; } };
1057    property bool offset { property_category $"Behavior" set { buttonStyle.offset = value; } get { return buttonStyle.offset; } };
1058
1059    property ButtonState buttonState { get { return state; } set { state = value; } };
1060    property BoxAlignment bitmapAlignment { property_category $"Appearance" set { bitmapAlignment = value; } get { return bitmapAlignment; } };
1061 };