1 /***************************************************************
2  * Name:      wxledpanel.cpp
3  * Purpose:   Code for Class wxLEDPanel
4  * Author:    Christian Gr�fe (info@mcs-soft.de)
5  * Created:   2007-02-28
6  * Copyright: Christian Gr�fe (www.mcs-soft.de)
7  * License:      wxWindows licence
8  **************************************************************/
9 
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12 
13 #ifdef __BORLANDC__
14 #pragma hdrstop
15 #endif
16 
17 #ifndef WX_PRECOMP
18     #include <wx/wx.h>
19 #endif
20 
21 #include <wx/dcbuffer.h>
22 #include "wx/wxledpanel.h"
23 
24 #define TIMER_SCROLL_ID 1000
25 
BEGIN_EVENT_TABLE(wxLEDPanel,wxControl)26 BEGIN_EVENT_TABLE(wxLEDPanel, wxControl)
27     EVT_PAINT(wxLEDPanel::OnPaint)
28     EVT_ERASE_BACKGROUND(wxLEDPanel::OnEraseBackground)
29     EVT_TIMER(TIMER_SCROLL_ID,wxLEDPanel::OnScrollTimer)
30 END_EVENT_TABLE()
31 
32 wxLEDPanel::wxLEDPanel() :
33     m_align(wxALIGN_LEFT|wxALIGN_TOP),
34     m_padLeft(1),
35     m_padRight(1),
36     m_invert(false),
37     m_show_inactivs(true),
38     m_scrollspeed(0),
39     m_scrolldirection(wxALL),
40     m_aniFrameNr(-1)
41 {
42 }
43 
wxLEDPanel(wxWindow * parent,wxWindowID id,const wxSize & ledsize,const wxSize & fieldsize,int padding,const wxPoint & pos,long style,const wxValidator & validator)44 wxLEDPanel::wxLEDPanel(wxWindow* parent, wxWindowID id, const wxSize& ledsize,
45                     const wxSize& fieldsize, int padding, const wxPoint& pos,
46                     long style, const wxValidator& validator) :
47     m_align(wxALIGN_LEFT|wxALIGN_TOP),
48     m_padLeft(1),
49     m_padRight(1),
50     m_invert(false),
51     m_show_inactivs(true),
52     m_scrollspeed(0),
53     m_scrolldirection(wxALL),
54     m_aniFrameNr(-1)
55 {
56     Create(parent,id,ledsize,fieldsize,padding,pos,style,validator);
57 }
58 
~wxLEDPanel()59 wxLEDPanel::~wxLEDPanel()
60 {
61 }
62 
Create(wxWindow * parent,wxWindowID id,const wxSize & ledsize,const wxSize & fieldsize,int padding,const wxPoint & pos,long style,const wxValidator & validator)63 bool wxLEDPanel::Create(wxWindow* parent, wxWindowID id, const wxSize& ledsize,
64                     const wxSize& fieldsize, int padding, const wxPoint& pos,
65                     long style, const wxValidator& validator)
66 {
67     // save in member
68     m_ledsize=ledsize;
69     m_padding=padding;
70     wxSize size;
71     size.SetWidth((ledsize.GetWidth()+padding)*fieldsize.GetWidth()+padding);
72     size.SetHeight((ledsize.GetHeight()+padding)*fieldsize.GetHeight()+padding);
73 
74     // create the control
75     if(!wxControl::Create(parent,id,pos,size,style,validator))
76         return false;
77 
78     // initialise MatrixObjekt
79     m_field.Init(0,fieldsize.GetWidth(),fieldsize.GetHeight());
80 
81     // default backgroundcolor is black (call parent, to prevent the call of PrepareBackground)
82     wxWindow::SetBackgroundColour(*wxBLACK);
83 
84     // default led-color is red
85     this->SetLEDColour(wxLED_COLOUR_RED);
86 
87     // no Input Events
88     this->Enable(false);
89 
90     // bind timer
91     m_scrollTimer.SetOwner(this,TIMER_SCROLL_ID);
92 
93     return true;
94 }
95 
DoGetBestSize() const96 wxSize wxLEDPanel::DoGetBestSize() const
97 {
98     wxSize size;
99     size.SetWidth((m_ledsize.GetWidth()+m_padding)*m_field.GetWidth()+m_padding);
100     size.SetHeight((m_ledsize.GetHeight()+m_padding)*m_field.GetHeight()+m_padding);
101     return size;
102 }
103 
Clear()104 void wxLEDPanel::Clear()
105 {
106     m_field.Clear();
107 }
108 
Reset()109 void wxLEDPanel::Reset()
110 {
111     SetText(m_text);
112 }
113 
114 /** @return the size of the field in points */
GetFieldsize() const115 wxSize wxLEDPanel::GetFieldsize() const
116 {
117     return m_field.GetSize();
118 }
119 
120 /** @return the size of one LED on the field */
GetLEDSize() const121 wxSize wxLEDPanel::GetLEDSize() const
122 {
123     return m_ledsize;
124 }
125 
126 /**
127 * Sets the colour of the LEDs
128 * @param colourID the ID of the new colour
129 */
SetLEDColour(wxLEDColour colourID)130 void wxLEDPanel::SetLEDColour(wxLEDColour colourID)
131 {
132     // for drawing
133     wxBrush brush;
134     wxPen pen;
135 
136     // colourID speichern
137     m_activ_colour_id=colourID;
138 
139     int w=m_ledsize.GetWidth()+m_padding;
140     int h=m_ledsize.GetHeight()+m_padding;
141 
142     // create Bitmaps for "LED on" und "LED off"
143     wxBitmap led_on(w,h);
144     wxBitmap led_off(w,h);
145     wxBitmap led_none(w,h);
146 
147     // draw "LED on"
148     m_mdc_led_on.SelectObject(led_on);
149 
150     // Clear Background
151     m_mdc_led_on.SetBackground(this->GetBackgroundColour());
152     m_mdc_led_on.Clear();
153 
154     // complete point
155     pen.SetColour(s_colour_dark[colourID-1]);
156     brush.SetColour(s_colour[colourID-1]);
157     m_mdc_led_on.SetPen(pen);
158     m_mdc_led_on.SetBrush(brush);
159     m_mdc_led_on.DrawEllipse(wxPoint(0,0),m_ledsize);
160 
161     // left top corner in lighter colour
162     pen.SetColour(s_colour_light[colourID-1]);
163     m_mdc_led_on.SetPen(pen);
164     m_mdc_led_on.DrawEllipticArc(0,0,m_ledsize.GetWidth(),m_ledsize.GetHeight(),75.0,195.0);
165 
166 
167     // draw "LED off"
168     m_mdc_led_off.SelectObject(led_off);
169 
170     // cleare Background
171     m_mdc_led_off.SetBackground(this->GetBackgroundColour());
172     m_mdc_led_off.Clear();
173 
174     // complete point
175     pen.SetColour(s_colour_dark[colourID-1]);
176     brush.SetColour(s_colour_verydark[colourID-1]);
177     m_mdc_led_off.SetPen(pen);
178     m_mdc_led_off.SetBrush(brush);
179     m_mdc_led_off.DrawEllipse(wxPoint(0,0),m_ledsize);
180 
181 
182     // draw "no LED"
183     m_mdc_led_none.SelectObject(led_none);
184     m_mdc_led_none.SetBackground(this->GetBackgroundColour());
185     m_mdc_led_none.Clear();
186 
187 
188     PrepareBackground();
189 }
190 
191 /** @return the real colour of a LED */
GetLEDColour() const192 const wxColour& wxLEDPanel::GetLEDColour() const
193 {
194     return s_colour[m_activ_colour_id];
195 }
196 
197 /** Overwritten to prepare the background with the new backgroundcolour
198 * @param colour the new backroundcolour
199 */
SetBackgroundColour(const wxColour & colour)200 bool wxLEDPanel::SetBackgroundColour(const wxColour& colour)
201 {
202     if (wxWindow::SetBackgroundColour(colour))
203     {
204       PrepareBackground();
205       return true;
206     }
207 
208     return false;
209 }
210 
211 /** Sets the speed for the scrolling
212 * @param speed the speed in ms (optimal range between 80-120)
213 */
SetScrollSpeed(int speed)214 void wxLEDPanel::SetScrollSpeed(int speed)
215 {
216     // the save way
217     m_scrollTimer.Stop();
218 
219     // save speed
220     m_scrollspeed=speed;
221 
222     // start timer
223     if(m_scrollspeed>0 && m_scrolldirection!=wxALL)
224         m_scrollTimer.Start(speed,true);
225 }
226 
227 /** @return the speed of the scrolling */
GetScrollSpeed() const228 int wxLEDPanel::GetScrollSpeed() const
229 {
230     return m_scrollspeed;
231 }
232 
233 /** Sets the direction to scroll
234 * @param d the direction (wxALL for no scrolling)
235 */
SetScrollDirection(wxDirection d)236 void wxLEDPanel::SetScrollDirection(wxDirection d)
237 {
238     // the save way
239     m_scrollTimer.Stop();
240 
241     // save direction
242     m_scrolldirection=d;
243 
244     if(m_scrollspeed>0 && m_scrolldirection!=wxALL)
245         m_scrollTimer.Start(m_scrollspeed,true);
246 }
247 
248 /** @return the current direction of the scrolling (wxALL for no scrolling)*/
GetScrollDirection() const249 wxDirection wxLEDPanel::GetScrollDirection() const
250 {
251     return m_scrolldirection;
252 }
253 
254 /** Swaps the LED states
255 * @param invert if true, all active LEDs are drawn as inactiv and all inactiv drawn as activ
256 */
ShowInvertet(bool invert)257 void wxLEDPanel::ShowInvertet(bool invert)
258 {
259     if(m_invert==invert) return;
260 
261     m_invert=invert;
262     PrepareBackground();
263 }
264 
265 /** Should the inactive LEDs be drawn */
ShowInactivLEDs(bool show_inactivs)266 void wxLEDPanel::ShowInactivLEDs(bool show_inactivs)
267 {
268     if(m_show_inactivs==show_inactivs) return;
269 
270     m_show_inactivs=show_inactivs;
271     PrepareBackground();
272 }
273 
SetContentAlign(int a)274 void wxLEDPanel::SetContentAlign(int a)
275 {
276     // save value
277     m_align=a;
278 
279     // Reset the Horizontal position
280     ResetPos();
281 
282     // Reinit the field
283     m_field.Clear();
284     m_field.SetDatesAt(m_pos,m_content_mo);
285 }
286 
GetContentAlign() const287 int wxLEDPanel::GetContentAlign() const
288 {
289     return m_align;
290 }
291 
SetText(const wxString & text,int align)292 void wxLEDPanel::SetText(const wxString& text, int align)
293 {
294     // String emtpy
295     if(text.IsEmpty()) return;
296 
297     // the MO for the Text
298     MatrixObject* tmp=NULL;
299 
300     // save the align
301     if(align!=-1) m_align=align;
302 
303     // save the string
304     m_text=text;
305     m_aniFrameNr=-1;
306 
307     // get the MO for the text
308     if(m_align&wxALIGN_CENTER_HORIZONTAL)
309         tmp=m_font.GetMOForText(text,wxALIGN_CENTER_HORIZONTAL);
310     else if(m_align&wxALIGN_RIGHT)
311         tmp=m_font.GetMOForText(text,wxALIGN_RIGHT);
312     else tmp=m_font.GetMOForText(text);    // wxALIGN_LEFT
313 
314     // save the MO, and delete the tmp
315     m_content_mo.Init(*tmp);
316     delete tmp;
317 
318     // Find the place for the text
319     ResetPos();
320 
321     // Set in field
322     m_field.Clear();
323     m_field.SetDatesAt(m_pos,m_content_mo);
324 }
325 
326 /** @return the current text */
GetText() const327 wxString wxLEDPanel::GetText() const
328 {
329     return m_text;
330 }
331 
SetImage(const wxImage img)332 void wxLEDPanel::SetImage(const wxImage img)
333 {
334     if(!img.IsOk()) return;
335     m_text.Empty();
336 
337     m_content_mo.Init(img);
338     m_aniFrameNr=-1;
339 
340     // Find the place for the bitmap
341     ResetPos();
342 
343     // Set in field
344     m_field.Clear();
345     m_field.SetDatesAt(m_pos,m_content_mo);
346 }
347 
GetContentAsImage() const348 wxImage wxLEDPanel::GetContentAsImage() const
349 {
350     return m_content_mo.GetAsImage();
351 }
352 
SetAnimation(const wxAnimation ani)353 void wxLEDPanel::SetAnimation(const wxAnimation ani)
354 {
355     if(!ani.IsOk() || ani.GetFrameCount()==0) return;
356 
357     m_ani = ani;
358     m_text.Empty();
359     m_aniFrameNr = 0;
360 
361     m_content_mo.Init(ani.GetFrame(0));
362 
363     // Find the place for the bitmap
364     ResetPos();
365 
366     // Set in field
367     m_field.Clear();
368     m_field.SetDatesAt(m_pos,m_content_mo);
369 
370     // start timer
371     m_scrollTimer.Stop();
372     m_scrollspeed = m_ani.GetDelay(0);
373     m_scrollTimer.Start(m_scrollspeed,true);
374 }
375 
GetAnimation() const376 const wxAnimation wxLEDPanel::GetAnimation() const
377 {
378     return m_ani;
379 }
380 
SetContentPaddingLeft(int padLeft)381 void wxLEDPanel::SetContentPaddingLeft(int padLeft)
382 {
383     // Save value
384     m_padLeft=padLeft;
385 
386     // Reset the text position
387     ResetPos();
388 
389     // Reinit the field
390     m_field.Clear();
391     m_field.SetDatesAt(m_pos,m_content_mo);
392 }
393 
GetContentPaddingLeft() const394 int wxLEDPanel::GetContentPaddingLeft() const
395 {
396     return m_padLeft;
397 }
398 
SetContentPaddingRight(int padRight)399 void wxLEDPanel::SetContentPaddingRight(int padRight)
400 {
401     // Save the Value
402     m_padRight=padRight;
403 
404     // Reset the text position
405     ResetPos();
406 
407     // Reinit the field
408     m_field.Clear();
409     m_field.SetDatesAt(m_pos,m_content_mo);
410 }
411 
GetContentPaddingRight() const412 int wxLEDPanel::GetContentPaddingRight() const
413 {
414     return m_padRight;
415 }
416 
417 /** Sets the space between two letters
418 * @param leterSpace the space in points (one point = one LED)
419 */
SetLetterSpace(int letterSpace)420 void wxLEDPanel::SetLetterSpace(int letterSpace)
421 {
422     // is already this size?
423     if(m_font.GetLetterSpace()==letterSpace) return;
424 
425     m_font.SetLetterSpace(letterSpace);
426     Reset();
427 }
428 
429 /** @return the space between two letters in points */
GetLetterSpace() const430 int wxLEDPanel::GetLetterSpace() const
431 {
432     return m_font.GetLetterSpace();
433 }
434 
SetFontType(wxLEDFontType t)435 void wxLEDPanel::SetFontType(wxLEDFontType t)
436 {
437     if(m_font.GetFontType()==t) return;
438 
439     m_font.SetFontType(t);
440     Reset();
441 }
442 
GetFontType() const443 wxLEDFontType wxLEDPanel::GetFontType() const
444 {
445     return m_font.GetFontType();
446 }
447 
448 /** this draws the data on the Control */
DrawField(wxDC & dc,bool backgroundMode)449 void wxLEDPanel::DrawField(wxDC& dc, bool backgroundMode)
450 {
451     wxPoint point;
452     int w=m_ledsize.GetWidth()+m_padding;
453     int h=m_ledsize.GetHeight()+m_padding;
454 
455     // Z�hler f�r Zeile und Spalte
456     int x=0,y=0;
457 
458     // Pointer to avoid unnesecerie if blocks in the for block
459     wxMemoryDC* p_mdc_data=((m_invert)?((m_show_inactivs)?(&m_mdc_led_off):(&m_mdc_led_none)):(&m_mdc_led_on));
460     wxMemoryDC* p_mdc_nodata=((m_invert)?(&m_mdc_led_on):((m_show_inactivs)?(&m_mdc_led_off):(&m_mdc_led_none)));
461 
462     int l = m_field.GetLength();
463     int fw = m_field.GetWidth();
464     const char* field = m_field.GetData();
465     for(int i=0;i<l;++i)
466     {
467         // Koordinaten
468         point.x=x*w+m_padding;
469         point.y=y*h+m_padding;
470 
471         // zeichnen
472         if(field[i] && !backgroundMode)
473         {
474             dc.Blit(point.x,point.y,w,h,p_mdc_data,0,0);
475         }
476         else if(backgroundMode)
477         {
478             dc.Blit(point.x,point.y,w,h,p_mdc_nodata,0,0);
479         }
480 
481         // hochz�hlen
482         ++x;
483         if(x==fw) {++y; x=0;}
484     }
485 }
486 
487 /** Do nothing to avoid flicker */
OnEraseBackground(wxEraseEvent & event)488 void wxLEDPanel::OnEraseBackground(wxEraseEvent& event)
489 {
490     (void)event;
491 }
492 
OnPaint(wxPaintEvent & event)493 void wxLEDPanel::OnPaint(wxPaintEvent &event)
494 {
495     (void)event;
496     wxBufferedPaintDC dc(this);
497     //dc.SetBackground(this->GetBackgroundColour());
498     //dc.Clear();
499 
500     // background
501     dc.Blit(0,0,m_mdc_background.GetSize().GetWidth(),m_mdc_background.GetSize().GetHeight(),&m_mdc_background,0,0);
502     // field
503     DrawField(dc);
504 }
505 
ShiftLeft()506 void wxLEDPanel::ShiftLeft()
507 {
508     // new text Pos
509     m_pos.x--;
510 
511     // out of bound
512     if(m_pos.x+m_content_mo.GetWidth()<=0)
513     {
514         m_pos.x=m_field.GetWidth();
515         return;
516     }
517 
518     // Shift
519     m_field.ShiftLeft();
520 
521     // TODO check bounds!
522     // data for the new line
523     for(int i=0;i<m_content_mo.GetHeight();++i)
524     {
525         char d=m_content_mo.GetDataFrom(abs(m_pos.x-m_field.GetWidth()+1),i);
526         if(d>0) m_field.SetDataAt(m_field.GetWidth()-1,m_pos.y+i,d);
527     }
528 }
529 
ShiftRight()530 void wxLEDPanel::ShiftRight()
531 {
532     // new text Pos
533     m_pos.x++;
534     // out of bound
535     if(m_pos.x>=m_field.GetWidth())
536     {
537         m_pos.x=-m_content_mo.GetWidth();    // TODO without +1 error (in SetDatesAt??)
538         return;
539     }
540 
541     // Shift
542     m_field.ShiftRight();
543 
544     // TODO check bounds!
545     // TODO at first run -> false y-pos!
546     // data for the new line
547     for(int i=0;i<m_content_mo.GetHeight();++i)
548     {
549         char d=m_content_mo.GetDataFrom(abs(m_pos.x-m_field.GetWidth()+1),i);
550         if(d>0) m_field.SetDataAt(0,m_pos.y+i,d);
551     }
552 }
553 
ShiftUp()554 void wxLEDPanel::ShiftUp()
555 {
556     // new text Pos
557     m_pos.y--;
558     // out of bound
559     if(m_pos.y+m_content_mo.GetHeight()<=0)
560         m_pos.y=m_field.GetHeight();
561 
562     // TODO optimize with shift
563     m_field.Clear();
564     m_field.SetDatesAt(m_pos,m_content_mo);
565 }
566 
ShiftDown()567 void wxLEDPanel::ShiftDown()
568 {
569     // new text Pos
570     m_pos.y++;
571     // out of bound
572     if(m_pos.y>=m_field.GetHeight())
573         m_pos.y=-m_content_mo.GetHeight();
574 
575     // TODO optimize with shift
576     m_field.Clear();
577     m_field.SetDatesAt(m_pos,m_content_mo);
578 
579 }
580 
OnScrollTimer(wxTimerEvent & event)581 void wxLEDPanel::OnScrollTimer(wxTimerEvent& event)
582 {
583     (void)event;
584     if(m_scrollspeed==0||m_content_mo.IsEmpty()) return;
585 
586     // the save way
587     m_scrollTimer.Stop();
588 
589     if(m_aniFrameNr < 0)
590     {
591 
592         // Scroll
593         switch(m_scrolldirection)
594         {
595             case wxALL: return;
596             case wxLEFT: this->ShiftLeft(); break;
597             case wxRIGHT: this->ShiftRight(); break;
598             case wxDOWN: this->ShiftDown(); break;
599             case wxUP: this->ShiftUp(); break;
600             default: return;
601         }
602     }
603     else
604     {
605         m_aniFrameNr++;
606         if(m_aniFrameNr >= m_ani.GetFrameCount())
607             m_aniFrameNr=0;
608 
609         m_content_mo.Init(m_ani.GetFrame(m_aniFrameNr));
610         m_field.Clear();
611         m_field.SetDatesAt(m_pos,m_content_mo);
612         m_scrollspeed = m_ani.GetDelay(m_aniFrameNr);
613     }
614 
615     // Repaint
616     this->Refresh();
617 
618     // start timer again
619     m_scrollTimer.Start(m_scrollspeed,true);
620 }
621 
622 /** Resets the position of the content after scrolling */
ResetPos()623 void wxLEDPanel::ResetPos()
624 {
625     // has a text?
626     if(m_content_mo.GetData()==NULL) return;
627 
628     // horizontal text pos
629     if(m_scrolldirection!=wxLEFT && m_scrolldirection!=wxRIGHT)
630     {
631         if(m_align & wxALIGN_RIGHT)
632             m_pos.x=m_field.GetWidth()-m_content_mo.GetWidth()-m_padRight;
633         else if(m_align & wxALIGN_CENTER_HORIZONTAL)
634             m_pos.x=(m_field.GetWidth()-m_content_mo.GetWidth())/2;
635         else // wxALING_LEFT
636             m_pos.x=m_padLeft;
637     }
638     else if(m_scrolldirection==wxLEFT)
639         m_pos.x=m_field.GetWidth();
640     else if(m_scrolldirection==wxRIGHT)
641         m_pos.x=-m_content_mo.GetWidth();
642 
643     // vertical text pos
644     if(m_scrolldirection!=wxUP && m_scrolldirection!=wxDOWN)
645     {
646         if(m_align & wxALIGN_BOTTOM)
647             m_pos.y=m_field.GetHeight()-m_content_mo.GetHeight();
648         else if(m_align & wxALIGN_CENTER_VERTICAL)
649             m_pos.y=(m_field.GetHeight()-m_content_mo.GetHeight())/2;
650         else // wxALIGN TOP
651             m_pos.y=0;
652     }
653     else if(m_scrolldirection==wxUP)
654         m_pos.y=m_field.GetHeight();
655     else if(m_scrolldirection==wxDOWN)
656         m_pos.y=-m_content_mo.GetHeight();
657 }
658 
659 /** Prepares the backgroundimage, to optimze speed */
PrepareBackground()660 void wxLEDPanel::PrepareBackground()
661 {
662     wxSize s=DoGetBestSize();
663     wxBitmap bmpBG(s.GetWidth(),s.GetHeight());
664 
665     m_mdc_background.SelectObject(bmpBG);
666 
667     // clear the background
668     m_mdc_background.SetBackground(this->GetBackgroundColour());
669     m_mdc_background.Clear();
670 
671     if(m_invert || m_show_inactivs)
672         DrawField(m_mdc_background, true);
673 }
674 
675 // Red, Green, Blue, Yellow, Magenta, Cyan, Grey
676 const wxColour wxLEDPanel::s_colour[7]=
677     {    wxColour(255,0,0), wxColour(0,255,0), wxColour(0,0,255),
678         wxColour(255,255,0), wxColour(255,0,255), wxColour(0,255,255),
679         wxColour(128,128,128) };
680 
681 const wxColour wxLEDPanel::s_colour_dark[7]=
682     {    wxColour(128,0,0), wxColour(0,128,0), wxColour(0,0,128),
683         wxColour(128,128,0), wxColour(128,0,128), wxColour(0,128,128),
684         wxColour(64,64,64) };
685 
686 const wxColour wxLEDPanel::s_colour_verydark[7]=
687     {    wxColour(64,0,0), wxColour(0,64,0), wxColour(0,0,64),
688         wxColour(64,64,0), wxColour(64,0,64), wxColour(0,64,64),
689         wxColour(32,32,32) };
690 
691 const wxColour wxLEDPanel::s_colour_light[7]=
692     {    wxColour(255,128,128), wxColour(128,255,128), wxColour(128,128,255),
693         wxColour(255,255,128), wxColour(255,128,255), wxColour(128,255,255),
694         wxColour(192,192,192) };
695