/* * File : filelist_view.c * This file is part of RTGUI in RT-Thread RTOS * COPYRIGHT (C) 2010, 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 * 2010-01-06 Bernard first version */ #include #include #include #include #include #include #include #include #include #include #if defined(RTGUI_USING_DFS_FILERW) || defined(RTGUI_USING_STDIO_FILERW) #ifdef _WIN32 #include #include #include #define PATH_SEPARATOR '\\' #else #include #define PATH_SEPARATOR '/' #endif #include #define RTGUI_FILELIST_MARGIN 5 const static char * file_xpm[] = { "16 16 21 1", " c None", ". c #999999", "+ c #818181", "@ c #FFFFFF", "# c #ECECEC", "$ c #EAEAEA", "% c #EBEBEB", "& c #EDEDED", "* c #F0F0F0", "= c #C4C4C4", "- c #C5C5C5", "; c #C6C6C6", "> c #C7C7C7", ", c #EEEEEE", "' c #EDEDE5", ") c #EDEDE6", "! c #EFEFEF", "~ c #C8C8C8", "{ c #F1F1F1", "] c #F2F2F2", "^ c #959595", ".++++++++++++ ", "+@@@@@@@@@@@@+ ", "+@#$$%%%##&*@+ ", "+@$=--;;;;>*@+ ", "+@$%%###&&,*@+ ", "+@%-;;;;;;>*@+ ", "+@%%##&&'#,*@+ ", "+@%;;;;,,),*@+ ", "+@##&&,,!!!*@+ ", "+@#;;;>>~~~*@+ ", "+@#&,,!!*{{{@+ ", "+@&;>>~~~{{]@+ ", "+@&&,!!**{]]@+ ", "+@@@@@@@@@@@@+ ", "^++++++++++++^ ", " "}; const static char * folder_xpm[] = { "16 16 121 2", " c None", ". c #D9B434", "+ c #E1C25E", "@ c #E2C360", "# c #E2C35F", "$ c #DBB63C", "% c #DAB336", "& c #FEFEFD", "* c #FFFFFE", "= c #FFFEFE", "- c #FFFEFD", "; c #FBF7EA", "> c #E4C76B", ", c #E3C76B", "' c #E6CD79", ") c #E5CA74", "! c #DAAF35", "~ c #FEFCF7", "{ c #F8E48E", "] c #F5DE91", "^ c #F5E09F", "/ c #F6E1AC", "( c #FEFBEF", "_ c #FEFDF4", ": c #FEFCF3", "< c #FEFCF1", "[ c #FEFBEE", "} c #FFFDFA", "| c #DAAF36", "1 c #DAAA36", "2 c #FDFAF1", "3 c #F5DE94", "4 c #F4DC93", "5 c #F2D581", "6 c #EDCA6A", "7 c #EACB6C", "8 c #EFD385", "9 c #EFD280", "0 c #EFD07A", "a c #EECF76", "b c #EECF72", "c c #FBF7E9", "d c #DAAE34", "e c #DAAB35", "f c #FBF6E8", "g c #EFD494", "h c #EECE88", "i c #E9C173", "j c #F6E9C9", "k c #FEFCF2", "l c #FEFCF0", "m c #DAAB36", "n c #DAA637", "o c #FFFDF8", "p c #FFFDF6", "q c #FFFCF5", "r c #FCF6D8", "s c #F8E694", "t c #F7E385", "u c #F6DF76", "v c #F5DB68", "w c #F4D85C", "x c #FCF4D7", "y c #DAA435", "z c #DAA136", "A c #FEFCF6", "B c #FCF2C8", "C c #FBEFB9", "D c #FAECAC", "E c #F9E89C", "F c #F7E38B", "G c #F6E07C", "H c #F6DC6C", "I c #F5D95D", "J c #F4D64F", "K c #F3D344", "L c #FCF3D0", "M c #DA9F35", "N c #DA9A36", "O c #FDFAF2", "P c #FAEDB3", "Q c #F9E9A4", "R c #F8E695", "S c #F7E285", "T c #F6DE76", "U c #F5DB65", "V c #F4D757", "W c #F3D449", "X c #F2D13B", "Y c #F1CE30", "Z c #FBF2CC", "` c #DA9835", " . c #DA9435", ".. c #FEFAEF", "+. c #F9E9A1", "@. c #F8E591", "#. c #F7E181", "$. c #F6DE72", "%. c #F5DA63", "&. c #F4D754", "*. c #F3D347", "=. c #F2D039", "-. c #F1CD2E", ";. c #F0CB26", ">. c #FBF2CA", ",. c #D98E33", "'. c #FAF0DC", "). c #F4DDA7", "!. c #F4DB9E", "~. c #F3DA96", "{. c #F3D88E", "]. c #F3D786", "^. c #F2D47F", "/. c #F2D379", "(. c #F1D272", "_. c #F1D06C", ":. c #F1CF69", "<. c #F8EAC2", "[. c #D8882D", "}. c #D8872D", "|. c #D8862C", " ", " ", " ", " . + @ @ @ # $ ", " % & * = - * ; > , , , ' ) ", " ! ~ { ] ^ / ( _ : < ( [ } | ", " 1 2 3 4 5 6 7 8 9 0 a b c d ", " e f g h i j k : k l ( [ * m ", " n * o p q : r s t u v w x y ", " z A B C D E F G H I J K L M ", " N O P Q R S T U V W X Y Z ` ", " ...+.@.#.$.%.&.*.=.-.;.>. . ", " ,.'.).!.~.{.].^./.(._.:.<.,. ", " [.}.[.[.[.[.[.[.[.[.}.[.|. ", " ", " "}; /* image for file and folder */ static rtgui_image_t *file_image, *folder_image; static struct rtgui_listbox_item items[] = { #ifdef RTGUI_USING_FONTHZ {"ː`", RT_NULL}, {"v`", RT_NULL}, {"X", RT_NULL} #else {"Open folder", RT_NULL}, {"Select folder", RT_NULL}, {"Cancel", RT_NULL} #endif }; static void rtgui_filelist_view_clear(rtgui_filelist_view_t* view); static void rtgui_filelist_view_on_folder_item(rtgui_widget_t* widget, struct rtgui_event* event) { rtgui_win_t *menu; rtgui_listbox_t *listbox; rtgui_filelist_view_t *view; listbox = RTGUI_LISTBOX(widget); menu = RTGUI_WIN(rtgui_widget_get_toplevel(widget)); view = RTGUI_FILELIST_VIEW(menu->user_data); /* hide window */ rtgui_win_hiden(menu); switch (listbox->current_item) { case 0: { char* dir_ptr; /* destroy menu window */ rtgui_win_destroy(menu); dir_ptr = (char*) rtgui_malloc (256); rtgui_filelist_view_get_fullpath(view, dir_ptr, 256); rtgui_filelist_view_set_directory(view, dir_ptr); rt_free(dir_ptr); } break; case 1: /* destroy menu window */ rtgui_win_destroy(menu); break; default: /* destroy menu window */ rtgui_win_destroy(menu); break; } } static rt_bool_t rtgui_filelist_view_on_menu_deactivate(rtgui_widget_t* widget, struct rtgui_event* event) { rtgui_win_t *menu; menu = RTGUI_WIN(rtgui_widget_get_toplevel(widget)); /* destroy menu window */ rtgui_win_destroy(menu); return RT_TRUE; } static void rtgui_filelist_view_menu_pop(rtgui_widget_t *parent) { rtgui_win_t *menu; rtgui_listbox_t *listbox; rtgui_rect_t screen, rect = {0, 0, 140, 85}; rtgui_graphic_driver_get_rect(rtgui_graphic_driver_get_default(), &screen); rtgui_rect_moveto_align(&screen, &rect, RTGUI_ALIGN_CENTER_HORIZONTAL | RTGUI_ALIGN_CENTER_VERTICAL); menu = rtgui_win_create(rtgui_widget_get_toplevel(parent), "Folder Menu", &rect, RTGUI_WIN_STYLE_DEFAULT); if (menu != RT_NULL) { /* set user data on menu window */ menu->user_data = (rt_uint32_t)parent; rtgui_win_set_ondeactivate(menu, rtgui_filelist_view_on_menu_deactivate); listbox = rtgui_listbox_create(items, sizeof(items)/sizeof(items[0]), &rect); rtgui_listbox_set_onitem(listbox, rtgui_filelist_view_on_folder_item); rtgui_container_add_child(RTGUI_CONTAINER(menu), RTGUI_WIDGET(listbox)); rtgui_win_show(menu, RT_FALSE); rtgui_widget_focus(RTGUI_WIDGET(listbox)); rtgui_listbox_set_current_item(listbox, 0); } } static void _rtgui_filelist_view_constructor(struct rtgui_filelist_view *view) { /* default rect */ struct rtgui_rect rect = {0, 0, 200, 200}; /* set default widget rect and set event handler */ rtgui_object_set_event_handler(RTGUI_OBJECT(view), rtgui_filelist_view_event_handler); rtgui_widget_set_rect(RTGUI_WIDGET(view), &rect); RTGUI_WIDGET(view)->flag |= RTGUI_WIDGET_FLAG_FOCUSABLE; view->current_item = 0; view->items_count = 0; view->page_items = 0; view->current_directory = RT_NULL; view->pattern = RT_NULL; RTGUI_WIDGET_BACKGROUND(RTGUI_WIDGET(view)) = white; RTGUI_WIDGET_TEXTALIGN(RTGUI_WIDGET(view)) = RTGUI_ALIGN_CENTER_VERTICAL; file_image = rtgui_image_create_from_mem("xpm", (rt_uint8_t*)file_xpm, sizeof(file_xpm), RT_TRUE); folder_image = rtgui_image_create_from_mem("xpm", (rt_uint8_t*)folder_xpm, sizeof(folder_xpm), RT_TRUE); } static void _rtgui_filelist_view_destructor(struct rtgui_filelist_view *view) { /* delete all file items */ rtgui_filelist_view_clear(view); /* delete current directory and pattern */ rtgui_free(view->current_directory); view->current_directory = RT_NULL; rtgui_free(view->pattern); view->pattern = RT_NULL; /* delete image */ rtgui_image_destroy(file_image); rtgui_image_destroy(folder_image); } DEFINE_CLASS_TYPE(filelist, "filelist", RTGUI_CONTAINER_TYPE, _rtgui_filelist_view_constructor, _rtgui_filelist_view_destructor, sizeof(struct rtgui_filelist_view)); void rtgui_filelist_view_ondraw(struct rtgui_filelist_view* view) { struct rtgui_dc* dc; rt_uint16_t page_index, index; struct rtgui_file_item* item; struct rtgui_rect rect, item_rect, image_rect; dc = rtgui_dc_begin_drawing(RTGUI_WIDGET(view)); if (dc == RT_NULL) return; rtgui_widget_get_rect(RTGUI_WIDGET(view), &rect); rtgui_dc_fill_rect(dc, &rect); /* get item base rect */ item_rect = rect; item_rect.y1 += 1; item_rect.y2 = item_rect.y1 + (1 + rtgui_theme_get_selected_height()); /* get image base rect */ image_rect.x1 = RTGUI_FILELIST_MARGIN; image_rect.y1 = 0; image_rect.x2 = RTGUI_FILELIST_MARGIN + file_image->w; image_rect.y2 = file_image->h; rtgui_rect_moveto_align(&item_rect, &image_rect, RTGUI_ALIGN_CENTER_VERTICAL); /* get current page */ page_index = (view->current_item / view->page_items) * view->page_items; for (index = 0; index < view->page_items; index ++) { if (page_index + index >= view->items_count) break; item = &(view->items[page_index + index]); if (page_index + index == view->current_item) { rtgui_theme_draw_selected(dc, &item_rect); } else { /* draw background */ rtgui_dc_fill_rect(dc, &item_rect); } /* draw item */ if (item->type == RTGUI_FITEM_FILE) rtgui_image_blit(file_image, dc, &image_rect); else rtgui_image_blit(folder_image, dc, &image_rect); /* draw text */ item_rect.x1 += RTGUI_FILELIST_MARGIN + file_image->w + 2; rtgui_dc_draw_text(dc, item->name, &item_rect); item_rect.x1 -= RTGUI_FILELIST_MARGIN + file_image->w + 2; /* move to next item position */ item_rect.y1 += (rtgui_theme_get_selected_height() + 1); item_rect.y2 += (rtgui_theme_get_selected_height() + 1); image_rect.y1 += (rtgui_theme_get_selected_height() + 1); image_rect.y2 += (rtgui_theme_get_selected_height() + 1); } rtgui_dc_end_drawing(dc); } void rtgui_filelist_view_update_current(struct rtgui_filelist_view* view, rt_uint16_t old_item) { struct rtgui_dc* dc; struct rtgui_file_item* item; rtgui_rect_t rect, item_rect, image_rect; if (old_item/view->page_items != view->current_item/view->page_items) { /* it's not a same page, update all */ rtgui_widget_update(RTGUI_WIDGET(view)); return; } dc = rtgui_dc_begin_drawing(RTGUI_WIDGET(view)); if (dc == RT_NULL) return; rtgui_widget_get_rect(RTGUI_WIDGET(view), &rect); /* get old item rect */ item_rect = rect; item_rect.y1 += 1; item_rect.y1 += (old_item % view->page_items) * (1 + rtgui_theme_get_selected_height()); item_rect.y2 = item_rect.y1 + (1 + rtgui_theme_get_selected_height()); /* get image rect */ image_rect.x1 = RTGUI_FILELIST_MARGIN; image_rect.y1 = 0; image_rect.x2 = RTGUI_FILELIST_MARGIN + file_image->w; image_rect.y2 = file_image->h; rtgui_rect_moveto_align(&item_rect, &image_rect, RTGUI_ALIGN_CENTER_VERTICAL); /* draw old item */ rtgui_dc_fill_rect(dc, &item_rect); item = &(view->items[old_item]); if (item->type == RTGUI_FITEM_FILE) /* draw item image */ rtgui_image_blit(file_image, dc, &image_rect); else rtgui_image_blit(folder_image, dc, &image_rect); item_rect.x1 += RTGUI_FILELIST_MARGIN + file_image->w + 2; rtgui_dc_draw_text(dc, item->name, &item_rect); /* draw current item */ item_rect = rect; item_rect.y1 += 1; item_rect.y1 += (view->current_item % view->page_items) * (1 + rtgui_theme_get_selected_height()); item_rect.y2 = item_rect.y1 + (1 + rtgui_theme_get_selected_height()); rtgui_theme_draw_selected(dc, &item_rect); /* get image base rect */ image_rect.x1 = RTGUI_FILELIST_MARGIN; image_rect.y1 = 0; image_rect.x2 = RTGUI_FILELIST_MARGIN + file_image->w; image_rect.y2 = file_image->h; rtgui_rect_moveto_align(&item_rect, &image_rect, RTGUI_ALIGN_CENTER_VERTICAL); item = &(view->items[view->current_item]); if (item->type == RTGUI_FITEM_FILE) /* draw item image */ rtgui_image_blit(file_image, dc, &image_rect); else rtgui_image_blit(folder_image, dc, &image_rect); item_rect.x1 += RTGUI_FILELIST_MARGIN + file_image->w + 2; rtgui_dc_draw_text(dc, item->name, &item_rect); rtgui_dc_end_drawing(dc); } static void rtgui_filelist_view_onenturn(struct rtgui_filelist_view* view) { if (view->items[view->current_item].type == RTGUI_FITEM_DIR) { char new_path[64]; if (strcmp(view->items[view->current_item].name, ".") == 0) return ; if (strcmp(view->items[view->current_item].name, "..") == 0) { char *ptr; ptr = strrchr(view->current_directory, PATH_SEPARATOR); if (ptr == RT_NULL) return ; if (ptr == &(view->current_directory[0])) { /* it's root directory */ new_path[0] = PATH_SEPARATOR; new_path[1] = '\0'; } else { strncpy(new_path, view->current_directory, ptr - view->current_directory + 1); new_path[ptr - view->current_directory] = '\0'; } } else if (view->current_item == 0 && #ifdef _WIN32 (view->current_directory[1] == ':') && (view->current_directory[2] == '\\') #else (view->current_directory[0] == '/') && (view->current_directory[1] == '\0') #endif ) { rtgui_filelist_view_destroy(view); return ; } else { rtgui_filelist_view_menu_pop(RTGUI_WIDGET(view)); return ; } rtgui_filelist_view_set_directory(view, new_path); } } rt_bool_t rtgui_filelist_view_event_handler(struct rtgui_widget* widget, struct rtgui_event* event) { struct rtgui_filelist_view* view = RT_NULL; view = RTGUI_FILELIST_VIEW(widget); switch (event->type) { case RTGUI_EVENT_PAINT: rtgui_filelist_view_ondraw(view); return RT_FALSE; case RTGUI_EVENT_RESIZE: { struct rtgui_event_resize* resize; resize = (struct rtgui_event_resize*)event; /* recalculate page items */ if (file_image != RT_NULL) view->page_items = resize->h / (1 + rtgui_theme_get_selected_height()); else view->page_items = resize->h / (2 + 14); } break; case RTGUI_EVENT_MOUSE_BUTTON: { rtgui_rect_t rect; struct rtgui_event_mouse* emouse; emouse = (struct rtgui_event_mouse*)event; /* calculate selected item */ /* get physical extent information */ rtgui_widget_get_rect(widget, &rect); rtgui_widget_rect_to_device(widget, &rect); if (rtgui_rect_contains_point(&rect, emouse->x, emouse->y) == RT_EOK) { rt_uint16_t index; rt_uint16_t current_page; rt_uint16_t old_item; index = (emouse->y - rect.y1) / (2 + rtgui_theme_get_selected_height()); /* get current page */ current_page = view->current_item/view->page_items; old_item = view->current_item; if (index + current_page * view->page_items < view->items_count) { /* set selected item */ view->current_item = index + current_page * view->page_items; if (emouse->button & RTGUI_MOUSE_BUTTON_DOWN) { rtgui_filelist_view_update_current(view, old_item); } else { /* up event */ rtgui_filelist_view_onenturn(view); } } return RT_TRUE; } } break; case RTGUI_EVENT_KBD: { struct rtgui_event_kbd* ekbd = (struct rtgui_event_kbd*)event; if (ekbd->type == RTGUI_KEYDOWN) { rt_uint16_t old_item; old_item = view->current_item; switch (ekbd->key) { case RTGUIK_UP: if (view->current_item > 0) view->current_item --; rtgui_filelist_view_update_current(view, old_item); return RT_TRUE; case RTGUIK_DOWN: if (view->current_item < view->items_count - 1) view->current_item ++; rtgui_filelist_view_update_current(view, old_item); return RT_TRUE; case RTGUIK_LEFT: if (view->current_item - view->page_items >= 0) view->current_item -= view->page_items; rtgui_filelist_view_update_current(view, old_item); return RT_TRUE; case RTGUIK_RIGHT: if (view->current_item + view->page_items < view->items_count - 1) view->current_item += view->page_items; rtgui_filelist_view_update_current(view, old_item); return RT_TRUE; case RTGUIK_RETURN: rtgui_filelist_view_onenturn(view); return RT_TRUE; default: break; } } } return RT_FALSE; } /* use view event handler */ return rtgui_container_event_handler(RTGUI_OBJECT(widget), event); } rtgui_filelist_view_t* rtgui_filelist_view_create(const char* directory, const char* pattern, const rtgui_rect_t* rect) { struct rtgui_filelist_view* view = RT_NULL; /* create a new view */ view = (struct rtgui_filelist_view*) rtgui_widget_create(RTGUI_FILELIST_VIEW_TYPE); if (view != RT_NULL) { view->items = RT_NULL; view->pattern = rt_strdup(pattern); view->page_items = rtgui_rect_height(*rect) / (1 + rtgui_theme_get_selected_height()); rtgui_filelist_view_set_directory(view, directory); } return view; } void rtgui_filelist_view_destroy(rtgui_filelist_view_t* view) { /* destroy view */ rtgui_widget_destroy(RTGUI_WIDGET(view)); } /* clear all file items */ static void rtgui_filelist_view_clear(rtgui_filelist_view_t* view) { rt_uint32_t index; struct rtgui_file_item* item; for (index = 0; index < view->items_count; index ++) { item = &(view->items[index]); /* release item name */ rt_free(item->name); item->name = RT_NULL; } /* release items */ rtgui_free(view->items); view->items = RT_NULL; view->items_count = 0; view->current_item = 0; } void rtgui_filelist_view_set_directory(rtgui_filelist_view_t* view, const char* directory) { struct rtgui_file_item *item; RT_ASSERT(view != RT_NULL); /* clear file items firstly */ rtgui_filelist_view_clear(view); if (directory != RT_NULL) { DIR* dir; struct stat s; char* fullpath; rt_uint32_t index; struct dirent* dirent; view->items_count = 0; dir = opendir(directory); if (dir == RT_NULL) goto __return; /* current directory exists, set it */ if (view->current_directory != RT_NULL) rt_free(view->current_directory); view->current_directory = rt_strdup(directory); do { dirent = readdir(dir); if (dirent == RT_NULL) break; if (strcmp(dirent->d_name, ".") == 0) continue; if (strcmp(dirent->d_name, "..") == 0) continue; view->items_count ++; } while (dirent != RT_NULL); closedir(dir); view->items_count ++; /* root directory for [x] exit, others for .. */ view->items = (struct rtgui_file_item*) rtgui_malloc(sizeof(struct rtgui_file_item) * view->items_count); if (view->items == RT_NULL) return; /* no memory */ index = 0; if (directory[0] == '/' && directory[1] != '\0') { item = &(view->items[0]); /* add .. directory */ item->name = rt_strdup(".."); item->type = RTGUI_FITEM_DIR; item->size = 0; index ++; } else { item = &(view->items[0]); /* add .. directory */ item->name = rt_strdup("X̐`㚔M"); item->type = RTGUI_FITEM_DIR; item->size = 0; index ++; } /* reopen directory */ dir = opendir(directory); fullpath = rtgui_malloc(256); while (index < view->items_count) { dirent = readdir(dir); if (dirent == RT_NULL) break; if (strcmp(dirent->d_name, ".") == 0) continue; if (strcmp(dirent->d_name, "..") == 0) continue; item = &(view->items[index]); item->name = rt_strdup(dirent->d_name); rt_memset(&s, 0, sizeof(struct stat)); /* build full path for the file */ if (directory[strlen(directory) - 1] != PATH_SEPARATOR) rt_snprintf(fullpath, 256, "%s%c%s", directory, PATH_SEPARATOR, dirent->d_name); else rt_snprintf(fullpath, 256, "%s%s", directory, dirent->d_name); stat(fullpath, &s); if ( s.st_mode & S_IFDIR ) { item->type = RTGUI_FITEM_DIR; item->size = 0; } else { item->type = RTGUI_FITEM_FILE; item->size = s.st_size; } index ++; } rtgui_free(fullpath); closedir(dir); } view->current_item = 0; __return: /* update view */ rtgui_widget_update(RTGUI_WIDGET(view)); } void rtgui_filelist_view_get_fullpath(rtgui_filelist_view_t* view, char* path, rt_size_t len) { RT_ASSERT(view != RT_NULL); if (view->current_directory[strlen(view->current_directory) - 1] != PATH_SEPARATOR) rt_snprintf(path, len, "%s%c%s",view->current_directory, PATH_SEPARATOR, view->items[view->current_item].name); else rt_snprintf(path, len, "%s%s",view->current_directory, view->items[view->current_item].name); } #endif