]> git.saurik.com Git - wxWidgets.git/blame - src/univ/themes/metal.cpp
Undo/Redo update handlers now set the text as well
[wxWidgets.git] / src / univ / themes / metal.cpp
CommitLineData
67d947ba
RR
1// Name: univ/themes/metal.cpp
2// Purpose: wxUniversal theme implementing Win32-like LNF
3// Author: Vadim Zeitlin, Robert Roebling
4// Modified by:
5// Created: 06.08.00
6// RCS-ID: $Id$
7// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
8// Licence: wxWindows license
9///////////////////////////////////////////////////////////////////////////////
10
11// ===========================================================================
12// declarations
13// ===========================================================================
14
15// ---------------------------------------------------------------------------
16// headers
17// ---------------------------------------------------------------------------
18
19// For compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
26#ifndef WX_PRECOMP
27 #include "wx/timer.h"
28 #include "wx/intl.h"
29 #include "wx/dc.h"
30 #include "wx/window.h"
31
32 #include "wx/dcmemory.h"
33
34 #include "wx/button.h"
35 #include "wx/listbox.h"
36 #include "wx/checklst.h"
37 #include "wx/combobox.h"
38 #include "wx/scrolbar.h"
39 #include "wx/slider.h"
40 #include "wx/textctrl.h"
41 #include "wx/toolbar.h"
42
43 #ifdef __WXMSW__
44 // for COLOR_* constants
45 #include "wx/msw/private.h"
46 #endif
47#endif // WX_PRECOMP
48
49#include "wx/notebook.h"
50#include "wx/spinbutt.h"
51#include "wx/settings.h"
52#include "wx/menu.h"
53#include "wx/artprov.h"
54#include "wx/toplevel.h"
55
56#include "wx/univ/scrtimer.h"
57#include "wx/univ/renderer.h"
58#include "wx/univ/inphand.h"
59#include "wx/univ/colschem.h"
60#include "wx/univ/theme.h"
61
62// ----------------------------------------------------------------------------
63// constants
64// ----------------------------------------------------------------------------
65
66static const int BORDER_THICKNESS = 2;
67
68// the offset between the label and focus rect around it
69static const int FOCUS_RECT_OFFSET_X = 1;
70static const int FOCUS_RECT_OFFSET_Y = 1;
71
72static const int FRAME_BORDER_THICKNESS = 3;
73static const int RESIZEABLE_FRAME_BORDER_THICKNESS = 4;
74static const int FRAME_TITLEBAR_HEIGHT = 18;
75static const int FRAME_BUTTON_WIDTH = 16;
76static const int FRAME_BUTTON_HEIGHT = 14;
77
78static const size_t NUM_STATUSBAR_GRIP_BANDS = 3;
79static const size_t WIDTH_STATUSBAR_GRIP_BAND = 4;
80static const size_t STATUSBAR_GRIP_SIZE =
81 WIDTH_STATUSBAR_GRIP_BAND*NUM_STATUSBAR_GRIP_BANDS;
82
83enum IndicatorType
84{
85 IndicatorType_Check,
86 IndicatorType_Radio,
87 IndicatorType_Menu,
88 IndicatorType_Max
89};
90
91enum IndicatorState
92{
93 IndicatorState_Normal,
94 IndicatorState_Pressed, // this one is for check/radioboxes
95 IndicatorState_Selected = IndicatorState_Pressed, // for menus
96 IndicatorState_Disabled,
97 IndicatorState_SelectedDisabled, // only for the menus
98 IndicatorState_Max
99};
100
101enum IndicatorStatus
102{
103 IndicatorStatus_Checked,
104 IndicatorStatus_Unchecked,
105 IndicatorStatus_Max
106};
107
108// wxMetalRenderer: draw the GUI elements in Metal style
109// ----------------------------------------------------------------------------
110
111class wxMetalRenderer : public wxRenderer
112{
113public:
114 // constants
115 enum wxArrowDirection
116 {
117 Arrow_Left,
118 Arrow_Right,
119 Arrow_Up,
120 Arrow_Down,
121 Arrow_Max
122 };
123
124 enum wxArrowStyle
125 {
126 Arrow_Normal,
127 Arrow_Disabled,
128 Arrow_Pressed,
129 Arrow_Inversed,
130 Arrow_InversedDisabled,
131 Arrow_StateMax
132 };
133
134 enum wxFrameButtonType
135 {
136 FrameButton_Close,
137 FrameButton_Minimize,
138 FrameButton_Maximize,
139 FrameButton_Restore,
140 FrameButton_Help,
141 FrameButton_Max
142 };
143
144 // ctor
145 wxMetalRenderer(const wxColourScheme *scheme);
146
147 // implement the base class pure virtuals
148 virtual void DrawBackground(wxDC& dc,
149 const wxColour& col,
150 const wxRect& rect,
151 int flags = 0);
152 virtual void DrawLabel(wxDC& dc,
153 const wxString& label,
154 const wxRect& rect,
155 int flags = 0,
156 int alignment = wxALIGN_LEFT | wxALIGN_TOP,
157 int indexAccel = -1,
158 wxRect *rectBounds = NULL);
159 virtual void DrawButtonLabel(wxDC& dc,
160 const wxString& label,
161 const wxBitmap& image,
162 const wxRect& rect,
163 int flags = 0,
164 int alignment = wxALIGN_LEFT | wxALIGN_TOP,
165 int indexAccel = -1,
166 wxRect *rectBounds = NULL);
167 virtual void DrawBorder(wxDC& dc,
168 wxBorder border,
169 const wxRect& rect,
170 int flags = 0,
171 wxRect *rectIn = (wxRect *)NULL);
172 virtual void DrawHorizontalLine(wxDC& dc,
173 wxCoord y, wxCoord x1, wxCoord x2);
174 virtual void DrawVerticalLine(wxDC& dc,
175 wxCoord x, wxCoord y1, wxCoord y2);
176 virtual void DrawFrame(wxDC& dc,
177 const wxString& label,
178 const wxRect& rect,
179 int flags = 0,
180 int alignment = wxALIGN_LEFT,
181 int indexAccel = -1);
182 virtual void DrawTextBorder(wxDC& dc,
183 wxBorder border,
184 const wxRect& rect,
185 int flags = 0,
186 wxRect *rectIn = (wxRect *)NULL);
187 virtual void DrawButtonBorder(wxDC& dc,
188 const wxRect& rect,
189 int flags = 0,
190 wxRect *rectIn = (wxRect *)NULL);
191 virtual void DrawArrow(wxDC& dc,
192 wxDirection dir,
193 const wxRect& rect,
194 int flags = 0);
195 virtual void DrawScrollbarArrow(wxDC& dc,
196 wxDirection dir,
197 const wxRect& rect,
198 int flags = 0)
199 { DrawArrow(dc, dir, rect, flags); }
200 virtual void DrawScrollbarThumb(wxDC& dc,
201 wxOrientation orient,
202 const wxRect& rect,
203 int flags = 0);
204 virtual void DrawScrollbarShaft(wxDC& dc,
205 wxOrientation orient,
206 const wxRect& rect,
207 int flags = 0);
208 virtual void DrawScrollCorner(wxDC& dc,
209 const wxRect& rect);
210 virtual void DrawItem(wxDC& dc,
211 const wxString& label,
212 const wxRect& rect,
213 int flags = 0);
214 virtual void DrawCheckItem(wxDC& dc,
215 const wxString& label,
216 const wxBitmap& bitmap,
217 const wxRect& rect,
218 int flags = 0);
219 virtual void DrawCheckButton(wxDC& dc,
220 const wxString& label,
221 const wxBitmap& bitmap,
222 const wxRect& rect,
223 int flags = 0,
224 wxAlignment align = wxALIGN_LEFT,
225 int indexAccel = -1);
226 virtual void DrawRadioButton(wxDC& dc,
227 const wxString& label,
228 const wxBitmap& bitmap,
229 const wxRect& rect,
230 int flags = 0,
231 wxAlignment align = wxALIGN_LEFT,
232 int indexAccel = -1);
233 virtual void DrawToolBarButton(wxDC& dc,
234 const wxString& label,
235 const wxBitmap& bitmap,
236 const wxRect& rect,
237 int flags);
238 virtual void DrawTextLine(wxDC& dc,
239 const wxString& text,
240 const wxRect& rect,
241 int selStart = -1,
242 int selEnd = -1,
243 int flags = 0);
244 virtual void DrawLineWrapMark(wxDC& dc, const wxRect& rect);
245 virtual void DrawTab(wxDC& dc,
246 const wxRect& rect,
247 wxDirection dir,
248 const wxString& label,
249 const wxBitmap& bitmap = wxNullBitmap,
250 int flags = 0,
251 int indexAccel = -1);
252
253 virtual void DrawSliderShaft(wxDC& dc,
254 const wxRect& rect,
255 wxOrientation orient,
256 int flags = 0,
257 wxRect *rectShaft = NULL);
258 virtual void DrawSliderThumb(wxDC& dc,
259 const wxRect& rect,
260 wxOrientation orient,
261 int flags = 0);
262 virtual void DrawSliderTicks(wxDC& dc,
263 const wxRect& rect,
264 const wxSize& sizeThumb,
265 wxOrientation orient,
266 int start,
267 int end,
268 int step = 1,
269 int flags = 0);
270
271 virtual void DrawMenuBarItem(wxDC& dc,
272 const wxRect& rect,
273 const wxString& label,
274 int flags = 0,
275 int indexAccel = -1);
276 virtual void DrawMenuItem(wxDC& dc,
277 wxCoord y,
278 const wxMenuGeometryInfo& geometryInfo,
279 const wxString& label,
280 const wxString& accel,
281 const wxBitmap& bitmap = wxNullBitmap,
282 int flags = 0,
283 int indexAccel = -1);
284 virtual void DrawMenuSeparator(wxDC& dc,
285 wxCoord y,
286 const wxMenuGeometryInfo& geomInfo);
287
288 virtual void DrawStatusField(wxDC& dc,
289 const wxRect& rect,
290 const wxString& label,
291 int flags = 0);
292
293 // titlebars
294 virtual void DrawFrameTitleBar(wxDC& dc,
295 const wxRect& rect,
296 const wxString& title,
297 const wxIcon& icon,
298 int flags,
299 int specialButton = 0,
300 int specialButtonFlags = 0);
301 virtual void DrawFrameBorder(wxDC& dc,
302 const wxRect& rect,
303 int flags);
304 virtual void DrawFrameBackground(wxDC& dc,
305 const wxRect& rect,
306 int flags);
307 virtual void DrawFrameTitle(wxDC& dc,
308 const wxRect& rect,
309 const wxString& title,
310 int flags);
311 virtual void DrawFrameIcon(wxDC& dc,
312 const wxRect& rect,
313 const wxIcon& icon,
314 int flags);
315 virtual void DrawFrameButton(wxDC& dc,
316 wxCoord x, wxCoord y,
317 int button,
318 int flags = 0);
319 virtual wxRect GetFrameClientArea(const wxRect& rect, int flags) const;
320 virtual wxSize GetFrameTotalSize(const wxSize& clientSize, int flags) const;
321 virtual wxSize GetFrameMinSize(int flags) const;
322 virtual wxSize GetFrameIconSize() const;
323 virtual int HitTestFrame(const wxRect& rect, const wxPoint& pt, int flags) const;
324
325 virtual void GetComboBitmaps(wxBitmap *bmpNormal,
326 wxBitmap *bmpFocus,
327 wxBitmap *bmpPressed,
328 wxBitmap *bmpDisabled);
329
330 virtual void AdjustSize(wxSize *size, const wxWindow *window);
331 virtual wxRect GetBorderDimensions(wxBorder border) const;
332 virtual bool AreScrollbarsInsideBorder() const;
333
334 virtual wxSize GetScrollbarArrowSize() const
335 { return m_sizeScrollbarArrow; }
336 virtual wxRect GetScrollbarRect(const wxScrollBar *scrollbar,
337 wxScrollBar::Element elem,
338 int thumbPos = -1) const;
339 virtual wxCoord GetScrollbarSize(const wxScrollBar *scrollbar);
340 virtual wxHitTest HitTestScrollbar(const wxScrollBar *scrollbar,
341 const wxPoint& pt) const;
342 virtual wxCoord ScrollbarToPixel(const wxScrollBar *scrollbar,
343 int thumbPos = -1);
344 virtual int PixelToScrollbar(const wxScrollBar *scrollbar, wxCoord coord);
345 virtual wxCoord GetListboxItemHeight(wxCoord fontHeight)
346 { return fontHeight + 2; }
347 virtual wxSize GetCheckBitmapSize() const
348 { return wxSize(13, 13); }
349 virtual wxSize GetRadioBitmapSize() const
350 { return wxSize(12, 12); }
351 virtual wxCoord GetCheckItemMargin() const
352 { return 0; }
353
354 virtual wxSize GetToolBarButtonSize(wxCoord *separator) const
355 { if ( separator ) *separator = 5; return wxSize(16, 15); }
356 virtual wxSize GetToolBarMargin() const
357 { return wxSize(4, 4); }
358
359 virtual wxRect GetTextTotalArea(const wxTextCtrl *text,
360 const wxRect& rect) const;
361 virtual wxRect GetTextClientArea(const wxTextCtrl *text,
362 const wxRect& rect,
363 wxCoord *extraSpaceBeyond) const;
364
365 virtual wxSize GetTabIndent() const { return wxSize(2, 2); }
366 virtual wxSize GetTabPadding() const { return wxSize(6, 5); }
367
368 virtual wxCoord GetSliderDim() const { return 20; }
369 virtual wxCoord GetSliderTickLen() const { return 4; }
370 virtual wxRect GetSliderShaftRect(const wxRect& rect,
371 wxOrientation orient) const;
372 virtual wxSize GetSliderThumbSize(const wxRect& rect,
373 wxOrientation orient) const;
374 virtual wxSize GetProgressBarStep() const { return wxSize(16, 32); }
375
376 virtual wxSize GetMenuBarItemSize(const wxSize& sizeText) const;
377 virtual wxMenuGeometryInfo *GetMenuGeometry(wxWindow *win,
378 const wxMenu& menu) const;
379
380 virtual wxSize GetStatusBarBorders(wxCoord *borderBetweenFields) const;
381
382protected:
383 // helper of DrawLabel() and DrawCheckOrRadioButton()
384 void DoDrawLabel(wxDC& dc,
385 const wxString& label,
386 const wxRect& rect,
387 int flags = 0,
388 int alignment = wxALIGN_LEFT | wxALIGN_TOP,
389 int indexAccel = -1,
390 wxRect *rectBounds = NULL,
391 const wxPoint& focusOffset
392 = wxPoint(FOCUS_RECT_OFFSET_X, FOCUS_RECT_OFFSET_Y));
393
394 // common part of DrawLabel() and DrawItem()
395 void DrawFocusRect(wxDC& dc, const wxRect& rect);
396
397 // DrawLabel() and DrawButtonLabel() helper
398 void DrawLabelShadow(wxDC& dc,
399 const wxString& label,
400 const wxRect& rect,
401 int alignment,
402 int indexAccel);
403
404 // DrawButtonBorder() helper
405 void DoDrawBackground(wxDC& dc,
406 const wxColour& col,
407 const wxRect& rect);
408
409 // DrawBorder() helpers: all of them shift and clip the DC after drawing
410 // the border
411
412 // just draw a rectangle with the given pen
413 void DrawRect(wxDC& dc, wxRect *rect, const wxPen& pen);
414
415 // draw the lower left part of rectangle
416 void DrawHalfRect(wxDC& dc, wxRect *rect, const wxPen& pen);
417
418 // draw the rectange using the first brush for the left and top sides and
419 // the second one for the bottom and right ones
420 void DrawShadedRect(wxDC& dc, wxRect *rect,
421 const wxPen& pen1, const wxPen& pen2);
422
423 // draw the normal 3D border
424 void DrawRaisedBorder(wxDC& dc, wxRect *rect);
425
426 // draw the sunken 3D border
427 void DrawSunkenBorder(wxDC& dc, wxRect *rect);
428
429 // draw the border used for scrollbar arrows
430 void DrawArrowBorder(wxDC& dc, wxRect *rect, bool isPressed = FALSE);
431
432 // public DrawArrow()s helper
433 void DrawArrow(wxDC& dc, const wxRect& rect,
434 wxArrowDirection arrowDir, wxArrowStyle arrowStyle);
435
436 // DrawArrowButton is used by DrawScrollbar and DrawComboButton
437 void DrawArrowButton(wxDC& dc, const wxRect& rect,
438 wxArrowDirection arrowDir,
439 wxArrowStyle arrowStyle);
440
441 // DrawCheckButton/DrawRadioButton helper
442 void DrawCheckOrRadioButton(wxDC& dc,
443 const wxString& label,
444 const wxBitmap& bitmap,
445 const wxRect& rect,
446 int flags,
447 wxAlignment align,
448 int indexAccel,
449 wxCoord focusOffsetY);
450
451 // draw a normal or transposed line (useful for using the same code fo both
452 // horizontal and vertical widgets)
453 void DrawLine(wxDC& dc,
454 wxCoord x1, wxCoord y1,
455 wxCoord x2, wxCoord y2,
456 bool transpose = FALSE)
457 {
458 if ( transpose )
459 dc.DrawLine(y1, x1, y2, x2);
460 else
461 dc.DrawLine(x1, y1, x2, y2);
462 }
463
464 // get the standard check/radio button bitmap
465 wxBitmap GetIndicator(IndicatorType indType, int flags);
466 wxBitmap GetCheckBitmap(int flags)
467 { return GetIndicator(IndicatorType_Check, flags); }
468 wxBitmap GetRadioBitmap(int flags)
469 { return GetIndicator(IndicatorType_Radio, flags); }
470
471private:
472 const wxColourScheme *m_scheme;
473
474 // the sizing parameters (TODO make them changeable)
475 wxSize m_sizeScrollbarArrow;
476
477 // GDI objects we use for drawing
478 wxColour m_colDarkGrey,
479 m_colHighlight;
480
481 wxPen m_penBlack,
482 m_penDarkGrey,
483 m_penLightGrey,
484 m_penHighlight;
485
486 wxFont m_titlebarFont;
487
488 // the checked and unchecked bitmaps for DrawCheckItem()
489 wxBitmap m_bmpCheckBitmaps[IndicatorStatus_Max];
490
491 // the bitmaps returned by GetIndicator()
492 wxBitmap m_bmpIndicators[IndicatorType_Max]
493 [IndicatorState_Max]
494 [IndicatorStatus_Max];
495
496 // titlebar icons:
497 wxBitmap m_bmpFrameButtons[FrameButton_Max];
498
499 // first row is for the normal state, second - for the disabled
500 wxBitmap m_bmpArrows[Arrow_StateMax][Arrow_Max];
501};
502
503// ----------------------------------------------------------------------------
504// wxMetalInputHandler and derived classes: process the keyboard and mouse
505// messages according to Windows standards
506// ----------------------------------------------------------------------------
507
508class wxMetalInputHandler : public wxInputHandler
509{
510public:
511 wxMetalInputHandler(wxMetalRenderer *renderer);
512
513 virtual bool HandleKey(wxInputConsumer *control,
514 const wxKeyEvent& event,
515 bool pressed);
516 virtual bool HandleMouse(wxInputConsumer *control,
517 const wxMouseEvent& event);
518
519protected:
520 wxMetalRenderer *m_renderer;
521};
522
523class wxMetalScrollBarInputHandler : public wxStdScrollBarInputHandler
524{
525public:
526 wxMetalScrollBarInputHandler(wxMetalRenderer *renderer,
527 wxInputHandler *handler);
528
529 virtual bool HandleMouse(wxInputConsumer *control, const wxMouseEvent& event);
530 virtual bool HandleMouseMove(wxInputConsumer *control, const wxMouseEvent& event);
531
532 virtual bool OnScrollTimer(wxScrollBar *scrollbar,
533 const wxControlAction& action);
534
535protected:
536 virtual bool IsAllowedButton(int button) { return button == 1; }
537
538 virtual void Highlight(wxScrollBar *scrollbar, bool doIt)
539 {
540 // we don't highlight anything
541 }
542
543 // the first and last event which caused the thumb to move
544 wxMouseEvent m_eventStartDrag,
545 m_eventLastDrag;
546
547 // have we paused the scrolling because the mouse moved?
548 bool m_scrollPaused;
549
550 // we remember the interval of the timer to be able to restart it
551 int m_interval;
552};
553
554class wxMetalCheckboxInputHandler : public wxStdCheckboxInputHandler
555{
556public:
557 wxMetalCheckboxInputHandler(wxInputHandler *handler)
558 : wxStdCheckboxInputHandler(handler) { }
559
560 virtual bool HandleKey(wxInputConsumer *control,
561 const wxKeyEvent& event,
562 bool pressed);
563};
564
565class wxMetalTextCtrlInputHandler : public wxStdTextCtrlInputHandler
566{
567public:
568 wxMetalTextCtrlInputHandler(wxInputHandler *handler)
569 : wxStdTextCtrlInputHandler(handler) { }
570
571 virtual bool HandleKey(wxInputConsumer *control,
572 const wxKeyEvent& event,
573 bool pressed);
574};
575
576class wxMetalStatusBarInputHandler : public wxStdInputHandler
577{
578public:
579 wxMetalStatusBarInputHandler(wxInputHandler *handler);
580
581 virtual bool HandleMouse(wxInputConsumer *consumer,
582 const wxMouseEvent& event);
583
584 virtual bool HandleMouseMove(wxInputConsumer *consumer,
585 const wxMouseEvent& event);
586
587protected:
588 // is the given point over the statusbar grip?
589 bool IsOnGrip(wxWindow *statbar, const wxPoint& pt) const;
590
591private:
592 // the cursor we had replaced with the resize one
593 wxCursor m_cursorOld;
594
595 // was the mouse over the grip last time we checked?
596 bool m_isOnGrip;
597};
598
599class wxMetalSystemMenuEvtHandler;
600
601class wxMetalFrameInputHandler : public wxStdFrameInputHandler
602{
603public:
604 wxMetalFrameInputHandler(wxInputHandler *handler);
605 ~wxMetalFrameInputHandler();
606
607 virtual bool HandleMouse(wxInputConsumer *control,
608 const wxMouseEvent& event);
609
610 virtual bool HandleActivation(wxInputConsumer *consumer, bool activated);
611
612 void PopupSystemMenu(wxTopLevelWindow *window, const wxPoint& pos) const;
613
614private:
615 // was the mouse over the grip last time we checked?
616 wxMetalSystemMenuEvtHandler *m_menuHandler;
617};
618
619// ----------------------------------------------------------------------------
620// wxMetalColourScheme: uses (default) Metal colours
621// ----------------------------------------------------------------------------
622
623class wxMetalColourScheme : public wxColourScheme
624{
625public:
626 virtual wxColour Get(StdColour col) const;
627 virtual wxColour GetBackground(wxWindow *win) const;
628};
629
630// ----------------------------------------------------------------------------
631// wxMetalArtProvider
632// ----------------------------------------------------------------------------
633
634class wxMetalArtProvider : public wxArtProvider
635{
636protected:
637 virtual wxBitmap CreateBitmap(const wxArtID& id,
638 const wxArtClient& client,
639 const wxSize& size);
640};
641
642// ----------------------------------------------------------------------------
643// wxMetalTheme
644// ----------------------------------------------------------------------------
645
646WX_DEFINE_ARRAY(wxInputHandler *, wxArrayHandlers);
647
648class wxMetalTheme : public wxTheme
649{
650public:
651 wxMetalTheme();
652 virtual ~wxMetalTheme();
653
654 virtual wxRenderer *GetRenderer();
655 virtual wxArtProvider *GetArtProvider();
656 virtual wxInputHandler *GetInputHandler(const wxString& control);
657 virtual wxColourScheme *GetColourScheme();
658
659private:
660 // get the default input handler
661 wxInputHandler *GetDefaultInputHandler();
662
663 wxMetalRenderer *m_renderer;
664
665 wxMetalArtProvider *m_artProvider;
666
667 // the names of the already created handlers and the handlers themselves
668 // (these arrays are synchronized)
669 wxSortedArrayString m_handlerNames;
670 wxArrayHandlers m_handlers;
671
672 wxMetalInputHandler *m_handlerDefault;
673
674 wxMetalColourScheme *m_scheme;
675
676 WX_DECLARE_THEME(Metal)
677};
678
679// ----------------------------------------------------------------------------
680// standard bitmaps
681// ----------------------------------------------------------------------------
682
683// frame buttons bitmaps
684
685static const char *frame_button_close_xpm[] = {
686"12 10 2 1",
687" c None",
688". c black",
689" ",
690" .. .. ",
691" .. .. ",
692" .... ",
693" .. ",
694" .... ",
695" .. .. ",
696" .. .. ",
697" ",
698" "};
699
700static const char *frame_button_help_xpm[] = {
701"12 10 2 1",
702" c None",
703". c #000000",
704" .... ",
705" .. .. ",
706" .. .. ",
707" .. ",
708" .. ",
709" .. ",
710" ",
711" .. ",
712" .. ",
713" "};
714
715static const char *frame_button_maximize_xpm[] = {
716"12 10 2 1",
717" c None",
718". c #000000",
719" ......... ",
720" ......... ",
721" . . ",
722" . . ",
723" . . ",
724" . . ",
725" . . ",
726" . . ",
727" ......... ",
728" "};
729
730static const char *frame_button_minimize_xpm[] = {
731"12 10 2 1",
732" c None",
733". c #000000",
734" ",
735" ",
736" ",
737" ",
738" ",
739" ",
740" ",
741" ...... ",
742" ...... ",
743" "};
744
745static const char *frame_button_restore_xpm[] = {
746"12 10 2 1",
747" c None",
748". c #000000",
749" ...... ",
750" ...... ",
751" . . ",
752" ...... . ",
753" ...... . ",
754" . ... ",
755" . . ",
756" . . ",
757" ...... ",
758" "};
759
760// menu bitmaps
761
762static const char *checked_menu_xpm[] = {
763/* columns rows colors chars-per-pixel */
764"9 9 2 1",
765"w c None",
766"b c black",
767/* pixels */
768"wwwwwwwww",
769"wwwwwwwbw",
770"wwwwwwbbw",
771"wbwwwbbbw",
772"wbbwbbbww",
773"wbbbbbwww",
774"wwbbbwwww",
775"wwwbwwwww",
776"wwwwwwwww"
777};
778
779static const char *selected_checked_menu_xpm[] = {
780/* columns rows colors chars-per-pixel */
781"9 9 2 1",
782"w c None",
783"b c white",
784/* pixels */
785"wwwwwwwww",
786"wwwwwwwbw",
787"wwwwwwbbw",
788"wbwwwbbbw",
789"wbbwbbbww",
790"wbbbbbwww",
791"wwbbbwwww",
792"wwwbwwwww",
793"wwwwwwwww"
794};
795
796static const char *disabled_checked_menu_xpm[] = {
797/* columns rows colors chars-per-pixel */
798"9 9 3 1",
799"w c None",
800"b c #7f7f7f",
801"W c #e0e0e0",
802/* pixels */
803"wwwwwwwww",
804"wwwwwwwbw",
805"wwwwwwbbW",
806"wbwwwbbbW",
807"wbbwbbbWW",
808"wbbbbbWWw",
809"wwbbbWWww",
810"wwwbWWwww",
811"wwwwWwwww"
812};
813
814static const char *selected_disabled_checked_menu_xpm[] = {
815/* columns rows colors chars-per-pixel */
816"9 9 2 1",
817"w c None",
818"b c #7f7f7f",
819/* pixels */
820"wwwwwwwww",
821"wwwwwwwbw",
822"wwwwwwbbw",
823"wbwwwbbbw",
824"wbbwbbbww",
825"wbbbbbwww",
826"wwbbbwwww",
827"wwwbwwwww",
828"wwwwwwwww"
829};
830
831// checkbox and radiobox bitmaps below
832
833static const char *checked_xpm[] = {
834/* columns rows colors chars-per-pixel */
835"13 13 5 1",
836"w c white",
837"b c black",
838"d c #7f7f7f",
839"g c #c0c0c0",
840"h c #e0e0e0",
841/* pixels */
842"ddddddddddddh",
843"dbbbbbbbbbbgh",
844"dbwwwwwwwwwgh",
845"dbwwwwwwwbwgh",
846"dbwwwwwwbbwgh",
847"dbwbwwwbbbwgh",
848"dbwbbwbbbwwgh",
849"dbwbbbbbwwwgh",
850"dbwwbbbwwwwgh",
851"dbwwwbwwwwwgh",
852"dbwwwwwwwwwgh",
853"dgggggggggggh",
854"hhhhhhhhhhhhh"
855};
856
857static const char *pressed_checked_xpm[] = {
858/* columns rows colors chars-per-pixel */
859"13 13 4 1",
860"b c black",
861"d c #7f7f7f",
862"g c #c0c0c0",
863"h c #e0e0e0",
864/* pixels */
865"ddddddddddddh",
866"dbbbbbbbbbbgh",
867"dbggggggggggh",
868"dbgggggggbggh",
869"dbggggggbbggh",
870"dbgbgggbbbggh",
871"dbgbbgbbbgggh",
872"dbgbbbbbggggh",
873"dbggbbbgggggh",
874"dbgggbggggggh",
875"dbggggggggggh",
876"dgggggggggggh",
877"hhhhhhhhhhhhh"
878};
879
880static const char *pressed_disabled_checked_xpm[] = {
881/* columns rows colors chars-per-pixel */
882"13 13 4 1",
883"b c black",
884"d c #7f7f7f",
885"g c #c0c0c0",
886"h c #e0e0e0",
887/* pixels */
888"ddddddddddddh",
889"dbbbbbbbbbbgh",
890"dbggggggggggh",
891"dbgggggggdggh",
892"dbggggggddggh",
893"dbgdgggdddggh",
894"dbgddgdddgggh",
895"dbgdddddggggh",
896"dbggdddgggggh",
897"dbgggdggggggh",
898"dbggggggggggh",
899"dgggggggggggh",
900"hhhhhhhhhhhhh"
901};
902
903static const char *checked_item_xpm[] = {
904/* columns rows colors chars-per-pixel */
905"13 13 3 1",
906"w c white",
907"b c black",
908"d c #808080",
909/* pixels */
910"wwwwwwwwwwwww",
911"wdddddddddddw",
912"wdwwwwwwwwwdw",
913"wdwwwwwwwbwdw",
914"wdwwwwwwbbwdw",
915"wdwbwwwbbbwdw",
916"wdwbbwbbbwwdw",
917"wdwbbbbbwwwdw",
918"wdwwbbbwwwwdw",
919"wdwwwbwwwwwdw",
920"wdwwwwwwwwwdw",
921"wdddddddddddw",
922"wwwwwwwwwwwww"
923};
924
925static const char *unchecked_xpm[] = {
926/* columns rows colors chars-per-pixel */
927"13 13 5 1",
928"w c white",
929"b c black",
930"d c #7f7f7f",
931"g c #c0c0c0",
932"h c #e0e0e0",
933/* pixels */
934"ddddddddddddh",
935"dbbbbbbbbbbgh",
936"dbwwwwwwwwwgh",
937"dbwwwwwwwwwgh",
938"dbwwwwwwwwwgh",
939"dbwwwwwwwwwgh",
940"dbwwwwwwwwwgh",
941"dbwwwwwwwwwgh",
942"dbwwwwwwwwwgh",
943"dbwwwwwwwwwgh",
944"dbwwwwwwwwwgh",
945"dgggggggggggh",
946"hhhhhhhhhhhhh"
947};
948
949static const char *pressed_unchecked_xpm[] = {
950/* columns rows colors chars-per-pixel */
951"13 13 4 1",
952"b c black",
953"d c #7f7f7f",
954"g c #c0c0c0",
955"h c #e0e0e0",
956/* pixels */
957"ddddddddddddh",
958"dbbbbbbbbbbgh",
959"dbggggggggggh",
960"dbggggggggggh",
961"dbggggggggggh",
962"dbggggggggggh",
963"dbggggggggggh",
964"dbggggggggggh",
965"dbggggggggggh",
966"dbggggggggggh",
967"dbggggggggggh",
968"dbggggggggggh",
969"hhhhhhhhhhhhh"
970};
971
972static const char *unchecked_item_xpm[] = {
973/* columns rows colors chars-per-pixel */
974"13 13 2 1",
975"w c white",
976"d c #808080",
977/* pixels */
978"wwwwwwwwwwwww",
979"wdddddddddddw",
980"wdwwwwwwwwwdw",
981"wdwwwwwwwwwdw",
982"wdwwwwwwwwwdw",
983"wdwwwwwwwwwdw",
984"wdwwwwwwwwwdw",
985"wdwwwwwwwwwdw",
986"wdwwwwwwwwwdw",
987"wdwwwwwwwwwdw",
988"wdwwwwwwwwwdw",
989"wdddddddddddw",
990"wwwwwwwwwwwww"
991};
992
993static const char *checked_radio_xpm[] = {
994/* columns rows colors chars-per-pixel */
995"12 12 6 1",
996" c None",
997"w c white",
998"b c black",
999"d c #7f7f7f",
1000"g c #c0c0c0",
1001"h c #e0e0e0",
1002/* pixels */
1003" dddd ",
1004" ddbbbbdd ",
1005" dbbwwwwbbh ",
1006" dbwwwwwwgh ",
1007"dbwwwbbwwwgh",
1008"dbwwbbbbwwgh",
1009"dbwwbbbbwwgh",
1010"dbwwwbbwwwgh",
1011" dbwwwwwwgh ",
1012" dggwwwwggh ",
1013" hhgggghh ",
1014" hhhh "
1015};
1016
1017static const char *pressed_checked_radio_xpm[] = {
1018/* columns rows colors chars-per-pixel */
1019"12 12 6 1",
1020" c None",
1021"w c white",
1022"b c black",
1023"d c #7f7f7f",
1024"g c #c0c0c0",
1025"h c #e0e0e0",
1026/* pixels */
1027" dddd ",
1028" ddbbbbdd ",
1029" dbbggggbbh ",
1030" dbgggggggh ",
1031"dbgggbbggggh",
1032"dbggbbbbgggh",
1033"dbggbbbbgggh",
1034"dbgggbbggggh",
1035" dbgggggggh ",
1036" dggggggggh ",
1037" hhgggghh ",
1038" hhhh "
1039};
1040
1041static const char *pressed_disabled_checked_radio_xpm[] = {
1042/* columns rows colors chars-per-pixel */
1043"12 12 6 1",
1044" c None",
1045"w c white",
1046"b c black",
1047"d c #7f7f7f",
1048"g c #c0c0c0",
1049"h c #e0e0e0",
1050/* pixels */
1051" dddd ",
1052" ddbbbbdd ",
1053" dbbggggbbh ",
1054" dbgggggggh ",
1055"dbgggddggggh",
1056"dbggddddgggh",
1057"dbggddddgggh",
1058"dbgggddggggh",
1059" dbgggggggh ",
1060" dggggggggh ",
1061" hhgggghh ",
1062" hhhh ",
1063};
1064
1065static const char *unchecked_radio_xpm[] = {
1066/* columns rows colors chars-per-pixel */
1067"12 12 6 1",
1068" c None",
1069"w c white",
1070"b c black",
1071"d c #7f7f7f",
1072"g c #c0c0c0",
1073"h c #e0e0e0",
1074/* pixels */
1075" dddd ",
1076" ddbbbbdd ",
1077" dbbwwwwbbh ",
1078" dbwwwwwwgh ",
1079"dbwwwwwwwwgh",
1080"dbwwwwwwwwgh",
1081"dbwwwwwwwwgh",
1082"dbwwwwwwwwgh",
1083" dbwwwwwwgh ",
1084" dggwwwwggh ",
1085" hhgggghh ",
1086" hhhh "
1087};
1088
1089static const char *pressed_unchecked_radio_xpm[] = {
1090/* columns rows colors chars-per-pixel */
1091"12 12 6 1",
1092" c None",
1093"w c white",
1094"b c black",
1095"d c #7f7f7f",
1096"g c #c0c0c0",
1097"h c #e0e0e0",
1098/* pixels */
1099" dddd ",
1100" ddbbbbdd ",
1101" dbbggggbbh ",
1102" dbgggggggh ",
1103"dbgggggggggh",
1104"dbgggggggggh",
1105"dbgggggggggh",
1106"dbgggggggggh",
1107" dbgggggggh ",
1108" dggggggggh ",
1109" hhgggghh ",
1110" hhhh "
1111};
1112
1113static const char **
1114 xpmIndicators[IndicatorType_Max][IndicatorState_Max][IndicatorStatus_Max] =
1115{
1116 // checkboxes first
1117 {
1118 // normal state
1119 { checked_xpm, unchecked_xpm },
1120
1121 // pressed state
1122 { pressed_checked_xpm, pressed_unchecked_xpm },
1123
1124 // disabled state
1125 { pressed_disabled_checked_xpm, pressed_unchecked_xpm },
1126 },
1127
1128 // radio
1129 {
1130 // normal state
1131 { checked_radio_xpm, unchecked_radio_xpm },
1132
1133 // pressed state
1134 { pressed_checked_radio_xpm, pressed_unchecked_radio_xpm },
1135
1136 // disabled state
1137 { pressed_disabled_checked_radio_xpm, pressed_unchecked_radio_xpm },
1138 },
1139
1140 // menu
1141 {
1142 // normal state
1143 { checked_menu_xpm, NULL },
1144
1145 // selected state
1146 { selected_checked_menu_xpm, NULL },
1147
1148 // disabled state
1149 { disabled_checked_menu_xpm, NULL },
1150
1151 // disabled selected state
1152 { selected_disabled_checked_menu_xpm, NULL },
1153 }
1154};
1155
1156static const char **xpmChecked[IndicatorStatus_Max] =
1157{
1158 checked_item_xpm,
1159 unchecked_item_xpm
1160};
1161
1162// ============================================================================
1163// implementation
1164// ============================================================================
1165
1166WX_IMPLEMENT_THEME(wxMetalTheme, Metal, wxTRANSLATE("Metal theme"));
1167
1168// ----------------------------------------------------------------------------
1169// wxMetalTheme
1170// ----------------------------------------------------------------------------
1171
1172wxMetalTheme::wxMetalTheme()
1173{
1174 m_scheme = NULL;
1175 m_renderer = NULL;
1176 m_handlerDefault = NULL;
1177}
1178
1179wxMetalTheme::~wxMetalTheme()
1180{
1181 size_t count = m_handlers.GetCount();
1182 for ( size_t n = 0; n < count; n++ )
1183 {
1184 if ( m_handlers[n] != m_handlerDefault )
1185 delete m_handlers[n];
1186 }
1187
1188 delete m_handlerDefault;
1189
1190 delete m_renderer;
1191 delete m_scheme;
1192}
1193
1194wxRenderer *wxMetalTheme::GetRenderer()
1195{
1196 if ( !m_renderer )
1197 {
1198 m_renderer = new wxMetalRenderer(GetColourScheme());
1199 }
1200
1201 return m_renderer;
1202}
1203
1204wxArtProvider *wxMetalTheme::GetArtProvider()
1205{
1206 if ( !m_artProvider )
1207 {
1208 m_artProvider = new wxMetalArtProvider;
1209 }
1210
1211 return m_artProvider;
1212}
1213
1214wxInputHandler *wxMetalTheme::GetDefaultInputHandler()
1215{
1216 if ( !m_handlerDefault )
1217 {
1218 m_handlerDefault = new wxMetalInputHandler(m_renderer);
1219 }
1220
1221 return m_handlerDefault;
1222}
1223
1224wxInputHandler *wxMetalTheme::GetInputHandler(const wxString& control)
1225{
1226 wxInputHandler *handler;
1227 int n = m_handlerNames.Index(control);
1228 if ( n == wxNOT_FOUND )
1229 {
1230 // create a new handler
1231 if ( control == wxINP_HANDLER_SCROLLBAR )
1232 handler = new wxMetalScrollBarInputHandler(m_renderer,
1233 GetDefaultInputHandler());
1234#if wxUSE_BUTTON
1235 else if ( control == wxINP_HANDLER_BUTTON )
1236 handler = new wxStdButtonInputHandler(GetDefaultInputHandler());
1237#endif // wxUSE_BUTTON
1238#if wxUSE_CHECKBOX
1239 else if ( control == wxINP_HANDLER_CHECKBOX )
1240 handler = new wxMetalCheckboxInputHandler(GetDefaultInputHandler());
1241#endif // wxUSE_CHECKBOX
1242#if wxUSE_COMBOBOX
1243 else if ( control == wxINP_HANDLER_COMBOBOX )
1244 handler = new wxStdComboBoxInputHandler(GetDefaultInputHandler());
1245#endif // wxUSE_COMBOBOX
1246#if wxUSE_LISTBOX
1247 else if ( control == wxINP_HANDLER_LISTBOX )
1248 handler = new wxStdListboxInputHandler(GetDefaultInputHandler());
1249#endif // wxUSE_LISTBOX
1250#if wxUSE_CHECKLISTBOX
1251 else if ( control == wxINP_HANDLER_CHECKLISTBOX )
1252 handler = new wxStdCheckListboxInputHandler(GetDefaultInputHandler());
1253#endif // wxUSE_CHECKLISTBOX
1254#if wxUSE_TEXTCTRL
1255 else if ( control == wxINP_HANDLER_TEXTCTRL )
1256 handler = new wxMetalTextCtrlInputHandler(GetDefaultInputHandler());
1257#endif // wxUSE_TEXTCTRL
1258#if wxUSE_SLIDER
1259 else if ( control == wxINP_HANDLER_SLIDER )
1260 handler = new wxStdSliderButtonInputHandler(GetDefaultInputHandler());
1261#endif // wxUSE_SLIDER
1262#if wxUSE_SPINBTN
1263 else if ( control == wxINP_HANDLER_SPINBTN )
1264 handler = new wxStdSpinButtonInputHandler(GetDefaultInputHandler());
1265#endif // wxUSE_SPINBTN
1266#if wxUSE_NOTEBOOK
1267 else if ( control == wxINP_HANDLER_NOTEBOOK )
1268 handler = new wxStdNotebookInputHandler(GetDefaultInputHandler());
1269#endif // wxUSE_NOTEBOOK
1270#if wxUSE_STATUSBAR
1271 else if ( control == wxINP_HANDLER_STATUSBAR )
1272 handler = new wxMetalStatusBarInputHandler(GetDefaultInputHandler());
1273#endif // wxUSE_STATUSBAR
1274#if wxUSE_TOOLBAR
1275 else if ( control == wxINP_HANDLER_TOOLBAR )
1276 handler = new wxStdToolbarInputHandler(GetDefaultInputHandler());
1277#endif // wxUSE_TOOLBAR
1278 else if ( control == wxINP_HANDLER_TOPLEVEL )
1279 handler = new wxMetalFrameInputHandler(GetDefaultInputHandler());
1280 else
1281 handler = GetDefaultInputHandler();
1282
1283 n = m_handlerNames.Add(control);
1284 m_handlers.Insert(handler, n);
1285 }
1286 else // we already have it
1287 {
1288 handler = m_handlers[n];
1289 }
1290
1291 return handler;
1292}
1293
1294wxColourScheme *wxMetalTheme::GetColourScheme()
1295{
1296 if ( !m_scheme )
1297 {
1298 m_scheme = new wxMetalColourScheme;
1299 }
1300 return m_scheme;
1301}
1302
1303// ============================================================================
1304// wxMetalColourScheme
1305// ============================================================================
1306
1307wxColour wxMetalColourScheme::GetBackground(wxWindow *win) const
1308{
1309 wxColour col;
1310 if ( win->UseBgCol() )
1311 {
1312 // use the user specified colour
1313 col = win->GetBackgroundColour();
1314 }
1315
1316 if ( win->IsContainerWindow() )
1317 {
1318 wxTextCtrl *text = wxDynamicCast(win, wxTextCtrl);
1319 if ( text )
1320 {
1321 if ( !text->IsEnabled() ) // not IsEditable()
1322 col = Get(CONTROL);
1323 //else: execute code below
1324 }
1325
1326 if ( !col.Ok() )
1327 {
1328 // doesn't depend on the state
1329 col = Get(WINDOW);
1330 }
1331 }
1332 else
1333 {
1334 if ( win->HasDialogBackground() )
1335 {
1336 col = win->GetParent()->GetBackgroundColour();
1337 return col;
1338 }
1339
1340 int flags = win->GetStateFlags();
1341
1342 // the colour set by the user should be used for the normal state
1343 // and for the states for which we don't have any specific colours
1344 if ( !col.Ok() || (flags & wxCONTROL_PRESSED) != 0 )
1345 {
1346 if ( wxDynamicCast(win, wxScrollBar) )
1347 col = Get(flags & wxCONTROL_PRESSED ? SCROLLBAR_PRESSED
1348 : SCROLLBAR);
1349 else
1350 col = Get(CONTROL);
1351 }
1352 }
1353
1354 return col;
1355}
1356
1357wxColour wxMetalColourScheme::Get(wxMetalColourScheme::StdColour col) const
1358{
1359 switch ( col )
1360 {
1361 // use the system colours under Windows
1362#if defined(__WXMSW__)
1363 case WINDOW: return wxColour(GetSysColor(COLOR_WINDOW));
1364
1365 case CONTROL_PRESSED:
1366 case CONTROL_CURRENT:
1367 case CONTROL: return wxColour(GetSysColor(COLOR_BTNFACE));
1368
1369 case CONTROL_TEXT: return wxColour(GetSysColor(COLOR_BTNTEXT));
1370
1371#if defined(COLOR_3DLIGHT)
1372 case SCROLLBAR: return wxColour(GetSysColor(COLOR_3DLIGHT));
1373#else
1374 case SCROLLBAR: return wxColour(0xe0e0e0);
1375#endif
1376 case SCROLLBAR_PRESSED: return wxColour(GetSysColor(COLOR_BTNTEXT));
1377
1378 case HIGHLIGHT: return wxColour(GetSysColor(COLOR_HIGHLIGHT));
1379 case HIGHLIGHT_TEXT: return wxColour(GetSysColor(COLOR_HIGHLIGHTTEXT));
1380
1381#if defined(COLOR_3DDKSHADOW)
1382 case SHADOW_DARK: return wxColour(GetSysColor(COLOR_3DDKSHADOW));
1383#else
1384 case SHADOW_DARK: return wxColour(GetSysColor(COLOR_3DHADOW));
1385#endif
1386
1387 case CONTROL_TEXT_DISABLED:
1388 case SHADOW_HIGHLIGHT: return wxColour(GetSysColor(COLOR_BTNHIGHLIGHT));
1389
1390 case SHADOW_IN: return wxColour(GetSysColor(COLOR_BTNFACE));
1391
1392 case CONTROL_TEXT_DISABLED_SHADOW:
1393 case SHADOW_OUT: return wxColour(GetSysColor(COLOR_BTNSHADOW));
1394
1395 case TITLEBAR: return wxColour(GetSysColor(COLOR_INACTIVECAPTION));
1396 case TITLEBAR_ACTIVE: return wxColour(GetSysColor(COLOR_ACTIVECAPTION));
1397 case TITLEBAR_TEXT: return wxColour(GetSysColor(COLOR_INACTIVECAPTIONTEXT));
1398 case TITLEBAR_ACTIVE_TEXT: return wxColour(GetSysColor(COLOR_CAPTIONTEXT));
1399
1400 case DESKTOP: return wxColour(0x808000);
1401#else // !__WXMSW__
1402 // use the standard Windows colours elsewhere
1403 case WINDOW: return *wxWHITE;
1404
1405 case CONTROL_PRESSED:
1406 case CONTROL_CURRENT:
1407 case CONTROL: return wxColour(0xc0c0c0);
1408
1409 case CONTROL_TEXT: return *wxBLACK;
1410
1411 case SCROLLBAR: return wxColour(0xe0e0e0);
1412 case SCROLLBAR_PRESSED: return *wxBLACK;
1413
1414 case HIGHLIGHT: return wxColour(0x800000);
1415 case HIGHLIGHT_TEXT: return wxColour(0xffffff);
1416
1417 case SHADOW_DARK: return *wxBLACK;
1418
1419 case CONTROL_TEXT_DISABLED:return wxColour(0xe0e0e0);
1420 case SHADOW_HIGHLIGHT: return wxColour(0xffffff);
1421
1422 case SHADOW_IN: return wxColour(0xc0c0c0);
1423
1424 case CONTROL_TEXT_DISABLED_SHADOW:
1425 case SHADOW_OUT: return wxColour(0x7f7f7f);
1426
1427 case TITLEBAR: return wxColour(0xaeaaae);
1428 case TITLEBAR_ACTIVE: return wxColour(0x820300);
1429 case TITLEBAR_TEXT: return wxColour(0xc0c0c0);
1430 case TITLEBAR_ACTIVE_TEXT:return *wxWHITE;
1431
1432 case DESKTOP: return wxColour(0x808000);
1433#endif // __WXMSW__
1434
1435 case GAUGE: return Get(HIGHLIGHT);
1436
1437 case MAX:
1438 default:
1439 wxFAIL_MSG(_T("invalid standard colour"));
1440 return *wxBLACK;
1441 }
1442}
1443
1444// ============================================================================
1445// wxMetalRenderer
1446// ============================================================================
1447
1448// ----------------------------------------------------------------------------
1449// construction
1450// ----------------------------------------------------------------------------
1451
1452wxMetalRenderer::wxMetalRenderer(const wxColourScheme *scheme)
1453{
1454 // init data
1455 m_scheme = scheme;
1456 m_sizeScrollbarArrow = wxSize(16, 16);
1457
1458 // init colours and pens
1459 m_penBlack = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_DARK), 0, wxSOLID);
1460
1461 m_colDarkGrey = wxSCHEME_COLOUR(scheme, SHADOW_OUT);
1462 m_penDarkGrey = wxPen(m_colDarkGrey, 0, wxSOLID);
1463
1464 m_penLightGrey = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_IN), 0, wxSOLID);
1465
1466 m_colHighlight = wxSCHEME_COLOUR(scheme, SHADOW_HIGHLIGHT);
1467 m_penHighlight = wxPen(m_colHighlight, 0, wxSOLID);
1468
1469 m_titlebarFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
1470 m_titlebarFont.SetWeight(wxFONTWEIGHT_BOLD);
1471
1472 // init the arrow bitmaps
1473 static const size_t ARROW_WIDTH = 7;
1474 static const size_t ARROW_LENGTH = 4;
1475
1476 wxMask *mask;
1477 wxMemoryDC dcNormal,
1478 dcDisabled,
1479 dcInverse;
1480 for ( size_t n = 0; n < Arrow_Max; n++ )
1481 {
1482 bool isVertical = n > Arrow_Right;
1483 int w, h;
1484 if ( isVertical )
1485 {
1486 w = ARROW_WIDTH;
1487 h = ARROW_LENGTH;
1488 }
1489 else
1490 {
1491 h = ARROW_WIDTH;
1492 w = ARROW_LENGTH;
1493 }
1494
1495 // disabled arrow is larger because of the shadow
1496 m_bmpArrows[Arrow_Normal][n].Create(w, h);
1497 m_bmpArrows[Arrow_Disabled][n].Create(w + 1, h + 1);
1498
1499 dcNormal.SelectObject(m_bmpArrows[Arrow_Normal][n]);
1500 dcDisabled.SelectObject(m_bmpArrows[Arrow_Disabled][n]);
1501
1502 dcNormal.SetBackground(*wxWHITE_BRUSH);
1503 dcDisabled.SetBackground(*wxWHITE_BRUSH);
1504 dcNormal.Clear();
1505 dcDisabled.Clear();
1506
1507 dcNormal.SetPen(m_penBlack);
1508 dcDisabled.SetPen(m_penDarkGrey);
1509
1510 // calculate the position of the point of the arrow
1511 wxCoord x1, y1;
1512 if ( isVertical )
1513 {
1514 x1 = (ARROW_WIDTH - 1)/2;
1515 y1 = n == Arrow_Up ? 0 : ARROW_LENGTH - 1;
1516 }
1517 else // horizontal
1518 {
1519 x1 = n == Arrow_Left ? 0 : ARROW_LENGTH - 1;
1520 y1 = (ARROW_WIDTH - 1)/2;
1521 }
1522
1523 wxCoord x2 = x1,
1524 y2 = y1;
1525
1526 if ( isVertical )
1527 x2++;
1528 else
1529 y2++;
1530
1531 for ( size_t i = 0; i < ARROW_LENGTH; i++ )
1532 {
1533 dcNormal.DrawLine(x1, y1, x2, y2);
1534 dcDisabled.DrawLine(x1, y1, x2, y2);
1535
1536 if ( isVertical )
1537 {
1538 x1--;
1539 x2++;
1540
1541 if ( n == Arrow_Up )
1542 {
1543 y1++;
1544 y2++;
1545 }
1546 else // down arrow
1547 {
1548 y1--;
1549 y2--;
1550 }
1551 }
1552 else // left or right arrow
1553 {
1554 y1--;
1555 y2++;
1556
1557 if ( n == Arrow_Left )
1558 {
1559 x1++;
1560 x2++;
1561 }
1562 else
1563 {
1564 x1--;
1565 x2--;
1566 }
1567 }
1568 }
1569
1570 // draw the shadow for the disabled one
1571 dcDisabled.SetPen(m_penHighlight);
1572 switch ( n )
1573 {
1574 case Arrow_Left:
1575 y1 += 2;
1576 dcDisabled.DrawLine(x1, y1, x2, y2);
1577 break;
1578
1579 case Arrow_Right:
1580 x1 = ARROW_LENGTH - 1;
1581 y1 = (ARROW_WIDTH - 1)/2 + 1;
1582 x2 = 0;
1583 y2 = ARROW_WIDTH;
1584 dcDisabled.DrawLine(x1, y1, x2, y2);
1585 dcDisabled.DrawLine(++x1, y1, x2, ++y2);
1586 break;
1587
1588 case Arrow_Up:
1589 x1 += 2;
1590 dcDisabled.DrawLine(x1, y1, x2, y2);
1591 break;
1592
1593 case Arrow_Down:
1594 x1 = ARROW_WIDTH - 1;
1595 y1 = 1;
1596 x2 = (ARROW_WIDTH - 1)/2;
1597 y2 = ARROW_LENGTH;
1598 dcDisabled.DrawLine(x1, y1, x2, y2);
1599 dcDisabled.DrawLine(++x1, y1, x2, ++y2);
1600 break;
1601
1602 }
1603
1604 // create the inversed bitmap but only for the right arrow as we only
1605 // use it for the menus
1606 if ( n == Arrow_Right )
1607 {
1608 m_bmpArrows[Arrow_Inversed][n].Create(w, h);
1609 dcInverse.SelectObject(m_bmpArrows[Arrow_Inversed][n]);
1610 dcInverse.Clear();
1611 dcInverse.Blit(0, 0, w, h,
1612 &dcNormal, 0, 0,
1613 wxXOR);
1614 dcInverse.SelectObject(wxNullBitmap);
1615
1616 mask = new wxMask(m_bmpArrows[Arrow_Inversed][n], *wxBLACK);
1617 m_bmpArrows[Arrow_Inversed][n].SetMask(mask);
1618
1619 m_bmpArrows[Arrow_InversedDisabled][n].Create(w, h);
1620 dcInverse.SelectObject(m_bmpArrows[Arrow_InversedDisabled][n]);
1621 dcInverse.Clear();
1622 dcInverse.Blit(0, 0, w, h,
1623 &dcDisabled, 0, 0,
1624 wxXOR);
1625 dcInverse.SelectObject(wxNullBitmap);
1626
1627 mask = new wxMask(m_bmpArrows[Arrow_InversedDisabled][n], *wxBLACK);
1628 m_bmpArrows[Arrow_InversedDisabled][n].SetMask(mask);
1629 }
1630
1631 dcNormal.SelectObject(wxNullBitmap);
1632 dcDisabled.SelectObject(wxNullBitmap);
1633
1634 mask = new wxMask(m_bmpArrows[Arrow_Normal][n], *wxWHITE);
1635 m_bmpArrows[Arrow_Normal][n].SetMask(mask);
1636 mask = new wxMask(m_bmpArrows[Arrow_Disabled][n], *wxWHITE);
1637 m_bmpArrows[Arrow_Disabled][n].SetMask(mask);
1638
1639 m_bmpArrows[Arrow_Pressed][n] = m_bmpArrows[Arrow_Normal][n];
1640 }
1641
1642 // init the frame buttons bitmaps
1643 m_bmpFrameButtons[FrameButton_Close] = wxBitmap(frame_button_close_xpm);
1644 m_bmpFrameButtons[FrameButton_Minimize] = wxBitmap(frame_button_minimize_xpm);
1645 m_bmpFrameButtons[FrameButton_Maximize] = wxBitmap(frame_button_maximize_xpm);
1646 m_bmpFrameButtons[FrameButton_Restore] = wxBitmap(frame_button_restore_xpm);
1647 m_bmpFrameButtons[FrameButton_Help] = wxBitmap(frame_button_help_xpm);
1648}
1649
1650// ----------------------------------------------------------------------------
1651// border stuff
1652// ----------------------------------------------------------------------------
1653
1654/*
1655 The raised border in Metal looks like this:
1656
1657 IIIIIIIIIIIIIIIIIIIIIIB
1658 I GB
1659 I GB I = white (HILIGHT)
1660 I GB H = light grey (LIGHT)
1661 I GB G = dark grey (SHADOI)
1662 I GB B = black (DKSHADOI)
1663 I GB I = hIghlight (COLOR_3DHILIGHT)
1664 I GB
1665 IGGGGGGGGGGGGGGGGGGGGGB
1666 BBBBBBBBBBBBBBBBBBBBBBB
1667
1668 The sunken border looks like this:
1669
1670 GGGGGGGGGGGGGGGGGGGGGGI
1671 GBBBBBBBBBBBBBBBBBBBBHI
1672 GB HI
1673 GB HI
1674 GB HI
1675 GB HI
1676 GB HI
1677 GB HI
1678 GHHHHHHHHHHHHHHHHHHHHHI
1679 IIIIIIIIIIIIIIIIIIIIIII
1680
1681 The static border (used for the controls which don't get focus) is like
1682 this:
1683
1684 GGGGGGGGGGGGGGGGGGGGGGW
1685 G W
1686 G W
1687 G W
1688 G W
1689 G W
1690 G W
1691 G W
1692 WWWWWWWWWWWWWWWWWWWWWWW
1693
1694 The most complicated is the double border:
1695
1696 HHHHHHHHHHHHHHHHHHHHHHB
1697 HWWWWWWWWWWWWWWWWWWWWGB
1698 HWHHHHHHHHHHHHHHHHHHHGB
1699 HWH HGB
1700 HWH HGB
1701 HWH HGB
1702 HWH HGB
1703 HWHHHHHHHHHHHHHHHHHHHGB
1704 HGGGGGGGGGGGGGGGGGGGGGB
1705 BBBBBBBBBBBBBBBBBBBBBBB
1706
1707 And the simple border is, well, simple:
1708
1709 BBBBBBBBBBBBBBBBBBBBBBB
1710 B B
1711 B B
1712 B B
1713 B B
1714 B B
1715 B B
1716 B B
1717 B B
1718 BBBBBBBBBBBBBBBBBBBBBBB
1719*/
1720
1721void wxMetalRenderer::DrawRect(wxDC& dc, wxRect *rect, const wxPen& pen)
1722{
1723 // draw
1724 dc.SetPen(pen);
1725 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1726 dc.DrawRectangle(*rect);
1727
1728 // adjust the rect
1729 rect->Inflate(-1);
1730}
1731
1732void wxMetalRenderer::DrawHalfRect(wxDC& dc, wxRect *rect, const wxPen& pen)
1733{
1734 // draw the bottom and right sides
1735 dc.SetPen(pen);
1736 dc.DrawLine(rect->GetLeft(), rect->GetBottom(),
1737 rect->GetRight() + 1, rect->GetBottom());
1738 dc.DrawLine(rect->GetRight(), rect->GetTop(),
1739 rect->GetRight(), rect->GetBottom());
1740
1741 // adjust the rect
1742 rect->width--;
1743 rect->height--;
1744}
1745
1746void wxMetalRenderer::DrawShadedRect(wxDC& dc, wxRect *rect,
1747 const wxPen& pen1, const wxPen& pen2)
1748{
1749 // draw the rectangle
1750 dc.SetPen(pen1);
1751 dc.DrawLine(rect->GetLeft(), rect->GetTop(),
1752 rect->GetLeft(), rect->GetBottom());
1753 dc.DrawLine(rect->GetLeft() + 1, rect->GetTop(),
1754 rect->GetRight(), rect->GetTop());
1755 dc.SetPen(pen2);
1756 dc.DrawLine(rect->GetRight(), rect->GetTop(),
1757 rect->GetRight(), rect->GetBottom());
1758 dc.DrawLine(rect->GetLeft(), rect->GetBottom(),
1759 rect->GetRight() + 1, rect->GetBottom());
1760
1761 // adjust the rect
1762 rect->Inflate(-1);
1763}
1764
1765void wxMetalRenderer::DrawRaisedBorder(wxDC& dc, wxRect *rect)
1766{
1767 DrawShadedRect(dc, rect, m_penHighlight, m_penBlack);
1768 DrawShadedRect(dc, rect, m_penLightGrey, m_penDarkGrey);
1769}
1770
1771void wxMetalRenderer::DrawSunkenBorder(wxDC& dc, wxRect *rect)
1772{
1773 DrawShadedRect(dc, rect, m_penDarkGrey, m_penHighlight);
1774 DrawShadedRect(dc, rect, m_penBlack, m_penLightGrey);
1775}
1776
1777void wxMetalRenderer::DrawArrowBorder(wxDC& dc, wxRect *rect, bool isPressed)
1778{
1779 if ( isPressed )
1780 {
1781 DrawRect(dc, rect, m_penDarkGrey);
1782
1783 // the arrow is usually drawn inside border of width 2 and is offset by
1784 // another pixel in both directions when it's pressed - as the border
1785 // in this case is more narrow as well, we have to adjust rect like
1786 // this:
1787 rect->Inflate(-1);
1788 rect->x++;
1789 rect->y++;
1790 }
1791 else
1792 {
1793 DrawShadedRect(dc, rect, m_penLightGrey, m_penBlack);
1794 DrawShadedRect(dc, rect, m_penHighlight, m_penDarkGrey);
1795 }
1796}
1797
1798void wxMetalRenderer::DrawBorder(wxDC& dc,
1799 wxBorder border,
1800 const wxRect& rectTotal,
1801 int WXUNUSED(flags),
1802 wxRect *rectIn)
1803{
1804 int i;
1805
1806 wxRect rect = rectTotal;
1807
1808 switch ( border )
1809 {
1810 case wxBORDER_SUNKEN:
1811 for ( i = 0; i < BORDER_THICKNESS / 2; i++ )
1812 {
1813 DrawSunkenBorder(dc, &rect);
1814 }
1815 break;
1816
1817 case wxBORDER_STATIC:
1818 DrawShadedRect(dc, &rect, m_penDarkGrey, m_penHighlight);
1819 break;
1820
1821 case wxBORDER_RAISED:
1822 for ( i = 0; i < BORDER_THICKNESS / 2; i++ )
1823 {
1824 DrawRaisedBorder(dc, &rect);
1825 }
1826 break;
1827
1828 case wxBORDER_DOUBLE:
1829 DrawArrowBorder(dc, &rect);
1830 DrawRect(dc, &rect, m_penLightGrey);
1831 break;
1832
1833 case wxBORDER_SIMPLE:
1834 for ( i = 0; i < BORDER_THICKNESS / 2; i++ )
1835 {
1836 DrawRect(dc, &rect, m_penBlack);
1837 }
1838 break;
1839
1840 default:
1841 wxFAIL_MSG(_T("unknown border type"));
1842 // fall through
1843
1844 case wxBORDER_DEFAULT:
1845 case wxBORDER_NONE:
1846 break;
1847 }
1848
1849 if ( rectIn )
1850 *rectIn = rect;
1851}
1852
1853wxRect wxMetalRenderer::GetBorderDimensions(wxBorder border) const
1854{
1855 wxCoord width;
1856 switch ( border )
1857 {
1858 case wxBORDER_RAISED:
1859 case wxBORDER_SUNKEN:
1860 width = BORDER_THICKNESS;
1861 break;
1862
1863 case wxBORDER_SIMPLE:
1864 case wxBORDER_STATIC:
1865 width = 1;
1866 break;
1867
1868 case wxBORDER_DOUBLE:
1869 width = 3;
1870 break;
1871
1872 default:
1873 {
1874 // char *crash = NULL;
1875 // *crash = 0;
1876 wxFAIL_MSG(_T("unknown border type"));
1877 // fall through
1878 }
1879
1880 case wxBORDER_DEFAULT:
1881 case wxBORDER_NONE:
1882 width = 0;
1883 break;
1884 }
1885
1886 wxRect rect;
1887 rect.x =
1888 rect.y =
1889 rect.width =
1890 rect.height = width;
1891
1892 return rect;
1893}
1894
1895bool wxMetalRenderer::AreScrollbarsInsideBorder() const
1896{
1897 return TRUE;
1898}
1899
1900// ----------------------------------------------------------------------------
1901// borders
1902// ----------------------------------------------------------------------------
1903
1904void wxMetalRenderer::DrawTextBorder(wxDC& dc,
1905 wxBorder border,
1906 const wxRect& rect,
1907 int flags,
1908 wxRect *rectIn)
1909{
1910 // text controls are not special under windows
1911 DrawBorder(dc, border, rect, flags, rectIn);
1912}
1913
1914void wxMetalRenderer::DrawButtonBorder(wxDC& dc,
1915 const wxRect& rectTotal,
1916 int flags,
1917 wxRect *rectIn)
1918{
1919 wxRect rect = rectTotal;
1920
1921 if ( flags & wxCONTROL_PRESSED )
1922 {
1923 // button pressed: draw a double border around it
1924 DrawRect(dc, &rect, m_penBlack);
1925 DrawRect(dc, &rect, m_penDarkGrey);
1926 }
1927 else
1928 {
1929 // button not pressed
1930
1931 if ( flags & (wxCONTROL_FOCUSED | wxCONTROL_ISDEFAULT) )
1932 {
1933 // button either default or focused (or both): add an extra border around it
1934 DrawRect(dc, &rect, m_penBlack);
1935 }
1936
1937 // now draw a normal button
1938 DrawShadedRect(dc, &rect, m_penHighlight, m_penBlack);
1939 DrawHalfRect(dc, &rect, m_penDarkGrey);
1940 }
1941
1942 if ( rectIn )
1943 {
1944 *rectIn = rect;
1945 }
1946}
1947
1948// ----------------------------------------------------------------------------
1949// lines and frame
1950// ----------------------------------------------------------------------------
1951
1952void wxMetalRenderer::DrawHorizontalLine(wxDC& dc,
1953 wxCoord y, wxCoord x1, wxCoord x2)
1954{
1955 dc.SetPen(m_penDarkGrey);
1956 dc.DrawLine(x1, y, x2 + 1, y);
1957 dc.SetPen(m_penHighlight);
1958 y++;
1959 dc.DrawLine(x1, y, x2 + 1, y);
1960}
1961
1962void wxMetalRenderer::DrawVerticalLine(wxDC& dc,
1963 wxCoord x, wxCoord y1, wxCoord y2)
1964{
1965 dc.SetPen(m_penDarkGrey);
1966 dc.DrawLine(x, y1, x, y2 + 1);
1967 dc.SetPen(m_penHighlight);
1968 x++;
1969 dc.DrawLine(x, y1, x, y2 + 1);
1970}
1971
1972void wxMetalRenderer::DrawFrame(wxDC& dc,
1973 const wxString& label,
1974 const wxRect& rect,
1975 int flags,
1976 int alignment,
1977 int indexAccel)
1978{
1979 wxCoord height = 0; // of the label
1980 wxRect rectFrame = rect;
1981 if ( !label.empty() )
1982 {
1983 // the text should touch the top border of the rect, so the frame
1984 // itself should be lower
1985 dc.GetTextExtent(label, NULL, &height);
1986 rectFrame.y += height / 2;
1987 rectFrame.height -= height / 2;
1988
1989 // we have to draw each part of the frame individually as we can't
1990 // erase the background beyond the label as it might contain some
1991 // pixmap already, so drawing everything and then overwriting part of
1992 // the frame with label doesn't work
1993
1994 // TODO: the +5 and space insertion should be customizable
1995
1996 wxRect rectText;
1997 rectText.x = rectFrame.x + 5;
1998 rectText.y = rect.y;
1999 rectText.width = rectFrame.width - 7; // +2 border width
2000 rectText.height = height;
2001
2002 wxString label2;
2003 label2 << _T(' ') << label << _T(' ');
2004 if ( indexAccel != -1 )
2005 {
2006 // adjust it as we prepended a space
2007 indexAccel++;
2008 }
2009
2010 wxRect rectLabel;
2011 DrawLabel(dc, label2, rectText, flags, alignment, indexAccel, &rectLabel);
2012
2013 StandardDrawFrame(dc, rectFrame, rectLabel);
2014 }
2015 else
2016 {
2017 // just draw the complete frame
2018 DrawShadedRect(dc, &rectFrame, m_penDarkGrey, m_penHighlight);
2019 DrawShadedRect(dc, &rectFrame, m_penHighlight, m_penDarkGrey);
2020 }
2021}
2022
2023// ----------------------------------------------------------------------------
2024// label
2025// ----------------------------------------------------------------------------
2026
2027void wxMetalRenderer::DrawFocusRect(wxDC& dc, const wxRect& rect)
2028{
2029 // VZ: this doesn't work under Windows, the dotted pen has dots of 3
2030 // pixels each while we really need dots here... PS_ALTERNATE might
2031 // work, but it is for NT 5 only
2032#if 0
2033 DrawRect(dc, &rect, wxPen(*wxBLACK, 0, wxDOT));
2034#else
2035 // draw the pixels manually: note that to behave in the same manner as
2036 // DrawRect(), we must exclude the bottom and right borders from the
2037 // rectangle
2038 wxCoord x1 = rect.GetLeft(),
2039 y1 = rect.GetTop(),
2040 x2 = rect.GetRight(),
2041 y2 = rect.GetBottom();
2042
2043 dc.SetPen(wxPen(*wxBLACK, 0, wxSOLID));
2044
2045 // this seems to be closer than what Windows does than wxINVERT although
2046 // I'm still not sure if it's correct
2047 dc.SetLogicalFunction(wxAND_REVERSE);
2048
2049 wxCoord z;
2050 for ( z = x1 + 1; z < x2; z += 2 )
2051 dc.DrawPoint(z, rect.GetTop());
2052
2053 wxCoord shift = z == x2 ? 0 : 1;
2054 for ( z = y1 + shift; z < y2; z += 2 )
2055 dc.DrawPoint(x2, z);
2056
2057 shift = z == y2 ? 0 : 1;
2058 for ( z = x2 - shift; z > x1; z -= 2 )
2059 dc.DrawPoint(z, y2);
2060
2061 shift = z == x1 ? 0 : 1;
2062 for ( z = y2 - shift; z > y1; z -= 2 )
2063 dc.DrawPoint(x1, z);
2064
2065 dc.SetLogicalFunction(wxCOPY);
2066#endif // 0/1
2067}
2068
2069void wxMetalRenderer::DrawLabelShadow(wxDC& dc,
2070 const wxString& label,
2071 const wxRect& rect,
2072 int alignment,
2073 int indexAccel)
2074{
2075 // draw shadow of the text
2076 dc.SetTextForeground(m_colHighlight);
2077 wxRect rectShadow = rect;
2078 rectShadow.x++;
2079 rectShadow.y++;
2080 dc.DrawLabel(label, rectShadow, alignment, indexAccel);
2081
2082 // make the text grey
2083 dc.SetTextForeground(m_colDarkGrey);
2084}
2085
2086void wxMetalRenderer::DrawLabel(wxDC& dc,
2087 const wxString& label,
2088 const wxRect& rect,
2089 int flags,
2090 int alignment,
2091 int indexAccel,
2092 wxRect *rectBounds)
2093{
2094 DoDrawLabel(dc, label, rect, flags, alignment, indexAccel, rectBounds);
2095}
2096
2097void wxMetalRenderer::DoDrawLabel(wxDC& dc,
2098 const wxString& label,
2099 const wxRect& rect,
2100 int flags,
2101 int alignment,
2102 int indexAccel,
2103 wxRect *rectBounds,
2104 const wxPoint& focusOffset)
2105{
2106 // the underscores are not drawn for focused controls in wxMSW
2107 if ( flags & wxCONTROL_FOCUSED )
2108 {
2109 indexAccel = -1;
2110 }
2111
2112 if ( flags & wxCONTROL_DISABLED )
2113 {
2114 // the combination of wxCONTROL_SELECTED and wxCONTROL_DISABLED
2115 // currently only can happen for a menu item and it seems that Windows
2116 // doesn't draw the shadow in this case, so we don't do it neither
2117 if ( flags & wxCONTROL_SELECTED )
2118 {
2119 // just make the label text greyed out
2120 dc.SetTextForeground(m_colDarkGrey);
2121 }
2122 else // draw normal disabled label
2123 {
2124 DrawLabelShadow(dc, label, rect, alignment, indexAccel);
2125 }
2126 }
2127
2128 wxRect rectLabel;
2129 dc.DrawLabel(label, wxNullBitmap, rect, alignment, indexAccel, &rectLabel);
2130
2131 if ( flags & wxCONTROL_DISABLED )
2132 {
2133 // restore the fg colour
2134 dc.SetTextForeground(*wxBLACK);
2135 }
2136
2137 if ( flags & wxCONTROL_FOCUSED )
2138 {
2139 if ( focusOffset.x || focusOffset.y )
2140 {
2141 rectLabel.Inflate(focusOffset.x, focusOffset.y);
2142 }
2143
2144 DrawFocusRect(dc, rectLabel);
2145 }
2146
2147 if ( rectBounds )
2148 *rectBounds = rectLabel;
2149}
2150
2151void wxMetalRenderer::DrawButtonLabel(wxDC& dc,
2152 const wxString& label,
2153 const wxBitmap& image,
2154 const wxRect& rect,
2155 int flags,
2156 int alignment,
2157 int indexAccel,
2158 wxRect *rectBounds)
2159{
2160 // the underscores are not drawn for focused controls in wxMSW
2161 if ( flags & wxCONTROL_PRESSED )
2162 {
2163 indexAccel = -1;
2164 }
2165
2166 wxRect rectLabel = rect;
2167 if ( !label.empty() )
2168 {
2169 // shift the label if a button is pressed
2170 if ( flags & wxCONTROL_PRESSED )
2171 {
2172 rectLabel.x++;
2173 rectLabel.y++;
2174 }
2175
2176 if ( flags & wxCONTROL_DISABLED )
2177 {
2178 DrawLabelShadow(dc, label, rectLabel, alignment, indexAccel);
2179 }
2180
2181 // leave enough space for the focus rectangle
2182 if ( flags & wxCONTROL_FOCUSED )
2183 {
2184 rectLabel.Inflate(-2);
2185 }
2186 }
2187
2188 dc.DrawLabel(label, image, rectLabel, alignment, indexAccel, rectBounds);
2189
2190 if ( !label.empty() && (flags & wxCONTROL_FOCUSED) )
2191 {
2192 if ( flags & wxCONTROL_PRESSED )
2193 {
2194 // the focus rectangle is never pressed, so undo the shift done
2195 // above
2196 rectLabel.x--;
2197 rectLabel.y--;
2198 rectLabel.width--;
2199 rectLabel.height--;
2200 }
2201
2202 DrawFocusRect(dc, rectLabel);
2203 }
2204}
2205
2206// ----------------------------------------------------------------------------
2207// (check)listbox items
2208// ----------------------------------------------------------------------------
2209
2210void wxMetalRenderer::DrawItem(wxDC& dc,
2211 const wxString& label,
2212 const wxRect& rect,
2213 int flags)
2214{
2215 wxDCTextColourChanger colChanger(dc);
2216
2217 if ( flags & wxCONTROL_SELECTED )
2218 {
2219 colChanger.Set(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));
2220
2221 wxColour colBg = wxSCHEME_COLOUR(m_scheme, HIGHLIGHT);
2222 dc.SetBrush(wxBrush(colBg, wxSOLID));
2223 dc.SetPen(wxPen(colBg, 0, wxSOLID));
2224 dc.DrawRectangle(rect);
2225 }
2226
2227 wxRect rectText = rect;
2228 rectText.x += 2;
2229 rectText.width -= 2;
2230 dc.DrawLabel(label, wxNullBitmap, rectText);
2231
2232 if ( flags & wxCONTROL_FOCUSED )
2233 {
2234 DrawFocusRect(dc, rect);
2235 }
2236}
2237
2238void wxMetalRenderer::DrawCheckItem(wxDC& dc,
2239 const wxString& label,
2240 const wxBitmap& bitmap,
2241 const wxRect& rect,
2242 int flags)
2243{
2244 wxBitmap bmp;
2245 if ( bitmap.Ok() )
2246 {
2247 bmp = bitmap;
2248 }
2249 else // use default bitmap
2250 {
2251 IndicatorStatus i = flags & wxCONTROL_CHECKED
2252 ? IndicatorStatus_Checked
2253 : IndicatorStatus_Unchecked;
2254
2255 if ( !m_bmpCheckBitmaps[i].Ok() )
2256 {
2257 m_bmpCheckBitmaps[i] = wxBitmap(xpmChecked[i]);
2258 }
2259
2260 bmp = m_bmpCheckBitmaps[i];
2261 }
2262
2263 dc.DrawBitmap(bmp, rect.x, rect.y + (rect.height - bmp.GetHeight()) / 2 - 1,
2264 TRUE /* use mask */);
2265
2266 wxRect rectLabel = rect;
2267 int bmpWidth = bmp.GetWidth();
2268 rectLabel.x += bmpWidth;
2269 rectLabel.width -= bmpWidth;
2270
2271 DrawItem(dc, label, rectLabel, flags);
2272}
2273
2274// ----------------------------------------------------------------------------
2275// check/radio buttons
2276// ----------------------------------------------------------------------------
2277
2278wxBitmap wxMetalRenderer::GetIndicator(IndicatorType indType, int flags)
2279{
2280 IndicatorState indState;
2281 if ( flags & wxCONTROL_SELECTED )
2282 indState = flags & wxCONTROL_DISABLED ? IndicatorState_SelectedDisabled
2283 : IndicatorState_Selected;
2284 else if ( flags & wxCONTROL_DISABLED )
2285 indState = IndicatorState_Disabled;
2286 else if ( flags & wxCONTROL_PRESSED )
2287 indState = IndicatorState_Pressed;
2288 else
2289 indState = IndicatorState_Normal;
2290
2291 IndicatorStatus indStatus = flags & wxCONTROL_CHECKED
2292 ? IndicatorStatus_Checked
2293 : IndicatorStatus_Unchecked;
2294
2295 wxBitmap bmp = m_bmpIndicators[indType][indState][indStatus];
2296 if ( !bmp.Ok() )
2297 {
2298 const char **xpm = xpmIndicators[indType][indState][indStatus];
2299 if ( xpm )
2300 {
2301 // create and cache it
2302 bmp = wxBitmap(xpm);
2303 m_bmpIndicators[indType][indState][indStatus] = bmp;
2304 }
2305 }
2306
2307 return bmp;
2308}
2309
2310void wxMetalRenderer::DrawCheckOrRadioButton(wxDC& dc,
2311 const wxString& label,
2312 const wxBitmap& bitmap,
2313 const wxRect& rect,
2314 int flags,
2315 wxAlignment align,
2316 int indexAccel,
2317 wxCoord focusOffsetY)
2318{
2319 // calculate the position of the bitmap and of the label
2320 wxCoord heightBmp = bitmap.GetHeight();
2321 wxCoord xBmp,
2322 yBmp = rect.y + (rect.height - heightBmp) / 2;
2323
2324 wxRect rectLabel;
2325 dc.GetMultiLineTextExtent(label, NULL, &rectLabel.height);
2326 rectLabel.y = rect.y + (rect.height - rectLabel.height) / 2;
2327
2328 // align label vertically with the bitmap - looks nicer like this
2329 rectLabel.y -= (rectLabel.height - heightBmp) % 2;
2330
2331 // calc horz position
2332 if ( align == wxALIGN_RIGHT )
2333 {
2334 xBmp = rect.GetRight() - bitmap.GetWidth();
2335 rectLabel.x = rect.x + 3;
2336 rectLabel.SetRight(xBmp);
2337 }
2338 else // normal (checkbox to the left of the text) case
2339 {
2340 xBmp = rect.x;
2341 rectLabel.x = xBmp + bitmap.GetWidth() + 5;
2342 rectLabel.SetRight(rect.GetRight());
2343 }
2344
2345 dc.DrawBitmap(bitmap, xBmp, yBmp, TRUE /* use mask */);
2346
2347 DoDrawLabel(
2348 dc, label, rectLabel,
2349 flags,
2350 wxALIGN_LEFT | wxALIGN_TOP,
2351 indexAccel,
2352 NULL, // we don't need bounding rect
2353 // use custom vert focus rect offset
2354 wxPoint(FOCUS_RECT_OFFSET_X, focusOffsetY)
2355 );
2356}
2357
2358void wxMetalRenderer::DrawRadioButton(wxDC& dc,
2359 const wxString& label,
2360 const wxBitmap& bitmap,
2361 const wxRect& rect,
2362 int flags,
2363 wxAlignment align,
2364 int indexAccel)
2365{
2366 wxBitmap bmp;
2367 if ( bitmap.Ok() )
2368 bmp = bitmap;
2369 else
2370 bmp = GetRadioBitmap(flags);
2371
2372 DrawCheckOrRadioButton(dc, label,
2373 bmp,
2374 rect, flags, align, indexAccel,
2375 FOCUS_RECT_OFFSET_Y); // default focus rect offset
2376}
2377
2378void wxMetalRenderer::DrawCheckButton(wxDC& dc,
2379 const wxString& label,
2380 const wxBitmap& bitmap,
2381 const wxRect& rect,
2382 int flags,
2383 wxAlignment align,
2384 int indexAccel)
2385{
2386 wxBitmap bmp;
2387 if ( bitmap.Ok() )
2388 bmp = bitmap;
2389 else
2390 bmp = GetCheckBitmap(flags);
2391
2392 DrawCheckOrRadioButton(dc, label,
2393 bmp,
2394 rect, flags, align, indexAccel,
2395 0); // no focus rect offset for checkboxes
2396}
2397
2398void wxMetalRenderer::DrawToolBarButton(wxDC& dc,
2399 const wxString& label,
2400 const wxBitmap& bitmap,
2401 const wxRect& rectOrig,
2402 int flags)
2403{
2404 if ( !label.empty() || bitmap.Ok() )
2405 {
2406 wxRect rect = rectOrig;
2407 rect.Deflate(BORDER_THICKNESS);
2408
2409 if ( flags & wxCONTROL_PRESSED )
2410 {
2411 DrawBorder(dc, wxBORDER_SUNKEN, rect, flags);
2412 }
2413 else if ( flags & wxCONTROL_CURRENT )
2414 {
2415 DrawBorder(dc, wxBORDER_RAISED, rect, flags);
2416 }
2417
2418 dc.DrawLabel(label, bitmap, rect, wxALIGN_CENTRE);
2419 }
2420 else // a separator
2421 {
2422 // leave a small gap aroudn the line, also account for the toolbar
2423 // border itself
2424 DrawVerticalLine(dc, rectOrig.x + rectOrig.width/2,
2425 rectOrig.y + 2*BORDER_THICKNESS,
2426 rectOrig.GetBottom() - BORDER_THICKNESS);
2427 }
2428}
2429
2430// ----------------------------------------------------------------------------
2431// text control
2432// ----------------------------------------------------------------------------
2433
2434void wxMetalRenderer::DrawTextLine(wxDC& dc,
2435 const wxString& text,
2436 const wxRect& rect,
2437 int selStart,
2438 int selEnd,
2439 int flags)
2440{
2441 // nothing special to do here
2442 StandardDrawTextLine(dc, text, rect, selStart, selEnd, flags);
2443}
2444
2445void wxMetalRenderer::DrawLineWrapMark(wxDC& dc, const wxRect& rect)
2446{
2447 // we don't draw them
2448}
2449
2450// ----------------------------------------------------------------------------
2451// notebook
2452// ----------------------------------------------------------------------------
2453
2454void wxMetalRenderer::DrawTab(wxDC& dc,
2455 const wxRect& rectOrig,
2456 wxDirection dir,
2457 const wxString& label,
2458 const wxBitmap& bitmap,
2459 int flags,
2460 int indexAccel)
2461{
2462 wxRect rect = rectOrig;
2463
2464 // the current tab is drawn indented (to the top for default case) and
2465 // bigger than the other ones
2466 const wxSize indent = GetTabIndent();
2467 if ( flags & wxCONTROL_SELECTED )
2468 {
2469 switch ( dir )
2470 {
2471 default:
2472 wxFAIL_MSG(_T("invaild notebook tab orientation"));
2473 // fall through
2474
2475 case wxTOP:
2476 rect.Inflate(indent.x, 0);
2477 rect.y -= indent.y;
2478 rect.height += indent.y;
2479 break;
2480
2481 case wxBOTTOM:
2482 rect.Inflate(indent.x, 0);
2483 rect.height += indent.y;
2484 break;
2485
2486 case wxLEFT:
2487 case wxRIGHT:
2488 wxFAIL_MSG(_T("TODO"));
2489 break;
2490 }
2491 }
2492
2493 // draw the text, image and the focus around them (if necessary)
2494 wxRect rectLabel = rect;
2495 rectLabel.Deflate(1, 1);
2496 DrawButtonLabel(dc, label, bitmap, rectLabel,
2497 flags, wxALIGN_CENTRE, indexAccel);
2498
2499 // now draw the tab border itself (maybe use DrawRoundedRectangle()?)
2500 static const wxCoord CUTOFF = 2; // radius of the rounded corner
2501 wxCoord x = rect.x,
2502 y = rect.y,
2503 x2 = rect.GetRight(),
2504 y2 = rect.GetBottom();
2505
2506 // FIXME: all this code will break if the tab indent or the border width,
2507 // it is tied to the fact that both of them are equal to 2
2508 switch ( dir )
2509 {
2510 default:
2511 case wxTOP:
2512 dc.SetPen(m_penHighlight);
2513 dc.DrawLine(x, y2, x, y + CUTOFF);
2514 dc.DrawLine(x, y + CUTOFF, x + CUTOFF, y);
2515 dc.DrawLine(x + CUTOFF, y, x2 - CUTOFF + 1, y);
2516
2517 dc.SetPen(m_penBlack);
2518 dc.DrawLine(x2, y2, x2, y + CUTOFF);
2519 dc.DrawLine(x2, y + CUTOFF, x2 - CUTOFF, y);
2520
2521 dc.SetPen(m_penDarkGrey);
2522 dc.DrawLine(x2 - 1, y2, x2 - 1, y + CUTOFF - 1);
2523
2524 if ( flags & wxCONTROL_SELECTED )
2525 {
2526 dc.SetPen(m_penLightGrey);
2527
2528 // overwrite the part of the border below this tab
2529 dc.DrawLine(x + 1, y2 + 1, x2 - 1, y2 + 1);
2530
2531 // and the shadow of the tab to the left of us
2532 dc.DrawLine(x + 1, y + CUTOFF + 1, x + 1, y2 + 1);
2533 }
2534 break;
2535
2536 case wxBOTTOM:
2537 dc.SetPen(m_penHighlight);
2538 // we need to continue one pixel further to overwrite the corner of
2539 // the border for the selected tab
2540 dc.DrawLine(x, y - (flags & wxCONTROL_SELECTED ? 1 : 0),
2541 x, y2 - CUTOFF);
2542 dc.DrawLine(x, y2 - CUTOFF, x + CUTOFF, y2);
2543
2544 dc.SetPen(m_penBlack);
2545 dc.DrawLine(x + CUTOFF, y2, x2 - CUTOFF + 1, y2);
2546 dc.DrawLine(x2, y, x2, y2 - CUTOFF);
2547 dc.DrawLine(x2, y2 - CUTOFF, x2 - CUTOFF, y2);
2548
2549 dc.SetPen(m_penDarkGrey);
2550 dc.DrawLine(x + CUTOFF, y2 - 1, x2 - CUTOFF + 1, y2 - 1);
2551 dc.DrawLine(x2 - 1, y, x2 - 1, y2 - CUTOFF + 1);
2552
2553 if ( flags & wxCONTROL_SELECTED )
2554 {
2555 dc.SetPen(m_penLightGrey);
2556
2557 // overwrite the part of the (double!) border above this tab
2558 dc.DrawLine(x + 1, y - 1, x2 - 1, y - 1);
2559 dc.DrawLine(x + 1, y - 2, x2 - 1, y - 2);
2560
2561 // and the shadow of the tab to the left of us
2562 dc.DrawLine(x + 1, y2 - CUTOFF, x + 1, y - 1);
2563 }
2564 break;
2565
2566 case wxLEFT:
2567 case wxRIGHT:
2568 wxFAIL_MSG(_T("TODO"));
2569 }
2570}
2571
2572// ----------------------------------------------------------------------------
2573// slider
2574// ----------------------------------------------------------------------------
2575
2576wxSize wxMetalRenderer::GetSliderThumbSize(const wxRect& rect,
2577 wxOrientation orient) const
2578{
2579 wxSize size;
2580
2581 wxRect rectShaft = GetSliderShaftRect(rect, orient);
2582 if ( orient == wxHORIZONTAL )
2583 {
2584 size.y = rect.height - 6;
2585 size.x = wxMin(size.y / 2, rectShaft.width);
2586 }
2587 else // vertical
2588 {
2589 size.x = rect.width - 6;
2590 size.y = wxMin(size.x / 2, rectShaft.height);
2591 }
2592
2593 return size;
2594}
2595
2596wxRect wxMetalRenderer::GetSliderShaftRect(const wxRect& rectOrig,
2597 wxOrientation orient) const
2598{
2599 static const wxCoord SLIDER_MARGIN = 6;
2600
2601 wxRect rect = rectOrig;
2602
2603 if ( orient == wxHORIZONTAL )
2604 {
2605 // make the rect of minimal width and centre it
2606 rect.height = 2*BORDER_THICKNESS;
2607 rect.y = rectOrig.y + (rectOrig.height - rect.height) / 2;
2608 if ( rect.y < 0 )
2609 rect.y = 0;
2610
2611 // leave margins on the sides
2612 rect.Deflate(SLIDER_MARGIN, 0);
2613 }
2614 else // vertical
2615 {
2616 // same as above but in other direction
2617 rect.width = 2*BORDER_THICKNESS;
2618 rect.x = rectOrig.x + (rectOrig.width - rect.width) / 2;
2619 if ( rect.x < 0 )
2620 rect.x = 0;
2621
2622 rect.Deflate(0, SLIDER_MARGIN);
2623 }
2624
2625 return rect;
2626}
2627
2628void wxMetalRenderer::DrawSliderShaft(wxDC& dc,
2629 const wxRect& rectOrig,
2630 wxOrientation orient,
2631 int flags,
2632 wxRect *rectShaft)
2633{
2634 if ( flags & wxCONTROL_FOCUSED )
2635 {
2636 DrawFocusRect(dc, rectOrig);
2637 }
2638
2639 wxRect rect = GetSliderShaftRect(rectOrig, orient);
2640
2641 if ( rectShaft )
2642 *rectShaft = rect;
2643
2644 DrawSunkenBorder(dc, &rect);
2645}
2646
2647void wxMetalRenderer::DrawSliderThumb(wxDC& dc,
2648 const wxRect& rect,
2649 wxOrientation orient,
2650 int flags)
2651{
2652 /*
2653 we are drawing a shape of this form
2654
2655 HHHHHHB <--- y
2656 H DB
2657 H DB
2658 H DB where H is hightlight colour
2659 H DB D dark grey
2660 H DB B black
2661 H DB
2662 H DB <--- y3
2663 H DB
2664 HDB
2665 B <--- y2
2666
2667 ^ ^ ^
2668 | | |
2669 x x3 x2
2670
2671 The interior of this shape is filled with the hatched brush if the thumb
2672 is pressed.
2673 */
2674
2675 DrawBackground(dc, wxNullColour, rect, flags);
2676
2677 bool transpose = orient == wxVERTICAL;
2678
2679 wxCoord x, y, x2, y2;
2680 if ( transpose )
2681 {
2682 x = rect.y;
2683 y = rect.x;
2684 x2 = rect.GetBottom();
2685 y2 = rect.GetRight();
2686 }
2687 else
2688 {
2689 x = rect.x;
2690 y = rect.y;
2691 x2 = rect.GetRight();
2692 y2 = rect.GetBottom();
2693 }
2694
2695 // the size of the pointed part of the thumb
2696 wxCoord sizeArrow = (transpose ? rect.height : rect.width) / 2;
2697
2698 wxCoord x3 = x + sizeArrow,
2699 y3 = y2 - sizeArrow;
2700
2701 dc.SetPen(m_penHighlight);
2702 DrawLine(dc, x, y, x2, y, transpose);
2703 DrawLine(dc, x, y + 1, x, y2 - sizeArrow, transpose);
2704 DrawLine(dc, x, y3, x3, y2, transpose);
2705
2706 dc.SetPen(m_penBlack);
2707 DrawLine(dc, x3, y2, x2, y3, transpose);
2708 DrawLine(dc, x2, y3, x2, y - 1, transpose);
2709
2710 dc.SetPen(m_penDarkGrey);
2711 DrawLine(dc, x3, y2 - 1, x2 - 1, y3, transpose);
2712 DrawLine(dc, x2 - 1, y3, x2 - 1, y, transpose);
2713
2714 if ( flags & wxCONTROL_PRESSED )
2715 {
2716 // TODO: MSW fills the entire area inside, not just the rect
2717 wxRect rectInt = rect;
2718 if ( transpose )
2719 rectInt.SetRight(y3);
2720 else
2721 rectInt.SetBottom(y3);
2722 rectInt.Deflate(2);
2723
2724#if !defined(__WXMGL__)
2725 static const char *stipple_xpm[] = {
2726 /* columns rows colors chars-per-pixel */
2727 "2 2 2 1",
2728 " c None",
2729 "w c white",
2730 /* pixels */
2731 "w ",
2732 " w",
2733 };
2734#else
2735 // VS: MGL can only do 8x8 stipple brushes
2736 static const char *stipple_xpm[] = {
2737 /* columns rows colors chars-per-pixel */
2738 "8 8 2 1",
2739 " c None",
2740 "w c white",
2741 /* pixels */
2742 "w w w w ",
2743 " w w w w",
2744 "w w w w ",
2745 " w w w w",
2746 "w w w w ",
2747 " w w w w",
2748 "w w w w ",
2749 " w w w w",
2750 };
2751#endif
2752 dc.SetBrush(wxBrush(stipple_xpm));
2753
2754 dc.SetTextForeground(wxSCHEME_COLOUR(m_scheme, SHADOW_HIGHLIGHT));
2755 dc.SetTextBackground(wxSCHEME_COLOUR(m_scheme, CONTROL));
2756 dc.SetPen(*wxTRANSPARENT_PEN);
2757 dc.DrawRectangle(rectInt);
2758 }
2759}
2760
2761void wxMetalRenderer::DrawSliderTicks(wxDC& dc,
2762 const wxRect& rect,
2763 const wxSize& sizeThumb,
2764 wxOrientation orient,
2765 int start,
2766 int end,
2767 int step,
2768 int flags)
2769{
2770 if ( end == start )
2771 {
2772 // empty slider?
2773 return;
2774 }
2775
2776 // the variable names correspond to horizontal case, but they can be used
2777 // for both orientations
2778 wxCoord x1, x2, y1, y2, len, widthThumb;
2779 if ( orient == wxHORIZONTAL )
2780 {
2781 x1 = rect.GetLeft();
2782 x2 = rect.GetRight();
2783
2784 // draw from bottom to top to leave one pixel space between the ticks
2785 // and the slider as Windows do
2786 y1 = rect.GetBottom();
2787 y2 = rect.GetTop();
2788
2789 len = rect.width;
2790
2791 widthThumb = sizeThumb.x;
2792 }
2793 else // vertical
2794 {
2795 x1 = rect.GetTop();
2796 x2 = rect.GetBottom();
2797
2798 y1 = rect.GetRight();
2799 y2 = rect.GetLeft();
2800
2801 len = rect.height;
2802
2803 widthThumb = sizeThumb.y;
2804 }
2805
2806 // the first tick should be positioned in such way that a thumb drawn in
2807 // the first position points down directly to it
2808 x1 += widthThumb / 2;
2809 x2 -= widthThumb / 2;
2810
2811 // this also means that we have slightly less space for the ticks in
2812 // between the first and the last
2813 len -= widthThumb;
2814
2815 dc.SetPen(m_penBlack);
2816
2817 int range = end - start;
2818 for ( int n = 0; n < range; n += step )
2819 {
2820 wxCoord x = x1 + (len*n) / range;
2821
2822 DrawLine(dc, x, y1, x, y2, orient == wxVERTICAL);
2823 }
2824
2825 // always draw the line at the end position
2826 DrawLine(dc, x2, y1, x2, y2, orient == wxVERTICAL);
2827}
2828
2829// ----------------------------------------------------------------------------
2830// menu and menubar
2831// ----------------------------------------------------------------------------
2832
2833// wxMetalMenuGeometryInfo: the wxMenuGeometryInfo used by wxMetalRenderer
2834class WXDLLEXPORT wxMetalMenuGeometryInfo : public wxMenuGeometryInfo
2835{
2836public:
2837 virtual wxSize GetSize() const { return m_size; }
2838
2839 wxCoord GetLabelOffset() const { return m_ofsLabel; }
2840 wxCoord GetAccelOffset() const { return m_ofsAccel; }
2841
2842 wxCoord GetItemHeight() const { return m_heightItem; }
2843
2844private:
2845 // the total size of the menu
2846 wxSize m_size;
2847
2848 // the offset of the start of the menu item label
2849 wxCoord m_ofsLabel;
2850
2851 // the offset of the start of the accel label
2852 wxCoord m_ofsAccel;
2853
2854 // the height of a normal (not separator) item
2855 wxCoord m_heightItem;
2856
2857 friend wxMenuGeometryInfo *
2858 wxMetalRenderer::GetMenuGeometry(wxWindow *, const wxMenu&) const;
2859};
2860
2861// FIXME: all constants are hardcoded but shouldn't be
2862static const wxCoord MENU_LEFT_MARGIN = 9;
2863static const wxCoord MENU_RIGHT_MARGIN = 18;
2864static const wxCoord MENU_VERT_MARGIN = 3;
2865
2866// the margin around bitmap/check marks (on each side)
2867static const wxCoord MENU_BMP_MARGIN = 2;
2868
2869// the margin between the labels and accel strings
2870static const wxCoord MENU_ACCEL_MARGIN = 8;
2871
2872// the separator height in pixels: in fact, strangely enough, the real height
2873// is 2 but Windows adds one extra pixel in the bottom margin, so take it into
2874// account here
2875static const wxCoord MENU_SEPARATOR_HEIGHT = 3;
2876
2877// the size of the standard checkmark bitmap
2878static const wxCoord MENU_CHECK_SIZE = 9;
2879
2880void wxMetalRenderer::DrawMenuBarItem(wxDC& dc,
2881 const wxRect& rectOrig,
2882 const wxString& label,
2883 int flags,
2884 int indexAccel)
2885{
2886 wxRect rect = rectOrig;
2887 rect.height--;
2888
2889 wxDCTextColourChanger colChanger(dc);
2890
2891 if ( flags & wxCONTROL_SELECTED )
2892 {
2893 colChanger.Set(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));
2894
2895 wxColour colBg = wxSCHEME_COLOUR(m_scheme, HIGHLIGHT);
2896 dc.SetBrush(wxBrush(colBg, wxSOLID));
2897 dc.SetPen(wxPen(colBg, 0, wxSOLID));
2898 dc.DrawRectangle(rect);
2899 }
2900
2901 // don't draw the focus rect around menu bar items
2902 DrawLabel(dc, label, rect, flags & ~wxCONTROL_FOCUSED,
2903 wxALIGN_CENTRE, indexAccel);
2904}
2905
2906void wxMetalRenderer::DrawMenuItem(wxDC& dc,
2907 wxCoord y,
2908 const wxMenuGeometryInfo& gi,
2909 const wxString& label,
2910 const wxString& accel,
2911 const wxBitmap& bitmap,
2912 int flags,
2913 int indexAccel)
2914{
2915 const wxMetalMenuGeometryInfo& geometryInfo =
2916 (const wxMetalMenuGeometryInfo&)gi;
2917
2918 wxRect rect;
2919 rect.x = 0;
2920 rect.y = y;
2921 rect.width = geometryInfo.GetSize().x;
2922 rect.height = geometryInfo.GetItemHeight();
2923
2924 // draw the selected item specially
2925 wxDCTextColourChanger colChanger(dc);
2926 if ( flags & wxCONTROL_SELECTED )
2927 {
2928 colChanger.Set(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));
2929
2930 wxColour colBg = wxSCHEME_COLOUR(m_scheme, HIGHLIGHT);
2931 dc.SetBrush(wxBrush(colBg, wxSOLID));
2932 dc.SetPen(wxPen(colBg, 0, wxSOLID));
2933 dc.DrawRectangle(rect);
2934 }
2935
2936 // draw the bitmap: use the bitmap provided or the standard checkmark for
2937 // the checkable items
2938 wxBitmap bmp = bitmap;
2939 if ( !bmp.Ok() && (flags & wxCONTROL_CHECKED) )
2940 {
2941 bmp = GetIndicator(IndicatorType_Menu, flags);
2942 }
2943
2944 if ( bmp.Ok() )
2945 {
2946 rect.SetRight(geometryInfo.GetLabelOffset());
2947 wxControlRenderer::DrawBitmap(dc, bmp, rect);
2948 }
2949
2950 // draw the label
2951 rect.x = geometryInfo.GetLabelOffset();
2952 rect.SetRight(geometryInfo.GetAccelOffset());
2953
2954 DrawLabel(dc, label, rect, flags, wxALIGN_CENTRE_VERTICAL, indexAccel);
2955
2956 // draw the accel string
2957 rect.x = geometryInfo.GetAccelOffset();
2958 rect.SetRight(geometryInfo.GetSize().x);
2959
2960 // NB: no accel index here
2961 DrawLabel(dc, accel, rect, flags, wxALIGN_CENTRE_VERTICAL);
2962
2963 // draw the submenu indicator
2964 if ( flags & wxCONTROL_ISSUBMENU )
2965 {
2966 rect.x = geometryInfo.GetSize().x - MENU_RIGHT_MARGIN;
2967 rect.width = MENU_RIGHT_MARGIN;
2968
2969 wxArrowStyle arrowStyle;
2970 if ( flags & wxCONTROL_DISABLED )
2971 arrowStyle = flags & wxCONTROL_SELECTED ? Arrow_InversedDisabled
2972 : Arrow_Disabled;
2973 else if ( flags & wxCONTROL_SELECTED )
2974 arrowStyle = Arrow_Inversed;
2975 else
2976 arrowStyle = Arrow_Normal;
2977
2978 DrawArrow(dc, rect, Arrow_Right, arrowStyle);
2979 }
2980}
2981
2982void wxMetalRenderer::DrawMenuSeparator(wxDC& dc,
2983 wxCoord y,
2984 const wxMenuGeometryInfo& geomInfo)
2985{
2986 DrawHorizontalLine(dc, y + MENU_VERT_MARGIN, 0, geomInfo.GetSize().x);
2987}
2988
2989wxSize wxMetalRenderer::GetMenuBarItemSize(const wxSize& sizeText) const
2990{
2991 wxSize size = sizeText;
2992
2993 // FIXME: menubar height is configurable under Windows
2994 size.x += 12;
2995 size.y += 6;
2996
2997 return size;
2998}
2999
3000wxMenuGeometryInfo *wxMetalRenderer::GetMenuGeometry(wxWindow *win,
3001 const wxMenu& menu) const
3002{
3003 // prepare the dc: for now we draw all the items with the system font
3004 wxClientDC dc(win);
3005 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
3006
3007 // the height of a normal item
3008 wxCoord heightText = dc.GetCharHeight();
3009
3010 // the total height
3011 wxCoord height = 0;
3012
3013 // the max length of label and accel strings: the menu width is the sum of
3014 // them, even if they're for different items (as the accels should be
3015 // aligned)
3016 //
3017 // the max length of the bitmap is never 0 as Windows always leaves enough
3018 // space for a check mark indicator
3019 wxCoord widthLabelMax = 0,
3020 widthAccelMax = 0,
3021 widthBmpMax = MENU_LEFT_MARGIN;
3022
3023 for ( wxMenuItemList::Node *node = menu.GetMenuItems().GetFirst();
3024 node;
3025 node = node->GetNext() )
3026 {
3027 // height of this item
3028 wxCoord h;
3029
3030 wxMenuItem *item = node->GetData();
3031 if ( item->IsSeparator() )
3032 {
3033 h = MENU_SEPARATOR_HEIGHT;
3034 }
3035 else // not separator
3036 {
3037 h = heightText;
3038
3039 wxCoord widthLabel;
3040 dc.GetTextExtent(item->GetLabel(), &widthLabel, NULL);
3041 if ( widthLabel > widthLabelMax )
3042 {
3043 widthLabelMax = widthLabel;
3044 }
3045
3046 wxCoord widthAccel;
3047 dc.GetTextExtent(item->GetAccelString(), &widthAccel, NULL);
3048 if ( widthAccel > widthAccelMax )
3049 {
3050 widthAccelMax = widthAccel;
3051 }
3052
3053 const wxBitmap& bmp = item->GetBitmap();
3054 if ( bmp.Ok() )
3055 {
3056 wxCoord widthBmp = bmp.GetWidth();
3057 if ( widthBmp > widthBmpMax )
3058 widthBmpMax = widthBmp;
3059 }
3060 //else if ( item->IsCheckable() ): no need to check for this as
3061 // MENU_LEFT_MARGIN is big enough to show the check mark
3062 }
3063
3064 h += 2*MENU_VERT_MARGIN;
3065
3066 // remember the item position and height
3067 item->SetGeometry(height, h);
3068
3069 height += h;
3070 }
3071
3072 // bundle the metrics into a struct and return it
3073 wxMetalMenuGeometryInfo *gi = new wxMetalMenuGeometryInfo;
3074
3075 gi->m_ofsLabel = widthBmpMax + 2*MENU_BMP_MARGIN;
3076 gi->m_ofsAccel = gi->m_ofsLabel + widthLabelMax;
3077 if ( widthAccelMax > 0 )
3078 {
3079 // if we actually have any accesl, add a margin
3080 gi->m_ofsAccel += MENU_ACCEL_MARGIN;
3081 }
3082
3083 gi->m_heightItem = heightText + 2*MENU_VERT_MARGIN;
3084
3085 gi->m_size.x = gi->m_ofsAccel + widthAccelMax + MENU_RIGHT_MARGIN;
3086 gi->m_size.y = height;
3087
3088 return gi;
3089}
3090
3091// ----------------------------------------------------------------------------
3092// status bar
3093// ----------------------------------------------------------------------------
3094
3095static const wxCoord STATBAR_BORDER_X = 2;
3096static const wxCoord STATBAR_BORDER_Y = 2;
3097
3098wxSize wxMetalRenderer::GetStatusBarBorders(wxCoord *borderBetweenFields) const
3099{
3100 if ( borderBetweenFields )
3101 *borderBetweenFields = 2;
3102
3103 return wxSize(STATBAR_BORDER_X, STATBAR_BORDER_Y);
3104}
3105
3106void wxMetalRenderer::DrawStatusField(wxDC& dc,
3107 const wxRect& rect,
3108 const wxString& label,
3109 int flags)
3110{
3111 wxRect rectIn;
3112
3113 if ( flags & wxCONTROL_ISDEFAULT )
3114 {
3115 // draw the size grip: it is a normal rect except that in the lower
3116 // right corner we have several bands which may be used for dragging
3117 // the status bar corner
3118 //
3119 // each band consists of 4 stripes: m_penHighlight, double
3120 // m_penDarkGrey and transparent one
3121 wxCoord x2 = rect.GetRight(),
3122 y2 = rect.GetBottom();
3123
3124 // draw the upper left part of the rect normally
3125 dc.SetPen(m_penDarkGrey);
3126 dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), y2);
3127 dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), x2, rect.GetTop());
3128
3129 // draw the grey stripes of the grip
3130 size_t n;
3131 wxCoord ofs = WIDTH_STATUSBAR_GRIP_BAND - 1;
3132 for ( n = 0; n < NUM_STATUSBAR_GRIP_BANDS; n++, ofs += WIDTH_STATUSBAR_GRIP_BAND )
3133 {
3134 dc.DrawLine(x2 - ofs + 1, y2 - 1, x2, y2 - ofs);
3135 dc.DrawLine(x2 - ofs, y2 - 1, x2, y2 - ofs - 1);
3136 }
3137
3138 // draw the white stripes
3139 dc.SetPen(m_penHighlight);
3140 ofs = WIDTH_STATUSBAR_GRIP_BAND + 1;
3141 for ( n = 0; n < NUM_STATUSBAR_GRIP_BANDS; n++, ofs += WIDTH_STATUSBAR_GRIP_BAND )
3142 {
3143 dc.DrawLine(x2 - ofs + 1, y2 - 1, x2, y2 - ofs);
3144 }
3145
3146 // draw the remaining rect boundaries
3147 ofs -= WIDTH_STATUSBAR_GRIP_BAND;
3148 dc.DrawLine(x2, rect.GetTop(), x2, y2 - ofs + 1);
3149 dc.DrawLine(rect.GetLeft(), y2, x2 - ofs + 1, y2);
3150
3151 rectIn = rect;
3152 rectIn.Deflate(1);
3153
3154 rectIn.width -= STATUSBAR_GRIP_SIZE;
3155 }
3156 else // normal pane
3157 {
3158 DrawBorder(dc, wxBORDER_STATIC, rect, flags, &rectIn);
3159 }
3160
3161 rectIn.Deflate(STATBAR_BORDER_X, STATBAR_BORDER_Y);
3162
3163 wxDCClipper clipper(dc, rectIn);
3164 DrawLabel(dc, label, rectIn, flags, wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
3165}
3166
3167// ----------------------------------------------------------------------------
3168// combobox
3169// ----------------------------------------------------------------------------
3170
3171void wxMetalRenderer::GetComboBitmaps(wxBitmap *bmpNormal,
3172 wxBitmap *bmpFocus,
3173 wxBitmap *bmpPressed,
3174 wxBitmap *bmpDisabled)
3175{
3176 static const wxCoord widthCombo = 16;
3177 static const wxCoord heightCombo = 17;
3178
3179 wxMemoryDC dcMem;
3180
3181 if ( bmpNormal )
3182 {
3183 bmpNormal->Create(widthCombo, heightCombo);
3184 dcMem.SelectObject(*bmpNormal);
3185 DrawArrowButton(dcMem, wxRect(0, 0, widthCombo, heightCombo),
3186 Arrow_Down, Arrow_Normal);
3187 }
3188
3189 if ( bmpPressed )
3190 {
3191 bmpPressed->Create(widthCombo, heightCombo);
3192 dcMem.SelectObject(*bmpPressed);
3193 DrawArrowButton(dcMem, wxRect(0, 0, widthCombo, heightCombo),
3194 Arrow_Down, Arrow_Pressed);
3195 }
3196
3197 if ( bmpDisabled )
3198 {
3199 bmpDisabled->Create(widthCombo, heightCombo);
3200 dcMem.SelectObject(*bmpDisabled);
3201 DrawArrowButton(dcMem, wxRect(0, 0, widthCombo, heightCombo),
3202 Arrow_Down, Arrow_Disabled);
3203 }
3204}
3205
3206// ----------------------------------------------------------------------------
3207// background
3208// ----------------------------------------------------------------------------
3209
3210void wxMetalRenderer::DoDrawBackground(wxDC& dc,
3211 const wxColour& col,
3212 const wxRect& rect)
3213{
3214 if (col == wxTheme::Get()->GetColourScheme()->Get( wxColourScheme::CONTROL ))
3215 {
3216 for (int y = rect.y; y < rect.height+rect.y; y++)
3217 {
3218 int intens = 230 + 80 * (rect.y-y) / rect.height;
3219 dc.SetPen( wxPen( wxColour(intens,intens,intens), 1, wxSOLID ) );
3220 dc.DrawLine( rect.x, y, rect.x+rect.width, y );
3221 }
3222 }
3223 else
3224 {
3225 wxBrush brush(col, wxSOLID);
3226 dc.SetBrush(brush);
3227 dc.SetPen(*wxTRANSPARENT_PEN);
3228 dc.DrawRectangle(rect);
3229 }
3230}
3231
3232void wxMetalRenderer::DrawBackground(wxDC& dc,
3233 const wxColour& col,
3234 const wxRect& rect,
3235 int flags)
3236{
3237 // just fill it with the given or default bg colour
3238 wxColour colBg = col.Ok() ? col : wxSCHEME_COLOUR(m_scheme, CONTROL);
3239 DoDrawBackground(dc, colBg, rect);
3240}
3241
3242// ----------------------------------------------------------------------------
3243// scrollbar
3244// ----------------------------------------------------------------------------
3245
3246void wxMetalRenderer::DrawArrow(wxDC& dc,
3247 wxDirection dir,
3248 const wxRect& rect,
3249 int flags)
3250{
3251 // get the bitmap for this arrow
3252 wxArrowDirection arrowDir;
3253 switch ( dir )
3254 {
3255 case wxLEFT: arrowDir = Arrow_Left; break;
3256 case wxRIGHT: arrowDir = Arrow_Right; break;
3257 case wxUP: arrowDir = Arrow_Up; break;
3258 case wxDOWN: arrowDir = Arrow_Down; break;
3259
3260 default:
3261 wxFAIL_MSG(_T("unknown arrow direction"));
3262 return;
3263 }
3264
3265 wxArrowStyle arrowStyle;
3266 if ( flags & wxCONTROL_PRESSED )
3267 {
3268 // can't be pressed and disabled
3269 arrowStyle = Arrow_Pressed;
3270 }
3271 else
3272 {
3273 arrowStyle = flags & wxCONTROL_DISABLED ? Arrow_Disabled : Arrow_Normal;
3274 }
3275
3276 DrawArrowButton(dc, rect, arrowDir, arrowStyle);
3277}
3278
3279void wxMetalRenderer::DrawArrow(wxDC& dc,
3280 const wxRect& rect,
3281 wxArrowDirection arrowDir,
3282 wxArrowStyle arrowStyle)
3283{
3284 const wxBitmap& bmp = m_bmpArrows[arrowStyle][arrowDir];
3285
3286 // under Windows the arrows always have the same size so just centre it in
3287 // the provided rectangle
3288 wxCoord x = rect.x + (rect.width - bmp.GetWidth()) / 2,
3289 y = rect.y + (rect.height - bmp.GetHeight()) / 2;
3290
3291 // Windows does it like this...
3292 if ( arrowDir == Arrow_Left )
3293 x--;
3294
3295 // draw it
3296 dc.DrawBitmap(bmp, x, y, TRUE /* use mask */);
3297}
3298
3299void wxMetalRenderer::DrawArrowButton(wxDC& dc,
3300 const wxRect& rectAll,
3301 wxArrowDirection arrowDir,
3302 wxArrowStyle arrowStyle)
3303{
3304 wxRect rect = rectAll;
3305 DoDrawBackground(dc, wxSCHEME_COLOUR(m_scheme, CONTROL), rect);
3306 DrawArrowBorder(dc, &rect, arrowStyle == Arrow_Pressed);
3307 DrawArrow(dc, rect, arrowDir, arrowStyle);
3308}
3309
3310void wxMetalRenderer::DrawScrollbarThumb(wxDC& dc,
3311 wxOrientation orient,
3312 const wxRect& rect,
3313 int flags)
3314{
3315 // we don't use the flags, the thumb never changes appearance
3316 wxRect rectThumb = rect;
3317 DrawArrowBorder(dc, &rectThumb);
3318 DrawBackground(dc, wxNullColour, rectThumb);
3319}
3320
3321void wxMetalRenderer::DrawScrollbarShaft(wxDC& dc,
3322 wxOrientation orient,
3323 const wxRect& rectBar,
3324 int flags)
3325{
3326 wxColourScheme::StdColour col = flags & wxCONTROL_PRESSED
3327 ? wxColourScheme::SCROLLBAR_PRESSED
3328 : wxColourScheme::SCROLLBAR;
3329 DoDrawBackground(dc, m_scheme->Get(col), rectBar);
3330}
3331
3332void wxMetalRenderer::DrawScrollCorner(wxDC& dc, const wxRect& rect)
3333{
3334 DoDrawBackground(dc, wxSCHEME_COLOUR(m_scheme, CONTROL), rect);
3335}
3336
3337wxRect wxMetalRenderer::GetScrollbarRect(const wxScrollBar *scrollbar,
3338 wxScrollBar::Element elem,
3339 int thumbPos) const
3340{
3341 return StandardGetScrollbarRect(scrollbar, elem,
3342 thumbPos, m_sizeScrollbarArrow);
3343}
3344
3345wxCoord wxMetalRenderer::GetScrollbarSize(const wxScrollBar *scrollbar)
3346{
3347 return StandardScrollBarSize(scrollbar, m_sizeScrollbarArrow);
3348}
3349
3350wxHitTest wxMetalRenderer::HitTestScrollbar(const wxScrollBar *scrollbar,
3351 const wxPoint& pt) const
3352{
3353 return StandardHitTestScrollbar(scrollbar, pt, m_sizeScrollbarArrow);
3354}
3355
3356wxCoord wxMetalRenderer::ScrollbarToPixel(const wxScrollBar *scrollbar,
3357 int thumbPos)
3358{
3359 return StandardScrollbarToPixel(scrollbar, thumbPos, m_sizeScrollbarArrow);
3360}
3361
3362int wxMetalRenderer::PixelToScrollbar(const wxScrollBar *scrollbar,
3363 wxCoord coord)
3364{
3365 return StandardPixelToScrollbar(scrollbar, coord, m_sizeScrollbarArrow);
3366}
3367
3368// ----------------------------------------------------------------------------
3369// top level windows
3370// ----------------------------------------------------------------------------
3371
3372int wxMetalRenderer::HitTestFrame(const wxRect& rect, const wxPoint& pt, int flags) const
3373{
3374 wxRect client = GetFrameClientArea(rect, flags);
3375
3376 if ( client.Inside(pt) )
3377 return wxHT_TOPLEVEL_CLIENT_AREA;
3378
3379 if ( flags & wxTOPLEVEL_TITLEBAR )
3380 {
3381 wxRect client = GetFrameClientArea(rect, flags & ~wxTOPLEVEL_TITLEBAR);
3382
3383 if ( flags & wxTOPLEVEL_ICON )
3384 {
3385 if ( wxRect(client.GetPosition(), GetFrameIconSize()).Inside(pt) )
3386 return wxHT_TOPLEVEL_ICON;
3387 }
3388
3389 wxRect btnRect(client.GetRight() - 2 - FRAME_BUTTON_WIDTH,
3390 client.GetTop() + (FRAME_TITLEBAR_HEIGHT-FRAME_BUTTON_HEIGHT)/2,
3391 FRAME_BUTTON_WIDTH, FRAME_BUTTON_HEIGHT);
3392
3393 if ( flags & wxTOPLEVEL_BUTTON_CLOSE )
3394 {
3395 if ( btnRect.Inside(pt) )
3396 return wxHT_TOPLEVEL_BUTTON_CLOSE;
3397 btnRect.x -= FRAME_BUTTON_WIDTH + 2;
3398 }
3399 if ( flags & wxTOPLEVEL_BUTTON_MAXIMIZE )
3400 {
3401 if ( btnRect.Inside(pt) )
3402 return wxHT_TOPLEVEL_BUTTON_MAXIMIZE;
3403 btnRect.x -= FRAME_BUTTON_WIDTH;
3404 }
3405 if ( flags & wxTOPLEVEL_BUTTON_RESTORE )
3406 {
3407 if ( btnRect.Inside(pt) )
3408 return wxHT_TOPLEVEL_BUTTON_RESTORE;
3409 btnRect.x -= FRAME_BUTTON_WIDTH;
3410 }
3411 if ( flags & wxTOPLEVEL_BUTTON_ICONIZE )
3412 {
3413 if ( btnRect.Inside(pt) )
3414 return wxHT_TOPLEVEL_BUTTON_ICONIZE;
3415 btnRect.x -= FRAME_BUTTON_WIDTH;
3416 }
3417 if ( flags & wxTOPLEVEL_BUTTON_HELP )
3418 {
3419 if ( btnRect.Inside(pt) )
3420 return wxHT_TOPLEVEL_BUTTON_HELP;
3421 btnRect.x -= FRAME_BUTTON_WIDTH;
3422 }
3423
3424 if ( pt.y >= client.y && pt.y < client.y + FRAME_TITLEBAR_HEIGHT )
3425 return wxHT_TOPLEVEL_TITLEBAR;
3426 }
3427
3428 if ( (flags & wxTOPLEVEL_BORDER) && !(flags & wxTOPLEVEL_MAXIMIZED) )
3429 {
3430 // we are certainly at one of borders, lets decide which one:
3431
3432 int border = 0;
3433 // dirty trick, relies on the way wxHT_TOPLEVEL_XXX are defined!
3434 if ( pt.x < client.x )
3435 border |= wxHT_TOPLEVEL_BORDER_W;
3436 else if ( pt.x >= client.width + client.x )
3437 border |= wxHT_TOPLEVEL_BORDER_E;
3438 if ( pt.y < client.y )
3439 border |= wxHT_TOPLEVEL_BORDER_N;
3440 else if ( pt.y >= client.height + client.y )
3441 border |= wxHT_TOPLEVEL_BORDER_S;
3442 return border;
3443 }
3444
3445 return wxHT_NOWHERE;
3446}
3447
3448void wxMetalRenderer::DrawFrameTitleBar(wxDC& dc,
3449 const wxRect& rect,
3450 const wxString& title,
3451 const wxIcon& icon,
3452 int flags,
3453 int specialButton,
3454 int specialButtonFlags)
3455{
3456 if ( (flags & wxTOPLEVEL_BORDER) && !(flags & wxTOPLEVEL_MAXIMIZED) )
3457 {
3458 DrawFrameBorder(dc, rect, flags);
3459 }
3460 if ( flags & wxTOPLEVEL_TITLEBAR )
3461 {
3462 DrawFrameBackground(dc, rect, flags);
3463 if ( flags & wxTOPLEVEL_ICON )
3464 DrawFrameIcon(dc, rect, icon, flags);
3465 DrawFrameTitle(dc, rect, title, flags);
3466
3467 wxRect client = GetFrameClientArea(rect, flags & ~wxTOPLEVEL_TITLEBAR);
3468 wxCoord x,y;
3469 x = client.GetRight() - 2 - FRAME_BUTTON_WIDTH;
3470 y = client.GetTop() + (FRAME_TITLEBAR_HEIGHT-FRAME_BUTTON_HEIGHT)/2;
3471
3472 if ( flags & wxTOPLEVEL_BUTTON_CLOSE )
3473 {
3474 DrawFrameButton(dc, x, y, wxTOPLEVEL_BUTTON_CLOSE,
3475 (specialButton == wxTOPLEVEL_BUTTON_CLOSE) ?
3476 specialButtonFlags : 0);
3477 x -= FRAME_BUTTON_WIDTH + 2;
3478 }
3479 if ( flags & wxTOPLEVEL_BUTTON_MAXIMIZE )
3480 {
3481 DrawFrameButton(dc, x, y, wxTOPLEVEL_BUTTON_MAXIMIZE,
3482 (specialButton == wxTOPLEVEL_BUTTON_MAXIMIZE) ?
3483 specialButtonFlags : 0);
3484 x -= FRAME_BUTTON_WIDTH;
3485 }
3486 if ( flags & wxTOPLEVEL_BUTTON_RESTORE )
3487 {
3488 DrawFrameButton(dc, x, y, wxTOPLEVEL_BUTTON_RESTORE,
3489 (specialButton == wxTOPLEVEL_BUTTON_RESTORE) ?
3490 specialButtonFlags : 0);
3491 x -= FRAME_BUTTON_WIDTH;
3492 }
3493 if ( flags & wxTOPLEVEL_BUTTON_ICONIZE )
3494 {
3495 DrawFrameButton(dc, x, y, wxTOPLEVEL_BUTTON_ICONIZE,
3496 (specialButton == wxTOPLEVEL_BUTTON_ICONIZE) ?
3497 specialButtonFlags : 0);
3498 x -= FRAME_BUTTON_WIDTH;
3499 }
3500 if ( flags & wxTOPLEVEL_BUTTON_HELP )
3501 {
3502 DrawFrameButton(dc, x, y, wxTOPLEVEL_BUTTON_HELP,
3503 (specialButton == wxTOPLEVEL_BUTTON_HELP) ?
3504 specialButtonFlags : 0);
3505 x -= FRAME_BUTTON_WIDTH;
3506 }
3507 }
3508}
3509
3510void wxMetalRenderer::DrawFrameBorder(wxDC& dc,
3511 const wxRect& rect,
3512 int flags)
3513{
3514 if ( !(flags & wxTOPLEVEL_BORDER) ) return;
3515
3516 wxRect r(rect);
3517
3518 DrawShadedRect(dc, &r, m_penLightGrey, m_penBlack);
3519 DrawShadedRect(dc, &r, m_penHighlight, m_penDarkGrey);
3520 DrawShadedRect(dc, &r, m_penLightGrey, m_penLightGrey);
3521 if ( flags & wxTOPLEVEL_RESIZEABLE )
3522 DrawShadedRect(dc, &r, m_penLightGrey, m_penLightGrey);
3523}
3524
3525void wxMetalRenderer::DrawFrameBackground(wxDC& dc,
3526 const wxRect& rect,
3527 int flags)
3528{
3529 if ( !(flags & wxTOPLEVEL_TITLEBAR) ) return;
3530
3531 wxColour col = (flags & wxTOPLEVEL_ACTIVE) ?
3532 wxSCHEME_COLOUR(m_scheme, TITLEBAR_ACTIVE) :
3533 wxSCHEME_COLOUR(m_scheme, TITLEBAR);
3534
3535 wxRect r = GetFrameClientArea(rect, flags & ~wxTOPLEVEL_TITLEBAR);
3536 r.height = FRAME_TITLEBAR_HEIGHT;
3537
3538 DrawBackground(dc, col, r);
3539}
3540
3541void wxMetalRenderer::DrawFrameTitle(wxDC& dc,
3542 const wxRect& rect,
3543 const wxString& title,
3544 int flags)
3545{
3546 wxColour col = (flags & wxTOPLEVEL_ACTIVE) ?
3547 wxSCHEME_COLOUR(m_scheme, TITLEBAR_ACTIVE_TEXT) :
3548 wxSCHEME_COLOUR(m_scheme, TITLEBAR_TEXT);
3549
3550 wxRect r = GetFrameClientArea(rect, flags & ~wxTOPLEVEL_TITLEBAR);
3551 r.height = FRAME_TITLEBAR_HEIGHT;
3552 if ( flags & wxTOPLEVEL_ICON )
3553 {
3554 r.x += FRAME_TITLEBAR_HEIGHT;
3555 r.width -= FRAME_TITLEBAR_HEIGHT + 2;
3556 }
3557 else
3558 {
3559 r.x += 1;
3560 r.width -= 3;
3561 }
3562
3563 if ( flags & wxTOPLEVEL_BUTTON_CLOSE )
3564 r.width -= FRAME_BUTTON_WIDTH + 2;
3565 if ( flags & wxTOPLEVEL_BUTTON_MAXIMIZE )
3566 r.width -= FRAME_BUTTON_WIDTH;
3567 if ( flags & wxTOPLEVEL_BUTTON_RESTORE )
3568 r.width -= FRAME_BUTTON_WIDTH;
3569 if ( flags & wxTOPLEVEL_BUTTON_ICONIZE )
3570 r.width -= FRAME_BUTTON_WIDTH;
3571 if ( flags & wxTOPLEVEL_BUTTON_HELP )
3572 r.width -= FRAME_BUTTON_WIDTH;
3573
3574 dc.SetFont(m_titlebarFont);
3575 dc.SetTextForeground(col);
3576
3577 wxCoord textW;
3578 dc.GetTextExtent(title, &textW, NULL);
3579 if ( textW > r.width )
3580 {
3581 // text is too big, let's shorten it and add "..." after it:
3582 size_t len = title.length();
3583 wxCoord WSoFar, letterW;
3584
3585 dc.GetTextExtent(wxT("..."), &WSoFar, NULL);
3586 if ( WSoFar > r.width )
3587 {
3588 // not enough space to draw anything
3589 return;
3590 }
3591
3592 wxString s;
3593 s.Alloc(len);
3594 for (size_t i = 0; i < len; i++)
3595 {
3596 dc.GetTextExtent(title[i], &letterW, NULL);
3597 if ( letterW + WSoFar > r.width )
3598 break;
3599 WSoFar += letterW;
3600 s << title[i];
3601 }
3602 s << wxT("...");
3603 dc.DrawLabel(s, wxNullBitmap, r,
3604 wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
3605 }
3606 else
3607 dc.DrawLabel(title, wxNullBitmap, r,
3608 wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
3609}
3610
3611void wxMetalRenderer::DrawFrameIcon(wxDC& dc,
3612 const wxRect& rect,
3613 const wxIcon& icon,
3614 int flags)
3615{
3616 if ( icon.Ok() )
3617 {
3618 wxRect r = GetFrameClientArea(rect, flags & ~wxTOPLEVEL_TITLEBAR);
3619 dc.DrawIcon(icon, r.x, r.y);
3620 }
3621}
3622
3623void wxMetalRenderer::DrawFrameButton(wxDC& dc,
3624 wxCoord x, wxCoord y,
3625 int button,
3626 int flags)
3627{
3628 wxRect r(x, y, FRAME_BUTTON_WIDTH, FRAME_BUTTON_HEIGHT);
3629
3630 size_t idx = 0;
3631 switch (button)
3632 {
3633 case wxTOPLEVEL_BUTTON_CLOSE: idx = FrameButton_Close; break;
3634 case wxTOPLEVEL_BUTTON_MAXIMIZE: idx = FrameButton_Maximize; break;
3635 case wxTOPLEVEL_BUTTON_ICONIZE: idx = FrameButton_Minimize; break;
3636 case wxTOPLEVEL_BUTTON_RESTORE: idx = FrameButton_Restore; break;
3637 case wxTOPLEVEL_BUTTON_HELP: idx = FrameButton_Help; break;
3638 default:
3639 wxFAIL_MSG(wxT("incorrect button specification"));
3640 }
3641
3642 if ( flags & wxCONTROL_PRESSED )
3643 {
3644 DrawShadedRect(dc, &r, m_penBlack, m_penHighlight);
3645 DrawShadedRect(dc, &r, m_penDarkGrey, m_penLightGrey);
3646 DrawBackground(dc, wxSCHEME_COLOUR(m_scheme, CONTROL), r);
3647 dc.DrawBitmap(m_bmpFrameButtons[idx], r.x+1, r.y+1, TRUE);
3648 }
3649 else
3650 {
3651 DrawShadedRect(dc, &r, m_penHighlight, m_penBlack);
3652 DrawShadedRect(dc, &r, m_penLightGrey, m_penDarkGrey);
3653 DrawBackground(dc, wxSCHEME_COLOUR(m_scheme, CONTROL), r);
3654 dc.DrawBitmap(m_bmpFrameButtons[idx], r.x, r.y, TRUE);
3655 }
3656}
3657
3658
3659wxRect wxMetalRenderer::GetFrameClientArea(const wxRect& rect,
3660 int flags) const
3661{
3662 wxRect r(rect);
3663
3664 if ( (flags & wxTOPLEVEL_BORDER) && !(flags & wxTOPLEVEL_MAXIMIZED) )
3665 {
3666 int border = (flags & wxTOPLEVEL_RESIZEABLE) ?
3667 RESIZEABLE_FRAME_BORDER_THICKNESS :
3668 FRAME_BORDER_THICKNESS;
3669 r.Inflate(-border);
3670 }
3671 if ( flags & wxTOPLEVEL_TITLEBAR )
3672 {
3673 r.y += FRAME_TITLEBAR_HEIGHT;
3674 r.height -= FRAME_TITLEBAR_HEIGHT;
3675 }
3676
3677 return r;
3678}
3679
3680wxSize wxMetalRenderer::GetFrameTotalSize(const wxSize& clientSize,
3681 int flags) const
3682{
3683 wxSize s(clientSize);
3684
3685 if ( (flags & wxTOPLEVEL_BORDER) && !(flags & wxTOPLEVEL_MAXIMIZED) )
3686 {
3687 int border = (flags & wxTOPLEVEL_RESIZEABLE) ?
3688 RESIZEABLE_FRAME_BORDER_THICKNESS :
3689 FRAME_BORDER_THICKNESS;
3690 s.x += 2*border;
3691 s.y += 2*border;
3692 }
3693 if ( flags & wxTOPLEVEL_TITLEBAR )
3694 s.y += FRAME_TITLEBAR_HEIGHT;
3695
3696 return s;
3697}
3698
3699wxSize wxMetalRenderer::GetFrameMinSize(int flags) const
3700{
3701 wxSize s(0, 0);
3702
3703 if ( (flags & wxTOPLEVEL_BORDER) && !(flags & wxTOPLEVEL_MAXIMIZED) )
3704 {
3705 int border = (flags & wxTOPLEVEL_RESIZEABLE) ?
3706 RESIZEABLE_FRAME_BORDER_THICKNESS :
3707 FRAME_BORDER_THICKNESS;
3708 s.x += 2*border;
3709 s.y += 2*border;
3710 }
3711
3712 if ( flags & wxTOPLEVEL_TITLEBAR )
3713 {
3714 s.y += FRAME_TITLEBAR_HEIGHT;
3715
3716 if ( flags & wxTOPLEVEL_ICON )
3717 s.x += FRAME_TITLEBAR_HEIGHT + 2;
3718 if ( flags & wxTOPLEVEL_BUTTON_CLOSE )
3719 s.x += FRAME_BUTTON_WIDTH + 2;
3720 if ( flags & wxTOPLEVEL_BUTTON_MAXIMIZE )
3721 s.x += FRAME_BUTTON_WIDTH;
3722 if ( flags & wxTOPLEVEL_BUTTON_RESTORE )
3723 s.x += FRAME_BUTTON_WIDTH;
3724 if ( flags & wxTOPLEVEL_BUTTON_ICONIZE )
3725 s.x += FRAME_BUTTON_WIDTH;
3726 if ( flags & wxTOPLEVEL_BUTTON_HELP )
3727 s.x += FRAME_BUTTON_WIDTH;
3728 }
3729
3730 return s;
3731}
3732
3733wxSize wxMetalRenderer::GetFrameIconSize() const
3734{
3735 return wxSize(16, 16);
3736}
3737
3738
3739// ----------------------------------------------------------------------------
3740// standard icons
3741// ----------------------------------------------------------------------------
3742
3743static char *error_xpm[]={
3744"32 32 5 1",
3745". c None",
3746"# c #800000",
3747"b c #808080",
3748"a c #ff0000",
3749"c c #ffffff",
3750"...........########.............",
3751"........###aaaaaaaa###..........",
3752".......#aaaaaaaaaaaaaa#.........",
3753".....##aaaaaaaaaaaaaaaa##.......",
3754"....#aaaaaaaaaaaaaaaaaaaa#......",
3755"...#aaaaaaaaaaaaaaaaaaaaaa#.....",
3756"...#aaaaaaaaaaaaaaaaaaaaaa#b....",
3757"..#aaaaaacaaaaaaaaaacaaaaaa#b...",
3758".#aaaaaacccaaaaaaaacccaaaaaa#...",
3759".#aaaaacccccaaaaaacccccaaaaa#b..",
3760".#aaaaaacccccaaaacccccaaaaaa#bb.",
3761"#aaaaaaaacccccaacccccaaaaaaaa#b.",
3762"#aaaaaaaaaccccccccccaaaaaaaaa#b.",
3763"#aaaaaaaaaaccccccccaaaaaaaaaa#bb",
3764"#aaaaaaaaaaaccccccaaaaaaaaaaa#bb",
3765"#aaaaaaaaaaaccccccaaaaaaaaaaa#bb",
3766"#aaaaaaaaaaccccccccaaaaaaaaaa#bb",
3767"#aaaaaaaaaccccccccccaaaaaaaaa#bb",
3768"#aaaaaaaacccccaacccccaaaaaaaa#bb",
3769".#aaaaaacccccaaaacccccaaaaaa#bbb",
3770".#aaaaacccccaaaaaacccccaaaaa#bbb",
3771".#aaaaaacccaaaaaaaacccaaaaaa#bb.",
3772"..#aaaaaacaaaaaaaaaacaaaaaa#bbb.",
3773"...#aaaaaaaaaaaaaaaaaaaaaa#bbbb.",
3774"...#aaaaaaaaaaaaaaaaaaaaaa#bbb..",
3775"....#aaaaaaaaaaaaaaaaaaaa#bbb...",
3776".....##aaaaaaaaaaaaaaaa##bbbb...",
3777"......b#aaaaaaaaaaaaaa#bbbbb....",
3778".......b###aaaaaaaa###bbbbb.....",
3779".........bb########bbbbbb.......",
3780"..........bbbbbbbbbbbbbb........",
3781".............bbbbbbbb..........."};
3782
3783static char *info_xpm[]={
3784"32 32 6 1",
3785". c None",
3786"d c #000000",
3787"c c #0000ff",
3788"# c #808080",
3789"a c #c0c0c0",
3790"b c #ffffff",
3791"...........########.............",
3792"........###abbbbbba###..........",
3793"......##abbbbbbbbbbbba##........",
3794".....#abbbbbbbbbbbbbbbba#.......",
3795"....#bbbbbbbaccccabbbbbbbd......",
3796"...#bbbbbbbbccccccbbbbbbbbd.....",
3797"..#bbbbbbbbbccccccbbbbbbbbbd....",
3798".#abbbbbbbbbaccccabbbbbbbbbad...",
3799".#bbbbbbbbbbbbbbbbbbbbbbbbbbd#..",
3800"#abbbbbbbbbbbbbbbbbbbbbbbbbbad#.",
3801"#bbbbbbbbbbcccccccbbbbbbbbbbbd#.",
3802"#bbbbbbbbbbbbcccccbbbbbbbbbbbd##",
3803"#bbbbbbbbbbbbcccccbbbbbbbbbbbd##",
3804"#bbbbbbbbbbbbcccccbbbbbbbbbbbd##",
3805"#bbbbbbbbbbbbcccccbbbbbbbbbbbd##",
3806"#abbbbbbbbbbbcccccbbbbbbbbbbad##",
3807".#bbbbbbbbbbbcccccbbbbbbbbbbd###",
3808".#abbbbbbbbbbcccccbbbbbbbbbad###",
3809"..#bbbbbbbbcccccccccbbbbbbbd###.",
3810"...dbbbbbbbbbbbbbbbbbbbbbbd####.",
3811"....dbbbbbbbbbbbbbbbbbbbbd####..",
3812".....dabbbbbbbbbbbbbbbbad####...",
3813"......ddabbbbbbbbbbbbadd####....",
3814".......#dddabbbbbbaddd#####.....",
3815"........###dddabbbd#######......",
3816"..........####dbbbd#####........",
3817".............#dbbbd##...........",
3818"...............dbbd##...........",
3819"................dbd##...........",
3820".................dd##...........",
3821"..................###...........",
3822"...................##..........."};
3823
3824static char *question_xpm[]={
3825"32 32 6 1",
3826". c None",
3827"c c #000000",
3828"d c #0000ff",
3829"# c #808080",
3830"a c #c0c0c0",
3831"b c #ffffff",
3832"...........########.............",
3833"........###abbbbbba###..........",
3834"......##abbbbbbbbbbbba##........",
3835".....#abbbbbbbbbbbbbbbba#.......",
3836"....#bbbbbbbbbbbbbbbbbbbbc......",
3837"...#bbbbbbbaddddddabbbbbbbc.....",
3838"..#bbbbbbbadabbddddabbbbbbbc....",
3839".#abbbbbbbddbbbbddddbbbbbbbac...",
3840".#bbbbbbbbddddbbddddbbbbbbbbc#..",
3841"#abbbbbbbbddddbaddddbbbbbbbbac#.",
3842"#bbbbbbbbbaddabddddbbbbbbbbbbc#.",
3843"#bbbbbbbbbbbbbadddbbbbbbbbbbbc##",
3844"#bbbbbbbbbbbbbdddbbbbbbbbbbbbc##",
3845"#bbbbbbbbbbbbbddabbbbbbbbbbbbc##",
3846"#bbbbbbbbbbbbbddbbbbbbbbbbbbbc##",
3847"#abbbbbbbbbbbbbbbbbbbbbbbbbbac##",
3848".#bbbbbbbbbbbaddabbbbbbbbbbbc###",
3849".#abbbbbbbbbbddddbbbbbbbbbbac###",
3850"..#bbbbbbbbbbddddbbbbbbbbbbc###.",
3851"...cbbbbbbbbbaddabbbbbbbbbc####.",
3852"....cbbbbbbbbbbbbbbbbbbbbc####..",
3853".....cabbbbbbbbbbbbbbbbac####...",
3854"......ccabbbbbbbbbbbbacc####....",
3855".......#cccabbbbbbaccc#####.....",
3856"........###cccabbbc#######......",
3857"..........####cbbbc#####........",
3858".............#cbbbc##...........",
3859"...............cbbc##...........",
3860"................cbc##...........",
3861".................cc##...........",
3862"..................###...........",
3863"...................##..........."};
3864
3865static char *warning_xpm[]={
3866"32 32 6 1",
3867". c None",
3868"c c #000000",
3869"# c #808000",
3870"d c #808080",
3871"b c #c0c0c0",
3872"a c #ffff00",
3873".............###................",
3874"............#aabc...............",
3875"...........#aaaabcd.............",
3876"...........#aaaaacdd............",
3877"..........#aaaaaabcdd...........",
3878"..........#aaaaaaacdd...........",
3879".........#aaaaaaaabcdd..........",
3880".........#aaaaaaaaacdd..........",
3881"........#aaaaaaaaaabcdd.........",
3882"........#aaabcccbaaacdd.........",
3883".......#aaaacccccaaabcdd........",
3884".......#aaaacccccaaaacdd........",
3885"......#aaaaacccccaaaabcdd.......",
3886"......#aaaaacccccaaaaacdd.......",
3887".....#aaaaaacccccaaaaabcdd......",
3888".....#aaaaaa#ccc#aaaaaacdd......",
3889"....#aaaaaaabcccbaaaaaabcdd.....",
3890"....#aaaaaaaacccaaaaaaaacdd.....",
3891"...#aaaaaaaaa#c#aaaaaaaabcdd....",
3892"...#aaaaaaaaabcbaaaaaaaaacdd....",
3893"..#aaaaaaaaaaacaaaaaaaaaabcdd...",
3894"..#aaaaaaaaaaaaaaaaaaaaaaacdd...",
3895".#aaaaaaaaaaabccbaaaaaaaaabcdd..",
3896".#aaaaaaaaaaaccccaaaaaaaaaacdd..",
3897"#aaaaaaaaaaaaccccaaaaaaaaaabcdd.",
3898"#aaaaaaaaaaaabccbaaaaaaaaaaacdd.",
3899"#aaaaaaaaaaaaaaaaaaaaaaaaaaacddd",
3900"#aaaaaaaaaaaaaaaaaaaaaaaaaabcddd",
3901".#aaaaaaaaaaaaaaaaaaaaaaaabcdddd",
3902"..#ccccccccccccccccccccccccddddd",
3903"....ddddddddddddddddddddddddddd.",
3904".....ddddddddddddddddddddddddd.."};
3905
3906wxBitmap wxMetalArtProvider::CreateBitmap(const wxArtID& id,
3907 const wxArtClient& WXUNUSED(client),
3908 const wxSize& WXUNUSED(size))
3909{
3910 if ( id == wxART_INFORMATION )
3911 return wxBitmap(info_xpm);
3912 if ( id == wxART_ERROR )
3913 return wxBitmap(error_xpm);
3914 if ( id == wxART_WARNING )
3915 return wxBitmap(warning_xpm);
3916 if ( id == wxART_QUESTION )
3917 return wxBitmap(question_xpm);
3918 return wxNullBitmap;
3919}
3920
3921
3922// ----------------------------------------------------------------------------
3923// text control geometry
3924// ----------------------------------------------------------------------------
3925
3926static inline int GetTextBorderWidth()
3927{
3928 return 1;
3929}
3930
3931wxRect wxMetalRenderer::GetTextTotalArea(const wxTextCtrl *text,
3932 const wxRect& rect) const
3933{
3934 wxRect rectTotal = rect;
3935
3936 wxCoord widthBorder = GetTextBorderWidth();
3937 rectTotal.Inflate(widthBorder);
3938
3939 // this is what Windows does
3940 rectTotal.height++;
3941
3942 return rectTotal;
3943}
3944
3945wxRect wxMetalRenderer::GetTextClientArea(const wxTextCtrl *text,
3946 const wxRect& rect,
3947 wxCoord *extraSpaceBeyond) const
3948{
3949 wxRect rectText = rect;
3950
3951 // undo GetTextTotalArea()
3952 if ( rectText.height > 0 )
3953 rectText.height--;
3954
3955 wxCoord widthBorder = GetTextBorderWidth();
3956 rectText.Inflate(-widthBorder);
3957
3958 if ( extraSpaceBeyond )
3959 *extraSpaceBeyond = 0;
3960
3961 return rectText;
3962}
3963
3964// ----------------------------------------------------------------------------
3965// size adjustments
3966// ----------------------------------------------------------------------------
3967
3968void wxMetalRenderer::AdjustSize(wxSize *size, const wxWindow *window)
3969{
3970#if wxUSE_SCROLLBAR
3971 if ( wxDynamicCast(window, wxScrollBar) )
3972 {
3973 // we only set the width of vert scrollbars and height of the
3974 // horizontal ones
3975 if ( window->GetWindowStyle() & wxSB_HORIZONTAL )
3976 size->y = m_sizeScrollbarArrow.y;
3977 else
3978 size->x = m_sizeScrollbarArrow.x;
3979
3980 // skip border width adjustments, they don't make sense for us
3981 return;
3982 }
3983#endif // wxUSE_SCROLLBAR/!wxUSE_SCROLLBAR
3984
3985#if wxUSE_BUTTON
3986 if ( wxDynamicCast(window, wxButton) )
3987 {
3988 if ( !(window->GetWindowStyle() & wxBU_EXACTFIT) )
3989 {
3990 // TODO: don't harcode all this
3991 size->x += 3*window->GetCharWidth();
3992
3993 wxCoord heightBtn = (11*(window->GetCharHeight() + 8))/10;
3994 if ( size->y < heightBtn - 8 )
3995 size->y = heightBtn;
3996 else
3997 size->y += 9;
3998 }
3999
4000 // no border width adjustments for buttons
4001 return;
4002 }
4003#endif // wxUSE_BUTTON
4004
4005 // take into account the border width
4006 wxRect rectBorder = GetBorderDimensions(window->GetBorder());
4007 size->x += rectBorder.x + rectBorder.width;
4008 size->y += rectBorder.y + rectBorder.height;
4009}
4010
4011// ============================================================================
4012// wxInputHandler
4013// ============================================================================
4014
4015// ----------------------------------------------------------------------------
4016// wxMetalInputHandler
4017// ----------------------------------------------------------------------------
4018
4019wxMetalInputHandler::wxMetalInputHandler(wxMetalRenderer *renderer)
4020{
4021 m_renderer = renderer;
4022}
4023
4024bool wxMetalInputHandler::HandleKey(wxInputConsumer *control,
4025 const wxKeyEvent& event,
4026 bool pressed)
4027{
4028 return FALSE;
4029}
4030
4031bool wxMetalInputHandler::HandleMouse(wxInputConsumer *control,
4032 const wxMouseEvent& event)
4033{
4034 // clicking on the control gives it focus
4035 if ( event.ButtonDown() )
4036 {
4037 wxWindow *win = control->GetInputWindow();
4038
4039 if (( wxWindow::FindFocus() != control->GetInputWindow() ) &&
4040 ( win->AcceptsFocus() ) )
4041 {
4042 win->SetFocus();
4043
4044 return TRUE;
4045 }
4046 }
4047
4048 return FALSE;
4049}
4050
4051// ----------------------------------------------------------------------------
4052// wxMetalScrollBarInputHandler
4053// ----------------------------------------------------------------------------
4054
4055wxMetalScrollBarInputHandler::
4056wxMetalScrollBarInputHandler(wxMetalRenderer *renderer,
4057 wxInputHandler *handler)
4058 : wxStdScrollBarInputHandler(renderer, handler)
4059{
4060 m_scrollPaused = FALSE;
4061 m_interval = 0;
4062}
4063
4064bool wxMetalScrollBarInputHandler::OnScrollTimer(wxScrollBar *scrollbar,
4065 const wxControlAction& action)
4066{
4067 // stop if went beyond the position of the original click (this can only
4068 // happen when we scroll by pages)
4069 bool stop = FALSE;
4070 if ( action == wxACTION_SCROLL_PAGE_DOWN )
4071 {
4072 stop = m_renderer->HitTestScrollbar(scrollbar, m_ptStartScrolling)
4073 != wxHT_SCROLLBAR_BAR_2;
4074 }
4075 else if ( action == wxACTION_SCROLL_PAGE_UP )
4076 {
4077 stop = m_renderer->HitTestScrollbar(scrollbar, m_ptStartScrolling)
4078 != wxHT_SCROLLBAR_BAR_1;
4079 }
4080
4081 if ( stop )
4082 {
4083 StopScrolling(scrollbar);
4084
4085 scrollbar->Refresh();
4086
4087 return FALSE;
4088 }
4089
4090 return wxStdScrollBarInputHandler::OnScrollTimer(scrollbar, action);
4091}
4092
4093bool wxMetalScrollBarInputHandler::HandleMouse(wxInputConsumer *control,
4094 const wxMouseEvent& event)
4095{
4096 // remember the current state
4097 bool wasDraggingThumb = m_htLast == wxHT_SCROLLBAR_THUMB;
4098
4099 // do process the message
4100 bool rc = wxStdScrollBarInputHandler::HandleMouse(control, event);
4101
4102 // analyse the changes
4103 if ( !wasDraggingThumb && (m_htLast == wxHT_SCROLLBAR_THUMB) )
4104 {
4105 // we just started dragging the thumb, remember its initial position to
4106 // be able to restore it if the drag is cancelled later
4107 m_eventStartDrag = event;
4108 }
4109
4110 return rc;
4111}
4112
4113bool wxMetalScrollBarInputHandler::HandleMouseMove(wxInputConsumer *control,
4114 const wxMouseEvent& event)
4115{
4116 // we don't highlight scrollbar elements, so there is no need to process
4117 // mouse move events normally - only do it while mouse is captured (i.e.
4118 // when we're dragging the thumb or pressing on something)
4119 if ( !m_winCapture )
4120 return FALSE;
4121
4122 if ( event.Entering() )
4123 {
4124 // we're not interested in this at all
4125 return FALSE;
4126 }
4127
4128 wxScrollBar *scrollbar = wxStaticCast(control->GetInputWindow(), wxScrollBar);
4129 wxHitTest ht;
4130 if ( m_scrollPaused )
4131 {
4132 // check if the mouse returned to its original location
4133
4134 if ( event.Leaving() )
4135 {
4136 // it surely didn't
4137 return FALSE;
4138 }
4139
4140 ht = m_renderer->HitTestScrollbar(scrollbar, event.GetPosition());
4141 if ( ht == m_htLast )
4142 {
4143 // yes it did, resume scrolling
4144 m_scrollPaused = FALSE;
4145 if ( m_timerScroll )
4146 {
4147 // we were scrolling by line/page, restart timer
4148 m_timerScroll->Start(m_interval);
4149
4150 Press(scrollbar, TRUE);
4151 }
4152 else // we were dragging the thumb
4153 {
4154 // restore its last location
4155 HandleThumbMove(scrollbar, m_eventLastDrag);
4156 }
4157
4158 return TRUE;
4159 }
4160 }
4161 else // normal case, scrolling hasn't been paused
4162 {
4163 // if we're scrolling the scrollbar because the arrow or the shaft was
4164 // pressed, check that the mouse stays on the same scrollbar element
4165
4166#if 0
4167 // Always let thumb jump back if we leave the scrollbar
4168 if ( event.Moving() )
4169 {
4170 ht = m_renderer->HitTestScrollbar(scrollbar, event.GetPosition());
4171 }
4172 else // event.Leaving()
4173 {
4174 ht = wxHT_NOWHERE;
4175 }
4176#else
4177 // Jump back only if we get far away from it
4178 wxPoint pos = event.GetPosition();
4179 if (scrollbar->HasFlag( wxVERTICAL ))
4180 {
4181 if (pos.x > -40 && pos.x < scrollbar->GetSize().x+40)
4182 pos.x = 5;
4183 }
4184 else
4185 {
4186 if (pos.y > -40 && pos.y < scrollbar->GetSize().y+40)
4187 pos.y = 5;
4188 }
4189 ht = m_renderer->HitTestScrollbar(scrollbar, pos );
4190#endif
4191
4192 // if we're dragging the thumb and the mouse stays in the scrollbar, it
4193 // is still ok - we only want to catch the case when the mouse leaves
4194 // the scrollbar here
4195 if ( m_htLast == wxHT_SCROLLBAR_THUMB && ht != wxHT_NOWHERE )
4196 {
4197 ht = wxHT_SCROLLBAR_THUMB;
4198 }
4199
4200 if ( ht != m_htLast )
4201 {
4202 // what were we doing? 2 possibilities: either an arrow/shaft was
4203 // pressed in which case we have a timer and so we just stop it or
4204 // we were dragging the thumb
4205 if ( m_timerScroll )
4206 {
4207 // pause scrolling
4208 m_interval = m_timerScroll->GetInterval();
4209 m_timerScroll->Stop();
4210 m_scrollPaused = TRUE;
4211
4212 // unpress the arrow
4213 Press(scrollbar, FALSE);
4214 }
4215 else // we were dragging the thumb
4216 {
4217 // remember the current thumb position to be able to restore it
4218 // if the mouse returns to it later
4219 m_eventLastDrag = event;
4220
4221 // and restore the original position (before dragging) of the
4222 // thumb for now
4223 HandleThumbMove(scrollbar, m_eventStartDrag);
4224 }
4225
4226 return TRUE;
4227 }
4228 }
4229
4230 return wxStdScrollBarInputHandler::HandleMouseMove(control, event);
4231}
4232
4233// ----------------------------------------------------------------------------
4234// wxMetalCheckboxInputHandler
4235// ----------------------------------------------------------------------------
4236
4237bool wxMetalCheckboxInputHandler::HandleKey(wxInputConsumer *control,
4238 const wxKeyEvent& event,
4239 bool pressed)
4240{
4241 if ( pressed )
4242 {
4243 wxControlAction action;
4244 int keycode = event.GetKeyCode();
4245 switch ( keycode )
4246 {
4247 case WXK_SPACE:
4248 action = wxACTION_CHECKBOX_TOGGLE;
4249 break;
4250
4251 case WXK_SUBTRACT:
4252 case WXK_NUMPAD_SUBTRACT:
4253 action = wxACTION_CHECKBOX_CHECK;
4254 break;
4255
4256 case WXK_ADD:
4257 case WXK_NUMPAD_ADD:
4258 case WXK_NUMPAD_EQUAL:
4259 action = wxACTION_CHECKBOX_CLEAR;
4260 break;
4261 }
4262
4263 if ( !!action )
4264 {
4265 control->PerformAction(action);
4266
4267 return TRUE;
4268 }
4269 }
4270
4271 return FALSE;
4272}
4273
4274// ----------------------------------------------------------------------------
4275// wxMetalTextCtrlInputHandler
4276// ----------------------------------------------------------------------------
4277
4278bool wxMetalTextCtrlInputHandler::HandleKey(wxInputConsumer *control,
4279 const wxKeyEvent& event,
4280 bool pressed)
4281{
4282 // handle only MSW-specific text bindings here, the others are handled in
4283 // the base class
4284 if ( pressed )
4285 {
4286 int keycode = event.GetKeyCode();
4287
4288 wxControlAction action;
4289 if ( keycode == WXK_DELETE && event.ShiftDown() )
4290 {
4291 action = wxACTION_TEXT_CUT;
4292 }
4293 else if ( keycode == WXK_INSERT )
4294 {
4295 if ( event.ControlDown() )
4296 action = wxACTION_TEXT_COPY;
4297 else if ( event.ShiftDown() )
4298 action = wxACTION_TEXT_PASTE;
4299 }
4300
4301 if ( action != wxACTION_NONE )
4302 {
4303 control->PerformAction(action);
4304
4305 return TRUE;
4306 }
4307 }
4308
4309 return wxStdTextCtrlInputHandler::HandleKey(control, event, pressed);
4310}
4311
4312// ----------------------------------------------------------------------------
4313// wxMetalStatusBarInputHandler
4314// ----------------------------------------------------------------------------
4315
4316wxMetalStatusBarInputHandler::
4317wxMetalStatusBarInputHandler(wxInputHandler *handler)
4318 : wxStdInputHandler(handler)
4319{
4320 m_isOnGrip = FALSE;
4321}
4322
4323bool wxMetalStatusBarInputHandler::IsOnGrip(wxWindow *statbar,
4324 const wxPoint& pt) const
4325{
4326 if ( statbar->HasFlag(wxST_SIZEGRIP) &&
4327 statbar->GetParent()->HasFlag(wxRESIZE_BORDER) )
4328 {
4329 wxTopLevelWindow *
4330 parentTLW = wxDynamicCast(statbar->GetParent(), wxTopLevelWindow);
4331
4332 wxCHECK_MSG( parentTLW, FALSE,
4333 _T("the status bar should be a child of a TLW") );
4334
4335 // a maximized window can't be resized anyhow
4336 if ( !parentTLW->IsMaximized() )
4337 {
4338 // VZ: I think that the standard Windows behaviour is to only
4339 // show the resizing cursor when the mouse is on top of the
4340 // grip itself but apparently different Windows versions behave
4341 // differently (?) and it seems a better UI to allow resizing
4342 // the status bar even when the mouse is above the grip
4343 wxSize sizeSbar = statbar->GetSize();
4344
4345 int diff = sizeSbar.x - pt.x;
4346 return diff >= 0 && diff < (wxCoord)STATUSBAR_GRIP_SIZE;
4347 }
4348 }
4349
4350 return FALSE;
4351}
4352
4353bool wxMetalStatusBarInputHandler::HandleMouse(wxInputConsumer *consumer,
4354 const wxMouseEvent& event)
4355{
4356 if ( event.Button(1) )
4357 {
4358 if ( event.ButtonDown(1) )
4359 {
4360 wxWindow *statbar = consumer->GetInputWindow();
4361
4362 if ( IsOnGrip(statbar, event.GetPosition()) )
4363 {
4364 wxTopLevelWindow *tlw = wxDynamicCast(statbar->GetParent(),
4365 wxTopLevelWindow);
4366 if ( tlw )
4367 {
4368 tlw->PerformAction(wxACTION_TOPLEVEL_RESIZE,
4369 wxHT_TOPLEVEL_BORDER_SE);
4370
4371 statbar->SetCursor(m_cursorOld);
4372
4373 return TRUE;
4374 }
4375 }
4376 }
4377 }
4378
4379 return wxStdInputHandler::HandleMouse(consumer, event);
4380}
4381
4382bool wxMetalStatusBarInputHandler::HandleMouseMove(wxInputConsumer *consumer,
4383 const wxMouseEvent& event)
4384{
4385 wxWindow *statbar = consumer->GetInputWindow();
4386
4387 bool isOnGrip = IsOnGrip(statbar, event.GetPosition());
4388 if ( isOnGrip != m_isOnGrip )
4389 {
4390 m_isOnGrip = isOnGrip;
4391 if ( isOnGrip )
4392 {
4393 m_cursorOld = statbar->GetCursor();
4394 statbar->SetCursor(wxCURSOR_SIZENWSE);
4395 }
4396 else
4397 {
4398 statbar->SetCursor(m_cursorOld);
4399 }
4400 }
4401
4402 return wxStdInputHandler::HandleMouseMove(consumer, event);
4403}
4404
4405// ----------------------------------------------------------------------------
4406// wxMetalFrameInputHandler
4407// ----------------------------------------------------------------------------
4408
4409class wxMetalSystemMenuEvtHandler : public wxEvtHandler
4410{
4411public:
4412 wxMetalSystemMenuEvtHandler(wxMetalFrameInputHandler *handler);
4413
4414 void Attach(wxInputConsumer *consumer);
4415 void Detach();
4416
4417private:
4418 DECLARE_EVENT_TABLE()
4419 void OnSystemMenu(wxCommandEvent &event);
4420 void OnCloseFrame(wxCommandEvent &event);
4421 void OnClose(wxCloseEvent &event);
4422
4423 wxMetalFrameInputHandler *m_inputHnd;
4424 wxTopLevelWindow *m_wnd;
4425 wxAcceleratorTable m_oldAccelTable;
4426};
4427
4428wxMetalSystemMenuEvtHandler::wxMetalSystemMenuEvtHandler(
4429 wxMetalFrameInputHandler *handler)
4430{
4431 m_inputHnd = handler;
4432 m_wnd = NULL;
4433}
4434
4435void wxMetalSystemMenuEvtHandler::Attach(wxInputConsumer *consumer)
4436{
4437 wxASSERT_MSG( m_wnd == NULL, _T("can't attach the handler twice!") );
4438
4439 m_wnd = wxStaticCast(consumer->GetInputWindow(), wxTopLevelWindow);
4440 m_wnd->PushEventHandler(this);
4441
4442 // VS: This code relies on using generic implementation of
4443 // wxAcceleratorTable in wxUniv!
4444 wxAcceleratorTable table = *m_wnd->GetAcceleratorTable();
4445 m_oldAccelTable = table;
4446 table.Add(wxAcceleratorEntry(wxACCEL_ALT, WXK_SPACE, wxID_SYSTEM_MENU));
4447 table.Add(wxAcceleratorEntry(wxACCEL_ALT, WXK_F4, wxID_CLOSE_FRAME));
4448 m_wnd->SetAcceleratorTable(table);
4449}
4450
4451void wxMetalSystemMenuEvtHandler::Detach()
4452{
4453 if ( m_wnd )
4454 {
4455 m_wnd->SetAcceleratorTable(m_oldAccelTable);
4456 m_wnd->RemoveEventHandler(this);
4457 m_wnd = NULL;
4458 }
4459}
4460
4461BEGIN_EVENT_TABLE(wxMetalSystemMenuEvtHandler, wxEvtHandler)
4462 EVT_MENU(wxID_SYSTEM_MENU, wxMetalSystemMenuEvtHandler::OnSystemMenu)
4463 EVT_MENU(wxID_CLOSE_FRAME, wxMetalSystemMenuEvtHandler::OnCloseFrame)
4464 EVT_CLOSE(wxMetalSystemMenuEvtHandler::OnClose)
4465END_EVENT_TABLE()
4466
4467void wxMetalSystemMenuEvtHandler::OnSystemMenu(wxCommandEvent &WXUNUSED(event))
4468{
4469 int border = ((m_wnd->GetWindowStyle() & wxRESIZE_BORDER) &&
4470 !m_wnd->IsMaximized()) ?
4471 RESIZEABLE_FRAME_BORDER_THICKNESS :
4472 FRAME_BORDER_THICKNESS;
4473 wxPoint pt = m_wnd->GetClientAreaOrigin();
4474 pt.x = -pt.x + border;
4475 pt.y = -pt.y + border + FRAME_TITLEBAR_HEIGHT;
4476
4477 wxAcceleratorTable table = *m_wnd->GetAcceleratorTable();
4478 m_wnd->SetAcceleratorTable(wxNullAcceleratorTable);
4479 m_inputHnd->PopupSystemMenu(m_wnd, pt);
4480 m_wnd->SetAcceleratorTable(table);
4481}
4482
4483void wxMetalSystemMenuEvtHandler::OnCloseFrame(wxCommandEvent &WXUNUSED(event))
4484{
4485 m_wnd->PerformAction(wxACTION_TOPLEVEL_BUTTON_CLICK,
4486 wxTOPLEVEL_BUTTON_CLOSE);
4487}
4488
4489void wxMetalSystemMenuEvtHandler::OnClose(wxCloseEvent &event)
4490{
4491 m_wnd = NULL;
4492 event.Skip();
4493}
4494
4495
4496wxMetalFrameInputHandler::wxMetalFrameInputHandler(wxInputHandler *handler)
4497 : wxStdFrameInputHandler(handler)
4498{
4499 m_menuHandler = new wxMetalSystemMenuEvtHandler(this);
4500}
4501
4502wxMetalFrameInputHandler::~wxMetalFrameInputHandler()
4503{
4504 if ( m_menuHandler )
4505 {
4506 m_menuHandler->Detach();
4507 delete m_menuHandler;
4508 }
4509}
4510
4511bool wxMetalFrameInputHandler::HandleMouse(wxInputConsumer *consumer,
4512 const wxMouseEvent& event)
4513{
4514 if ( event.LeftDClick() || event.LeftDown() || event.RightDown() )
4515 {
4516 wxTopLevelWindow *tlw =
4517 wxStaticCast(consumer->GetInputWindow(), wxTopLevelWindow);
4518
4519 long hit = tlw->HitTest(event.GetPosition());
4520
4521 if ( event.LeftDClick() && hit == wxHT_TOPLEVEL_TITLEBAR )
4522 {
4523 tlw->PerformAction(wxACTION_TOPLEVEL_BUTTON_CLICK,
4524 tlw->IsMaximized() ? wxTOPLEVEL_BUTTON_RESTORE
4525 : wxTOPLEVEL_BUTTON_MAXIMIZE);
4526 return TRUE;
4527 }
4528 else if ( tlw->GetWindowStyle() & wxSYSTEM_MENU )
4529 {
4530 if ( (event.LeftDown() && hit == wxHT_TOPLEVEL_ICON) ||
4531 (event.RightDown() &&
4532 (hit == wxHT_TOPLEVEL_TITLEBAR ||
4533 hit == wxHT_TOPLEVEL_ICON)) )
4534 {
4535 PopupSystemMenu(tlw, event.GetPosition());
4536 return TRUE;
4537 }
4538 }
4539 }
4540
4541 return wxStdFrameInputHandler::HandleMouse(consumer, event);
4542}
4543
4544void wxMetalFrameInputHandler::PopupSystemMenu(wxTopLevelWindow *window,
4545 const wxPoint& pos) const
4546{
4547 wxMenu *menu = new wxMenu;
4548
4549 if ( window->GetWindowStyle() & wxMAXIMIZE_BOX )
4550 menu->Append(wxID_RESTORE_FRAME , _("&Restore"));
4551 menu->Append(wxID_MOVE_FRAME , _("&Move"));
4552 if ( window->GetWindowStyle() & wxRESIZE_BORDER )
4553 menu->Append(wxID_RESIZE_FRAME , _("&Size"));
4554 if ( wxSystemSettings::HasFeature(wxSYS_CAN_ICONIZE_FRAME) )
4555 menu->Append(wxID_ICONIZE_FRAME , _("Mi&nimize"));
4556 if ( window->GetWindowStyle() & wxMAXIMIZE_BOX )
4557 menu->Append(wxID_MAXIMIZE_FRAME , _("Ma&ximize"));
4558 menu->AppendSeparator();
4559 menu->Append(wxID_CLOSE_FRAME, _("Close\tAlt-F4"));
4560
4561 if ( window->GetWindowStyle() & wxMAXIMIZE_BOX )
4562 {
4563 if ( window->IsMaximized() )
4564 {
4565 menu->Enable(wxID_MAXIMIZE_FRAME, FALSE);
4566 menu->Enable(wxID_MOVE_FRAME, FALSE);
4567 if ( window->GetWindowStyle() & wxRESIZE_BORDER )
4568 menu->Enable(wxID_RESIZE_FRAME, FALSE);
4569 }
4570 else
4571 menu->Enable(wxID_RESTORE_FRAME, FALSE);
4572 }
4573
4574 window->PopupMenu(menu, pos);
4575 delete menu;
4576}
4577
4578bool wxMetalFrameInputHandler::HandleActivation(wxInputConsumer *consumer,
4579 bool activated)
4580{
4581 if ( consumer->GetInputWindow()->GetWindowStyle() & wxSYSTEM_MENU )
4582 {
4583 // always detach if active frame changed:
4584 m_menuHandler->Detach();
4585
4586 if ( activated )
4587 {
4588 m_menuHandler->Attach(consumer);
4589 }
4590 }
4591
4592 return wxStdFrameInputHandler::HandleActivation(consumer, activated);
4593}