1// ============================================================================
2// headers
3// ============================================================================
4
5#include "wx/wxprec.h"
6
7#ifdef __BORLANDC__
8    #pragma hdrstop
9#endif  //__BORLANDC__
10
11#ifndef WX_PRECOMP
12    #include "wx/dcclient.h"
13    #include "wx/dcmemory.h"
14    #include "wx/intl.h"
15#endif
16
17#include "wx/gizmos/ledctrl.h"
18
19// ----------------------------------------------------------------------------
20// constants
21// ----------------------------------------------------------------------------
22
23// A LED digit is build up like this, with maximum 7 Lines :
24//
25// 111
26// 6 2
27// 777
28// 5 3
29// 444
30//
31// Each number contains combinations of the lines, and they are set up below.
32
33const int LINE1 = 1;
34const int LINE2 = 2;
35const int LINE3 = 4;
36const int LINE4 = 8;
37const int LINE5 = 16;
38const int LINE6 = 32;
39const int LINE7 = 64;
40const int DECIMALSIGN = 128;
41
42const int DIGIT0 = LINE1 | LINE2 | LINE3 | LINE4 | LINE5 | LINE6;
43const int DIGIT1 = LINE2 | LINE3;
44const int DIGIT2 = LINE1 | LINE2 | LINE4 | LINE5 | LINE7;
45const int DIGIT3 = LINE1 | LINE2 | LINE3 | LINE4 | LINE7;
46const int DIGIT4 = LINE2 | LINE3 | LINE6 | LINE7;
47const int DIGIT5 = LINE1 | LINE3 | LINE4 | LINE6 | LINE7;
48const int DIGIT6 = LINE1 | LINE3 | LINE4 | LINE5 | LINE6 | LINE7;
49const int DIGIT7 = LINE1 | LINE2 | LINE3;
50const int DIGIT8 = LINE1 | LINE2 | LINE3 | LINE4 | LINE5 | LINE6 | LINE7;
51const int DIGIT9 = LINE1 | LINE2 | LINE3 | LINE6 | LINE7;
52const int DASH   = LINE7;
53
54const int DIGITALL = -1;
55
56// ============================================================================
57// wxLEDNumberCtrl class implementation
58// ============================================================================
59
60wxLEDNumberCtrl::wxLEDNumberCtrl()
61:   m_Alignment(wxLED_ALIGN_LEFT),
62    m_LineMargin(-1),
63    m_DigitMargin(-1),
64    m_LineLength(-1),
65    m_LineWidth(-1),
66    m_DrawFaded(false),
67    m_LeftStartPos(-1)
68{
69}
70
71
72wxLEDNumberCtrl::wxLEDNumberCtrl(wxWindow *parent, wxWindowID id,
73                                 const wxPoint& pos, const wxSize& size,
74                                 long style)
75:   m_Alignment(wxLED_ALIGN_LEFT),
76    m_LineMargin(-1),
77    m_DigitMargin(-1),
78    m_LineLength(-1),
79    m_LineWidth(-1),
80    m_DrawFaded(false),
81    m_LeftStartPos(-1)
82{
83    Create(parent, id, pos, size, style);
84}
85
86
87bool wxLEDNumberCtrl::Create(wxWindow *parent, wxWindowID id,
88                                 const wxPoint& pos, const wxSize& size,
89                                 long style)
90{
91    bool RetVal = wxControl::Create(parent, id, pos, size, style);
92
93    if ((style & wxLED_DRAW_FADED) != 0)
94        SetDrawFaded(true);
95    if ((style & wxLED_ALIGN_MASK) != 0)
96        SetAlignment((wxLEDValueAlign)(style & wxLED_ALIGN_MASK));
97
98    SetBackgroundColour(*wxBLACK);
99    SetForegroundColour(*wxGREEN);
100
101    return RetVal;
102}
103
104
105void wxLEDNumberCtrl::SetAlignment(wxLEDValueAlign Alignment, bool Redraw)
106{
107    if (Alignment != m_Alignment)
108    {
109        m_Alignment = Alignment;
110        RecalcInternals(GetClientSize());
111
112        if (Redraw)
113            Refresh(false);
114    }
115}
116
117
118void wxLEDNumberCtrl::SetDrawFaded(bool DrawFaded, bool Redraw)
119{
120    if (DrawFaded != m_DrawFaded)
121    {
122        m_DrawFaded = DrawFaded;
123
124        if (Redraw)
125            Refresh(false);
126    }
127}
128
129
130void wxLEDNumberCtrl::SetValue(wxString const &Value, bool Redraw)
131{
132    if (Value != m_Value)
133    {
134#ifdef __WXDEBUG__
135        if (!Value.empty())
136        {
137            for(size_t i=0; i<Value.Length(); i++) {
138                wxChar ch = Value[i];
139                wxASSERT_MSG((ch>='0' && ch<='9') || ch=='-' || ch==' ' || ch=='.',
140                             wxT("wxLEDNumberCtrl can only display numeric string values."));
141            }
142        }
143#endif
144
145        m_Value = Value;
146        RecalcInternals(GetClientSize());
147
148        if (Redraw)
149            Refresh(false);
150    }
151}
152
153
154BEGIN_EVENT_TABLE(wxLEDNumberCtrl, wxControl)
155    EVT_ERASE_BACKGROUND(wxLEDNumberCtrl::OnEraseBackground)
156    EVT_PAINT(wxLEDNumberCtrl::OnPaint)
157    EVT_SIZE(wxLEDNumberCtrl::OnSize)
158END_EVENT_TABLE()
159
160
161void wxLEDNumberCtrl::OnEraseBackground(wxEraseEvent &WXUNUSED(event))
162{
163}
164
165
166void wxLEDNumberCtrl::OnPaint(wxPaintEvent &WXUNUSED(event))
167{
168    wxPaintDC Dc(this);
169
170    int Width, Height;
171    GetClientSize(&Width, &Height);
172
173    wxBitmap *pMemoryBitmap = new wxBitmap(Width, Height);
174    wxMemoryDC MemDc;
175
176    MemDc.SelectObject(*pMemoryBitmap);
177
178    // Draw background.
179    MemDc.SetBrush(wxBrush(GetBackgroundColour(), wxSOLID));
180    MemDc.DrawRectangle(wxRect(0, 0, Width, Height));
181    MemDc.SetBrush(wxNullBrush);
182
183    // Iterate each digit in the value, and draw.
184    const int DigitCount = m_Value.Len();
185    for (int offset=0, i = 0; offset < DigitCount; ++offset, ++i)
186    {
187        wxChar c = m_Value.GetChar(offset);
188
189        // Draw faded lines if wanted.
190        if (m_DrawFaded && (c != _T('.')))
191            DrawDigit(MemDc, DIGITALL, i);
192
193        // Draw the digits.
194        switch (c)
195        {
196            case _T('0') :
197                DrawDigit(MemDc, DIGIT0, i);
198                break;
199            case _T('1') :
200                DrawDigit(MemDc, DIGIT1, i);
201                break;
202            case _T('2') :
203                DrawDigit(MemDc, DIGIT2, i);
204                break;
205            case _T('3') :
206                DrawDigit(MemDc, DIGIT3, i);
207                break;
208            case _T('4') :
209                DrawDigit(MemDc, DIGIT4, i);
210                break;
211            case _T('5') :
212                DrawDigit(MemDc, DIGIT5, i);
213                break;
214            case _T('6') :
215                DrawDigit(MemDc, DIGIT6, i);
216                break;
217            case _T('7') :
218                DrawDigit(MemDc, DIGIT7, i);
219                break;
220            case _T('8') :
221                DrawDigit(MemDc, DIGIT8, i);
222                break;
223            case _T('9') :
224                DrawDigit(MemDc, DIGIT9, i);
225                break;
226            case _T('-') :
227                DrawDigit(MemDc, DASH, i);
228                break;
229            case _T('.') :
230                // Display the decimal in the previous segment
231                i--;
232                DrawDigit(MemDc, DECIMALSIGN, i);
233                break;
234            case _T(' ') :
235                // just skip it
236                break;
237            default :
238                wxFAIL_MSG(wxT("Unknown digit value"));
239                break;
240        }
241    }
242
243    // Blit the memory dc to screen.
244    Dc.Blit(0, 0, Width, Height, &MemDc, 0, 0, wxCOPY);
245    delete pMemoryBitmap;
246}
247
248
249void wxLEDNumberCtrl::DrawDigit(wxDC &Dc, int Digit, int Column)
250{
251    wxColour LineColor(GetForegroundColour());
252
253    if (Digit == DIGITALL)
254    {
255        const unsigned char R = (unsigned char)(LineColor.Red() / 16);
256        const unsigned char G = (unsigned char)(LineColor.Green() / 16);
257        const unsigned char B = (unsigned char)(LineColor.Blue() / 16);
258
259        LineColor.Set(R, G, B);
260    }
261
262    int XPos = m_LeftStartPos + Column * (m_LineLength + m_DigitMargin);
263
264    // Create a pen and draw the lines.
265    wxPen Pen(LineColor, m_LineWidth, wxSOLID);
266    Dc.SetPen(Pen);
267
268    if ((Digit & LINE1))
269    {
270        Dc.DrawLine(XPos + m_LineMargin*2, m_LineMargin,
271            XPos + m_LineLength + m_LineMargin*2, m_LineMargin);
272    }
273
274    if (Digit & LINE2)
275    {
276        Dc.DrawLine(XPos + m_LineLength + m_LineMargin*3, m_LineMargin*2,
277            XPos + m_LineLength + m_LineMargin*3, m_LineLength + (m_LineMargin*2));
278    }
279
280    if (Digit & LINE3)
281    {
282        Dc.DrawLine(XPos + m_LineLength + m_LineMargin*3, m_LineLength + (m_LineMargin*4),
283            XPos + m_LineLength + m_LineMargin*3, m_LineLength*2 + (m_LineMargin*4));
284    }
285
286    if (Digit & LINE4)
287    {
288        Dc.DrawLine(XPos + m_LineMargin*2, m_LineLength*2 + (m_LineMargin*5),
289            XPos + m_LineLength + m_LineMargin*2, m_LineLength*2 + (m_LineMargin*5));
290    }
291
292    if (Digit & LINE5)
293    {
294        Dc.DrawLine(XPos + m_LineMargin, m_LineLength + (m_LineMargin*4),
295            XPos + m_LineMargin, m_LineLength*2 + (m_LineMargin*4));
296    }
297
298    if (Digit & LINE6)
299    {
300        Dc.DrawLine(XPos + m_LineMargin, m_LineMargin*2,
301            XPos + m_LineMargin, m_LineLength + (m_LineMargin*2));
302    }
303
304    if (Digit & LINE7)
305    {
306        Dc.DrawLine(XPos + m_LineMargin*2, m_LineLength + (m_LineMargin*3),
307            XPos + m_LineMargin*2 + m_LineLength, m_LineLength + (m_LineMargin*3));
308    }
309
310    if (Digit & DECIMALSIGN)
311    {
312        Dc.DrawLine(XPos + m_LineLength + m_LineMargin*4, m_LineLength*2 + (m_LineMargin*5),
313            XPos + m_LineLength + m_LineMargin*4, m_LineLength*2 + (m_LineMargin*5));
314    }
315
316    Dc.SetPen(wxNullPen);
317}
318
319
320void wxLEDNumberCtrl::RecalcInternals(const wxSize &CurrentSize)
321{
322    // Dimensions of LED segments
323    //
324    // Size of character is based on the HEIGH of the widget, NOT the width.
325    // Segment height is calculated as follows:
326    // Each segment is m_LineLength pixels long.
327    // There is m_LineMargin pixels at the top and bottom of each line segment
328    // There is m_LineMargin pixels at the top and bottom of each digit
329    //
330    //  Therefore, the heigth of each character is:
331    //  m_LineMargin                            : Top digit boarder
332    //  m_LineMargin+m_LineLength+m_LineMargin  : Top half of segment
333    //  m_LineMargin+m_LineLength+m_LineMargin  : Bottom half of segment
334    //  m_LineMargin                            : Bottom digit boarder
335    //  ----------------------
336    //  m_LineMargin*6 + m_LineLength*2 == Total height of digit.
337    //  Therefore, (m_LineMargin*6 + m_LineLength*2) must equal Height
338    //
339    //  Spacing between characters can then be calculated as follows:
340    //  m_LineMargin                            : before the digit,
341    //  m_LineMargin+m_LineLength+m_LineMargin  : for the digit width
342    //  m_LineMargin                            : after the digit
343    //  = m_LineMargin*4 + m_LineLength
344    const int Height = CurrentSize.GetHeight();
345
346    if ((Height * 0.075) < 1)
347        m_LineMargin = 1;
348    else
349        m_LineMargin = (int)(Height * 0.075);
350
351    if ((Height * 0.275) < 1)
352        m_LineLength = 1;
353    else
354        m_LineLength = (int)(Height * 0.275);
355
356    m_LineWidth = m_LineMargin;
357
358    m_DigitMargin = m_LineMargin * 4;
359
360    // Count the number of characters in the string; '.' characters are not
361    // included because they do not take up space in the display
362    int count = 0;
363    for (unsigned int i = 0; i < m_Value.Len(); i++)
364        if (m_Value.GetChar(i) != '.')
365            count++;
366    const int ValueWidth = (m_LineLength + m_DigitMargin) * count;
367    const int ClientWidth = CurrentSize.GetWidth();
368
369    switch (m_Alignment)
370    {
371        case wxLED_ALIGN_LEFT :
372            m_LeftStartPos = m_LineMargin;
373            break;
374        case wxLED_ALIGN_RIGHT :
375            m_LeftStartPos = ClientWidth - ValueWidth - m_LineMargin;
376            break;
377        case wxLED_ALIGN_CENTER :
378            m_LeftStartPos = (ClientWidth - ValueWidth) / 2;
379            break;
380        default :
381            wxFAIL_MSG(wxT("Unknown alignent value for wxLEDNumberCtrl."));
382            break;
383    }
384}
385
386
387void wxLEDNumberCtrl::OnSize(wxSizeEvent &Event)
388{
389    RecalcInternals(Event.GetSize());
390
391    Event.Skip();
392}
393