BigW Consortium Gitlab

Commit fef902f1 by Gabor Kiss-Vamosi

keyboard device support

parent a006082a
......@@ -41,7 +41,7 @@ static lv_indev_t *indev_list = NULL;
* @param driver pointer to an initialized 'lv_indev_drv_t' variable (can be local variable)
* @return pointer to the new input device or NULL on error
*/
lv_indev_t * lv_indev_register(lv_indev_drv_t *driver)
lv_indev_t * lv_indev_add(lv_indev_drv_t *driver)
{
lv_indev_t *node;
......@@ -76,8 +76,8 @@ lv_indev_t * lv_indev_next(lv_indev_t * indev)
if(indev == NULL) {
return indev_list;
} else {
if(indev_list->next == NULL) return NULL;
else return indev_list->next;
if(indev->next == NULL) return NULL;
else return indev->next;
}
}
......
......@@ -30,62 +30,75 @@ extern "C" {
/*Possible input device types*/
typedef enum {
LV_INDEV_TYPE_TOUCH, /*Touch pad*/
LV_INDEV_TYPE_POINTER, /*Mouse or similar pointer device*/
LV_INDEV_TYPE_TOUCHPAD, /*Touch pad*/
LV_INDEV_TYPE_MOUSE, /*Mouse or similar pointer device*/
LV_INDEV_TYPE_KEYPAD, /*Keypad or keyboard*/
LV_INDEV_TYPE_BUTTON, /*Hardware button assigned to a point on the screen*/
LV_INDEV_TYPE_BUTTON, /*External (hardware) button assigned to a point on the screen*/
} lv_hal_indev_type_t;
/*State for input devices*/
/*States for input devices*/
typedef enum {
LV_INDEV_EVENT_REL,
LV_INDEV_EVENT_PR
}lv_indev_event_t;
/*Data read from an input device. */
/*Data type when an input device is read */
typedef struct {
union {
point_t point; /*For INDEV_TYPE_TOUCH, INDEV_TYPE_POINTER, INDEV_TYPE_BUTTON*/
uint32_t key; /*For INDEV_TYPE_BUTTON*/
point_t point; /*For INDEV_TYPE_TOUCHPAD, INDEV_TYPE_POINTER, LV_INDEV_TYPE_BUTTON*/
uint32_t key; /*For INDEV_TYPE_KEYPAD*/
};
lv_indev_event_t state;
lv_indev_event_t state; /*LV_INDEV_EVENT_REL or LV_INDEV_EVENT_PR*/
}lv_indev_data_t;
/*Initialized by the user and registered by 'lv_hal_indev_drv_register'*/
/*Initialized by the user and registered by 'lv_indev_add()'*/
typedef struct {
const char * name; /*Input device name*/
lv_hal_indev_type_t type; /*Input device type*/
bool (*get_data)(lv_indev_data_t * data); /*Function pointer to read data. Return 'true' if there is still data to be read (buffered)*/
bool (*get_data)(lv_indev_data_t *data); /*Function pointer to read data. Return 'true' if there is still data to be read (buffered)*/
}lv_indev_drv_t;
struct __LV_OBJ_T;
struct _lv_obj_t;
typedef struct _lv_indev_state_t
{
typedef struct _lv_indev_state_t {
lv_indev_event_t event;
union {
struct {
point_t act_point;
point_t last_point;
point_t vect;
point_t vect_sum;
struct __LV_OBJ_T * act_obj;
struct __LV_OBJ_T * last_obj;
uint32_t pr_timestamp; /*Pressed time stamp*/
uint32_t longpr_rep_timestamp; /*Long press repeat time stamp*/
struct _lv_obj_t * act_obj;
struct _lv_obj_t * last_obj;
/*Flags*/
uint8_t drag_range_out :1;
uint8_t drag_in_prog :1;
uint8_t long_pr_sent :1;
uint8_t wait_unil_release :1;
};
uint32_t last_key;
};
uint32_t pr_timestamp; /*Pressed time stamp*/
uint32_t longpr_rep_timestamp; /*Long press repeat time stamp*/
/*Flags*/
uint8_t long_pr_sent :1;
uint8_t reset_query :1;
uint8_t disabled :1;
}lv_indev_state_t;
struct _lv_obj_t;
struct _lv_group_t;
typedef struct _lv_indev_t {
lv_indev_drv_t driver;
lv_indev_state_t state;
struct __LV_OBJ_T *cursor;
union {
struct _lv_obj_t *cursor;
struct _lv_group_t *group; /*Keypad destination group*/
};
struct _lv_indev_t *next;
} lv_indev_t;
......@@ -93,18 +106,17 @@ typedef struct _lv_indev_t {
* GLOBAL PROTOTYPES
**********************/
/**
* Register an initialized input device driver.
* @param driver pointer to an initialized 'lv_indev_drv_t' variable (can be local variable)
* @return pointer to the new input device or NULL on error
*/
lv_indev_t * lv_indev_register(lv_indev_drv_t *driver);
lv_indev_t * lv_indev_add(lv_indev_drv_t *driver);
/**
* Get the next input device.
* @param indev pointer to the current input device. NULL to initialize.
* @return the next input devise or NULL if no more. Give the first input device when the parameter is NULL
* @return the next input devise or NULL if no more. Gives the first input device when the parameter is NULL
*/
lv_indev_t * lv_indev_next(lv_indev_t * indev);
......
......@@ -191,9 +191,9 @@ void lv_group_focus_freeze(lv_group_t * group, bool en)
/**
* Send a control character to the focuses object of a group
* @param group pointer to a group
* @param c a control character (use LV_GROUP_KEY_.. to navigate)
* @param c a character (use LV_GROUP_KEY_.. to navigate)
*/
void lv_group_send(lv_group_t * group, char c)
void lv_group_send(lv_group_t * group, uint32_t c)
{
lv_obj_t * act = lv_group_get_focused(group);
if(act == NULL) return;
......
......@@ -34,7 +34,7 @@ extern "C" {
/**********************
* TYPEDEFS
**********************/
typedef struct
typedef struct _lv_group_t
{
ll_dsc_t obj_ll;
lv_obj_t ** obj_focus;
......@@ -53,7 +53,7 @@ void lv_group_focus_obj(lv_obj_t * obj);
void lv_group_focus_next(lv_group_t * group);
void lv_group_focus_prev(lv_group_t * group);
void lv_group_focus_freeze(lv_group_t * group, bool en);
void lv_group_send(lv_group_t * group, char c);
void lv_group_send(lv_group_t * group, uint32_t c);
lv_style_t * lv_group_mod_style(lv_group_t * group, const lv_style_t * style);
lv_obj_t * lv_group_get_focused(lv_group_t * group);
......
......@@ -10,6 +10,7 @@
#include "lv_conf.h"
#include "../lv_hal/lv_hal_tick.h"
#include "../lv_obj/lv_group.h"
#include "misc/os/ptask.h"
#include "misc/math/math_base.h"
#include "../lv_draw/lv_draw_rbasic.h"
......@@ -114,17 +115,29 @@ void lv_indev_enable(lv_hal_indev_type_t type, bool enable)
}
/**
* Enable input devices device by type
* @param indev pointer to an input device
* Set a cursor for a mouse input device
* @param indev pointer to an input device (type: 'LV_INDEV_TYPE_MOUSE')
* @param cur_obj pointer to an object to be used as cursor
*/
void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj)
void lv_indev_set_cursor(lv_indev_t *indev, lv_obj_t *cur_obj)
{
if(indev->driver.type != LV_INDEV_TYPE_MOUSE) return;
indev->cursor = cur_obj;
lv_obj_set_parent(indev->cursor, lv_layer_sys());
lv_obj_set_pos(indev->cursor, indev->state.act_point.x, indev->state.act_point.y);
}
/**
* Set a destination group for a keypad input device
* @param indev pointer to an input device (type: 'LV_INDEV_TYPE_KEYPAD')
* @param group point to a group
*/
void lv_indev_set_group(lv_indev_t *indev, lv_group_t *group)
{
indev->group = group;
}
/**
* Get the last point of an input device
......@@ -206,19 +219,37 @@ static void indev_proc_task(void * param)
i->state.event = data.state;
/*Move the cursor if set and moved*/
if(i->cursor != NULL &&
if(i->driver.type == LV_INDEV_TYPE_MOUSE &&
i->cursor != NULL &&
(i->state.last_point.x != data.point.x ||
i->state.last_point.y != data.point.y))
{
lv_obj_set_pos_scale(i->cursor, data.point.x, data.point.y);
}
if(i->driver.type == LV_INDEV_TYPE_MOUSE ||
i->driver.type == LV_INDEV_TYPE_TOUCHPAD ||
i->driver.type == LV_INDEV_TYPE_BUTTON)
{
i->state.act_point.x = data.point.x << LV_ANTIALIAS;
i->state.act_point.y = data.point.y << LV_ANTIALIAS;
/*Process the current point*/
indev_proc_point(&i->state);
}
else if (i->driver.type == LV_INDEV_TYPE_KEYPAD) {
if(i->group != NULL && data.state == LV_INDEV_EVENT_PR) {
if(data.key == LV_GROUP_KEY_NEXT) { lv_group_focus_next(i->group);
}
else if(data.key == LV_GROUP_KEY_PREV) {
lv_group_focus_prev(i->group);
}
else {
lv_group_send(i->group, data.key);
}
}
}
}
/*Handle reset query if it happened in during processing*/
if(i->state.reset_query) {
......@@ -251,10 +282,10 @@ static void indev_proc_point(lv_indev_state_t * indev)
if(indev->event == LV_INDEV_EVENT_PR){
#if LV_INDEV_TP_MARKER != 0
area_t area;
area.x1 = x - (LV_INDEV_TP_MARKER >> 1);
area.y1 = y - (LV_INDEV_TP_MARKER >> 1);
area.x2 = x + ((LV_INDEV_TP_MARKER >> 1) | 0x1);
area.y2 = y + ((LV_INDEV_TP_MARKER >> 1) | 0x1);
area.x1 = x - (LV_INDEV_POINT_MARKER >> 1);
area.y1 = y - (LV_INDEV_POINT_MARKER >> 1);
area.x2 = x + ((LV_INDEV_POINT_MARKER >> 1) | 0x1);
area.y2 = y + ((LV_INDEV_POINT_MARKER >> 1) | 0x1);
lv_rfill(&area, NULL, COLOR_MAKE(0xFF, 0, 0), OPA_COVER);
#endif
indev_proc_press(indev);
......
......@@ -15,6 +15,7 @@ extern "C" {
*********************/
#include "lv_obj.h"
#include "../lv_hal/lv_hal_indev.h"
#include "../lv_obj/lv_group.h"
/*********************
* DEFINES
......@@ -29,7 +30,7 @@ extern "C" {
**********************/
/**
* Initialize the display input subsystem
* Initialize the display input device subsystem
*/
void lv_indev_init(void);
......@@ -40,49 +41,63 @@ void lv_indev_init(void);
lv_indev_t * lv_indev_get_act(void);
/**
* Reset all display inputs
* Reset one or all input devices
* @param indev
*/
void lv_indev_reset(lv_indev_t * indev);
/**
* Reset the long press state of a display input
* @param indev_proc pointer to a display input
* Reset the long press state of an input device
* @param indev_proc pointer to an input device
*/
void lv_indev_reset_lpr(lv_indev_t * indev_proc);
/**
* Enable input devices device by type
* @param indev pointer to an input device
* @param type Input device type
* @param enable true: enable this type; false: disable this type
*/
void lv_indev_enable(lv_hal_indev_type_t type, bool enable);
/**
* Set a cursor for a mouse input device
* @param indev pointer to an input device (type: 'LV_INDEV_TYPE_MOUSE')
* @param cur_obj pointer to an object to be used as cursor
*/
void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj);
void lv_indev_set_cursor(lv_indev_t *indev, lv_obj_t *cur_obj);
/**
* Get the last point on display input
* @param indev_proc pointer to a display input
* @param point pointer to a point to store the result
* Set a destination group for a keypad input device
* @param indev pointer to an input device (type: 'LV_INDEV_TYPE_KEYPAD')
* @param group point to a group
*/
void lv_indev_get_point(lv_indev_t * indev_proc, point_t * point);
void lv_indev_set_group(lv_indev_t *indev, lv_group_t *group);
/**
* Check if there is dragging on display input or not
* @param indev_proc pointer to a display input
* Get the last point of an input device
* @param indev pointer to an input device
* @param point pointer to a point to store the result
*/
void lv_indev_get_point(lv_indev_t * indev, point_t * point);
/**
* Check if there is dragging with an input device or not
* @param indev pointer to an input device
* @return true: drag is in progress
*/
bool lv_indev_is_dragging(lv_indev_t * indev_proc);
bool lv_indev_is_dragging(lv_indev_t * indev);
/**
* Get the vector of dragging on a display input
* @param indev_proc pointer to a display input
* Get the vector of dragging of an input device
* @param indev pointer to an input device
* @param point pointer to a point to store the vector
*/
void lv_indev_get_vect(lv_indev_t * indev_proc, point_t * point);
void lv_indev_get_vect(lv_indev_t * indev, point_t * point);
/**
* Do nothing until the next release
* @param indev_proc pointer to a display input
* @param indev pointer to an input device
*/
void lv_indev_wait_release(lv_indev_t * indev_proc);
void lv_indev_wait_release(lv_indev_t * indev);
/**********************
* MACROS
......
......@@ -958,14 +958,14 @@ void lv_obj_refresh_ext_size(lv_obj_t * obj)
lv_obj_invalidate(obj);
}
#if LV_OBJ_FREE_NUM != 0
#ifdef LV_OBJ_FREE_NUM_TYPE
/**
* Set an application specific number for an object.
* It can help to identify objects in the application.
* @param obj pointer to an object
* @param free_num the new free number
*/
void lv_obj_set_free_num(lv_obj_t * obj, uint8_t free_num)
void lv_obj_set_free_num(lv_obj_t * obj, LV_OBJ_FREE_NUM_TYPE free_num)
{
obj->free_num = free_num;
}
......@@ -1070,8 +1070,8 @@ void lv_obj_animate(lv_obj_t * obj, lv_anim_builtin_t type, uint16_t time, uint1
*-----------------*/
/**
* Return with the actual screen
* @return pointer to the actual screen object
* Return with a pointer to the active screen
* @return pointer to the active screen object (loaded by 'lv_scr_load()')
*/
lv_obj_t * lv_scr_act(void)
{
......@@ -1414,13 +1414,13 @@ void * lv_obj_get_ext_attr(lv_obj_t * obj)
return obj->ext_attr;
}
#if LV_OBJ_FREE_NUM != 0
#ifdef LV_OBJ_FREE_NUM_TYPE
/**
* Get the free number
* @param obj pointer to an object
* @return the free number
*/
uint8_t lv_obj_get_free_num(lv_obj_t * obj)
LV_OBJ_FREE_NUM_TYPE lv_obj_get_free_num(lv_obj_t * obj)
{
return obj->free_num;
}
......
......@@ -55,7 +55,7 @@ extern "C" {
* TYPEDEFS
**********************/
struct __LV_OBJ_T;
struct _lv_obj_t;
typedef enum
{
......@@ -64,7 +64,7 @@ typedef enum
LV_DESIGN_COVER_CHK,
}lv_design_mode_t;
typedef bool (* lv_design_func_t) (struct __LV_OBJ_T * obj, const area_t * mask_p, lv_design_mode_t mode);
typedef bool (* lv_design_func_t) (struct _lv_obj_t * obj, const area_t * mask_p, lv_design_mode_t mode);
typedef enum
{
......@@ -97,11 +97,11 @@ typedef enum
LV_SIGNAL_CONTROLL,
}lv_signal_t;
typedef lv_res_t (* lv_signal_func_t) (struct __LV_OBJ_T * obj, lv_signal_t sign, void * param);
typedef lv_res_t (* lv_signal_func_t) (struct _lv_obj_t * obj, lv_signal_t sign, void * param);
typedef struct __LV_OBJ_T
typedef struct _lv_obj_t
{
struct __LV_OBJ_T * par; /*Pointer to the parent object*/
struct _lv_obj_t * par; /*Pointer to the parent object*/
ll_dsc_t child_ll; /*Linked list to store the children objects*/
area_t coords; /*Coordinates of the object (x1, y1, x2, y2)*/
......@@ -131,12 +131,12 @@ typedef struct __LV_OBJ_T
cord_t ext_size; /*EXTtend the size of the object in every direction. E.g. for shadow drawing*/
#if LV_OBJ_FREE_NUM != 0
uint8_t free_num; /*Application specific identifier (set it freely)*/
#ifdef LV_OBJ_FREE_NUM_TYPE
LV_OBJ_FREE_NUM_TYPE free_num; /*Application specific identifier (set it freely)*/
#endif
}lv_obj_t;
typedef lv_res_t (*lv_action_t) (struct __LV_OBJ_T * obj);
typedef lv_res_t (*lv_action_t) (struct _lv_obj_t * obj);
/*Protect some attributes (max. 8 bit)*/
typedef enum
......@@ -459,14 +459,14 @@ void * lv_obj_allocate_ext_attr(lv_obj_t * obj, uint16_t ext_size);
*/
void lv_obj_refresh_ext_size(lv_obj_t * obj);
#if LV_OBJ_FREE_NUM != 0
#ifdef LV_OBJ_FREE_NUM_TYPE
/**
* Set an application specific number for an object.
* It can help to identify objects in the application.
* @param obj pointer to an object
* @param free_num the new free number
*/
void lv_obj_set_free_num(lv_obj_t * obj, uint8_t free_number);
void lv_obj_set_free_num(lv_obj_t * obj, LV_OBJ_FREE_NUM_TYPE free_number);
#endif
#if LV_OBJ_FREE_PTR != 0
......@@ -669,13 +669,13 @@ lv_design_func_t lv_obj_get_design_func(lv_obj_t * obj);
*/
void * lv_obj_get_ext_attr(lv_obj_t * obj);
#if LV_OBJ_FREE_NUM != 0
#ifdef LV_OBJ_FREE_NUM_TYPE
/**
* Get the free number
* @param obj pointer to an object
* @return the free number
*/
uint8_t lv_obj_get_free_num(lv_obj_t * obj);
LV_OBJ_FREE_NUM_TYPE lv_obj_get_free_num(lv_obj_t * obj);
#endif
#if LV_OBJ_FREE_PTR != 0
......
......@@ -88,7 +88,7 @@ void lv_style_init (void)
lv_style_scr.text.opa = OPA_COVER;
lv_style_scr.text.color = COLOR_MAKE(0x30, 0x30, 0x30);
lv_style_scr.text.font = FONT_DEFAULT;
lv_style_scr.text.font = LV_FONT_DEFAULT;
lv_style_scr.text.letter_space = 1 << LV_ANTIALIAS;
lv_style_scr.text.line_space = 2 << LV_ANTIALIAS;
......
......@@ -913,12 +913,34 @@ static lv_res_t lv_ta_signal(lv_obj_t * ta, lv_signal_t sign, void * param)
}
}
else if (sign == LV_SIGNAL_CONTROLL) {
char c = *((char*)param);
uint32_t c = *((uint32_t*)param); /*uint32_t because can be UTF-8*/
if(c == LV_GROUP_KEY_RIGHT) lv_ta_cursor_right(ta);
else if(c == LV_GROUP_KEY_LEFT) lv_ta_cursor_left(ta);
else if(c == LV_GROUP_KEY_UP) lv_ta_cursor_up(ta);
else if(c == LV_GROUP_KEY_DOWN) lv_ta_cursor_down(ta);
else lv_ta_add_char(ta, c);
else {
#if TXT_UTF8 != 0
/*Swap the bytes (UTF-8 is big endian, but the MCUs are little endian)*/
if((c & 0x80) == 0) { /*ASCII*/
lv_ta_add_char(ta, (char)c);
}
else {
uint32_t swapped[2] = {0, 0}; /*the 2. element is the closing '\0'*/
uint8_t c8[4];
memcpy(c8, &c, 4);
swapped[0] = (c8[0] << 24) + (c8[1] << 16) + (c8[2] << 8) + (c8[3]);
char *p = (char*)swapped;
uint8_t i;
for(i = 0; i < 4; i++) {
if(p[0] == 0) p++; /*Ignore leading zeros (they were in the end originally)*/
}
lv_ta_add_text(ta, p);
}
#else
lv_ta_add_char(ta, (char)c);
#endif
}
}
return res;
......
......@@ -88,7 +88,6 @@ lv_obj_t * lv_tabview_create(lv_obj_t * par, lv_obj_t * copy)
/*Init the new tab tab*/
if(copy == NULL) {
lv_obj_set_size(new_tabview, LV_HOR_RES, LV_VER_RES);
lv_obj_set_style(new_tabview, &lv_style_pretty);
ext->btns = lv_btnm_create(new_tabview, NULL);
lv_obj_set_height(ext->btns, 3 * LV_DPI / 4);
......@@ -108,7 +107,6 @@ lv_obj_t * lv_tabview_create(lv_obj_t * par, lv_obj_t * copy)
lv_obj_set_height(ext->content, LV_VER_RES - lv_obj_get_height(ext->btns));
lv_obj_align(ext->content, ext->btns, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
/*Set the default styles*/
lv_theme_t *th = lv_theme_get_current();
if(th) {
......@@ -120,6 +118,7 @@ lv_obj_t * lv_tabview_create(lv_obj_t * par, lv_obj_t * copy)
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_TGL_REL, th->tabview.btn.tgl_rel);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_TGL_PR, th->tabview.btn.tgl_pr);
} else {
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BG, &lv_style_plain);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_BG, &lv_style_transp);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_INDIC, &lv_style_plain_color);
}
......@@ -174,9 +173,9 @@ lv_obj_t * lv_tabview_add_tab(lv_obj_t * tabview, const char * name)
/*Create the container page*/
lv_obj_t * h = lv_page_create(ext->content, NULL);
lv_obj_set_size(h, lv_obj_get_width(tabview), lv_obj_get_height(ext->content));
lv_page_set_sb_mode(h, LV_SB_MODE_AUTO);
lv_page_set_style(h, LV_PAGE_STYLE_BG, &lv_style_transp);
lv_page_set_style(h, LV_PAGE_STYLE_SCRL, &lv_style_transp_tight);
lv_page_set_sb_mode(h, LV_SB_MODE_AUTO);
if(page_signal == NULL) page_signal = lv_obj_get_signal_func(h);
if(page_scrl_signal == NULL) page_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(h));
......
......@@ -96,7 +96,7 @@ lv_obj_t * lv_win_create(lv_obj_t * par, lv_obj_t * copy)
lv_win_set_style(new_win, LV_WIN_STYLE_BTN_PR, th->win.btn.pr);
} else {
lv_win_set_style(new_win, LV_WIN_STYLE_BG, &lv_style_pretty);
lv_win_set_style(new_win, LV_WIN_STYLE_BG, &lv_style_plain);
lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT_BG, &lv_style_transp);
lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT_SCRL, &lv_style_transp);
lv_win_set_style(new_win, LV_WIN_STYLE_HEADER, &lv_style_plain_color);
......
......@@ -702,7 +702,7 @@ static void win_init(void)
*/
lv_theme_t * lv_theme_alien_init(uint16_t hue, font_t *font)
{
if(font == NULL) font = FONT_DEFAULT;
if(font == NULL) font = LV_FONT_DEFAULT;
_hue = hue;
_font = font;
......
......@@ -367,7 +367,7 @@ static void win_init(void)
*/
lv_theme_t * lv_theme_default_init(uint16_t hue, font_t *font)
{
if(font == NULL) font = FONT_DEFAULT;
if(font == NULL) font = LV_FONT_DEFAULT;
_hue = hue;
_font = font;
......
......@@ -328,7 +328,7 @@ static void win_init(void)
*/
lv_theme_t * lv_theme_templ_init(uint16_t hue, font_t *font)
{
if(font == NULL) font = FONT_DEFAULT;
if(font == NULL) font = LV_FONT_DEFAULT;
_hue = hue;
_font = font;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment