wip II
[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(!caption && bitmap && bitmap.bitmap)
703          {
704             if(!*width)
705                *width = bitmap.bitmap.width;
706             if(!*height)
707                *height = bitmap.bitmap.height;
708          }
709          else if(guiApp.textMode && buttonStyle.bevel)
710          {
711             if(!*width)
712                *width = Max(*width, captionWidth + 8);
713             if(!*height)
714                *height = Max(*height, captionHeight + 16);
715          }
716          else
717          {
718             if(!*width)
719                *width = Max(*width, captionWidth + /*4*/12);
720             if(!*height)
721                *height = Max(*height, captionHeight + /*6*/ 8);
722          }
723       }
724       return true;
725    }
726
727
728    bool OnKeyUp(Key key, unichar character)
729    {
730       if(key == space || key.code == enter || key.code == keyPadEnter || (key == hotKey && !buttonStyle.keyRepeat))
731       {
732          if(this.key)
733          {
734             this.key = false;
735             /*return */OnLeftButtonUp(0,0, key.modifiers);
736             if(key == hotKey)
737                return false;
738          }
739       }
740       return true;
741    }
742
743    bool OnKeyDown(Key key, unichar character)
744    {
745       if(!key.alt && !key.ctrl)
746       {
747          switch(key.code)
748          {
749             case hotKey:
750                if(buttonStyle.keyRepeat) break;
751             case space:
752             case defaultKey:
753                this.key = true;
754                OnLeftButtonDown(0,0, key.modifiers);
755                if(key.code == hotKey)
756                   return true;
757                else
758                   return false;
759             case escape:
760                if(this.key)
761                {
762                   OnActivate(false, null, null, true);
763                   return false;
764                }
765                break;
766          }
767       }
768       return true;
769    }
770
771    bool OnKeyHit(Key key, unichar character)
772    {
773       switch(key)
774       {
775          case left: case up:
776             if(parent.CycleChildren(false, false, false, !(buttonStyle.bevelOver && buttonStyle.radio)))
777             {
778                if(buttonStyle.bevelOver && buttonStyle.radio)
779                {
780                   Button b = (Button)parent.activeChild;
781                   if(b && b != this && eClass_IsDerived(b._class, class(Button)) && b.buttonStyle.radio && b.buttonStyle.bevelOver)
782                   {
783                      b.OnLeftButtonDown(0,0,(Modifiers)key);   // Casting Key to Modifiers is bad
784                      b.OnLeftButtonUp(0,0,(Modifiers)key);
785                   }
786                }
787                return false;
788             }
789             break;
790          case right: case down:
791             if(parent.CycleChildren(true, false, false, !(buttonStyle.bevelOver && buttonStyle.radio)))
792             {
793                if(buttonStyle.bevelOver && buttonStyle.radio)
794                {
795                   Button b = (Button)parent.activeChild;
796                   if(b && b != this && eClass_IsDerived(b._class, class(Button)) && b.buttonStyle.radio && b.buttonStyle.bevelOver)
797                   {
798                      b.OnLeftButtonDown(0,0,(Modifiers)key);
799                      b.OnLeftButtonUp(0,0,(Modifiers)key);
800                   }
801                }
802                return false;
803             }
804             break;
805          case hotKey:
806             if(buttonStyle.keyRepeat)
807             {
808                this.key = true;
809                OnLeftButtonDown(0,0, key.modifiers);
810                this.key = false;
811                /*return */OnLeftButtonUp(0,0, key.modifiers);
812                return false;
813             }
814             break;
815       }
816       return true;
817    }
818 public:
819    void SetColor(ButtonState state, Color value)
820    {
821       colors[state] = value;
822    }
823
824    void SetTextColor(ButtonState state, Color value)
825    {
826       textColor[state] = value;
827    }
828
829    void RemoveRadio()
830    {
831       if(buttonStyle.radio)
832       {
833          RemoveResource(bitmaps[up]);
834          RemoveResource(bitmaps[down]);
835          RemoveResource(bitmaps[disabled]);
836          RemoveResource(bitmaps[upChecked]);
837          RemoveResource(bitmaps[downChecked]);
838          RemoveResource(bitmaps[disabledChecked]);
839
840          bitmaps[up] = null;
841          bitmaps[down] = null;
842          bitmaps[disabled] = null;
843          bitmaps[upChecked] = null;
844          bitmaps[downChecked] = null;
845          bitmaps[disabledChecked] = null;
846       }
847       buttonStyle.radio = false;
848    }
849
850    // Notifications
851    virtual bool Window::NotifyClicked(Button button, int x, int y, Modifiers mods);
852    virtual bool Window::NotifyDoubleClick(Button button, int x, int y, Modifiers mods);
853    virtual bool Window::NotifyPushed(Button button, int x, int y, Modifiers mods);
854    virtual bool Window::NotifyReleased(Button button, int x, int y, Modifiers mods);
855    virtual bool Window::NotifyMouseMove(Button button, int x, int y, Modifiers mods);
856    virtual bool Window::NotifyMouseOver(Button button, int x, int y, Modifiers mods);
857    virtual bool Window::NotifyMouseLeave(Button button, Modifiers mods);
858
859    // Properties
860    property bool isRadio
861    {
862       property_category $"Behavior" 
863       set
864       {
865          if(value)
866          {
867             if(!bitmaps[up])
868             {
869                RemoveResource(bitmaps[up]);
870                RemoveResource(bitmaps[down]);
871                RemoveResource(bitmaps[disabled]);
872                RemoveResource(bitmaps[upChecked]);
873                RemoveResource(bitmaps[downChecked]);
874                RemoveResource(bitmaps[disabledChecked]);
875
876                bitmaps[up] = BitmapResource { };
877                bitmaps[down] = BitmapResource { };
878                bitmaps[disabled] = BitmapResource { };
879                bitmaps[upChecked] = BitmapResource { };
880                bitmaps[downChecked] = BitmapResource { };
881                bitmaps[disabledChecked] = BitmapResource { };
882                bitmaps[up].fileName = "<:ecere>elements/optionBoxUp.png";
883                bitmaps[down].fileName = "<:ecere>elements/optionBoxDown.png";
884                bitmaps[disabled].fileName = "<:ecere>elements/optionBoxDisabled.png";
885                bitmaps[upChecked].fileName = "<:ecere>elements/optionBoxSelectedUp.png";
886                bitmaps[downChecked].fileName = "<:ecere>elements/optionBoxSelectedDown.png";
887                bitmaps[disabledChecked].fileName = "<:ecere>elements/optionBoxDisabledSelected.png";
888
889                AddResource(bitmaps[up]);
890                AddResource(bitmaps[down]);
891                AddResource(bitmaps[disabled]);
892                AddResource(bitmaps[upChecked]);
893                AddResource(bitmaps[downChecked]);
894                AddResource(bitmaps[disabledChecked]);
895             }
896
897             buttonStyle.radio = true;
898             buttonStyle.checkBox = false;
899             buttonStyle.bevel = false;
900             opacity = 0;
901             drawBehind = true;
902          }
903          else
904          {
905             RemoveRadio();
906          }
907          OnLoadGraphics();
908
909          SetInitSize(initSize);
910       }
911       get { return buttonStyle.radio; }
912    };
913    property bool isCheckbox
914    {
915       property_category $"Behavior" 
916       set
917       {
918          if(value)
919          {
920             buttonStyle.checkBox = true;
921             background = white;
922             RemoveRadio();
923             buttonStyle.radio = false;
924             buttonStyle.bevel = false;
925             opacity = 0;
926             drawBehind = true;
927          }
928          else
929             buttonStyle.checkBox = false;
930          OnLoadGraphics();
931
932          SetInitSize(initSize);
933       }
934       get { return buttonStyle.checkBox; }
935    };
936    property bool bevel
937    {
938       property_category $"Behavior" 
939       set
940       {
941          if(value)
942          {
943             buttonStyle.bevel = true;
944             RemoveRadio();
945             buttonStyle.checkBox = false;
946             buttonStyle.radio = false;
947             buttonStyle.bevelOver = false;
948             opacity = 1;
949             drawBehind = false;
950          }
951          else
952          {
953             buttonStyle.bevel = false;
954             opacity = 0;
955             drawBehind = true;
956          }
957       }
958       get { return buttonStyle.bevel; }
959    };
960    property bool bevelOver
961    {
962       property_category $"Behavior" 
963       set
964       {
965          if(value)
966          {
967             buttonStyle.bevelOver = true;
968             buttonStyle.offset = true;
969             buttonStyle.bevel = false;
970          }
971          else
972          {
973             buttonStyle.bevelOver = false;
974             buttonStyle.offset = false;
975          }
976       }
977       get { return buttonStyle.bevelOver; }
978    };
979    property bool toggle { property_category $"Behavior" set { buttonStyle.toggle = value; } get { return buttonStyle.toggle; } };
980    property bool checked
981    {
982       property_category $"Appearance" 
983       set
984       {
985          if(buttonStyle.toggle)
986          {
987             state = value ? down : up;
988          }
989          else 
990          {
991             if(buttonStyle.radio && value)
992             {
993                Window parent = this.parent;
994                if(parent)
995                {
996                   Window otherWindow;
997                   for(otherWindow = parent.firstChild; otherWindow; otherWindow = otherWindow.next)
998                   {
999                      if(otherWindow != this && eClass_IsDerived(otherWindow._class, _class))
1000                      {
1001                         Button otherButton = (Button)otherWindow;
1002                         if(otherButton.buttonStyle.radio)
1003                            otherButton.checked = false;
1004                      }
1005                   }
1006                }
1007             }
1008             checked = value;
1009          }
1010          Update(null);
1011       }
1012       get { return buttonStyle.toggle ? (state == down) : checked; }
1013    };
1014    property Alignment alignment { property_category $"Appearance" set { alignment = value; } get { return alignment; } };
1015    property BitmapResource bitmap
1016    {
1017       property_category $"Appearance" 
1018       set
1019       {
1020          AddResource(value);
1021          RemoveResource(bitmaps[up]);
1022          bitmaps[up] = value;
1023
1024          RemoveResource(bitmaps[over]);
1025          RemoveResource(bitmaps[down]);
1026          RemoveResource(bitmaps[disabled]);
1027          RemoveResource(bitmaps[upChecked]);
1028          RemoveResource(bitmaps[overChecked]);
1029          RemoveResource(bitmaps[downChecked]);
1030          RemoveResource(bitmaps[disabledChecked]);
1031          
1032          bitmaps[over] = null;
1033          bitmaps[down] = null;
1034          bitmaps[disabled] = null;
1035          bitmaps[upChecked] = null;
1036          bitmaps[overChecked] = null;
1037          bitmaps[downChecked] = null;
1038          bitmaps[disabledChecked] = null;
1039
1040          /*
1041          if(buttonStyle.radio && !value)
1042          {
1043             AddResource((bitmaps[up] = BitmapResource { fileName = "<:ecere>elements/optionBox.png" }));
1044             AddResource((bitmaps[down] = BitmapResource { fileName = "<:ecere>elements/optionBox.png" }));
1045             AddResource((bitmaps[disabled] = BitmapResource { fileName = "<:ecere>elements/optionBoxDisabled.png" }));
1046             AddResource((bitmaps[upChecked] = BitmapResource { fileName = "<:ecere>elements/optionBoxSelected.png" }));
1047             AddResource((bitmaps[downChecked] = BitmapResource { fileName = "<:ecere>elements/optionBoxSelected.png" }));
1048             AddResource((bitmaps[disabledChecked] = BitmapResource { fileName = "<:ecere>elements/optionBoxDisabledSelected.png" }));
1049          }
1050          else */if(!bitmaps[disabled])
1051          {
1052             bitmaps[disabled] = BitmapResource { fileName = bitmap.fileName, grayed = true };
1053             AddResource(bitmaps[disabled]);
1054          }
1055       }
1056       get { return bitmaps[up]; }
1057    };
1058    property bool stayUp { property_category $"Behavior" set { buttonStyle.stayUp = value; } get { return buttonStyle.stayUp; } };
1059    property bool scaleBitmap { property_category $"Appearance" set { buttonStyle.scale = value; } get { return buttonStyle.scale; } };
1060    property bool keyRepeat { property_category $"Behavior" set { buttonStyle.keyRepeat = value; } get { return buttonStyle.keyRepeat; } };
1061    property unichar symbol { property_category $"Appearance" set { this.symbol = value; } get { return symbol; } };
1062    property bool ellipsis { property_category $"Appearance" set { buttonStyle.ellipsis = value; } get { return buttonStyle.ellipsis; } };
1063    property bool stayDown { property_category $"Behavior" set { buttonStyle.stayDown = value; } get { return buttonStyle.stayDown; } };
1064    property bool offset { property_category $"Behavior" set { buttonStyle.offset = value; } get { return buttonStyle.offset; } };
1065
1066    property ButtonState buttonState { get { return state; } set { state = value; } };
1067    property BoxAlignment bitmapAlignment { property_category $"Appearance" set { bitmapAlignment = value; } get { return bitmapAlignment; } };
1068 };