diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2019-07-24 14:09:52 -0400 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2019-08-05 04:10:25 +0200 |
commit | 701bf06b2655ab659621ae603fb4de784b660db2 (patch) | |
tree | af682a351a9a625acafda7c6f3da6db8e01a5871 | |
parent | 2a737d3e6f028c68b9c97723f4f14025d74860af (diff) | |
download | rockbox-701bf06b2655ab659621ae603fb4de784b660db2.tar.gz rockbox-701bf06b2655ab659621ae603fb4de784b660db2.zip |
Voice enable properties plugin
Patch by Igor Poretsky
Updated by Solomon Peachy
Change-Id: Ia2adf4a65723b08d6c438f836e3e811cba3dced0
-rw-r--r-- | apps/lang/english.lang | 196 | ||||
-rw-r--r-- | apps/plugins/properties.c | 146 |
2 files changed, 307 insertions, 35 deletions
diff --git a/apps/lang/english.lang b/apps/lang/english.lang index da0f21b9c7..075272ef09 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang | |||
@@ -15723,3 +15723,199 @@ id: VOICE_BAT_BENCH_KEYS | |||
15723 | *: "" | 15723 | *: "" |
15724 | </voice> | 15724 | </voice> |
15725 | </phrase> | 15725 | </phrase> |
15726 | <phrase> | ||
15727 | id: LANG_PROPERTIES_PATH | ||
15728 | desc: in properties plugin | ||
15729 | user: core | ||
15730 | <source> | ||
15731 | *: "[Path]" | ||
15732 | </source> | ||
15733 | <dest> | ||
15734 | *: "[Path]" | ||
15735 | </dest> | ||
15736 | <voice> | ||
15737 | *: "Path" | ||
15738 | </voice> | ||
15739 | </phrase> | ||
15740 | <phrase> | ||
15741 | id: LANG_PROPERTIES_FILENAME | ||
15742 | desc: in properties plugin | ||
15743 | user: core | ||
15744 | <source> | ||
15745 | *: "[Filename]" | ||
15746 | </source> | ||
15747 | <dest> | ||
15748 | *: "[Filename]" | ||
15749 | </dest> | ||
15750 | <voice> | ||
15751 | *: "Filename" | ||
15752 | </voice> | ||
15753 | </phrase> | ||
15754 | <phrase> | ||
15755 | id: LANG_PROPERTIES_SIZE | ||
15756 | desc: in properties plugin | ||
15757 | user: core | ||
15758 | <source> | ||
15759 | *: "[Size]" | ||
15760 | </source> | ||
15761 | <dest> | ||
15762 | *: "[Size]" | ||
15763 | </dest> | ||
15764 | <voice> | ||
15765 | *: "Size" | ||
15766 | </voice> | ||
15767 | </phrase> | ||
15768 | <phrase> | ||
15769 | id: LANG_PROPERTIES_DATE | ||
15770 | desc: in properties plugin | ||
15771 | user: core | ||
15772 | <source> | ||
15773 | *: "[Date]" | ||
15774 | </source> | ||
15775 | <dest> | ||
15776 | *: "[Date]" | ||
15777 | </dest> | ||
15778 | <voice> | ||
15779 | *: "Date" | ||
15780 | </voice> | ||
15781 | </phrase> | ||
15782 | <phrase> | ||
15783 | id: LANG_PROPERTIES_TIME | ||
15784 | desc: in properties plugin | ||
15785 | user: core | ||
15786 | <source> | ||
15787 | *: "[Time]" | ||
15788 | </source> | ||
15789 | <dest> | ||
15790 | *: "[Time]" | ||
15791 | </dest> | ||
15792 | <voice> | ||
15793 | *: "Time" | ||
15794 | </voice> | ||
15795 | </phrase> | ||
15796 | <phrase> | ||
15797 | id: LANG_PROPERTIES_ARTIST | ||
15798 | desc: in properties plugin | ||
15799 | user: core | ||
15800 | <source> | ||
15801 | *: "[Artist]" | ||
15802 | </source> | ||
15803 | <dest> | ||
15804 | *: "[Artist]" | ||
15805 | </dest> | ||
15806 | <voice> | ||
15807 | *: "Artist" | ||
15808 | </voice> | ||
15809 | </phrase> | ||
15810 | <phrase> | ||
15811 | id: LANG_PROPERTIES_TITLE | ||
15812 | desc: in properties plugin | ||
15813 | user: core | ||
15814 | <source> | ||
15815 | *: "[Title]" | ||
15816 | </source> | ||
15817 | <dest> | ||
15818 | *: "[Title]" | ||
15819 | </dest> | ||
15820 | <voice> | ||
15821 | *: "Title" | ||
15822 | </voice> | ||
15823 | </phrase> | ||
15824 | <phrase> | ||
15825 | id: LANG_PROPERTIES_ALBUM | ||
15826 | desc: in properties plugin | ||
15827 | user: core | ||
15828 | <source> | ||
15829 | *: "[Album]" | ||
15830 | </source> | ||
15831 | <dest> | ||
15832 | *: "[Album]" | ||
15833 | </dest> | ||
15834 | <voice> | ||
15835 | *: "Album" | ||
15836 | </voice> | ||
15837 | </phrase> | ||
15838 | <phrase> | ||
15839 | id: LANG_PROPERTIES_DURATION | ||
15840 | desc: in properties plugin | ||
15841 | user: core | ||
15842 | <source> | ||
15843 | *: "[Duration]" | ||
15844 | </source> | ||
15845 | <dest> | ||
15846 | *: "[Duration]" | ||
15847 | </dest> | ||
15848 | <voice> | ||
15849 | *: "Duration" | ||
15850 | </voice> | ||
15851 | </phrase> | ||
15852 | <phrase> | ||
15853 | id: LANG_PROPERTIES_SUBDIRS | ||
15854 | desc: in properties plugin | ||
15855 | user: core | ||
15856 | <source> | ||
15857 | *: "[Subdirs]" | ||
15858 | </source> | ||
15859 | <dest> | ||
15860 | *: "[Subdirs]" | ||
15861 | </dest> | ||
15862 | <voice> | ||
15863 | *: "Subdirs" | ||
15864 | </voice> | ||
15865 | </phrase> | ||
15866 | <phrase> | ||
15867 | id: LANG_PROPERTIES_FILES | ||
15868 | desc: in properties plugin | ||
15869 | user: core | ||
15870 | <source> | ||
15871 | *: "[Files]" | ||
15872 | </source> | ||
15873 | <dest> | ||
15874 | *: "[Files]" | ||
15875 | </dest> | ||
15876 | <voice> | ||
15877 | *: "Files" | ||
15878 | </voice> | ||
15879 | </phrase> | ||
15880 | <phrase> | ||
15881 | id: LANG_PROPERTIES_DIRECTORY_PROPERTIES | ||
15882 | desc: in properties plugin | ||
15883 | user: core | ||
15884 | <source> | ||
15885 | *: "Directory properties" | ||
15886 | </source> | ||
15887 | <dest> | ||
15888 | *: "Directory properties" | ||
15889 | </dest> | ||
15890 | <voice> | ||
15891 | *: "" | ||
15892 | </voice> | ||
15893 | </phrase> | ||
15894 | <phrase> | ||
15895 | id: LANG_PROPERTIES_FILE_PROPERTIES | ||
15896 | desc: in properties plugin | ||
15897 | user: core | ||
15898 | <source> | ||
15899 | *: "File properties" | ||
15900 | </source> | ||
15901 | <dest> | ||
15902 | *: "File properties" | ||
15903 | </dest> | ||
15904 | <voice> | ||
15905 | *: "" | ||
15906 | </voice> | ||
15907 | </phrase> | ||
15908 | <phrase> | ||
15909 | id: LANG_PROPERTIES_FAIL | ||
15910 | desc: in properties plugin | ||
15911 | user: core | ||
15912 | <source> | ||
15913 | *: "Failed to gather information" | ||
15914 | </source> | ||
15915 | <dest> | ||
15916 | *: "Failed to gather information" | ||
15917 | </dest> | ||
15918 | <voice> | ||
15919 | *: "Failed to gather information" | ||
15920 | </voice> | ||
15921 | </phrase> | ||
diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c index 3bddc24d69..686b4d6d1a 100644 --- a/apps/plugins/properties.c +++ b/apps/plugins/properties.c | |||
@@ -37,32 +37,44 @@ char str_artist[MAX_PATH]; | |||
37 | char str_album[MAX_PATH]; | 37 | char str_album[MAX_PATH]; |
38 | char str_duration[32]; | 38 | char str_duration[32]; |
39 | 39 | ||
40 | unsigned nseconds; | ||
41 | unsigned long nsize; | ||
42 | int32_t size_unit; | ||
43 | struct tm tm; | ||
44 | |||
40 | int num_properties; | 45 | int num_properties; |
41 | 46 | ||
42 | static const char* props_file[] = | 47 | static const unsigned char* const props_file[] = |
43 | { | 48 | { |
44 | "[Path]", str_dirname, | 49 | ID2P(LANG_PROPERTIES_PATH), str_dirname, |
45 | "[Filename]", str_filename, | 50 | ID2P(LANG_PROPERTIES_FILENAME), str_filename, |
46 | "[Size]", str_size, | 51 | ID2P(LANG_PROPERTIES_SIZE), str_size, |
47 | "[Date]", str_date, | 52 | ID2P(LANG_PROPERTIES_DATE), str_date, |
48 | "[Time]", str_time, | 53 | ID2P(LANG_PROPERTIES_TIME), str_time, |
49 | "[Artist]", str_artist, | 54 | ID2P(LANG_PROPERTIES_ARTIST), str_artist, |
50 | "[Title]", str_title, | 55 | ID2P(LANG_PROPERTIES_TITLE), str_title, |
51 | "[Album]", str_album, | 56 | ID2P(LANG_PROPERTIES_ALBUM), str_album, |
52 | "[Duration]", str_duration, | 57 | ID2P(LANG_PROPERTIES_DURATION), str_duration, |
53 | }; | 58 | }; |
54 | static const char* props_dir[] = | 59 | static const unsigned char* const props_dir[] = |
55 | { | 60 | { |
56 | "[Path]", str_dirname, | 61 | ID2P(LANG_PROPERTIES_PATH), str_dirname, |
57 | "[Subdirs]", str_dircount, | 62 | ID2P(LANG_PROPERTIES_SUBDIRS), str_dircount, |
58 | "[Files]", str_filecount, | 63 | ID2P(LANG_PROPERTIES_FILES), str_filecount, |
59 | "[Size]", str_size, | 64 | ID2P(LANG_PROPERTIES_SIZE), str_size, |
65 | }; | ||
66 | |||
67 | static const int32_t units[] = | ||
68 | { | ||
69 | LANG_BYTE, | ||
70 | LANG_KIBIBYTE, | ||
71 | LANG_MEBIBYTE, | ||
72 | LANG_GIBIBYTE | ||
60 | }; | 73 | }; |
61 | 74 | ||
62 | static const char human_size_prefix[4] = { '\0', 'K', 'M', 'G' }; | ||
63 | static unsigned human_size_log(unsigned long long size) | 75 | static unsigned human_size_log(unsigned long long size) |
64 | { | 76 | { |
65 | const size_t n = sizeof(human_size_prefix)/sizeof(human_size_prefix[0]); | 77 | const size_t n = sizeof(units)/sizeof(units[0]); |
66 | 78 | ||
67 | unsigned i; | 79 | unsigned i; |
68 | /* margin set at 10K boundary: 10239 B +1 => 10 KB */ | 80 | /* margin set at 10K boundary: 10239 B +1 => 10 KB */ |
@@ -89,9 +101,9 @@ static bool file_properties(const char* selected_file) | |||
89 | { | 101 | { |
90 | unsigned log; | 102 | unsigned log; |
91 | log = human_size_log((unsigned long)info.size); | 103 | log = human_size_log((unsigned long)info.size); |
92 | rb->snprintf(str_size, sizeof str_size, "%lu %cB", | 104 | nsize = ((unsigned long)info.size) >> (log*10); |
93 | ((unsigned long)info.size) >> (log*10), human_size_prefix[log]); | 105 | size_unit = units[log]; |
94 | struct tm tm; | 106 | rb->snprintf(str_size, sizeof str_size, "%lu %s", nsize, rb->str(size_unit)); |
95 | rb->gmtime_r(&info.mtime, &tm); | 107 | rb->gmtime_r(&info.mtime, &tm); |
96 | rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d", | 108 | rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d", |
97 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); | 109 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); |
@@ -119,6 +131,7 @@ static bool file_properties(const char* selected_file) | |||
119 | 131 | ||
120 | if (dur > 0) | 132 | if (dur > 0) |
121 | { | 133 | { |
134 | nseconds = dur; | ||
122 | if (dur < 3600) | 135 | if (dur < 3600) |
123 | rb->snprintf(str_duration, sizeof str_duration, | 136 | rb->snprintf(str_duration, sizeof str_duration, |
124 | "%d:%02d", (int)(dur / 60), | 137 | "%d:%02d", (int)(dur / 60), |
@@ -181,8 +194,6 @@ static bool _dir_properties(DPS *dps) | |||
181 | 194 | ||
182 | if (info.attribute & ATTR_DIRECTORY) | 195 | if (info.attribute & ATTR_DIRECTORY) |
183 | { | 196 | { |
184 | unsigned log; | ||
185 | |||
186 | if (!rb->strcmp((char *)entry->d_name, ".") || | 197 | if (!rb->strcmp((char *)entry->d_name, ".") || |
187 | !rb->strcmp((char *)entry->d_name, "..")) | 198 | !rb->strcmp((char *)entry->d_name, "..")) |
188 | continue; /* skip these */ | 199 | continue; /* skip these */ |
@@ -190,16 +201,16 @@ static bool _dir_properties(DPS *dps) | |||
190 | dps->dc++; /* new directory */ | 201 | dps->dc++; /* new directory */ |
191 | if (*rb->current_tick - lasttick > (HZ/8)) | 202 | if (*rb->current_tick - lasttick > (HZ/8)) |
192 | { | 203 | { |
204 | unsigned log = human_size_log(dps->bc); | ||
193 | lasttick = *rb->current_tick; | 205 | lasttick = *rb->current_tick; |
194 | rb->lcd_clear_display(); | 206 | rb->lcd_clear_display(); |
195 | rb->lcd_puts(0,0,"SCANNING..."); | 207 | rb->lcd_puts(0,0,"SCANNING..."); |
196 | rb->lcd_puts(0,1,dps->dirname); | 208 | rb->lcd_puts(0,1,dps->dirname); |
197 | rb->lcd_puts(0,2,entry->d_name); | 209 | rb->lcd_putsf(0,2,"Directories: %d", dps->dc); |
198 | rb->lcd_putsf(0,3,"Directories: %d", dps->dc); | 210 | rb->lcd_putsf(0,3,"Files: %d", dps->fc); |
199 | rb->lcd_putsf(0,4,"Files: %d", dps->fc); | ||
200 | log = human_size_log(dps->bc); | 211 | log = human_size_log(dps->bc); |
201 | rb->lcd_putsf(0,5,"Size: %lu %cB", (unsigned long)(dps->bc >> (10*log)), | 212 | rb->lcd_putsf(0,4,"Size: %lu %cB", (unsigned long)(dps->bc >> (10*log)), |
202 | human_size_prefix[log]); | 213 | rb->str(units[log])); |
203 | rb->lcd_update(); | 214 | rb->lcd_update(); |
204 | } | 215 | } |
205 | 216 | ||
@@ -245,12 +256,19 @@ static bool dir_properties(const char* selected_file, DPS *dps) | |||
245 | rb->snprintf(str_dircount, sizeof str_dircount, "%d", dps->dc); | 256 | rb->snprintf(str_dircount, sizeof str_dircount, "%d", dps->dc); |
246 | rb->snprintf(str_filecount, sizeof str_filecount, "%d", dps->fc); | 257 | rb->snprintf(str_filecount, sizeof str_filecount, "%d", dps->fc); |
247 | log = human_size_log(dps->bc); | 258 | log = human_size_log(dps->bc); |
248 | rb->snprintf(str_size, sizeof str_size, "%ld %cB", | 259 | nsize = (long) (dps->bc >> (log*10)); |
249 | (long) (dps->bc >> (log*10)), human_size_prefix[log]); | 260 | size_unit = units[log]; |
261 | rb->snprintf(str_size, sizeof str_size, "%ld %s", nsize, rb->str(size_unit)); | ||
250 | num_properties = 4; | 262 | num_properties = 4; |
251 | return true; | 263 | return true; |
252 | } | 264 | } |
253 | 265 | ||
266 | static const unsigned char* p2str(const unsigned char* p) | ||
267 | { | ||
268 | int id = P2ID(p); | ||
269 | return (id != -1) ? rb->str(id) : p; | ||
270 | } | ||
271 | |||
254 | static const char * get_props(int selected_item, void* data, | 272 | static const char * get_props(int selected_item, void* data, |
255 | char *buffer, size_t buffer_len) | 273 | char *buffer, size_t buffer_len) |
256 | { | 274 | { |
@@ -263,7 +281,7 @@ static const char * get_props(int selected_item, void* data, | |||
263 | } | 281 | } |
264 | else | 282 | else |
265 | { | 283 | { |
266 | rb->strlcpy(buffer, props_dir[selected_item], buffer_len); | 284 | rb->strlcpy(buffer, p2str(props_dir[selected_item]), buffer_len); |
267 | } | 285 | } |
268 | } | 286 | } |
269 | else | 287 | else |
@@ -274,12 +292,69 @@ static const char * get_props(int selected_item, void* data, | |||
274 | } | 292 | } |
275 | else | 293 | else |
276 | { | 294 | { |
277 | rb->strlcpy(buffer, props_file[selected_item], buffer_len); | 295 | rb->strlcpy(buffer, p2str(props_file[selected_item]), buffer_len); |
278 | } | 296 | } |
279 | } | 297 | } |
280 | return buffer; | 298 | return buffer; |
281 | } | 299 | } |
282 | 300 | ||
301 | static int speak_property_selection(int selected_item, void *data) | ||
302 | { | ||
303 | DPS *dps = data; | ||
304 | int32_t id = P2ID((its_a_dir ? props_dir : props_file)[selected_item]); | ||
305 | rb->talk_id(id, false); | ||
306 | switch (id) | ||
307 | { | ||
308 | case LANG_PROPERTIES_PATH: | ||
309 | if (str_dirname[0] == '/') | ||
310 | { | ||
311 | char *start = str_dirname; | ||
312 | char *ptr; | ||
313 | while (0 != (ptr = rb->strchr(start, '/'))) | ||
314 | { | ||
315 | *ptr = '\0'; | ||
316 | rb->talk_dir_or_spell(str_dirname, NULL, true); | ||
317 | *ptr = '/'; | ||
318 | rb->talk_id(VOICE_CHAR_SLASH, true); | ||
319 | start = ptr + 1; | ||
320 | } | ||
321 | if (*start) | ||
322 | rb->talk_dir_or_spell(str_dirname, NULL, true); | ||
323 | } | ||
324 | else | ||
325 | { | ||
326 | rb->talk_spell(str_dirname, true); | ||
327 | } | ||
328 | break; | ||
329 | case LANG_PROPERTIES_FILENAME: | ||
330 | rb->talk_file_or_spell(str_dirname, str_filename, NULL, true); | ||
331 | break; | ||
332 | case LANG_PROPERTIES_SIZE: | ||
333 | rb->talk_number(nsize, true); | ||
334 | rb->talk_id(size_unit, true); | ||
335 | break; | ||
336 | case LANG_PROPERTIES_DATE: | ||
337 | rb->talk_date(&tm, true); | ||
338 | break; | ||
339 | case LANG_PROPERTIES_TIME: | ||
340 | rb->talk_time(&tm, true); | ||
341 | break; | ||
342 | case LANG_PROPERTIES_DURATION: | ||
343 | rb->talk_value(nseconds, UNIT_TIME, true); | ||
344 | break; | ||
345 | case LANG_PROPERTIES_SUBDIRS: | ||
346 | rb->talk_number(dps->dc, true); | ||
347 | break; | ||
348 | case LANG_PROPERTIES_FILES: | ||
349 | rb->talk_number(dps->fc, true); | ||
350 | break; | ||
351 | default: | ||
352 | rb->talk_spell(props_file[selected_item + 1], true); | ||
353 | break; | ||
354 | } | ||
355 | return 0; | ||
356 | } | ||
357 | |||
283 | enum plugin_status plugin_start(const void* parameter) | 358 | enum plugin_status plugin_start(const void* parameter) |
284 | { | 359 | { |
285 | struct gui_synclist properties_lists; | 360 | struct gui_synclist properties_lists; |
@@ -337,7 +412,7 @@ enum plugin_status plugin_start(const void* parameter) | |||
337 | if(!(its_a_dir ? dir_properties(file, &dps) : file_properties(file))) | 412 | if(!(its_a_dir ? dir_properties(file, &dps) : file_properties(file))) |
338 | { | 413 | { |
339 | /* something went wrong (to do: tell user what it was (nesting,...) */ | 414 | /* something went wrong (to do: tell user what it was (nesting,...) */ |
340 | rb->splash(0, "Failed to gather information"); | 415 | rb->splash(0, ID2P(LANG_PROPERTIES_FAIL)); |
341 | rb->action_userabort(TIMEOUT_BLOCK); | 416 | rb->action_userabort(TIMEOUT_BLOCK); |
342 | return PLUGIN_OK; | 417 | return PLUGIN_OK; |
343 | } | 418 | } |
@@ -348,14 +423,15 @@ enum plugin_status plugin_start(const void* parameter) | |||
348 | #endif | 423 | #endif |
349 | 424 | ||
350 | rb->gui_synclist_init(&properties_lists, &get_props, &dps, false, 2, NULL); | 425 | rb->gui_synclist_init(&properties_lists, &get_props, &dps, false, 2, NULL); |
351 | rb->gui_synclist_set_title(&properties_lists, its_a_dir ? | 426 | rb->gui_synclist_set_title(&properties_lists, rb->str(its_a_dir ? LANG_PROPERTIES_DIRECTORY_PROPERTIES : LANG_PROPERTIES_FILE_PROPERTIES), NOICON); |
352 | "Directory properties" : | ||
353 | "File properties", NOICON); | ||
354 | rb->gui_synclist_set_icon_callback(&properties_lists, NULL); | 427 | rb->gui_synclist_set_icon_callback(&properties_lists, NULL); |
428 | if (rb->global_settings->talk_menu) | ||
429 | rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection); | ||
355 | rb->gui_synclist_set_nb_items(&properties_lists, num_properties * 2); | 430 | rb->gui_synclist_set_nb_items(&properties_lists, num_properties * 2); |
356 | rb->gui_synclist_limit_scroll(&properties_lists, true); | 431 | rb->gui_synclist_limit_scroll(&properties_lists, true); |
357 | rb->gui_synclist_select_item(&properties_lists, 0); | 432 | rb->gui_synclist_select_item(&properties_lists, 0); |
358 | rb->gui_synclist_draw(&properties_lists); | 433 | rb->gui_synclist_draw(&properties_lists); |
434 | rb->gui_synclist_speak_item(&properties_lists); | ||
359 | 435 | ||
360 | while(!quit) | 436 | while(!quit) |
361 | { | 437 | { |