ecere/printing: Fix for printing drivers broken by new default of nativeDecorations...
[sdk] / ecere / src / gui / skins / WindowsSkin.ec
1 #if defined(WIN32)
2 #define WIN32_LEAN_AND_MEAN
3 #define Method _Method
4 #include <windows.h>
5 #undef Method
6 #endif
7
8 import "Window"
9
10 #if !defined(WIN32)
11 bool gui::drivers::XGetBorderWidths(Window window, Box box);
12 #endif
13
14 namespace gui::skins;
15
16 #define BORDER       4
17 #define TOP          4
18 #define BOTTOM       4
19 #define CORNER       (BORDER * 2)
20 #define CAPTION      20
21 #define DEAD_BORDER  3
22 #define MIN_WIDTH    60
23 #define MIN_HEIGHT   3
24 #define BUTTON_OFFSET   2
25 #define NAME_OFFSET   2
26 #define NAME_OFFSETX  4
27
28 #define SB_WIDTH  16
29 #define SB_HEIGHT 16
30
31
32 #define GRADIENT_SMOOTHNESS 1.0f
33
34 #define TEXT_COLOR   black
35 /*
36
37 #define GRADIENT_DIRECTION horizontal
38
39 static ColorKey gradient[] =
40 {
41    { Color { 128, 128, 255}, 0.00f },
42    { Color { 254, 254, 254}, 0.60f },
43    { Color {   0,   0, 255}, 1.00f }
44 };
45 */
46
47 #define GRADIENT_DIRECTION vertical
48 static ColorKey gradient[] =
49 {
50    //{ ColorAlpha { 255, { 180, 200, 220} }, 0.00f },
51    { ColorAlpha { 255, menuBarColor }, 0.00f },
52    { ColorAlpha { 255, { 255, 255, 255} }, 0.60f },
53    { ColorAlpha { 255, { 158, 158, 160} }, 1.00f }
54 };
55
56 static ColorKey gradientInactive[] =
57 {
58    //{ ColorAlpha { 255, { 160, 180, 200} },  0.00f },
59    { ColorAlpha { 255, popupMenuColor }, 0.00f },
60    { ColorAlpha { 255, { 220, 220, 220} }, 0.60f },
61    { ColorAlpha { 255, { 120, 120, 120} }, 1.00f }
62 };
63
64 /*
65 #define GRADIENT_DIRECTION horizontal
66 #define TEXT_COLOR         white
67 */
68 //#define TEXT_INACTIVE      Color { 212,208,200 }
69 #define TEXT_INACTIVE      Color { 40, 50, 60 }
70 /*
71 static ColorKey gradient[] =
72 {
73    { ColorAlpha { 255, Color {  10,   36, 106 } }, 0.00f },
74    { ColorAlpha { 255, Color { 166,  202, 240 } }, 1.00f }
75 };
76 static ColorKey gradientInactive[] =
77 {
78    { ColorAlpha { 255, Color { 128, 128, 128 } }, 0.00f },
79    { ColorAlpha { 255, Color { 192, 192, 192 } }, 1.00f }
80 };
81 */
82 char * cursorsBitmaps[] = 
83 {
84    "<:ecere>cursors/arrow.png",
85    "<:ecere>cursors/iBeam.png",
86    "<:ecere>cursors/cross.png",
87    "<:ecere>cursors/move.png",
88    "<:ecere>cursors/sizeNorthEastSouthWest.png",
89    "<:ecere>cursors/sizeNorthSouth.png",
90    "<:ecere>cursors/sizeNorthWestSouthEast.png",
91    "<:ecere>cursors/sizeWestEast.png",
92    "<:ecere>cursors/move.png"
93 };
94
95 static Point cursorsHotSpots[] =
96 {
97    { 0, 0 },
98    { 0, 0 },
99    { 8, 8 },
100    { 10, 10 },
101    { 8, 8 },
102    { 4, 10 },
103    { 7, 7 },
104    { 5, 0 }
105 };
106
107 static char * skinBitmaps[SkinBitmap] =
108 {
109    "<:ecere>elements/areaMinimize.png",
110    "<:ecere>elements/areaMaximize.png",
111    "<:ecere>elements/areaRestore.png",
112    "<:ecere>elements/areaClose.png"
113 };
114
115 class WindowsSkin : Skin
116 {
117    class_property(name) = "Windows";
118    class_property(selectionColor) = Color { 10, 36, 106 };
119    class_property(selectionText)  = (Color)white;
120    class_property(disabledFrontColor) = Color { 128,128,128 };
121    class_property(disabledBackColor) = (Color)white;
122
123    FontResource ::SystemFont()
124    {
125       return FontResource { faceName = $"Tahoma", size = 8.25f };
126    }
127
128    FontResource ::CaptionFont()
129    {
130       return FontResource { faceName = $"Tahoma", size = 8.25f, bold = true };
131    }
132
133    char * ::CursorsBitmaps(uint id, int * hotSpotX, int *hotSpotY, byte ** paletteShades)
134    {
135       *hotSpotX = cursorsHotSpots[id].x;
136       *hotSpotY = cursorsHotSpots[id].y;
137       *paletteShades = null;
138       return cursorsBitmaps[id];
139    }
140
141    BitmapResource ::GetBitmap(SkinBitmap id)
142    {
143       return BitmapResource { fileName = skinBitmaps[id] };
144    }
145
146    int ::VerticalSBW() { return SB_WIDTH; }
147    int ::HorizontalSBH() { return SB_HEIGHT; }
148 };
149
150
151 public class WindowsSkin_Window : Window
152 {
153    void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h)
154    {
155       *w = *h = 0;
156
157       if(hasMenuBar && state != minimized)
158       {
159          *h += skinMenuHeight;
160       }
161       if(statusBar && state != minimized)
162       {
163          *h += statusBarHeight;
164       }
165
166       if(nativeDecorations && rootWindow == this && windowHandle)
167       {
168 #if defined(WIN32)
169          RECT rcClient = { 0 }, rcWindow = { 0 };
170          if(GetClientRect(windowHandle, &rcClient) && GetWindowRect(windowHandle, &rcWindow))
171          {
172             *w += (rcWindow.right - rcWindow.left) - rcClient.right;
173             *h += (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
174          }
175
176          // PrintLn(_class.name, " is at l = ", rcWindow.left, ", r = ", rcWindow.right);
177 #else
178          Box widths = { 0 };
179          XGetBorderWidths(this, widths);
180          *w += widths.left + widths.right;
181          *h += widths.top + widths.bottom;
182 #endif
183          return;
184       }
185       if((((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel) && state != minimized)
186       {
187          *w += 4;
188          *h += 4;
189       }
190       if(((BorderBits)borderStyle).sizable && (state == normal))
191       {
192          *w += 2 * BORDER;
193          *h += TOP + BOTTOM;
194       }
195       if(((BorderBits)borderStyle).fixed && (state != maximized || !GetParentMenuBar()))
196       {
197          *h += CAPTION;
198          if(!((BorderBits)borderStyle).sizable || state == minimized)
199          {
200             *h += 2*DEAD_BORDER;
201             *w += 2*DEAD_BORDER;
202          }
203       }
204       if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
205       {
206          *w += 2;
207          *h += 2;
208       }
209    }
210
211    void SetWindowMinimum(MinMaxValue * mw, MinMaxValue * mh)
212    {
213       bool isNormal = (state == normal);
214       if(nativeDecorations && rootWindow == this && windowHandle) return;
215       if(((BorderBits)borderStyle).fixed && (state != maximized || !GetParentMenuBar()))
216       {
217          *mw = MIN_WIDTH;
218          *mh = MIN_HEIGHT;
219       }
220       else
221          *mw = *mh = 0;
222       if(((BorderBits)borderStyle).sizable && isNormal)
223          *mw += 2*CORNER;
224       // GetDecorationsSize(window, mw, mh);
225
226       if(hasVertScroll)
227          *mw += SB_WIDTH;
228       if(hasHorzScroll)
229          *mh += SB_HEIGHT;
230       if(hasVertScroll && hasHorzScroll)
231       {
232          *mw += 2 * SB_WIDTH + SB_WIDTH;
233          *mh += 2 * SB_HEIGHT + SB_HEIGHT;
234          if(((BorderBits)borderStyle).sizable && isNormal)
235             *mw -= 2*CORNER;
236       }
237    }
238
239    void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
240    {
241       bool isNormal = (state == normal);
242       MinMaxValue aw = 0, ah = 0;
243
244       *x = *y = 0;
245
246       if(hasMenuBar)
247       {
248          *y += skinMenuHeight;
249       }
250
251       GetDecorationsSize(&aw, &ah);
252
253       if(nativeDecorations && rootWindow == this && windowHandle)
254       {
255 #if defined(WIN32)
256          RECT rcWindow;
257          POINT client00 = { 0, 0 };
258          ClientToScreen(windowHandle, &client00);
259          GetWindowRect(windowHandle, &rcWindow);
260          *x += client00.x - rcWindow.left;
261          *y += client00.y - rcWindow.top;
262 #else
263          Box widths;
264          XGetBorderWidths(this, widths);
265          *x += widths.left;
266          *y += widths.top;
267 #endif
268       }
269       else
270       {
271          // Compute client area start
272          if(((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel)
273          {
274             *x += 2;
275             *y += 2;
276          }
277
278          if(((BorderBits)borderStyle).sizable && isNormal)
279          {
280             *x += BORDER;
281             *y += TOP;
282          }
283
284          if(((BorderBits)borderStyle).fixed && (state != maximized || !GetParentMenuBar()))
285          {
286             *y += CAPTION;
287             if(!((BorderBits)borderStyle).sizable || state == minimized)
288             {
289                *y += DEAD_BORDER;
290                *x += DEAD_BORDER;
291             }
292          }
293
294          if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
295          {
296             *x += 1;
297             *y += 1;
298          }
299       }
300
301       // Reduce client area
302       *cw = *w - aw;
303       *ch = *h - ah;
304
305       *cw = Max(*cw, 0);
306       *ch = Max(*ch, 0);
307    }
308
309    void ShowDecorations(Font captionFont, Surface surface, char * name, bool active, bool moving)
310    {
311       bool isNormal = (state == normal);
312       int top = 0, border = 0, bottom = 0;
313       Window parentMenuBar = GetParentMenuBar();
314
315       if(nativeDecorations && rootWindow == this && windowHandle) return;
316
317       if(state == minimized)
318          top = border = bottom = DEAD_BORDER;
319       else if(((BorderBits)borderStyle).sizable)
320       {
321          top = isNormal ? TOP : 0;
322          border = isNormal ? BORDER : 0;
323          bottom = BOTTOM;
324       }
325       else if(((BorderBits)borderStyle).fixed)
326       {
327          top = DEAD_BORDER;
328          border = DEAD_BORDER;
329          bottom = DEAD_BORDER;
330       }
331       else if(((BorderBits)borderStyle).contour)
332       {
333          top = 1;
334          border = 1;
335          bottom = 1;
336       }
337
338       if(((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel)
339       {
340          int deepTop = 0, deepBottom = 0, deepBorder = 0;
341          if(((BorderBits)borderStyle).contour)
342          {
343             deepBorder = border;
344             deepTop = (((BorderBits)borderStyle).fixed && (state != maximized || !parentMenuBar)) ? (top + CAPTION) : top;
345             deepBottom = (((BorderBits)borderStyle).sizable && isNormal) ? bottom : border;
346          }
347
348          surface.Bevel(((BorderBits)borderStyle).bevel ? false : true, deepBorder, deepTop, 
349             size.w - deepBorder - deepBorder, size.h - deepBottom - deepTop);
350       }
351
352       if(((BorderBits)borderStyle).fixed && (state != maximized || !parentMenuBar))
353       {
354          if(state != maximized || !((BorderBits)borderStyle).sizable)
355          {
356             // Frame for ES_CAPTION windows
357             surface.Bevel(false, 0, 0, size.w, size.h);
358             surface.SetForeground(formColor);
359             surface.Rectangle(2, 2, size.w-3, size.h-3);
360
361             // Resizeable frame is 1 pixel thicker 
362             if(((BorderBits)borderStyle).sizable && isNormal)
363                surface.Rectangle(3, 3, size.w - 4, size.h - 4);
364          }
365
366          // Caption
367          if(active)
368             surface.Gradient(gradient, sizeof(gradient) / sizeof(ColorKey), GRADIENT_SMOOTHNESS, GRADIENT_DIRECTION,
369                border, top, size.w - border - 1, top + CAPTION - 2);
370          else
371             surface.Gradient(gradientInactive, sizeof(gradientInactive) / sizeof(ColorKey), 
372             GRADIENT_SMOOTHNESS, GRADIENT_DIRECTION,
373                border, top, size.w - border - 1, top + CAPTION - 2);
374
375          surface.SetForeground(formColor);
376          if(state != minimized)
377             surface.HLine(border, size.w-border-1, top + CAPTION-1);
378
379          surface.SetForeground((active ? TEXT_COLOR : TEXT_INACTIVE));
380          surface.TextOpacity(false);
381          surface.TextFont(captionFont);
382          if(name)
383          {
384             int buttonsSize = border +
385                ((hasMaximize || hasMinimize) ? 52 : 18);
386             surface.WriteTextDots(left, border + NAME_OFFSETX, top + NAME_OFFSET, 
387                size.w - (buttonsSize + border + 4), name, strlen(name));
388          }
389       }
390       if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
391       {
392          surface.SetForeground(black);
393          surface.Rectangle(0, 0, size.w - 1, size.h - 1);
394       }
395
396       if(state != minimized && hasHorzScroll && hasVertScroll)
397       {
398          if(sbh && sbh.visible && sbv && sbv.visible)
399          {
400             surface.SetBackground(formColor);
401             surface.Area(
402                clientStart.x + clientSize.w,
403                clientStart.y + clientSize.h,
404                clientStart.x + clientSize.w + SB_WIDTH - 1,
405                clientStart.y + clientSize.h + SB_HEIGHT - 1);
406          }
407       }
408    }
409
410    bool IsMouseMoving(int x, int y, int w, int h)
411    {
412       bool isNormal = (state == normal);
413       bool result = false;
414       if(nativeDecorations && rootWindow == this && windowHandle) return false;
415
416       if(((BorderBits\)borderStyle).fixed && (state != maximized || !GetParentMenuBar()))
417       {
418          int corner = 0, border = 0, top = 0;
419          if(((BorderBits)borderStyle).sizable && isNormal)
420          {
421             corner = CORNER;
422             border = BORDER;
423             top    = TOP;
424          }
425          // Special case for having caption on resize bar
426          if(!CAPTION)
427             result = Box { corner, 0, w-corner-1, TOP-1 }.IsPointInside({x,y});
428          else
429             result = Box { border, top, w-border-1, top+CAPTION-1 }.IsPointInside({x, y});
430       }
431       return result;
432    }
433
434    bool IsMouseResizing(int x, int y, int w, int h, bool *resizeX, bool *resizeY, bool *resizeEndX, bool *resizeEndY)
435    {
436       bool result = false;
437
438       *resizeX = *resizeY = *resizeEndX = *resizeEndY = false;
439       if(nativeDecorations && rootWindow == this && windowHandle) return false;
440
441       if(((BorderBits)borderStyle).sizable && (state == normal))
442       {
443          // TopLeft Corner
444          if(Box { 0, 0,CORNER-1, TOP-1 }.IsPointInside({x, y}))
445             result = *resizeX = *resizeY = true;
446          // TopRight Corner
447          if(Box { w-CORNER-1, 0, w-1, TOP-1 }.IsPointInside({x, y}))
448             result = *resizeEndX = *resizeY = true;
449          // BottomLeft Corner
450          if(Box { 0, h-BOTTOM-1, CORNER-1, h-1 }.IsPointInside({x, y}))
451             result = *resizeX = *resizeEndY = true;
452          // BottomRight Corner
453          if(Box { w-CORNER-1, h-BOTTOM-1, w-1, h-1 }.IsPointInside({x, y}))
454             result = *resizeEndX = *resizeEndY = true;
455          // Left Border
456          if(Box { 0,TOP, BORDER, h-BOTTOM-1 }.IsPointInside({x, y}))
457             result = *resizeX = true;
458          // Right Border
459          if(Box { w-BORDER-1, TOP, w-1, h-BOTTOM-1 }.IsPointInside({x, y}))
460             result = *resizeEndX = true;
461          // Top Border
462          if(Box { CORNER, 0, w-CORNER-1, TOP-1 }.IsPointInside({x, y}))
463             result = *resizeY = true;
464          // Bottom Border
465          if(Box { CORNER, h-BOTTOM-1, w-CORNER-1, h-1 }.IsPointInside({x, y}))
466             result = *resizeEndY = true;
467       }
468       return result;
469    }
470
471    void UpdateNonClient()
472    {
473       bool isNormal = (state == normal);
474       int top = 0, border = 0;
475       int insideBorder = 0;
476
477       if(!nativeDecorations || rootWindow != this || !windowHandle)
478       {
479          if(state == minimized)
480             top = border = DEAD_BORDER;
481          else if(((BorderBits)borderStyle).sizable)
482          {
483             if(state == maximized && GetParentMenuBar())
484             {
485                top = 2;
486                border = 2;
487             }
488             else
489             {
490                top = isNormal ? TOP : 0;
491                border = isNormal ? BORDER : 0;
492             }
493          }
494          else if(((BorderBits)borderStyle).fixed)
495          {
496             top = DEAD_BORDER;
497             border = DEAD_BORDER;
498          }
499          else if(((BorderBits)borderStyle).contour)
500          {
501             top = 1;
502             border = 1;
503          }
504          insideBorder = border;
505          if(((BorderBits)borderStyle).deep)
506             insideBorder += 2;
507       }
508       else
509       {
510          border = clientStart.x;
511          insideBorder = border;
512       }
513
514       if(menuBar)
515       {
516          if(state == minimized)
517             menuBar.visible = false;
518          else
519             menuBar.visible = true;
520          menuBar.Move(clientStart.x, clientStart.y - skinMenuHeight, size.w - insideBorder * 2, skinMenuHeight);
521       }
522       if(statusBar)
523       {
524          if(state == minimized)
525             statusBar.visible = false;
526          else
527          {
528             statusBar.visible = true;
529             if(nativeDecorations && rootWindow == this && windowHandle)
530             {
531                statusBar.anchor = { left = clientStart.x, bottom = (int)(size.h - clientSize.h - clientStart.y - statusBarHeight ) };
532                statusBar.size.w = size.w - insideBorder * 2;
533             }
534             else
535             {
536                statusBar.anchor = { left = clientStart.x, bottom = border };
537                statusBar.size.w = size.w - insideBorder * 2;
538             }
539          }
540       }
541       if(!nativeDecorations || rootWindow != this || !windowHandle)
542       {
543          if(sysButtons[0])
544          {
545             sysButtons[0].anchor = { right = 35 + border, top = top + BUTTON_OFFSET };
546             sysButtons[0].size = { 15, 15 };
547             sysButtons[0].bevel = true;
548             sysButtons[0].bitmap = { skinBitmaps[(state == minimized) ? restore : minimize] };
549             sysButtons[0].visible = true;
550          }
551          if(sysButtons[1])
552          {
553             sysButtons[1].anchor = { right = 20 + border, top = top + BUTTON_OFFSET };
554             sysButtons[1].size = { 15, 15 };
555             sysButtons[1].bevel = true;
556             sysButtons[1].bitmap = { skinBitmaps[(state == maximized) ? restore : maximize] };
557             sysButtons[1].visible = true;
558          }
559          if(sysButtons[2])
560          {
561             sysButtons[2].anchor = { right = 2 + border, top = top + BUTTON_OFFSET };
562             sysButtons[2].size = { 15, 15 };
563             sysButtons[2].bevel = true;
564             sysButtons[2].bitmap = { skinBitmaps[close] };
565             sysButtons[2].visible = true;
566          }
567       }
568    }
569 }
570
571
572 #define PUREVTBL(c)     ((int (**)())*(void **)((byte *)class(c).data + 4))
573 #define CAPTION_DISTANCE   18
574
575 default:
576 static void Dummy()
577 {
578    Window a;
579    a.OnApplyGraphics();
580    a.OnRedraw(null);
581 }
582
583 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnApplyGraphics;
584 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRedraw;
585 private:
586
587 public class WindowsSkin_Button : Button
588 {
589    void OnRedraw(Surface surface)
590    {
591       if(isRadio)
592       {
593          PUREVTBL(Button)[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRedraw](this, surface);
594          return;
595       }
596       // if(bevel)
597       {
598          ButtonState state = this.buttonState;
599          Bitmap buttonBitmap = bitmap ? bitmap.bitmap : null;
600          char * text = this.text;
601          int offset = (state == down && this.offset) ? 1 : 0;
602          Color backColor = background;
603          int isDefault = this.isDefault;
604          Font font;
605          int tw = 0, th = 0;
606          int bw = 0, bh = 0;
607
608          font = fontObject;
609          surface.TextFont(font);
610          if(text)
611             surface.TextExtent(text, strlen(text),&tw, &th);
612
613          if(bevelOver && checked)
614             offset = 1;
615
616          if(!isEnabled)
617          {
618             if(bitmaps[disabled]) buttonBitmap = bitmaps[disabled].bitmap;
619             state = disabled;
620          }
621
622          if(buttonBitmap && buttonStyle.bevelOver && !buttonStyle.checkBox && !buttonStyle.radio && text)
623             isDefault = 0;
624
625          // Background
626          if((bevel /*|| bevelOver*/) && opacity && backColor)
627          {
628             if(!scaleBitmap || !buttonBitmap)
629             {
630                ColorKey keys[2] = { { silver, 0.0f }, { white, 1.0f } };
631                surface.Gradient(keys, sizeof(keys) / sizeof(ColorKey), 1, vertical, 0, 0, clientSize.w-1, clientSize.h-1);
632             }
633          }
634
635          // Checkbox
636          if(isCheckbox && !buttonBitmap)
637          {
638             int height = 16;
639             int start = (clientSize.h - height) / 2;
640
641             if(!isEnabled)
642                // surface.SetBackground(formColor);
643                surface.SetBackground(gainsboro);
644             else if(active && !text)
645                surface.SetBackground((offset ? formColor : Color { 0,0,170 }));
646             else
647                surface.SetBackground((offset ? formColor : white));
648             surface.Area(2, start+2,height-3,start+height-3);
649
650             surface.SetForeground(Color { 85, 85, 85 });
651             surface.HLine(0, height - 2, start + 0);
652             surface.VLine(start+1, start+height - 2, 0);
653
654             surface.SetForeground(Color { 64,64,64 });
655             surface.HLine(1, height - 3, start + 1);
656             surface.VLine(start+2, start+height - 3, 1);
657
658             surface.SetForeground(Color { 212,208,200 });
659             surface.HLine(1, height - 2, start + height-2);
660             surface.VLine(start+1, start+height - 3, height-2);
661
662             surface.SetForeground(white);
663             surface.HLine(0, height - 1, start + height-1);
664             surface.VLine(start+0, start+height - 2, height-1);
665
666             if(checked)
667             {
668                if(active && !text)
669                   surface.SetForeground(white);
670                else if(!isEnabled)
671                   surface.SetForeground(Color { 85, 85, 85 });
672                else
673                   surface.SetForeground(black);
674                surface.DrawLine(4, start+8, 7,start+11);
675                surface.DrawLine(4, start+9, 7,start+12);
676                surface.DrawLine(7, start+11, 11,start+3);
677                surface.DrawLine(7, start+12, 11,start+4);
678             }
679          }
680
681          // Bitmaps
682          if(buttonBitmap)
683          {
684             surface.SetForeground(white);
685             if(isRadio || isCheckbox)
686             {
687                int x = 0, y = (clientSize.h-buttonBitmap.height)/2;
688                if(bevelOver && text)
689                {
690                   x = (CAPTION_DISTANCE-buttonBitmap.width)/2 + offset;
691                   y = (clientSize.h-buttonBitmap.height)/2 + offset;
692
693
694                }
695
696                // Radio Buttons and Checkboxes
697                surface.Blit(buttonBitmap,
698                   x, y,
699                   0,0,buttonBitmap.width,buttonBitmap.height);
700             }
701             else 
702             {
703                // Push Buttons
704                if(scaleBitmap)
705                {
706                   if(bevel || offset)
707                      surface.Stretch(buttonBitmap, 
708                         1 + offset, 1 + offset,0,0,
709                         clientSize.w-3,clientSize.h-3,buttonBitmap.width,buttonBitmap.height);
710                   else
711                      surface.Stretch(buttonBitmap, 0,0, 0,0,
712                         clientSize.w,clientSize.h,buttonBitmap.width,buttonBitmap.height);
713                }
714                else
715                {
716                   int x, y;
717                   bw = buttonBitmap.width;
718                   bh = buttonBitmap.height;
719
720                   if(bitmapAlignment == left || bitmapAlignment == right)
721                   {
722                      if(bitmapAlignment == left)
723                         x = 2;
724                      else
725                         x = clientSize.w-bw-2;
726                      y = (clientSize.h-bh)/2;
727                   }
728                   else if(bitmapAlignment == top || bitmapAlignment == bottom)
729                   {
730                      x = (clientSize.w-bw)/2;
731                      if(bitmapAlignment == top)
732                         y = 2;
733                      else
734                         y = clientSize.h-bh-2;
735                   }
736                   else
737                   {
738                      x = (clientSize.w-bw)/2;
739                      y = (clientSize.h-bh - (int)(buttonStyle.bevelOver && text) * th)/2;
740                   }
741                   if(buttonStyle.bevel || buttonStyle.offset)
742                   {
743                      x += offset;
744                      y += offset;
745                   }
746                   surface.Blit(buttonBitmap, x,y, 0,0, bw,bh);
747                }
748             }
749          }
750
751          // Shadows
752          if(bevel || (bevelOver && (state == down || state == over || checked)))
753          {
754             if(state == down || checked)
755             {
756                surface.SetForeground(Color { 85, 85, 85 });
757                surface.HLine(isDefault + 0, clientSize.w-2-isDefault, 0);
758                surface.VLine(isDefault + 1, clientSize.h-2-isDefault, 0);
759                surface.SetForeground(white);
760                surface.HLine(isDefault + 0, clientSize.w-1-isDefault, clientSize.h-1-isDefault);
761                surface.VLine(isDefault + 0, clientSize.h-2-isDefault, clientSize.w-1-isDefault);
762             }
763             else
764             {
765                surface.SetForeground(white);
766                surface.HLine(0 + isDefault, clientSize.w-2 - isDefault,  isDefault);
767                surface.VLine(1 + isDefault, clientSize.h-2 - isDefault,  isDefault);
768                surface.SetForeground(Color { 85, 85, 85 });
769                surface.HLine(1 + isDefault, clientSize.w-2 - isDefault, clientSize.h-2 - isDefault);
770                surface.VLine(1 + isDefault, clientSize.h-3 - isDefault, clientSize.w-2 - isDefault);
771
772                if(bevel)
773                {
774                   surface.SetForeground(black);
775                   surface.HLine( isDefault, clientSize.w-1 - isDefault, clientSize.h-1 - isDefault);
776                   surface.VLine( isDefault, clientSize.h-2 - isDefault, clientSize.w-1 - isDefault);
777                }
778             }
779          }
780
781          // Text
782          surface.TextOpacity(false);
783          surface.SetForeground(foreground);
784          if(text)
785          {
786             int tw, th;
787             surface.TextExtent(text, strlen(text),&tw, &th);
788
789             if((isRadio || isCheckbox) && !bevelOver)
790                WriteCaption(surface, CAPTION_DISTANCE + 3, // + clientSize.h,
791                   (clientSize.h - th - 4)/2);
792             else 
793             {
794                int x, y = (clientSize.h - th - 1)/2 + offset;
795                
796                if(buttonStyle.bevelOver && buttonBitmap && !buttonStyle.checkBox && !buttonStyle.radio)
797                {
798                   if(bitmapAlignment == top)
799                      y = (clientSize.h - bh - 4 - th - 5)/2 + offset + bh + 4;
800                   else if(bitmapAlignment == bottom)
801                      y = (clientSize.h - bh - 4 - th - 5)/2 + offset;
802                   else//if(bitmapAlignment == left || bitmapAlignment == right)
803                      y = clientSize.h - th - 5 + offset;
804                }
805                else
806                   y = (clientSize.h - th - 1)/2 + offset;
807
808                if(ellipsis)
809                {
810                   int width = clientSize.w - 2*6;
811                   int x = 6 + offset;
812
813                   surface.WriteTextDots(alignment, x, y, width, text, strlen(text));
814                }
815                else
816                {
817                   int width = clientSize.w - 2 * 6;
818                   x = 6 + offset;
819                   if(bitmapAlignment == left || bitmapAlignment == right)
820                   {
821                      if(bitmapAlignment == left)
822                         x += bw + 4;
823                      width -= bw + 4;
824                   }
825                   if(isCheckbox || ((isRadio /*|| bevelOver*/) && buttonBitmap))
826                   {
827                      x += CAPTION_DISTANCE + 3;
828                   }
829
830                   if(tw < width)
831                   {
832                      if(alignment == right)
833                         x += width - tw - 1;
834                      else if(alignment == center)
835                         x += (width - tw) / 2;
836                   }
837                   WriteCaption(surface, x, y);
838                }
839             }
840          }
841
842          // Activation Highlight
843          if(isDefault)
844          {
845             surface.SetForeground(black);
846             surface.Rectangle(0,0,clientSize.w-1,clientSize.h-1);
847          }
848          if(!bevelOver && !isRemote)
849          {
850             if(active) // && (text || !(buttonStyle.radio || buttonStyle.checkBox)))
851             {
852                int x1,y1,x2,y2;
853                surface.SetForeground(black);
854                surface.LineStipple(0x5555);
855
856                if((isRadio || isCheckbox) && text)
857                {
858                   x1 = CAPTION_DISTANCE;  // + clientSize.h;
859                   y1 = 0;
860                   x2 = clientSize.w-4;
861                   y2 = clientSize.h-4;
862                }
863                else
864                {
865                   x1 = 3+offset;
866                   y1 = 3+offset;
867                   x2 = clientSize.w - 5+offset;
868                   y2 = clientSize.h - 5+offset;
869
870                   if(isRadio || isCheckbox)
871                   {
872                      x1-=3;
873                      y1-=3;
874                      x2+=1;
875                      y2+=1;
876                   }
877                }
878                if((x2 - x1) & 1) x2++;
879                if((y2 - y1) & 1) y2++;
880
881                surface.Rectangle(x1, y1, x2, y2);
882                surface.LineStipple(0);
883             }
884          }
885       }
886    }
887 }