Wakeup Light with touch user interface, anti-aliased Font, SD card access and RTC usage on STM32F746NG-DISCO board

Dependencies:   BSP_DISCO_F746NG_patch_fixed LCD_DISCO_F746NG TS_DISCO_F746NG FATFileSystem TinyJpgDec_interwork mbed-src

UI.cpp

Committer:
the_sz
Date:
2015-11-12
Revision:
7:dc29f6647486
Parent:
6:aa51cc3b9f90
Child:
9:fe2c9b3a312b

File content as of revision 7:dc29f6647486:

#include "WakeupLight.h"

#include "Images/ic_navigate_before_white_24dp_1x.h"
#include "Images/ic_notifications_none_white_48dp_1x.h"
#include "Images/ic_query_builder_white_48dp_1x.h"

#define CLIENT_COLOR_BG                 ((uint32_t)0xFF000000)
#define CLIENT_COLOR_FG                 ((uint32_t)0xFFD0D0D0)

#define HEADER_HEIGHT                   25
#define HEADER_COLOR_BG                 ((uint32_t)0xFF404040)
#define HEADER_COLOR_FG                 ((uint32_t)0xFFD3D3D3)

#define CLOCK_COLOR_BG                  ((uint32_t)0xFF000000)
#define CLOCK_COLOR_FG                  ((uint32_t)0xFF707070)

#define BUTTON_WIDTH                    100
#define BUTTON_HEIGHT                   60
#define BUTTON_SMALL_WIDTH              50
#define BUTTON_SMALL_HEIGHT             40
#define BUTTON_COLOR_BG                 ((uint32_t)0x00000000)
#define BUTTON_COLOR_FG                 CLIENT_COLOR_FG
#define BUTTON_COLOR_BG_START           0x0C2696
#define BUTTON_COLOR_BG_END             0x07185E
#define BUTTON_COLOR_BG_START_INACTIVE  0x515151
#define BUTTON_COLOR_BG_END_INACTIVE    0x333333

#define COLOR_BG                        ((uint32_t)0xFF000000)

#define MAX_BOXES_PER_LINE              3
#define BOX_SPACING                     10
#define BOX_TEXT_SPACING                10
#define BOX_COLOR_BG                    BUTTON_COLOR_BG
#define BOX_COLOR_FG                    CLIENT_COLOR_FG
#define BOX_COLOR_BG_START              BUTTON_COLOR_BG_START
#define BOX_COLOR_BG_END                BUTTON_COLOR_BG_END

LCD_DISCO_F746NG                        uiLcd;
TS_DISCO_F746NG                         uiTs;
uint16_t                                uiLastTouchX;
uint16_t                                uiLastTouchY;
UI_STRUCT                               *uiCurrent=NULL;
UI_STRUCT                               uiClock;
UI_STRUCT                               uiClockInWords;
UI_STRUCT                               uiColorTest;
UI_STRUCT                               uiWakeup;
UI_STRUCT                               uiMain;
static UI_BOX_LIST_ITEM_STRUCT          uiMainItems[]=
{
    { "Clock", ic_query_builder_white_48dp_1x }, { "Clock\nWith Words", ic_query_builder_white_48dp_1x }, { "Adjust\nTimers", ic_notifications_none_white_48dp_1x }, { "Lights On", NULL }, { "Lights Off", NULL }, { "Color Test", NULL }
};

//
// helper function
//
void UI_ShowClearClientRect(void)
{
    uiLcd.SetTextColor(CLIENT_COLOR_BG);
    uiLcd.FillRect(0,HEADER_HEIGHT,uiLcd.GetXSize(),uiLcd.GetYSize()-HEADER_HEIGHT);
}

void UI_ShowDisplayText(int16_t x,int16_t y,char *text)
{
    int16_t             xStart;
    int16_t             charWidth;
    int16_t             charHeight;

    xStart=x;
    charHeight=uiLcd.GetFont()->Height+3;
    charWidth=uiLcd.GetFont()->Width;
    
    while ((*text)!='\0')
    {
        if ((*text)=='\n')
        {
            y+=charHeight;
            x=xStart;
        }
        else
        {
            uiLcd.DisplayChar(x,y,*text);
            x+=charWidth;
        }

        text++;
    }
}

void UI_ShowDisplayTextCenter(int16_t x,int16_t y,int16_t width,int16_t height,char *text)
{
    int16_t             charWidth;
    int16_t             charHeight;

    charHeight=uiLcd.GetFont()->Height+3;
    charWidth=uiLcd.GetFont()->Width;

    x+=((width-(strlen(text)*charWidth))/2);
    y+=(((height-charHeight)/2)+3);                 // +3 to have it more centered

    while ((*text)!='\0')
    {
        uiLcd.DisplayChar(x,y,*text);
        x+=charWidth;

        text++;
    }
}

void UI_ShowDrawGradientButton(uint16_t x,uint16_t y,uint16_t width,uint16_t height,uint32_t colorStart,uint32_t colorEnd)
{
    float       deltaRed;
    float       deltaGreen;
    float       deltaBlue;
    float       red;
    float       green;
    float       blue;
    uint16_t    offset;
    uint32_t    color;

    deltaRed=(float)((int32_t)(colorEnd & 0xFF) - (int32_t)(colorStart & 0xFF))/height;
    deltaGreen=(float)((int32_t)((colorEnd >> 8) & 0xFF) - (int32_t)((colorStart >> 8)& 0xFF))/height;
    deltaBlue=(float)((int32_t)((colorEnd >> 16) & 0xFF) - (int32_t)((colorStart >> 16)& 0xFF))/height;

    red=colorStart & 0xFF;
    green=((colorStart >> 8)& 0xFF);
    blue=((colorStart >> 16)& 0xFF);

    for (offset=0;offset<height;offset++)
    {
        color=(uint8_t)red | (((uint8_t)green)<<8) | (((uint8_t)blue)<<16) | (((uint32_t)0xFF)<<24);
        uiLcd.SetTextColor(color);

        if ((offset==0) || (offset==(height-1)))
            uiLcd.DrawHLine(x+1,y+offset,width-2);
        else
            uiLcd.DrawHLine(x,y+offset,width);

        red+=deltaRed;
        green+=deltaGreen;
        blue+=deltaBlue;
    }
}

void UI_ShowDrawButtonEx(uint16_t x,uint16_t y,uint16_t width,uint16_t height,char *text,bool active)
{
    // paint button background
    if (active==true)
        UI_ShowDrawGradientButton(x,y,width,height,BUTTON_COLOR_BG_START,BUTTON_COLOR_BG_END);
    else
        UI_ShowDrawGradientButton(x,y,width,height,BUTTON_COLOR_BG_START_INACTIVE,BUTTON_COLOR_BG_END_INACTIVE);

    // paint button text
    uiLcd.SetFont(&display_font_12x22);
    uiLcd.SetBackColor(BUTTON_COLOR_BG);
    uiLcd.SetTextColor(BUTTON_COLOR_FG);

    UI_ShowDisplayTextCenter(x,y,width,height,text);
}

void UI_ShowDrawButton(uint16_t x,uint16_t y,uint16_t width,uint16_t height,char *text)
{
    UI_ShowDrawButtonEx(x,y,width,height,text,true);
}

void UI_DrawBitmapWithAlpha(uint32_t Xpos,uint32_t Ypos,uint8_t *pbmp)
{
  uint32_t index = 0, width = 0, height = 0, bit_pixel = 0;
  uint32_t x;
  uint32_t part;
  uint32_t color[3];
  uint32_t value;

  /* Get bitmap data address offset */
  index = *(__IO uint16_t *) (pbmp + 10);
  index |= (*(__IO uint16_t *) (pbmp + 12)) << 16;
  
  /* Read bitmap width */
  width = *(uint16_t *) (pbmp + 18);
  width |= (*(uint16_t *) (pbmp + 20)) << 16;
  
  /* Read bitmap height */
  height = *(uint16_t *) (pbmp + 22);
  height |= (*(uint16_t *) (pbmp + 24)) << 16; 
  
  /* Read bit/pixel */
  bit_pixel = *(uint16_t *) (pbmp + 28);   
  
  /* Get the layer pixel format */    
  if ((bit_pixel/8) != 4)
  {
    DPrintf("UI_DrawBitmapWithAlpha: This is no alpha picture.\r\n");
    return;
  }

  /* Bypass the bitmap header */
  pbmp += (index + (width * (height - 1) * (bit_pixel/8)));  

  for(index=0; index < height; index++)
  {
    for (x=0;x<width;x++)
    {
      value=uiLcd.ReadPixel(Xpos+x,Ypos+index);

      color[0]=value & 0xFF;
      color[1]=(value >> 8) & 0xFF;
      color[2]=(value >> 16) & 0xFF;

      if (pbmp[3]>0)
      {
        // add red
        part=(color[0]+(pbmp[0]*pbmp[3]/255));
        if (part>255)
          part=255;
        color[0]=part;
    
        // add green
        part=(color[1]+(pbmp[1]*pbmp[3]/255));
        if (part>255)
          part=255;
        color[1]=part;
    
        // add blue
        part=(color[2]+(pbmp[2]*pbmp[3]/255));
        if (part>255)
          part=255;
        color[2]=part;

        // draw pixel
        uiLcd.DrawPixel(Xpos+x,Ypos+index,color[0] | (color[1] << 8) | (color[2] << 16) | ((uint32_t)0xFF << 24));
      }
      
      pbmp+=4;
    }

    /* Increment the source and destination buffers */
    pbmp -= 2*(width*(bit_pixel/8));
  } 
}

//
// box list
//
void UI_ShowBoxList(bool initial)
{
    if (initial==true)
    {
        int8_t          lines;
        int8_t          columns;
        int8_t          box;
        int16_t         width;
        int16_t         height;
        int16_t         startX;
        int16_t         startY;

        // fill background
        UI_ShowClearClientRect();

        // paint boxes
        if (uiCurrent->data.boxList.count<=MAX_BOXES_PER_LINE)
            lines=1;
        else
            lines=2;

        columns=((uiCurrent->data.boxList.count + (lines-1)) / lines);

        width=(uiLcd.GetXSize() - BOX_SPACING) / columns;
        height=(uiLcd.GetYSize() - HEADER_HEIGHT - BOX_SPACING) / lines;

        for (box=0;box<uiCurrent->data.boxList.count;box++)
        {
            startX=BOX_SPACING+(width*(box % columns));
            startY=HEADER_HEIGHT+BOX_SPACING+(height*(box/columns));

            // paint box background
            UI_ShowDrawGradientButton(startX,startY,width-BOX_SPACING,height-BOX_SPACING,BOX_COLOR_BG_START,BOX_COLOR_BG_END);

            // paint box text
            uiLcd.SetFont(&display_font_12x22);
            uiLcd.SetBackColor(BOX_COLOR_BG);
            uiLcd.SetTextColor(BOX_COLOR_FG);
            UI_ShowDisplayText(startX+BOX_TEXT_SPACING,startY+BOX_TEXT_SPACING,uiCurrent->data.boxList.items[box].name);
            
            // draw icon
            if (uiCurrent->data.boxList.items[box].image!=NULL)
                UI_DrawBitmapWithAlpha(startX+width-BOX_SPACING-48,startY+height-BOX_SPACING-48,uiCurrent->data.boxList.items[box].image);
        }
    }
}

void UI_ClickBoxList(uint16_t x,uint16_t y)
{
    int8_t          lines;
    int8_t          columns;
    int8_t          box;
    int16_t         width;
    int16_t         height;
    int16_t         startX;
    int16_t         startY;

    // detect at which box was clicked
    if (uiCurrent->data.boxList.count<=MAX_BOXES_PER_LINE)
        lines=1;
    else
        lines=2;

    columns=((uiCurrent->data.boxList.count + (lines-1)) / lines);

    width=(uiLcd.GetXSize() - BOX_SPACING) / columns;
    height=(uiLcd.GetYSize() - HEADER_HEIGHT - BOX_SPACING) / lines;

    for (box=0;box<uiCurrent->data.boxList.count;box++)
    {
        startX=BOX_SPACING+(width*(box % columns));
        startY=HEADER_HEIGHT+BOX_SPACING+(height*(box/columns));

        if (        (x>=startX) && (x<(startX+width-BOX_SPACING))
                &&
                    (y>=startY) && (y<(startY+height-BOX_SPACING))
           )
        {
            uiCurrent->handler(UR_CLICK,box,uiCurrent);
            break;
        }
    }
}

//
// message box
//
void UI_ShowMessageBox(bool initial)
{
    if (initial==true)
    {
        // fill background
        UI_ShowClearClientRect();
    }
}

void UI_ClickMessageBox(uint16_t x,uint16_t y)
{
    uint32_t                    index;

    // detect at which button was clicked
    index=0;

    uiCurrent->handler(UR_CLICK,index,uiCurrent);
}

//
// clock
//
void UI_ShowClock(bool initial)
{
    char                        buffer[100];
    struct tm                   *tmStruct;
    time_t                      timeValue;

    if (initial==true)
    {
        // fill background
        uiLcd.SetTextColor(CLOCK_COLOR_BG);
        uiLcd.FillRect(0,0,uiLcd.GetXSize(),uiLcd.GetYSize());
    }

    // show clock
    uiLcd.SetFont(&display_font_12x22);
    uiLcd.SetBackColor(CLOCK_COLOR_BG);
    uiLcd.SetTextColor(CLOCK_COLOR_FG);
    timeValue=time(NULL);
    tmStruct=localtime(&timeValue);
    snprintf(buffer,sizeof(buffer),"%u:%02u:%02u",tmStruct->tm_hour,tmStruct->tm_min,tmStruct->tm_sec);
    uiLcd.DisplayStringAt(0,100,(uint8_t *)buffer,CENTER_MODE);
}

void UI_ClickClock(uint16_t x,uint16_t y)
{
    // exit view
    UI_Show(&uiMain);
}

//
// clock in words
//
void UI_ShowClockInWords(bool initial)
{
    if (initial==true)
    {
        // fill background
        UI_ShowClearClientRect();
    }

    // show clock in words
    uiLcd.SetFont(&display_font_12x22);
    uiLcd.SetBackColor(CLIENT_COLOR_BG);
    uiLcd.SetTextColor(CLIENT_COLOR_FG);
    uiLcd.DisplayStringAt(5,30,(uint8_t *)"UM F\x9ANF ZEHN VIERTEL HALB",LEFT_MODE);
    uiLcd.DisplayStringAt(5,60,(uint8_t *)"NACH VOR",LEFT_MODE);
    uiLcd.DisplayStringAt(5,90,(uint8_t *)"EINS ZWEI DREI VIER",LEFT_MODE);
    uiLcd.DisplayStringAt(5,120,(uint8_t *)"F\x9ANF SECHS SIEBEN ACHT",LEFT_MODE);
    uiLcd.DisplayStringAt(5,150,(uint8_t *)"NEUN ZEHN ELF ZW\x99LF",LEFT_MODE);

    // draw charset
    /*
    int x;
    for (x=0x80;x<=0xFF;x++)
    {
        uiLcd.DisplayChar(1+(((x-0x80) % 16)*14),30+(((x-0x80)/16)*20),x);
    }
    */
}

void UI_ClickClockInWords(uint16_t x,uint16_t y)
{
    // exit view
    UI_Show(&uiMain);
}

//
// value adjust
//
void UI_ShowValueAdjust(bool initial)
{
    char                        buffer[20];

    if (initial==true)
    {
        // fill background
        UI_ShowClearClientRect();

        if (uiCurrent->data.valueAdjust.count==10)
        {
            UI_ShowDrawButton(60+(0*(BUTTON_WIDTH+15)),40,BUTTON_WIDTH,BUTTON_HEIGHT,"+");
            UI_ShowDrawButton(205+(0*(BUTTON_WIDTH+15)),40,BUTTON_WIDTH,BUTTON_HEIGHT,"+");
            UI_ShowDrawButton(205+(1*(BUTTON_WIDTH+15)),40,BUTTON_WIDTH,BUTTON_HEIGHT,"+");

            UI_ShowDisplayTextCenter(132,92,BUTTON_WIDTH,BUTTON_HEIGHT,":");

            UI_ShowDrawButton(60+(0*(BUTTON_WIDTH+15)),140,BUTTON_WIDTH,BUTTON_HEIGHT,"-");
            UI_ShowDrawButton(205+(0*(BUTTON_WIDTH+15)),140,BUTTON_WIDTH,BUTTON_HEIGHT,"-");
            UI_ShowDrawButton(205+(1*(BUTTON_WIDTH+15)),140,BUTTON_WIDTH,BUTTON_HEIGHT,"-");
        }
        else if (uiCurrent->data.valueAdjust.count==4)
        {
            UI_ShowDrawButton(17+(0*(BUTTON_WIDTH+15)),40,BUTTON_WIDTH,BUTTON_HEIGHT,"+");
            UI_ShowDrawButton(17+(1*(BUTTON_WIDTH+15)),40,BUTTON_WIDTH,BUTTON_HEIGHT,"+");
            UI_ShowDrawButton(17+(2*(BUTTON_WIDTH+15)),40,BUTTON_WIDTH,BUTTON_HEIGHT,"+");
            UI_ShowDrawButton(17+(3*(BUTTON_WIDTH+15)),40,BUTTON_WIDTH,BUTTON_HEIGHT,"+");

            UI_ShowDrawButton(17+(0*(BUTTON_WIDTH+15)),205,BUTTON_WIDTH,BUTTON_HEIGHT,"-");
            UI_ShowDrawButton(17+(1*(BUTTON_WIDTH+15)),205,BUTTON_WIDTH,BUTTON_HEIGHT,"-");
            UI_ShowDrawButton(17+(2*(BUTTON_WIDTH+15)),205,BUTTON_WIDTH,BUTTON_HEIGHT,"-");
            UI_ShowDrawButton(17+(3*(BUTTON_WIDTH+15)),205,BUTTON_WIDTH,BUTTON_HEIGHT,"-");
        }
    }

    uiLcd.SetBackColor(CLIENT_COLOR_BG);
    uiLcd.SetTextColor(CLIENT_COLOR_FG);

    if (uiCurrent->data.valueAdjust.count==10)
    {
        snprintf(buffer,sizeof(buffer),"% 2u",uiCurrent->data.valueAdjust.values[0]);
        UI_ShowDisplayTextCenter(60+(0*(BUTTON_WIDTH+15)),92,BUTTON_WIDTH,BUTTON_HEIGHT,buffer);

        snprintf(buffer,sizeof(buffer),"%u",uiCurrent->data.valueAdjust.values[1]);
        UI_ShowDisplayTextCenter(205+(0*(BUTTON_WIDTH+15)),92,BUTTON_WIDTH,BUTTON_HEIGHT,buffer);

        snprintf(buffer,sizeof(buffer),"%u",uiCurrent->data.valueAdjust.values[2]);
        UI_ShowDisplayTextCenter(205+(1*(BUTTON_WIDTH+15)),92,BUTTON_WIDTH,BUTTON_HEIGHT,buffer);

        if (uiCurrent->data.valueAdjust.values[3]==1)
            UI_ShowDrawButton(17+(0*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Mo");
        else
            UI_ShowDrawButtonEx(17+(0*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Mo",false);

        if (uiCurrent->data.valueAdjust.values[4]==1)
            UI_ShowDrawButton(17+(1*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Di");
        else
            UI_ShowDrawButtonEx(17+(1*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Di",false);

        if (uiCurrent->data.valueAdjust.values[5]==1)
            UI_ShowDrawButton(17+(2*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Mi");
        else
            UI_ShowDrawButtonEx(17+(2*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Mi",false);

        if (uiCurrent->data.valueAdjust.values[6]==1)
            UI_ShowDrawButton(17+(3*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Do");
        else
            UI_ShowDrawButtonEx(17+(3*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Do",false);

        if (uiCurrent->data.valueAdjust.values[7]==1)
            UI_ShowDrawButton(17+(4*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Fr");
        else
            UI_ShowDrawButtonEx(17+(4*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Fr",false);

        if (uiCurrent->data.valueAdjust.values[8]==1)
            UI_ShowDrawButton(17+(5*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Sa");
        else
            UI_ShowDrawButtonEx(17+(5*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"Sa",false);

        if (uiCurrent->data.valueAdjust.values[9]==1)
            UI_ShowDrawButton(17+(6*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"So");
        else
            UI_ShowDrawButtonEx(17+(6*(BUTTON_SMALL_WIDTH+15)),215,BUTTON_SMALL_WIDTH,BUTTON_SMALL_HEIGHT,"So",false);
    }
    else if (uiCurrent->data.valueAdjust.count==4)
    {
        snprintf(buffer,sizeof(buffer),"0x%02X",uiCurrent->data.valueAdjust.values[0]);
        UI_ShowDisplayTextCenter(17+(0*(BUTTON_WIDTH+15)),125,BUTTON_WIDTH,BUTTON_HEIGHT,buffer);

        snprintf(buffer,sizeof(buffer),"0x%02X",uiCurrent->data.valueAdjust.values[1]);
        UI_ShowDisplayTextCenter(17+(1*(BUTTON_WIDTH+15)),125,BUTTON_WIDTH,BUTTON_HEIGHT,buffer);

        snprintf(buffer,sizeof(buffer),"0x%02X",uiCurrent->data.valueAdjust.values[2]);
        UI_ShowDisplayTextCenter(17+(2*(BUTTON_WIDTH+15)),125,BUTTON_WIDTH,BUTTON_HEIGHT,buffer);

        snprintf(buffer,sizeof(buffer),"0x%02X",uiCurrent->data.valueAdjust.values[3]);
        UI_ShowDisplayTextCenter(17+(3*(BUTTON_WIDTH+15)),125,BUTTON_WIDTH,BUTTON_HEIGHT,buffer);
    }
}

void UI_ClickValueAdjust(uint16_t x,uint16_t y)
{
    int32_t                     index;

    // detect at which button was clicked
    index=-1;

    if (uiCurrent->data.valueAdjust.count==10)
    {
        if ((y>=40) && (y<(40+BUTTON_HEIGHT)))
        {
            if ((x>=(60+(0*(BUTTON_WIDTH+15)))) && (x<(17+(0*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=0;
            else if ((x>=(205+(0*(BUTTON_WIDTH+15)))) && (x<(205+(0*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=1;
            else if ((x>=(205+(1*(BUTTON_WIDTH+15)))) && (x<(205+(1*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=2;
        }
        else if ((y>=140) && (y<(140+BUTTON_HEIGHT)))
        {
            if ((x>=(60+(0*(BUTTON_WIDTH+15)))) && (x<(60+(0*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=3;
            else if ((x>=(205+(0*(BUTTON_WIDTH+15)))) && (x<(205+(0*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=4;
            else if ((x>=(205+(1*(BUTTON_WIDTH+15)))) && (x<(205+(1*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=5;
        }
        else if ((y>=215) && (y<(215+BUTTON_SMALL_HEIGHT)))
        {
            if ((x>=(17+(0*(BUTTON_SMALL_WIDTH+15)))) && (x<(17+(0*(BUTTON_SMALL_WIDTH+15))+BUTTON_SMALL_WIDTH)))
                index=6;
            else if ((x>=(17+(1*(BUTTON_SMALL_WIDTH+15)))) && (x<(17+(1*(BUTTON_SMALL_WIDTH+15))+BUTTON_SMALL_WIDTH)))
                index=7;
            else if ((x>=(17+(2*(BUTTON_SMALL_WIDTH+15)))) && (x<(17+(2*(BUTTON_SMALL_WIDTH+15))+BUTTON_SMALL_WIDTH)))
                index=8;
            else if ((x>=(17+(3*(BUTTON_SMALL_WIDTH+15)))) && (x<(17+(3*(BUTTON_SMALL_WIDTH+15))+BUTTON_SMALL_WIDTH)))
                index=9;
            else if ((x>=(17+(4*(BUTTON_SMALL_WIDTH+15)))) && (x<(17+(4*(BUTTON_SMALL_WIDTH+15))+BUTTON_SMALL_WIDTH)))
                index=10;
            else if ((x>=(17+(5*(BUTTON_SMALL_WIDTH+15)))) && (x<(17+(5*(BUTTON_SMALL_WIDTH+15))+BUTTON_SMALL_WIDTH)))
                index=11;
            else if ((x>=(17+(6*(BUTTON_SMALL_WIDTH+15)))) && (x<(17+(6*(BUTTON_SMALL_WIDTH+15))+BUTTON_SMALL_WIDTH)))
                index=12;
        }
    }
    else if (uiCurrent->data.valueAdjust.count==4)
    {
        if ((y>=40) && (y<(40+BUTTON_HEIGHT)))
        {
            if ((x>=(17+(0*(BUTTON_WIDTH+15)))) && (x<(17+(0*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=0;
            else if ((x>=(17+(1*(BUTTON_WIDTH+15)))) && (x<(17+(1*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=1;
            else if ((x>=(17+(2*(BUTTON_WIDTH+15)))) && (x<(17+(2*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=2;
            else if ((x>=(17+(3*(BUTTON_WIDTH+15)))) && (x<(17+(3*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=3;
        }
        else if ((y>=205) && (y<(205+BUTTON_HEIGHT)))
        {
            if ((x>=(17+(0*(BUTTON_WIDTH+15)))) && (x<(17+(0*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=4;
            else if ((x>=(17+(1*(BUTTON_WIDTH+15)))) && (x<(17+(1*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=5;
            else if ((x>=(17+(2*(BUTTON_WIDTH+15)))) && (x<(17+(2*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=6;
            else if ((x>=(17+(3*(BUTTON_WIDTH+15)))) && (x<(17+(3*(BUTTON_WIDTH+15))+BUTTON_WIDTH)))
                index=7;
        }
    }

    if (index!=-1)
        uiCurrent->handler(UR_CLICK,index,uiCurrent);
}

//
// common
//
void UI_Init(void)
{
    uiCurrent=NULL;

    uiLastTouchX=0;
    uiLastTouchY=0;

    uiLcd.Init();
    uiLcd.Clear(COLOR_BG);

    if (uiTs.Init(uiLcd.GetXSize(),uiLcd.GetYSize())==TS_OK)
        DPrintf("UI_Init: Size: %ux%u.\r\n",uiLcd.GetXSize(),uiLcd.GetYSize());
    else
        DPrintf("UI_Init: Can't init touch screen.\r\n");

    // setup ui structs
    uiClock.flags=UI_FLAG_TYPE_CLOCK;
    uiClock.handler=NULL;

    uiClockInWords.flags=UI_FLAG_TYPE_CLOCK_IN_WORDS;
    uiClockInWords.handler=NULL;

    uiColorTest.flags=UI_FLAG_TYPE_VALUE_ADJUST;
    uiColorTest.handler=UI_ColorTestHandler;
    uiColorTest.data.valueAdjust.count=4;
    uiColorTest.data.valueAdjust.values[0]=0x80;
    uiColorTest.data.valueAdjust.values[1]=0x80;
    uiColorTest.data.valueAdjust.values[2]=0x80;
    uiColorTest.data.valueAdjust.values[3]=0x80;

    uiMain.flags=UI_FLAG_TYPE_BOX_LIST;
    uiMain.handler=UI_MainHandler;
    uiMain.data.boxList.items=uiMainItems;
    uiMain.data.boxList.count=COUNT_OF(uiMainItems);

    UI_Show(&uiMain);
}

void UI_ShowChrome(bool initial)
{
    char                        buffer[100];
    struct tm                   *tmStruct;
    time_t                      timeValue;

    if (initial==true)
    {
        // fill background
        uiLcd.SetTextColor(HEADER_COLOR_BG);
        uiLcd.FillRect(0,0,uiLcd.GetXSize(),HEADER_HEIGHT);

        uiLcd.SetFont(&display_font_12x22);
        uiLcd.SetBackColor(HEADER_COLOR_BG);
        uiLcd.SetTextColor(HEADER_COLOR_FG);
        if ((uiCurrent->flags & UI_FLAG_HAS_BACK_BUTTON)!=0)
            UI_DrawBitmapWithAlpha(0,1,ic_navigate_before_white_24dp_1x);
    }

    // show clock
    uiLcd.SetFont(&display_font_12x22);
    uiLcd.SetBackColor(HEADER_COLOR_BG);
    uiLcd.SetTextColor(HEADER_COLOR_FG);
    timeValue=time(NULL);
    tmStruct=localtime(&timeValue);
    snprintf(buffer,sizeof(buffer),"%u:%02u",tmStruct->tm_hour,tmStruct->tm_min);
    uiLcd.DisplayStringAt(0,3,(uint8_t *)buffer,CENTER_MODE);

    // show next alarm
    //XXX
}

void UI_Update(bool initial)
{
    uint8_t     typeFlag;

    if ((uiCurrent->flags & UI_FLAG_NEEDS_CHROME)!=0)
        UI_ShowChrome(initial);

    typeFlag=uiCurrent->flags & ~(UI_FLAG_NEEDS_CHROME | UI_FLAG_HAS_BACK_BUTTON);
    if ((typeFlag & UI_FLAG_TYPE_BOX_LIST)!=0)
        UI_ShowBoxList(initial);
    else if ((typeFlag & UI_FLAG_TYPE_MESSAGE_BOX)!=0)
        UI_ShowMessageBox(initial);
    else if ((typeFlag & UI_FLAG_TYPE_CLOCK)!=0)
        UI_ShowClock(initial);
    else if ((typeFlag & UI_FLAG_TYPE_CLOCK_IN_WORDS)!=0)
        UI_ShowClockInWords(initial);
    else if ((typeFlag & UI_FLAG_TYPE_VALUE_ADJUST)!=0)
        UI_ShowValueAdjust(initial);
}

void UI_Show(UI_STRUCT *ui)
{
    DPrintf_("UI_Show: 0x%X.\r\n",ui->flags);

    uiCurrent=ui;

    if (uiCurrent->handler!=NULL)
        uiCurrent->handler(UR_SHOW,0,uiCurrent);

    UI_Update(true);
}

void UI_Click(uint16_t x,uint16_t y)
{
    uint8_t     typeFlag;

    DPrintf_("UI_Click: %u x %u.\r\n",x,y);

    if ((uiCurrent->flags & UI_FLAG_HAS_BACK_BUTTON)!=0)
    {
        if ((y<40) && (x<40))
        {
            if (uiCurrent->handler!=NULL)
            {
                uiCurrent->handler(UR_CLICK,-1,uiCurrent);
                return;
            }
        }
    }

    typeFlag=uiCurrent->flags & ~(UI_FLAG_NEEDS_CHROME | UI_FLAG_HAS_BACK_BUTTON);
    if ((typeFlag & UI_FLAG_TYPE_BOX_LIST)!=0)
        UI_ClickBoxList(x,y);
    else if ((typeFlag & UI_FLAG_TYPE_MESSAGE_BOX)!=0)
        UI_ClickMessageBox(x,y);
    else if ((typeFlag & UI_FLAG_TYPE_CLOCK)!=0)
        UI_ClickClock(x,y);
    else if ((typeFlag & UI_FLAG_TYPE_CLOCK_IN_WORDS)!=0)
        UI_ClickClockInWords(x,y);
    else if ((typeFlag & UI_FLAG_TYPE_VALUE_ADJUST)!=0)
        UI_ClickValueAdjust(x,y);
}

void UI_Poll(void)
{
    TS_StateTypeDef             tsState;

    uiTs.GetState(&tsState);
    if (tsState.touchDetected>0)
    {
        if (        (ABS(uiLastTouchX-tsState.touchX[0])>4)
                ||
                    (ABS(uiLastTouchY-tsState.touchY[0])>4)
           )
        {
            DPrintf_("UI_Poll: #%u - %ux%u.\r\n",tsState.touchDetected,tsState.touchX[0],tsState.touchY[0]);

            uiLastTouchX=tsState.touchX[0];
            uiLastTouchY=tsState.touchY[0];
    
            if (uiCurrent!=NULL)
                UI_Click(tsState.touchX[0],tsState.touchY[0]);
        }
    }
    else
    {
        uiLastTouchX=0;
        uiLastTouchY=0;
    }
  
    if (uiCurrent!=NULL)
    {
        if (uiCurrent->handler!=NULL)
            uiCurrent->handler(UR_TIMER,0,uiCurrent);

        UI_Update(false);
    }
}