|
@@ -0,0 +1,352 @@
|
|
|
+/*
|
|
|
+ * File : textview.c
|
|
|
+ * This file is part of RT-Thread RTOS
|
|
|
+ * COPYRIGHT (C) 2006 - 2011, RT-Thread Development Team
|
|
|
+ *
|
|
|
+ * The license and distribution terms for this file may be
|
|
|
+ * found in the file LICENSE in this distribution or at
|
|
|
+ * http://www.rt-thread.org/license/LICENSE
|
|
|
+ *
|
|
|
+ * Change Logs:
|
|
|
+ * Date Author Notes
|
|
|
+ * 2011-03-05 Bernard first version
|
|
|
+ */
|
|
|
+#include <rtgui/dc.h>
|
|
|
+#include <rtgui/rtgui_system.h>
|
|
|
+#include <rtgui/widgets/textview.h>
|
|
|
+
|
|
|
+rt_inline unsigned char* _get_line_text(rtgui_textview_t *textview, rt_uint16_t index)
|
|
|
+{
|
|
|
+ unsigned char* line;
|
|
|
+ if (index < textview->line_count)
|
|
|
+ {
|
|
|
+ line = textview->lines + (index * textview->line_width);
|
|
|
+ return line;
|
|
|
+ }
|
|
|
+
|
|
|
+ return RT_NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void _calc_line(rtgui_textview_t *textview, const char* text)
|
|
|
+{
|
|
|
+ unsigned char* line;
|
|
|
+ const unsigned char* ptr;
|
|
|
+ rt_ubase_t line_index, line_position;
|
|
|
+
|
|
|
+ if (textview->lines != RT_NULL)
|
|
|
+ {
|
|
|
+ rt_free(textview->lines);
|
|
|
+ textview->lines = RT_NULL;
|
|
|
+ textview->line_count = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* get line count */
|
|
|
+ line_index = 0; line_position = 0;
|
|
|
+ ptr = (const unsigned char*)text;
|
|
|
+ if (*ptr == 0) return;
|
|
|
+
|
|
|
+ while (*ptr != '\0')
|
|
|
+ {
|
|
|
+ if (*ptr == '\n')
|
|
|
+ {
|
|
|
+ line_index ++;
|
|
|
+ line_position = 0;
|
|
|
+ }
|
|
|
+ else if (*ptr == '\r')
|
|
|
+ {
|
|
|
+ ptr ++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if (*ptr == '\t')
|
|
|
+ {
|
|
|
+ line_position += 4;
|
|
|
+ if (line_position >= textview->line_width - 1)
|
|
|
+ {
|
|
|
+ line_index ++;
|
|
|
+ line_position = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if ((*ptr) >= 0x80)
|
|
|
+ {
|
|
|
+ /* fill cjk character */
|
|
|
+ if (line_position + 1 >= (textview->line_width - 1))
|
|
|
+ {
|
|
|
+ /* split to next line */
|
|
|
+ line_index ++;
|
|
|
+ line_position = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ line_position ++;
|
|
|
+ line_position ++;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ line_position ++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (line_position >= textview->line_width - 1)
|
|
|
+ {
|
|
|
+ line_index ++;
|
|
|
+ line_position = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ptr ++;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* set line count */
|
|
|
+ textview->line_count = line_index + 1;
|
|
|
+
|
|
|
+ /* allocate lines */
|
|
|
+ textview->lines = rt_malloc(textview->line_count * textview->line_width);
|
|
|
+ rt_memset(textview->lines, 0, (textview->line_count * textview->line_width));
|
|
|
+
|
|
|
+ /* fill lines */
|
|
|
+ line_index = 0; line_position = 0;
|
|
|
+ ptr = (const unsigned char*)text;
|
|
|
+ line = _get_line_text(textview, line_index);
|
|
|
+ while (*ptr)
|
|
|
+ {
|
|
|
+ if (*ptr == '\n')
|
|
|
+ {
|
|
|
+ line_index ++;
|
|
|
+ line_position = 0;
|
|
|
+ line = _get_line_text(textview, line_index);
|
|
|
+ }
|
|
|
+ else if (*ptr == '\r')
|
|
|
+ {
|
|
|
+ /* ignore '\r' */
|
|
|
+ ptr ++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if (*ptr == '\t')
|
|
|
+ {
|
|
|
+ line[line_position++] = ' ';
|
|
|
+ line[line_position++] = ' ';
|
|
|
+ line[line_position++] = ' ';
|
|
|
+ line[line_position++] = ' ';
|
|
|
+ if (line_position >= textview->line_width - 1)
|
|
|
+ {
|
|
|
+ line_index ++;
|
|
|
+ line_position = 0;
|
|
|
+ line = _get_line_text(textview, line_index);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if ((*ptr) >= 0x80)
|
|
|
+ {
|
|
|
+ /* fill cjk character */
|
|
|
+ if (line_position + 1 >= (textview->line_width - 1))
|
|
|
+ {
|
|
|
+ /* split to next line */
|
|
|
+ line_index ++;
|
|
|
+ line_position = 0;
|
|
|
+ line = _get_line_text(textview, line_index);
|
|
|
+ }
|
|
|
+
|
|
|
+ line[line_position ++] = *ptr ++;
|
|
|
+ line[line_position ++] = *ptr;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ line[line_position ++] = *ptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (line_position >= textview->line_width - 1)
|
|
|
+ {
|
|
|
+ line_index ++;
|
|
|
+ line_position = 0;
|
|
|
+ line = _get_line_text(textview, line_index);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ptr ++;
|
|
|
+ }
|
|
|
+
|
|
|
+ textview->line_current = 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void _calc_width(rtgui_textview_t *textview)
|
|
|
+{
|
|
|
+ rtgui_rect_t rect;
|
|
|
+ rt_uint16_t width, height;
|
|
|
+
|
|
|
+ width = rtgui_rect_width(RTGUI_WIDGET(textview)->extent) - 6;
|
|
|
+ height = rtgui_rect_height(RTGUI_WIDGET(textview)->extent);
|
|
|
+
|
|
|
+ rtgui_font_get_metrics(RTGUI_WIDGET_FONT(RTGUI_WIDGET(textview)), "W", &rect);
|
|
|
+ textview->line_width = width / rtgui_rect_width(rect) + 1;
|
|
|
+ textview->line_page_count = height / (rtgui_rect_height(rect) + 3);
|
|
|
+
|
|
|
+ /* set minimal value */
|
|
|
+ if (textview->line_page_count == 0) textview->line_page_count = 1;
|
|
|
+}
|
|
|
+
|
|
|
+static void _draw_textview(rtgui_textview_t *textview)
|
|
|
+{
|
|
|
+ struct rtgui_dc* dc;
|
|
|
+ struct rtgui_rect rect, font_rect;
|
|
|
+ char* line;
|
|
|
+ rt_ubase_t line_index, item_height;
|
|
|
+
|
|
|
+ rtgui_font_get_metrics(RTGUI_WIDGET_FONT(RTGUI_WIDGET(textview)), "W", &font_rect);
|
|
|
+ item_height = rtgui_rect_height(font_rect) + 3;
|
|
|
+
|
|
|
+ dc = rtgui_dc_begin_drawing(RTGUI_WIDGET(textview));
|
|
|
+ if (dc == RT_NULL) return ;
|
|
|
+
|
|
|
+ /* fill rect */
|
|
|
+ rtgui_widget_get_rect(RTGUI_WIDGET(textview), &rect);
|
|
|
+ rtgui_dc_fill_rect(dc, &rect);
|
|
|
+
|
|
|
+ rect.x1 += 3;
|
|
|
+ rect.x2 -= 3;
|
|
|
+
|
|
|
+ for (line_index = textview->line_current;
|
|
|
+ (line_index < textview->line_current + textview->line_page_count) &&
|
|
|
+ (line_index < textview->line_count);
|
|
|
+ line_index ++)
|
|
|
+ {
|
|
|
+ line = (char* )_get_line_text(textview, line_index);
|
|
|
+ rtgui_dc_draw_text(dc, line, &rect);
|
|
|
+
|
|
|
+ rect.y1 += item_height;
|
|
|
+ }
|
|
|
+
|
|
|
+ rtgui_dc_end_drawing(dc);
|
|
|
+}
|
|
|
+
|
|
|
+static void _rtgui_textview_constructor(rtgui_textview_t *textview)
|
|
|
+{
|
|
|
+ /* init widget and set event handler */
|
|
|
+ rtgui_widget_set_event_handler(RTGUI_WIDGET(textview), rtgui_textview_event_handler);
|
|
|
+ RTGUI_WIDGET(textview)->flag |= RTGUI_WIDGET_FLAG_FOCUSABLE;
|
|
|
+
|
|
|
+ /* set field */
|
|
|
+ textview->line_count = 0;
|
|
|
+ textview->lines = RT_NULL;
|
|
|
+
|
|
|
+ textview->line_current = -1;
|
|
|
+ textview->line_page_count = 1;
|
|
|
+}
|
|
|
+
|
|
|
+static void _rtgui_textview_destructor(rtgui_textview_t *textview)
|
|
|
+{
|
|
|
+ /* release line memory */
|
|
|
+ rt_free(textview->lines);
|
|
|
+ textview->lines = RT_NULL;
|
|
|
+}
|
|
|
+
|
|
|
+DEFINE_CLASS_TYPE(textview, "textview",
|
|
|
+ RTGUI_WIDGET_TYPE,
|
|
|
+ _rtgui_textview_constructor,
|
|
|
+ _rtgui_textview_destructor,
|
|
|
+ sizeof(struct rtgui_textview));
|
|
|
+
|
|
|
+rt_bool_t rtgui_textview_event_handler(struct rtgui_widget* widget, struct rtgui_event* event)
|
|
|
+{
|
|
|
+ struct rtgui_textview* textview;
|
|
|
+
|
|
|
+ RT_ASSERT(widget != RT_NULL);
|
|
|
+
|
|
|
+ textview = (struct rtgui_textview*) widget;
|
|
|
+ switch (event->type)
|
|
|
+ {
|
|
|
+ case RTGUI_EVENT_PAINT:
|
|
|
+ _draw_textview(textview);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case RTGUI_EVENT_KBD:
|
|
|
+ {
|
|
|
+ struct rtgui_event_kbd* ekbd = (struct rtgui_event_kbd*)event;
|
|
|
+ if (ekbd->type == RTGUI_KEYDOWN)
|
|
|
+ {
|
|
|
+ rt_int16_t line_current_update;
|
|
|
+ line_current_update = textview->line_current;
|
|
|
+ if (ekbd->key == RTGUIK_LEFT)
|
|
|
+ {
|
|
|
+ if (textview->line_current > textview->line_page_count)
|
|
|
+ {
|
|
|
+ line_current_update -= textview->line_page_count;
|
|
|
+ }
|
|
|
+ else if (textview->line_current > 0)
|
|
|
+ {
|
|
|
+ line_current_update = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (ekbd->key == RTGUIK_RIGHT)
|
|
|
+ {
|
|
|
+ if (textview->line_current + textview->line_page_count < textview->line_count - 1)
|
|
|
+ {
|
|
|
+ line_current_update += textview->line_page_count;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (ekbd->key == RTGUIK_UP)
|
|
|
+ {
|
|
|
+ if (textview->line_current > 0)
|
|
|
+ {
|
|
|
+ line_current_update --;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (ekbd->key == RTGUIK_DOWN)
|
|
|
+ {
|
|
|
+ if (textview->line_current + textview->line_page_count < textview->line_count - 1)
|
|
|
+ {
|
|
|
+ line_current_update ++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (textview->line_current != line_current_update)
|
|
|
+ {
|
|
|
+ textview->line_current = line_current_update;
|
|
|
+ rtgui_widget_update(widget);
|
|
|
+ return RT_TRUE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return RT_FALSE;
|
|
|
+}
|
|
|
+
|
|
|
+rtgui_textview_t* rtgui_textview_create(const char* text, const rtgui_rect_t *rect)
|
|
|
+{
|
|
|
+ struct rtgui_textview* textview;
|
|
|
+
|
|
|
+ textview = (struct rtgui_textview*) rtgui_widget_create(RTGUI_TEXTVIEW_TYPE);
|
|
|
+ if (textview != RT_NULL)
|
|
|
+ {
|
|
|
+ rtgui_widget_set_rect(RTGUI_WIDGET(textview), rect);
|
|
|
+
|
|
|
+ /* calculate line width and line page count */
|
|
|
+ _calc_width(textview);
|
|
|
+
|
|
|
+ /* set text */
|
|
|
+ _calc_line(textview, text);
|
|
|
+ }
|
|
|
+
|
|
|
+ return textview;
|
|
|
+}
|
|
|
+
|
|
|
+void rtgui_textview_destroy(rtgui_textview_t* textview)
|
|
|
+{
|
|
|
+ rtgui_widget_destroy(RTGUI_WIDGET(textview));
|
|
|
+}
|
|
|
+
|
|
|
+void rtgui_textview_set_text(rtgui_textview_t* textview, const char* text)
|
|
|
+{
|
|
|
+ RT_ASSERT(textview != RT_NULL);
|
|
|
+
|
|
|
+ /* calculate line width and line page count */
|
|
|
+ _calc_width(textview);
|
|
|
+
|
|
|
+ /* set text */
|
|
|
+ _calc_line(textview, text);
|
|
|
+
|
|
|
+ /* update widget */
|
|
|
+ rtgui_widget_update(RTGUI_WIDGET(textview));
|
|
|
+}
|